diff --git a/.clang-format b/.clang-format index a65a29f8c995..4bd962747fa4 100644 --- a/.clang-format +++ b/.clang-format @@ -25,6 +25,9 @@ CommentPragmas: '\$(FRR|clippy)' ContinuationIndentWidth: 8 ForEachMacros: # lib + - frr_each + - frr_each_safe + - frr_each_from - LIST_FOREACH - LIST_FOREACH_SAFE - SLIST_FOREACH diff --git a/.gitignore b/.gitignore index 5003c97572b7..6cfe23e92147 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ *.pb.h *.pb-c.h *.pb-c.c +*.pb.cc *_clippy.c ### dist @@ -87,5 +88,7 @@ GSYMS GRTAGS GPATH compile_commands.json +.ccls-cache .dirstamp refix +.vscode diff --git a/Makefile.am b/Makefile.am index 9c6c8663ee53..166ac29d1c7e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,7 +12,9 @@ AM_CFLAGS = \ # end AM_CPPFLAGS = \ -I$(top_srcdir) -I$(top_srcdir)/include -I$(top_srcdir)/lib \ - -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/lib + -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/lib \ + $(LUA_INCLUDE) \ + # end AM_LDFLAGS = \ -export-dynamic \ $(AC_LDFLAGS) \ @@ -96,7 +98,6 @@ noinst_LIBRARIES = nodist_noinst_DATA = lib_LTLIBRARIES = module_LTLIBRARIES = -libyang_plugins_LTLIBRARIES = pkginclude_HEADERS = nodist_pkginclude_HEADERS = dist_examples_DATA = @@ -111,7 +112,6 @@ vtysh_scan = $(AUTOMAKE_DUMMY)install-moduleLTLIBRARIES: install-libLTLIBRARIES $(AUTOMAKE_DUMMY)install-binPROGRAMS: install-libLTLIBRARIES $(AUTOMAKE_DUMMY)install-sbinPROGRAMS: install-libLTLIBRARIES -$(AUTOMAKE_DUMMY)install-libyang_pluginsLTLIBRARIES: install-libLTLIBRARIES include doc/subdir.am include doc/user/subdir.am @@ -123,6 +123,7 @@ include zebra/subdir.am include watchfrr/subdir.am include qpb/subdir.am include fpm/subdir.am +include grpc/subdir.am include tools/subdir.am include solaris/subdir.am @@ -146,6 +147,7 @@ include staticd/subdir.am include bfdd/subdir.am include yang/subdir.am include yang/libyang_plugins/subdir.am +include vrrpd/subdir.am include vtysh/subdir.am include tests/subdir.am @@ -187,7 +189,6 @@ EXTRA_DIST += \ snapcraft/defaults \ snapcraft/helpers \ snapcraft/snap \ - \ babeld/Makefile \ bgpd/Makefile \ bgpd/rfp-example/librfp/Makefile \ @@ -198,6 +199,7 @@ EXTRA_DIST += \ doc/user/Makefile \ eigrpd/Makefile \ fpm/Makefile \ + grpc/Makefile \ isisd/Makefile \ ldpd/Makefile \ lib/Makefile \ @@ -216,6 +218,7 @@ EXTRA_DIST += \ vtysh/Makefile \ watchfrr/Makefile \ zebra/Makefile \ + vrrpd/Makefile \ # end noinst_HEADERS += defaults.h diff --git a/babeld/babel_interface.c b/babeld/babel_interface.c index 0ff89abc495a..b84bc39cd8df 100644 --- a/babeld/babel_interface.c +++ b/babeld/babel_interface.c @@ -66,7 +66,7 @@ static struct cmd_node babel_interface_node = /* babeld's interface node. */ int -babel_interface_up (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf) +babel_interface_up (ZAPI_CALLBACK_ARGS) { struct stream *s = NULL; struct interface *ifp = NULL; @@ -74,7 +74,7 @@ babel_interface_up (int cmd, struct zclient *client, zebra_size_t length, vrf_id debugf(BABEL_DEBUG_IF, "receive a 'interface up'"); s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf); /* it updates iflist */ + ifp = zebra_interface_state_read(s, vrf_id); /* it updates iflist */ if (ifp == NULL) { return 0; @@ -85,7 +85,7 @@ babel_interface_up (int cmd, struct zclient *client, zebra_size_t length, vrf_id } int -babel_interface_down (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf) +babel_interface_down (ZAPI_CALLBACK_ARGS) { struct stream *s = NULL; struct interface *ifp = NULL; @@ -93,7 +93,7 @@ babel_interface_down (int cmd, struct zclient *client, zebra_size_t length, vrf_ debugf(BABEL_DEBUG_IF, "receive a 'interface down'"); s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf); /* it updates iflist */ + ifp = zebra_interface_state_read(s, vrf_id); /* it updates iflist */ if (ifp == NULL) { return 0; @@ -104,14 +104,14 @@ babel_interface_down (int cmd, struct zclient *client, zebra_size_t length, vrf_ } int -babel_interface_add (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf) +babel_interface_add (ZAPI_CALLBACK_ARGS) { struct interface *ifp = NULL; debugf(BABEL_DEBUG_IF, "receive a 'interface add'"); /* read and add the interface in the iflist. */ - ifp = zebra_interface_add_read (zclient->ibuf, vrf); + ifp = zebra_interface_add_read (zclient->ibuf, vrf_id); if (ifp == NULL) { return 0; @@ -122,7 +122,7 @@ babel_interface_add (int cmd, struct zclient *client, zebra_size_t length, vrf_i } int -babel_interface_delete (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf) +babel_interface_delete (ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -130,7 +130,7 @@ babel_interface_delete (int cmd, struct zclient *client, zebra_size_t length, vr debugf(BABEL_DEBUG_IF, "receive a 'interface delete'"); s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf); /* it updates iflist */ + ifp = zebra_interface_state_read(s, vrf_id); /* it updates iflist */ if (ifp == NULL) return 0; @@ -146,8 +146,7 @@ babel_interface_delete (int cmd, struct zclient *client, zebra_size_t length, vr } int -babel_interface_address_add (int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf) +babel_interface_address_add (ZAPI_CALLBACK_ARGS) { babel_interface_nfo *babel_ifp; struct connected *ifc; @@ -156,7 +155,7 @@ babel_interface_address_add (int cmd, struct zclient *client, debugf(BABEL_DEBUG_IF, "receive a 'interface address add'"); ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD, - zclient->ibuf, vrf); + zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -183,8 +182,7 @@ babel_interface_address_add (int cmd, struct zclient *client, } int -babel_interface_address_delete (int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf) +babel_interface_address_delete (ZAPI_CALLBACK_ARGS) { babel_interface_nfo *babel_ifp; struct connected *ifc; @@ -193,7 +191,7 @@ babel_interface_address_delete (int cmd, struct zclient *client, debugf(BABEL_DEBUG_IF, "receive a 'interface address delete'"); ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE, - zclient->ibuf, vrf); + zclient->ibuf, vrf_id); if (ifc == NULL) return 0; diff --git a/babeld/babel_main.c b/babeld/babel_main.c index eaff97a49565..a3f2b4e7d846 100644 --- a/babeld/babel_main.c +++ b/babeld/babel_main.c @@ -68,7 +68,7 @@ const unsigned char ones[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -static const char *state_file = DAEMON_VTY_DIR "/babel-state"; +static char state_file[1024]; unsigned char protocol_group[16]; /* babel's link-local multicast address */ int protocol_port; /* babel's port */ @@ -187,6 +187,9 @@ main(int argc, char **argv) } } + snprintf(state_file, sizeof(state_file), "%s/%s", + frr_vtydir, "babel-state"); + /* create the threads handler */ master = frr_init (); diff --git a/babeld/babel_zebra.c b/babeld/babel_zebra.c index e909f8ea7a4e..d70823544af2 100644 --- a/babeld/babel_zebra.c +++ b/babeld/babel_zebra.c @@ -56,8 +56,7 @@ static struct { /* Zebra route add and delete treatment. */ static int -babel_zebra_read_route (int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf) +babel_zebra_read_route (ZAPI_CALLBACK_ARGS) { struct zapi_route api; @@ -68,7 +67,7 @@ babel_zebra_read_route (int command, struct zclient *zclient, if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) return 0; - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { babel_route_add(&api); } else { babel_route_delete(&api); diff --git a/babeld/message.c b/babeld/message.c index 794b6e997609..d88790824cc2 100644 --- a/babeld/message.c +++ b/babeld/message.c @@ -1115,7 +1115,9 @@ really_send_update(struct interface *ifp, if(channels_len >= 0) { accumulate_byte(ifp, 2); accumulate_byte(ifp, channels_len); - accumulate_bytes(ifp, channels, channels_len); + + if (channels && channels_len > 0) + accumulate_bytes(ifp, channels, channels_len); } end_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit + channels_size); diff --git a/bfdd/bfd.c b/bfdd/bfd.c index e9645824f283..f013f6f35011 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -128,29 +128,32 @@ int bfd_session_enable(struct bfd_session *bs) * If the interface or VRF doesn't exist, then we must register * the session but delay its start. */ - if (bs->key.ifname[0]) { - ifp = if_lookup_by_name_all_vrf(bs->key.ifname); - if (ifp == NULL) { - log_error( - "session-enable: specified interface doesn't exists."); - return 0; - } - - vrf = vrf_lookup_by_id(ifp->vrf_id); + if (bs->key.vrfname[0]) { + vrf = vrf_lookup_by_name(bs->key.vrfname); if (vrf == NULL) { log_error( "session-enable: specified VRF doesn't exists."); return 0; } - } - - if (bs->key.vrfname[0]) { - vrf = vrf_lookup_by_name(bs->key.vrfname); - if (vrf == NULL) { + } else + vrf = vrf_lookup_by_id(VRF_DEFAULT); + if (!vrf) + return 0; + if (bs->key.ifname[0]) { + ifp = if_lookup_by_name(bs->key.ifname, vrf->vrf_id); + if (ifp == NULL) { log_error( - "session-enable: specified VRF doesn't exists."); + "session-enable: specified interface doesn't exists."); return 0; } + if (bs->key.ifname[0] && !vrf) { + vrf = vrf_lookup_by_id(ifp->vrf_id); + if (vrf == NULL) { + log_error( + "session-enable: specified VRF doesn't exists."); + return 0; + } + } } /* Assign interface/VRF pointers. */ @@ -164,7 +167,7 @@ int bfd_session_enable(struct bfd_session *bs) /* Sanity check: don't leak open sockets. */ if (bs->sock != -1) { - zlog_debug("session-enable: previous socket open"); + log_debug("session-enable: previous socket open"); close(bs->sock); bs->sock = -1; } @@ -291,7 +294,7 @@ void ptm_bfd_echo_start(struct bfd_session *bfd) ptm_bfd_echo_xmt_TO(bfd); } -void ptm_bfd_ses_up(struct bfd_session *bfd) +void ptm_bfd_sess_up(struct bfd_session *bfd) { int old_state = bfd->ses_state; @@ -315,7 +318,7 @@ void ptm_bfd_ses_up(struct bfd_session *bfd) } } -void ptm_bfd_ses_dn(struct bfd_session *bfd, uint8_t diag) +void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag) { int old_state = bfd->ses_state; @@ -432,7 +435,7 @@ int bfd_recvtimer_cb(struct thread *t) switch (bs->ses_state) { case PTM_BFD_INIT: case PTM_BFD_UP: - ptm_bfd_ses_dn(bs, BD_CONTROL_EXPIRED); + ptm_bfd_sess_dn(bs, BD_CONTROL_EXPIRED); bfd_recvtimer_update(bs); break; @@ -455,7 +458,7 @@ int bfd_echo_recvtimer_cb(struct thread *t) switch (bs->ses_state) { case PTM_BFD_INIT: case PTM_BFD_UP: - ptm_bfd_ses_dn(bs, BD_ECHO_FAILED); + ptm_bfd_sess_dn(bs, BD_ECHO_FAILED); break; } @@ -596,6 +599,17 @@ static void _bfd_session_update(struct bfd_session *bs, bfd_recvtimer_update(bs); bfd_xmttimer_update(bs, bs->xmt_TO); } + if (bpc->bpc_cbit) { + if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CBIT)) + return; + + BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT); + } else { + if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CBIT)) + return; + + BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT); + } } static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc) @@ -725,7 +739,7 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc) return bfd; } -int ptm_bfd_ses_del(struct bfd_peer_cfg *bpc) +int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc) { struct bfd_session *bs; @@ -805,7 +819,7 @@ static void bs_down_handler(struct bfd_session *bs, int nstate) * Remote peer told us his path is up, lets turn * activate the session. */ - ptm_bfd_ses_up(bs); + ptm_bfd_sess_up(bs); break; default: @@ -832,7 +846,7 @@ static void bs_init_handler(struct bfd_session *bs, int nstate) case PTM_BFD_INIT: case PTM_BFD_UP: /* We agreed on the settings and the path is up. */ - ptm_bfd_ses_up(bs); + ptm_bfd_sess_up(bs); break; default: @@ -847,7 +861,7 @@ static void bs_up_handler(struct bfd_session *bs, int nstate) case PTM_BFD_ADM_DOWN: case PTM_BFD_DOWN: /* Peer lost or asked to shutdown connection. */ - ptm_bfd_ses_dn(bs, BD_NEIGHBOR_DOWN); + ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN); break; case PTM_BFD_INIT: @@ -1197,10 +1211,6 @@ int bs_observer_add(struct bfd_session *bs) if (bso->bso_isinterface) strlcpy(bso->bso_entryname, bs->key.ifname, sizeof(bso->bso_entryname)); - else - strlcpy(bso->bso_entryname, bs->key.vrfname, - sizeof(bso->bso_entryname)); - /* Handle socket binding failures caused by missing local addresses. */ if (bs->sock == -1) { bso->bso_isaddress = true; @@ -1271,16 +1281,16 @@ 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; -static unsigned int bfd_id_hash_do(void *p); -static unsigned int bfd_key_hash_do(void *p); +static unsigned int bfd_id_hash_do(const void *p); +static unsigned int bfd_key_hash_do(const void *p); static void _bfd_free(struct hash_bucket *hb, void *arg __attribute__((__unused__))); /* BFD hash for our discriminator. */ -static unsigned int bfd_id_hash_do(void *p) +static unsigned int bfd_id_hash_do(const void *p) { - struct bfd_session *bs = p; + const struct bfd_session *bs = p; return jhash_1word(bs->discrs.my_discr, 0); } @@ -1293,9 +1303,9 @@ static bool bfd_id_hash_cmp(const void *n1, const void *n2) } /* BFD hash for single hop. */ -static unsigned int bfd_key_hash_do(void *p) +static unsigned int bfd_key_hash_do(const void *p) { - struct bfd_session *bs = p; + const struct bfd_session *bs = p; return jhash(&bs->key, sizeof(bs->key), 0); } @@ -1322,32 +1332,105 @@ struct bfd_session *bfd_id_lookup(uint32_t id) return hash_lookup(bfd_id_hash, &bs); } +struct bfd_key_walk_partial_lookup { + struct bfd_session *given; + struct bfd_session *result; +}; + +/* ignore some parameters */ +static int bfd_key_lookup_ignore_partial_walker(struct hash_bucket *b, void *data) +{ + struct bfd_key_walk_partial_lookup *ctx = + (struct bfd_key_walk_partial_lookup *)data; + struct bfd_session *given = ctx->given; + struct bfd_session *parsed = b->data; + + if (given->key.family != parsed->key.family) + return HASHWALK_CONTINUE; + if (given->key.mhop != parsed->key.mhop) + return HASHWALK_CONTINUE; + if (memcmp(&given->key.peer, &parsed->key.peer, sizeof(struct in6_addr))) + return HASHWALK_CONTINUE; + if (memcmp(given->key.vrfname, parsed->key.vrfname, MAXNAMELEN)) + return HASHWALK_CONTINUE; + ctx->result = parsed; + /* ignore localaddr or interface */ + return HASHWALK_ABORT; +} + struct bfd_session *bfd_key_lookup(struct bfd_key key) { struct bfd_session bs, *bsp; + struct bfd_key_walk_partial_lookup ctx; + char peer_buf[INET6_ADDRSTRLEN]; bs.key = key; bsp = hash_lookup(bfd_key_hash, &bs); + if (bsp) + return bsp; + inet_ntop(bs.key.family, &bs.key.peer, peer_buf, + sizeof(peer_buf)); /* Handle cases where local-address is optional. */ - if (bsp == NULL && bs.key.family == AF_INET) { + if (bs.key.family == AF_INET) { memset(&bs.key.local, 0, sizeof(bs.key.local)); bsp = hash_lookup(bfd_key_hash, &bs); + if (bsp) { + char addr_buf[INET6_ADDRSTRLEN]; + + inet_ntop(bs.key.family, &key.local, addr_buf, + sizeof(addr_buf)); + log_debug(" peer %s found, but loc-addr %s ignored", + peer_buf, addr_buf); + return bsp; + } } - /* Handle cases where ifname is optional. */ bs.key = key; - if (bsp == NULL && bs.key.ifname[0]) { + /* Handle cases where ifname is optional. */ + if (bs.key.ifname[0]) { memset(bs.key.ifname, 0, sizeof(bs.key.ifname)); bsp = hash_lookup(bfd_key_hash, &bs); + if (bsp) { + log_debug(" peer %s found, but ifp %s ignored", + peer_buf, key.ifname); + return bsp; + } + } - /* Handle cases where local-address and ifname are optional. */ - if (bsp == NULL && bs.key.family == AF_INET) { - memset(&bs.key.local, 0, sizeof(bs.key.local)); - bsp = hash_lookup(bfd_key_hash, &bs); + /* Handle cases where local-address and ifname are optional. */ + if (bs.key.family == AF_INET) { + memset(&bs.key.local, 0, sizeof(bs.key.local)); + bsp = hash_lookup(bfd_key_hash, &bs); + if (bsp) { + char addr_buf[INET6_ADDRSTRLEN]; + + inet_ntop(bs.key.family, &bs.key.local, addr_buf, + sizeof(addr_buf)); + log_debug(" peer %s found, but ifp %s" + " and loc-addr %s ignored", + peer_buf, key.ifname, + addr_buf); + return bsp; } } + bs.key = key; + /* Handle case where a context more complex ctx is present. + * input has no iface nor local-address, but a context may + * exist + */ + ctx.result = NULL; + ctx.given = &bs; + hash_walk(bfd_key_hash, + &bfd_key_lookup_ignore_partial_walker, + &ctx); + /* change key */ + if (ctx.result) { + bsp = ctx.result; + log_debug(" peer %s found, but ifp" + " and/or loc-addr params ignored"); + } return bsp; } @@ -1443,3 +1526,125 @@ void bfd_shutdown(void) hash_free(bfd_id_hash); hash_free(bfd_key_hash); } + +static int bfd_vrf_new(struct vrf *vrf) +{ + log_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id); + return 0; +} + +static int bfd_vrf_delete(struct vrf *vrf) +{ + log_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id); + return 0; +} + +static int bfd_vrf_enable(struct vrf *vrf) +{ + struct bfd_vrf_global *bvrf; + + /* a different name */ + if (!vrf->info) { + bvrf = XCALLOC(MTYPE_BFDD_VRF, sizeof(struct bfd_vrf_global)); + bvrf->vrf = vrf; + vrf->info = (void *)bvrf; + } else + bvrf = vrf->info; + log_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id); + + /* create sockets if needed */ + if (!bvrf->bg_shop) + bvrf->bg_shop = bp_udp_shop(vrf->vrf_id); + if (!bvrf->bg_mhop) + bvrf->bg_mhop = bp_udp_mhop(vrf->vrf_id); + if (!bvrf->bg_shop6) + bvrf->bg_shop6 = bp_udp6_shop(vrf->vrf_id); + if (!bvrf->bg_mhop6) + bvrf->bg_mhop6 = bp_udp6_mhop(vrf->vrf_id); + if (!bvrf->bg_echo) + bvrf->bg_echo = bp_echo_socket(vrf->vrf_id); + if (!bvrf->bg_echov6) + bvrf->bg_echov6 = bp_echov6_socket(vrf->vrf_id); + + /* Add descriptors to the event loop. */ + if (!bvrf->bg_ev[0]) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop, + &bvrf->bg_ev[0]); + if (!bvrf->bg_ev[1]) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop, + &bvrf->bg_ev[1]); + if (!bvrf->bg_ev[2]) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6, + &bvrf->bg_ev[2]); + if (!bvrf->bg_ev[3]) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6, + &bvrf->bg_ev[3]); + if (!bvrf->bg_ev[4]) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo, + &bvrf->bg_ev[4]); + if (!bvrf->bg_ev[5]) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6, + &bvrf->bg_ev[5]); + + if (vrf->vrf_id != VRF_DEFAULT) { + bfdd_zclient_register(vrf->vrf_id); + bfdd_sessions_enable_vrf(vrf); + } + return 0; +} + +static int bfd_vrf_disable(struct vrf *vrf) +{ + struct bfd_vrf_global *bvrf; + + if (!vrf->info) + return 0; + bvrf = vrf->info; + + if (vrf->vrf_id != VRF_DEFAULT) { + bfdd_sessions_disable_vrf(vrf); + bfdd_zclient_unregister(vrf->vrf_id); + } + + log_debug("VRF disable %s id %d", vrf->name, vrf->vrf_id); + /* Close all descriptors. */ + socket_close(&bvrf->bg_echo); + socket_close(&bvrf->bg_shop); + socket_close(&bvrf->bg_mhop); + socket_close(&bvrf->bg_shop6); + socket_close(&bvrf->bg_mhop6); + + /* free context */ + XFREE(MTYPE_BFDD_VRF, bvrf); + vrf->info = NULL; + + return 0; +} + +void bfd_vrf_init(void) +{ + vrf_init(bfd_vrf_new, bfd_vrf_enable, bfd_vrf_disable, + bfd_vrf_delete, NULL); +} + +void bfd_vrf_terminate(void) +{ + vrf_terminate(); +} + +struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd) +{ + struct vrf *vrf; + + if (!vrf_is_backend_netns()) { + vrf = vrf_lookup_by_id(VRF_DEFAULT); + if (vrf) + return (struct bfd_vrf_global *)vrf->info; + return NULL; + } + if (!bfd) + return NULL; + if (!bfd->vrf) + return NULL; + return bfd->vrf->info; +} diff --git a/bfdd/bfd.h b/bfdd/bfd.h index a69ff9a1a7b9..213e905bf0a8 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -48,6 +48,7 @@ DECLARE_MTYPE(BFDD_LABEL); DECLARE_MTYPE(BFDD_CONTROL); DECLARE_MTYPE(BFDD_SESSION_OBSERVER); DECLARE_MTYPE(BFDD_NOTIFICATION); +DECLARE_MTYPE(BFDD_VRF); struct bfd_timers { uint32_t desired_min_tx; @@ -128,6 +129,12 @@ struct bfd_echo_pkt { flags |= (val & 0x3) << 6; \ } #define BFD_GETSTATE(flags) ((flags >> 6) & 0x3) +#define BFD_SETCBIT(flags, val) \ + { \ + if ((val)) \ + flags |= val; \ + } +#define BFD_GETCBIT(flags) (flags & BFD_FBIT) #define BFD_ECHO_VERSION 1 #define BFD_ECHO_PKT_LEN sizeof(struct bfd_echo_pkt) @@ -166,6 +173,8 @@ enum bfd_session_flags { * expires */ BFD_SESS_FLAG_SHUTDOWN = 1 << 7, /* disable BGP peer function */ + BFD_SESS_FLAG_CONFIG = 1 << 8, /* Session configured with bfd NB API */ + BFD_SESS_FLAG_CBIT = 1 << 9, /* CBIT is set */ }; #define BFD_SET_FLAG(field, flag) (field |= flag) @@ -208,6 +217,7 @@ struct bfd_session { uint8_t detect_mult; uint8_t remote_detect_mult; uint8_t mh_ttl; + uint8_t remote_cbit; /* Timers */ struct bfd_timers timers; @@ -308,8 +318,8 @@ TAILQ_HEAD(obslist, bfd_session_observer); #define BFD_PKT_INFO_VAL 1 #define BFD_IPV6_PKT_INFO_VAL 1 #define BFD_IPV6_ONLY_VAL 1 -#define BFD_SRCPORTINIT 49142 -#define BFD_SRCPORTMAX 65536 +#define BFD_SRCPORTINIT 49152 +#define BFD_SRCPORTMAX 65535 #define BFD_DEFDESTPORT 3784 #define BFD_DEF_ECHO_PORT 3785 #define BFD_DEF_MHOP_DEST_PORT 4784 @@ -378,15 +388,19 @@ int control_accept(struct thread *t); * * Daemon specific code. */ -struct bfd_global { +struct bfd_vrf_global { int bg_shop; int bg_mhop; int bg_shop6; int bg_mhop6; int bg_echo; int bg_echov6; + struct vrf *vrf; + struct thread *bg_ev[6]; +}; +struct bfd_global { int bg_csock; struct thread *bg_csockev; struct bcslist bg_bcslist; @@ -394,6 +408,8 @@ struct bfd_global { struct pllist bg_pllist; struct obslist bg_obslist; + + struct zebra_privs_t bfdd_privs; }; extern struct bfd_global bglobal; extern struct bfd_diag_str_list diag_list[]; @@ -458,14 +474,14 @@ 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); -int bp_udp_shop(void); -int bp_udp_mhop(void); -int bp_udp6_shop(void); -int bp_udp6_mhop(void); +int bp_udp_shop(vrf_id_t vrf_id); +int bp_udp_mhop(vrf_id_t vrf_id); +int bp_udp6_shop(vrf_id_t vrf_id); +int bp_udp6_mhop(vrf_id_t vrf_id); int bp_peer_socket(const struct bfd_session *bs); int bp_peer_socketv6(const struct bfd_session *bs); -int bp_echo_socket(void); -int bp_echov6_socket(void); +int bp_echo_socket(vrf_id_t vrf_id); +int bp_echov6_socket(vrf_id_t vrf_id); void ptm_bfd_snd(struct bfd_session *bfd, int fbit); void ptm_bfd_echo_snd(struct bfd_session *bfd); @@ -504,9 +520,9 @@ void bfd_echo_xmttimer_assign(struct bfd_session *bs, bfd_ev_cb cb); int bfd_session_enable(struct bfd_session *bs); void bfd_session_disable(struct bfd_session *bs); struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc); -int ptm_bfd_ses_del(struct bfd_peer_cfg *bpc); -void ptm_bfd_ses_dn(struct bfd_session *bfd, uint8_t diag); -void ptm_bfd_ses_up(struct bfd_session *bfd); +int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc); +void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag); +void ptm_bfd_sess_up(struct bfd_session *bfd); 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); @@ -538,6 +554,9 @@ void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc); /* BFD hash data structures interface */ void bfd_initialize(void); void bfd_shutdown(void); +void bfd_vrf_init(void); +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); @@ -575,6 +594,10 @@ void bfdd_vty_init(void); */ void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv); void bfdd_zclient_stop(void); +void bfdd_zclient_unregister(vrf_id_t vrf_id); +void bfdd_zclient_register(vrf_id_t vrf_id); +void bfdd_sessions_enable_vrf(struct vrf *vrf); +void bfdd_sessions_disable_vrf(struct vrf *vrf); int ptm_bfd_notify(struct bfd_session *bs); diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index 93677ec85ace..d68a1ad5fd98 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -37,15 +37,14 @@ #include "bfd.h" - /* * Prototypes */ -static int ptm_bfd_process_echo_pkt(int s); +static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s); int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data, size_t datalen); -static void bfd_sd_reschedule(int sd); +static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd); ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, ifindex_t *ifindex, struct sockaddr_any *local, struct sockaddr_any *peer); @@ -54,7 +53,8 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, struct sockaddr_any *peer); 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(int sd, uint8_t *ttl, uint32_t *my_discr); +int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, + uint8_t *ttl, uint32_t *my_discr); /* socket related prototypes */ static void bp_set_ipopts(int sd); @@ -129,7 +129,10 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd) struct bfd_echo_pkt bep; struct sockaddr_in sin; struct sockaddr_in6 sin6; + struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd); + if (!bvrf) + return; if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE); @@ -139,7 +142,7 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd) bep.my_discr = htonl(bfd->discrs.my_discr); if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) { - sd = bglobal.bg_echov6; + sd = bvrf->bg_echov6; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; memcpy(&sin6.sin6_addr, &bfd->key.peer, sizeof(sin6.sin6_addr)); @@ -154,7 +157,7 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd) sa = (struct sockaddr *)&sin6; salen = sizeof(sin6); } else { - sd = bglobal.bg_echo; + sd = bvrf->bg_echo; memset(&sin6, 0, sizeof(sin6)); sin.sin_family = AF_INET; memcpy(&sin.sin_addr, &bfd->key.peer, sizeof(sin.sin_addr)); @@ -174,14 +177,14 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd) bfd->stats.tx_echo_pkt++; } -static int ptm_bfd_process_echo_pkt(int s) +static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s) { struct bfd_session *bfd; uint32_t my_discr = 0; uint8_t ttl = 0; /* Receive and parse echo packet. */ - if (bp_bfd_echo_in(s, &ttl, &my_discr) == -1) + if (bp_bfd_echo_in(bvrf, s, &ttl, &my_discr) == -1) return 0; /* Your discriminator not zero - use it to find session */ @@ -218,6 +221,10 @@ void ptm_bfd_snd(struct bfd_session *bfd, int fbit) BFD_SETVER(cp.diag, BFD_VERSION); cp.flags = 0; BFD_SETSTATE(cp.flags, bfd->ses_state); + + if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_CBIT)) + BFD_SETCBIT(cp.flags, BFD_CBIT); + BFD_SETDEMANDBIT(cp.flags, BFD_DEF_DEMAND); /* @@ -428,45 +435,47 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ *ifindex = pi6->ipi6_ifindex; + + /* Set scope ID for link local addresses. */ + if (IN6_IS_ADDR_LINKLOCAL( + &peer->sa_sin6.sin6_addr)) + peer->sa_sin6.sin6_scope_id = *ifindex; + if (IN6_IS_ADDR_LINKLOCAL( + &local->sa_sin6.sin6_addr)) + local->sa_sin6.sin6_scope_id = *ifindex; } } } - /* Set scope ID for link local addresses. */ - if (IN6_IS_ADDR_LINKLOCAL(&peer->sa_sin6.sin6_addr)) - peer->sa_sin6.sin6_scope_id = *ifindex; - if (IN6_IS_ADDR_LINKLOCAL(&local->sa_sin6.sin6_addr)) - local->sa_sin6.sin6_scope_id = *ifindex; - return mlen; } -static void bfd_sd_reschedule(int sd) +static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd) { - if (sd == bglobal.bg_shop) { - THREAD_OFF(bglobal.bg_ev[0]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop, - &bglobal.bg_ev[0]); - } else if (sd == bglobal.bg_mhop) { - THREAD_OFF(bglobal.bg_ev[1]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop, - &bglobal.bg_ev[1]); - } else if (sd == bglobal.bg_shop6) { - THREAD_OFF(bglobal.bg_ev[2]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop6, - &bglobal.bg_ev[2]); - } else if (sd == bglobal.bg_mhop6) { - THREAD_OFF(bglobal.bg_ev[3]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop6, - &bglobal.bg_ev[3]); - } else if (sd == bglobal.bg_echo) { - THREAD_OFF(bglobal.bg_ev[4]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echo, - &bglobal.bg_ev[4]); - } else if (sd == bglobal.bg_echov6) { - THREAD_OFF(bglobal.bg_ev[5]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echov6, - &bglobal.bg_ev[5]); + if (sd == bvrf->bg_shop) { + THREAD_OFF(bvrf->bg_ev[0]); + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop, + &bvrf->bg_ev[0]); + } else if (sd == bvrf->bg_mhop) { + THREAD_OFF(bvrf->bg_ev[1]); + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop, + &bvrf->bg_ev[1]); + } else if (sd == bvrf->bg_shop6) { + THREAD_OFF(bvrf->bg_ev[2]); + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6, + &bvrf->bg_ev[2]); + } else if (sd == bvrf->bg_mhop6) { + THREAD_OFF(bvrf->bg_ev[3]); + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6, + &bvrf->bg_ev[3]); + } else if (sd == bvrf->bg_echo) { + THREAD_OFF(bvrf->bg_ev[4]); + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo, + &bvrf->bg_ev[4]); + } else if (sd == bvrf->bg_echov6) { + THREAD_OFF(bvrf->bg_ev[5]); + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6, + &bvrf->bg_ev[5]); } } @@ -518,13 +527,16 @@ int bfd_recv_cb(struct thread *t) ifindex_t ifindex = IFINDEX_INTERNAL; struct sockaddr_any local, peer; uint8_t msgbuf[1516]; + struct bfd_vrf_global *bvrf = THREAD_ARG(t); + if (bvrf) + vrfid = bvrf->vrf->vrf_id; /* Schedule next read. */ - bfd_sd_reschedule(sd); + bfd_sd_reschedule(bvrf, sd); /* Handle echo packets. */ - if (sd == bglobal.bg_echo || sd == bglobal.bg_echov6) { - ptm_bfd_process_echo_pkt(sd); + if (sd == bvrf->bg_echo || sd == bvrf->bg_echov6) { + ptm_bfd_process_echo_pkt(bvrf, sd); return 0; } @@ -534,12 +546,12 @@ int bfd_recv_cb(struct thread *t) /* Handle control packets. */ is_mhop = false; - if (sd == bglobal.bg_shop || sd == bglobal.bg_mhop) { - is_mhop = sd == bglobal.bg_mhop; + if (sd == bvrf->bg_shop || sd == bvrf->bg_mhop) { + is_mhop = sd == bvrf->bg_mhop; mlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex, &local, &peer); - } else if (sd == bglobal.bg_shop6 || sd == bglobal.bg_mhop6) { - is_mhop = sd == bglobal.bg_mhop6; + } else if (sd == bvrf->bg_shop6 || sd == bvrf->bg_mhop6) { + is_mhop = sd == bvrf->bg_mhop6; mlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex, &local, &peer); } @@ -640,6 +652,11 @@ int bfd_recv_cb(struct thread *t) ntohl(cp->timers.required_min_echo); bfd->remote_detect_mult = cp->detect_mult; + if (BFD_GETCBIT(cp->flags)) + bfd->remote_cbit = 1; + else + bfd->remote_cbit = 0; + /* State switch from section 6.2. */ bs_state_handler(bfd, BFD_GETSTATE(cp->flags)); @@ -682,7 +699,8 @@ int bfd_recv_cb(struct thread *t) * * Returns -1 on error or loopback or 0 on success. */ -int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr) +int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, + uint8_t *ttl, uint32_t *my_discr) { struct bfd_echo_pkt *bep; ssize_t rlen; @@ -691,7 +709,7 @@ int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr) vrf_id_t vrfid = VRF_DEFAULT; uint8_t msgbuf[1516]; - if (sd == bglobal.bg_echo) + if (sd == bvrf->bg_echo) rlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), ttl, &ifindex, &local, &peer); else @@ -709,7 +727,7 @@ int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr) if (*ttl == BFD_TTL_VAL) { bp_udp_send(sd, *ttl - 1, msgbuf, rlen, (struct sockaddr *)&peer, - (sd == bglobal.bg_echo) ? sizeof(peer.sa_sin) + (sd == bvrf->bg_echo) ? sizeof(peer.sa_sin) : sizeof(peer.sa_sin6)); return -1; } @@ -872,25 +890,28 @@ static void bp_bind_ip(int sd, uint16_t port) log_fatal("bind-ip: bind: %s", strerror(errno)); } -int bp_udp_shop(void) +int bp_udp_shop(vrf_id_t vrf_id) { int sd; - sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC); + frr_elevate_privs(&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf_id, NULL); + } if (sd == -1) log_fatal("udp-shop: socket: %s", strerror(errno)); bp_set_ipopts(sd); bp_bind_ip(sd, BFD_DEFDESTPORT); - return sd; } -int bp_udp_mhop(void) +int bp_udp_mhop(vrf_id_t vrf_id) { int sd; - sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC); + frr_elevate_privs(&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf_id, NULL); + } if (sd == -1) log_fatal("udp-mhop: socket: %s", strerror(errno)); @@ -905,8 +926,18 @@ int bp_peer_socket(const struct bfd_session *bs) int sd, pcount; struct sockaddr_in sin; static int srcPort = BFD_SRCPORTINIT; + const char *device_to_bind = NULL; + + if (bs->key.ifname[0]) + device_to_bind = (const char *)bs->key.ifname; + else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) + && bs->key.vrfname[0]) + device_to_bind = (const char *)bs->key.vrfname; - sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC); + frr_elevate_privs(&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, + bs->vrf->vrf_id, device_to_bind); + } if (sd == -1) { log_error("ipv4-new: failed to create socket: %s", strerror(errno)); @@ -925,19 +956,6 @@ int bp_peer_socket(const struct bfd_session *bs) return -1; } - if (bs->key.ifname[0]) { - if (bp_bind_dev(sd, bs->key.ifname) != 0) { - close(sd); - return -1; - } - } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) - && bs->key.vrfname[0]) { - if (bp_bind_dev(sd, bs->key.vrfname) != 0) { - close(sd); - return -1; - } - } - /* Find an available source port in the proper range */ memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; @@ -975,8 +993,18 @@ int bp_peer_socketv6(const struct bfd_session *bs) int sd, pcount; struct sockaddr_in6 sin6; static int srcPort = BFD_SRCPORTINIT; + const char *device_to_bind = NULL; - sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC); + if (bs->key.ifname[0]) + device_to_bind = (const char *)bs->key.ifname; + else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) + && bs->key.vrfname[0]) + device_to_bind = (const char *)bs->key.vrfname; + + frr_elevate_privs(&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, + bs->vrf->vrf_id, device_to_bind); + } if (sd == -1) { log_error("ipv6-new: failed to create socket: %s", strerror(errno)); @@ -1005,19 +1033,6 @@ int bp_peer_socketv6(const struct bfd_session *bs) if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) sin6.sin6_scope_id = bs->ifp->ifindex; - if (bs->key.ifname[0]) { - if (bp_bind_dev(sd, bs->key.ifname) != 0) { - close(sd); - return -1; - } - } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) - && bs->key.vrfname[0]) { - if (bp_bind_dev(sd, bs->key.vrfname) != 0) { - close(sd); - return -1; - } - } - pcount = 0; do { if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) { @@ -1102,11 +1117,13 @@ static void bp_bind_ipv6(int sd, uint16_t port) log_fatal("bind-ipv6: bind: %s", strerror(errno)); } -int bp_udp6_shop(void) +int bp_udp6_shop(vrf_id_t vrf_id) { int sd; - sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC); + frr_elevate_privs(&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf_id, NULL); + } if (sd == -1) log_fatal("udp6-shop: socket: %s", strerror(errno)); @@ -1116,11 +1133,13 @@ int bp_udp6_shop(void) return sd; } -int bp_udp6_mhop(void) +int bp_udp6_mhop(vrf_id_t vrf_id) { int sd; - sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC); + frr_elevate_privs(&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf_id, NULL); + } if (sd == -1) log_fatal("udp6-mhop: socket: %s", strerror(errno)); @@ -1130,11 +1149,13 @@ int bp_udp6_mhop(void) return sd; } -int bp_echo_socket(void) +int bp_echo_socket(vrf_id_t vrf_id) { int s; - s = socket(AF_INET, SOCK_DGRAM, 0); + frr_elevate_privs(&bglobal.bfdd_privs) { + s = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf_id, NULL); + } if (s == -1) log_fatal("echo-socket: socket: %s", strerror(errno)); @@ -1144,11 +1165,13 @@ int bp_echo_socket(void) return s; } -int bp_echov6_socket(void) +int bp_echov6_socket(vrf_id_t vrf_id) { int s; - s = socket(AF_INET6, SOCK_DGRAM, 0); + frr_elevate_privs(&bglobal.bfdd_privs) { + s = vrf_socket(AF_INET6, SOCK_DGRAM, 0, vrf_id, NULL); + } if (s == -1) log_fatal("echov6-socket: socket: %s", strerror(errno)); diff --git a/bfdd/bfdctl.h b/bfdd/bfdctl.h index 0da1ca8df6c7..4ce23a8f2746 100644 --- a/bfdd/bfdctl.h +++ b/bfdd/bfdctl.h @@ -88,6 +88,8 @@ struct bfd_peer_cfg { bool bpc_createonly; bool bpc_shutdown; + bool bpc_cbit; + /* Status information */ enum bfd_peer_status bpc_bps; uint32_t bpc_id; diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c index 6023b5e4f0a5..06e01abcfa31 100644 --- a/bfdd/bfdd.c +++ b/bfdd/bfdd.c @@ -20,6 +20,8 @@ #include +#include "filter.h" + #include "bfd.h" #include "lib/version.h" @@ -34,25 +36,13 @@ DEFINE_MTYPE(BFDD, BFDD_LABEL, "long-lived label memory"); DEFINE_MTYPE(BFDD, BFDD_CONTROL, "long-lived control socket memory"); DEFINE_MTYPE(BFDD, BFDD_SESSION_OBSERVER, "Session observer"); DEFINE_MTYPE(BFDD, BFDD_NOTIFICATION, "short-lived control notification data"); +DEFINE_MTYPE(BFDD, BFDD_VRF, "BFD VRF"); /* Master of threads. */ struct thread_master *master; /* BFDd privileges */ -static zebra_capabilities_t _caps_p[] = {ZCAP_BIND}; - -struct zebra_privs_t bfdd_privs = { -#if defined(FRR_USER) && defined(FRR_GROUP) - .user = FRR_USER, - .group = FRR_GROUP, -#endif -#if defined(VTY_GROUP) - .vty_group = VTY_GROUP, -#endif - .caps_p = _caps_p, - .cap_num_p = array_size(_caps_p), - .cap_num_i = 0, -}; +static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_SYS_ADMIN, ZCAP_NET_RAW}; void socket_close(int *s) { @@ -85,12 +75,7 @@ static void sigterm_handler(void) /* Shutdown and free all protocol related memory. */ bfd_shutdown(); - /* Close all descriptors. */ - socket_close(&bglobal.bg_echo); - socket_close(&bglobal.bg_shop); - socket_close(&bglobal.bg_mhop); - socket_close(&bglobal.bg_shop6); - socket_close(&bglobal.bg_mhop6); + bfd_vrf_terminate(); /* Terminate and free() FRR related memory. */ frr_fini(); @@ -116,7 +101,7 @@ static struct quagga_signal_t bfd_signals[] = { FRR_DAEMON_INFO(bfdd, BFD, .vty_port = 2617, .proghelp = "Implementation of the BFD protocol.", .signals = bfd_signals, .n_signals = array_size(bfd_signals), - .privs = &bfdd_privs) + .privs = &bglobal.bfdd_privs) #define OPTION_CTLSOCK 1001 static struct option longopts[] = { @@ -153,26 +138,41 @@ struct bfd_state_str_list state_list[] = { static void bg_init(void) { + struct zebra_privs_t bfdd_privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0, + }; + TAILQ_INIT(&bglobal.bg_bcslist); TAILQ_INIT(&bglobal.bg_obslist); - bglobal.bg_shop = bp_udp_shop(); - bglobal.bg_mhop = bp_udp_mhop(); - bglobal.bg_shop6 = bp_udp6_shop(); - bglobal.bg_mhop6 = bp_udp6_mhop(); - bglobal.bg_echo = bp_echo_socket(); - bglobal.bg_echov6 = bp_echov6_socket(); + memcpy(&bglobal.bfdd_privs, &bfdd_privs, + sizeof(bfdd_privs)); } int main(int argc, char *argv[]) { - const char *ctl_path = BFDD_CONTROL_SOCKET; + char ctl_path[512]; + bool ctlsockused = false; int opt; + /* Initialize system sockets. */ + bg_init(); + frr_preinit(&bfdd_di, argc, argv); frr_opt_add("", longopts, " --bfdctl Specify bfdd control socket\n"); + snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET, + "", ""); while (true) { opt = frr_getopt(argc, argv, NULL); if (opt == EOF) @@ -180,7 +180,8 @@ int main(int argc, char *argv[]) switch (opt) { case OPTION_CTLSOCK: - ctl_path = optarg; + strlcpy(ctl_path, optarg, sizeof(ctl_path)); + ctlsockused = true; break; default: @@ -189,6 +190,10 @@ int main(int argc, char *argv[]) } } + if (bfdd_di.pathspace && !ctlsockused) + snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET, + "/", bfdd_di.pathspace); + #if 0 /* TODO add support for JSON configuration files. */ parse_config(conf); #endif @@ -196,9 +201,6 @@ int main(int argc, char *argv[]) /* Initialize logging API. */ log_init(1, BLOG_DEBUG, &bfdd_di); - /* Initialize system sockets. */ - bg_init(); - /* Initialize control socket. */ control_init(ctl_path); @@ -208,22 +210,13 @@ int main(int argc, char *argv[]) /* Initialize BFD data structures. */ bfd_initialize(); + bfd_vrf_init(); + + access_list_init(); + /* Initialize zebra connection. */ - bfdd_zclient_init(&bfdd_privs); - - /* Add descriptors to the event loop. */ - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop, - &bglobal.bg_ev[0]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop, - &bglobal.bg_ev[1]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop6, - &bglobal.bg_ev[2]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop6, - &bglobal.bg_ev[3]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echo, - &bglobal.bg_ev[4]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echov6, - &bglobal.bg_ev[5]); + bfdd_zclient_init(&bglobal.bfdd_privs); + thread_add_read(master, control_accept, NULL, bglobal.bg_csock, &bglobal.bg_csockev); diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c index c13949207642..75f6632db03a 100644 --- a/bfdd/bfdd_vty.c +++ b/bfdd/bfdd_vty.c @@ -64,7 +64,7 @@ static struct json_object *__display_peer_json(struct bfd_session *bs); static struct json_object *_peer_json_header(struct bfd_session *bs); static void _display_peer_json(struct vty *vty, struct bfd_session *bs); static void _display_peer(struct vty *vty, struct bfd_session *bs); -static void _display_all_peers(struct vty *vty, bool use_json); +static void _display_all_peers(struct vty *vty, char *vrfname, bool use_json); static void _display_peer_iter(struct hash_bucket *hb, void *arg); static void _display_peer_json_iter(struct hash_bucket *hb, void *arg); static void _display_peer_counter(struct vty *vty, struct bfd_session *bs); @@ -72,7 +72,7 @@ static struct json_object *__display_peer_counters_json(struct bfd_session *bs); static void _display_peer_counters_json(struct vty *vty, struct bfd_session *bs); static void _display_peer_counter_iter(struct hash_bucket *hb, void *arg); static void _display_peer_counter_json_iter(struct hash_bucket *hb, void *arg); -static void _display_peers_counter(struct vty *vty, bool use_json); +static void _display_peers_counter(struct vty *vty, char *vrfname, bool use_json); static struct bfd_session * _find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv, const char *label, const char *peer_str, @@ -90,7 +90,7 @@ DEFUN_NOSH(bfd_enter, bfd_enter_cmd, "bfd", "Configure BFD peers\n") DEFUN_NOSH( bfd_peer_enter, bfd_peer_enter_cmd, - "peer [{[multihop] local-address |interface IFNAME|vrf NAME}]", + "peer [{multihop|local-address |interface IFNAME|vrf NAME}]", PEER_STR PEER_IPV4_STR PEER_IPV6_STR MHOP_STR LOCAL_STR LOCAL_IPV4_STR LOCAL_IPV6_STR @@ -126,15 +126,6 @@ DEFUN_NOSH( if (argv_find(argv, argc, "vrf", &idx)) vrfname = argv[idx + 1]->arg; - if (vrfname && ifname) { - vty_out(vty, "%% VRF is not mixable with interface\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (vrfname && !mhop) { - vty_out(vty, "%% VRF only applies with multihop.\n"); - return CMD_WARNING_CONFIG_FAILED; - } - strtosa(peer, &psa); if (local) { strtosa(local, &lsa); @@ -158,6 +149,12 @@ DEFUN_NOSH( } } + if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) { + if (bs->refcount) + vty_out(vty, "%% session peer is now configurable via bfd daemon.\n"); + BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + } + VTY_PUSH_CONTEXT(BFD_PEER_NODE, bs); return CMD_SUCCESS; @@ -354,7 +351,7 @@ DEFPY(bfd_no_peer, bfd_no_peer_cmd, return CMD_WARNING_CONFIG_FAILED; } - if (ptm_bfd_ses_del(&bpc) != 0) { + if (ptm_bfd_sess_del(&bpc) != 0) { vty_out(vty, "%% Failed to remove peer.\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -543,19 +540,46 @@ static void _display_peer_json(struct vty *vty, struct bfd_session *bs) json_object_free(jo); } +struct bfd_vrf_tuple { + char *vrfname; + struct vty *vty; + struct json_object *jo; +}; + static void _display_peer_iter(struct hash_bucket *hb, void *arg) { - struct vty *vty = 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; + } _display_peer(vty, bs); } static void _display_peer_json_iter(struct hash_bucket *hb, void *arg) { - struct json_object *jo = arg, *jon = NULL; + 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; + } + jon = __display_peer_json(bs); if (jon == NULL) { log_warning("%s: not enough memory", __func__); @@ -565,18 +589,24 @@ static void _display_peer_json_iter(struct hash_bucket *hb, void *arg) json_object_array_add(jo, jon); } -static void _display_all_peers(struct vty *vty, bool use_json) +static void _display_all_peers(struct vty *vty, char *vrfname, bool use_json) { struct json_object *jo; + struct bfd_vrf_tuple bvt; + + memset(&bvt, 0, sizeof(bvt)); + bvt.vrfname = vrfname; if (!use_json) { + bvt.vty = vty; vty_out(vty, "BFD Peers:\n"); - bfd_id_iterate(_display_peer_iter, vty); + bfd_id_iterate(_display_peer_iter, &bvt); return; } jo = json_object_new_array(); - bfd_id_iterate(_display_peer_json_iter, jo); + bvt.jo = jo; + bfd_id_iterate(_display_peer_json_iter, &bvt); vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0)); json_object_free(jo); @@ -628,16 +658,38 @@ static void _display_peer_counters_json(struct vty *vty, struct bfd_session *bs) static void _display_peer_counter_iter(struct hash_bucket *hb, void *arg) { - struct vty *vty = 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; + } + _display_peer_counter(vty, bs); } static void _display_peer_counter_json_iter(struct hash_bucket *hb, void *arg) { - struct json_object *jo = arg, *jon = NULL; + 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; + } jon = __display_peer_counters_json(bs); if (jon == NULL) { @@ -648,17 +700,22 @@ static void _display_peer_counter_json_iter(struct hash_bucket *hb, void *arg) json_object_array_add(jo, jon); } -static void _display_peers_counter(struct vty *vty, bool use_json) +static void _display_peers_counter(struct vty *vty, char *vrfname, bool use_json) { struct json_object *jo; + struct bfd_vrf_tuple bvt; + memset(&bvt, 0, sizeof(struct bfd_vrf_tuple)); + bvt.vrfname = vrfname; if (!use_json) { + bvt.vty = vty; vty_out(vty, "BFD Peers:\n"); - bfd_id_iterate(_display_peer_counter_iter, vty); + bfd_id_iterate(_display_peer_counter_iter, &bvt); return; } jo = json_object_new_array(); + bvt.jo = jo; bfd_id_iterate(_display_peer_counter_json_iter, jo); vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0)); @@ -728,24 +785,31 @@ _find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv, /* * Show commands. */ -DEFPY(bfd_show_peers, bfd_show_peers_cmd, "show bfd peers [json]", +DEFPY(bfd_show_peers, bfd_show_peers_cmd, "show bfd [vrf ] peers [json]", SHOW_STR "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR "BFD peers status\n" JSON_STR) { - _display_all_peers(vty, use_json(argc, argv)); + char *vrf_name = NULL; + int idx_vrf = 0; + + if (argv_find(argv, argc, "vrf", &idx_vrf)) + vrf_name = argv[idx_vrf + 1]->arg; + + _display_all_peers(vty, vrf_name, use_json(argc, argv)); return CMD_SUCCESS; } DEFPY(bfd_show_peer, bfd_show_peer_cmd, - "show bfd peer $peer [{multihop|local-address $local|interface IFNAME$ifname|vrf NAME$vrfname}]> [json]", + "show bfd [vrf ] peer $peer [{multihop|local-address $local|interface IFNAME$ifname}]> [json]", SHOW_STR "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR "BFD peers status\n" "Peer label\n" PEER_IPV4_STR PEER_IPV6_STR MHOP_STR LOCAL_STR - LOCAL_IPV4_STR LOCAL_IPV6_STR INTERFACE_STR LOCAL_INTF_STR VRF_STR - VRF_NAME_STR JSON_STR) + LOCAL_IPV4_STR LOCAL_IPV6_STR INTERFACE_STR LOCAL_INTF_STR JSON_STR) { struct bfd_session *bs; @@ -766,9 +830,10 @@ DEFPY(bfd_show_peer, bfd_show_peer_cmd, } DEFPY(bfd_show_peer_counters, bfd_show_peer_counters_cmd, - "show bfd peer $peer [{multihop|local-address $local|interface IFNAME$ifname|vrf NAME$vrfname}]> counters [json]", + "show bfd [vrf ] peer $peer [{multihop|local-address $local|interface IFNAME$ifname}]> counters [json]", SHOW_STR "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR "BFD peers status\n" "Peer label\n" PEER_IPV4_STR @@ -779,8 +844,6 @@ DEFPY(bfd_show_peer_counters, bfd_show_peer_counters_cmd, LOCAL_IPV6_STR INTERFACE_STR LOCAL_INTF_STR - VRF_STR - VRF_NAME_STR "Show BFD peer counters information\n" JSON_STR) { @@ -801,14 +864,21 @@ DEFPY(bfd_show_peer_counters, bfd_show_peer_counters_cmd, } DEFPY(bfd_show_peers_counters, bfd_show_peers_counters_cmd, - "show bfd peers counters [json]", + "show bfd [vrf ] peers counters [json]", SHOW_STR "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR "BFD peers status\n" "Show BFD peer counters information\n" JSON_STR) { - _display_peers_counter(vty, use_json(argc, argv)); + char *vrf_name = NULL; + int idx_vrf = 0; + + if (argv_find(argv, argc, "vrf", &idx_vrf)) + vrf_name = argv[idx_vrf + 1]->arg; + + _display_peers_counter(vty, vrf_name, use_json(argc, argv)); return CMD_SUCCESS; } @@ -984,6 +1054,9 @@ static void _bfdd_peer_write_config_iter(struct hash_bucket *hb, void *arg) struct vty *vty = arg; struct bfd_session *bs = hb->data; + if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) + return; + _bfdd_peer_write_config(vty, bs); } diff --git a/bfdd/config.c b/bfdd/config.c index cd57ea9fe38b..74e7d63d0c6e 100644 --- a/bfdd/config.c +++ b/bfdd/config.c @@ -68,7 +68,7 @@ static int config_add(struct bfd_peer_cfg *bpc, static int config_del(struct bfd_peer_cfg *bpc, void *arg __attribute__((unused))) { - return ptm_bfd_ses_del(bpc) != 0; + return ptm_bfd_sess_del(bpc) != 0; } static int parse_config_json(struct json_object *jo, bpc_handle h, void *arg) diff --git a/bfdd/linux.c b/bfdd/linux.c deleted file mode 100644 index 3a76b459d7ea..000000000000 --- a/bfdd/linux.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Linux specific code - * - * Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF") - * - * FRR is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * FRR is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with FRR; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -#ifdef BFD_LINUX - -#include "bfd.h" - - -/* - * Definitions. - */ -int bp_bind_dev(int sd __attribute__((__unused__)), - const char *dev __attribute__((__unused__))) -{ - /* - * TODO: implement this differently. It is not possible to - * SO_BINDTODEVICE after the daemon has dropped its privileges. - */ -#if 0 - size_t devlen = strlen(dev) + 1; - - if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, dev, devlen) == -1) { - log_warning("%s: setsockopt(SO_BINDTODEVICE, \"%s\"): %s", - __func__, dev, strerror(errno)); - return -1; - } -#endif - - return 0; -} - -#endif /* BFD_LINUX */ diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c index 8d80b9468db5..3e2ace6ea630 100644 --- a/bfdd/ptm_adapter.c +++ b/bfdd/ptm_adapter.c @@ -58,7 +58,7 @@ static struct zclient *zclient; static int _ptm_msg_address(struct stream *msg, int family, const void *addr); static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa); -static int _ptm_msg_read(struct stream *msg, int command, +static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id, struct bfd_peer_cfg *bpc, struct ptm_client **pc); static struct ptm_client *pc_lookup(uint32_t pid); @@ -72,8 +72,8 @@ static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc, static void pcn_free(struct ptm_client_notification *pcn); -static void bfdd_dest_register(struct stream *msg); -static void bfdd_dest_deregister(struct stream *msg); +static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id); +static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id); static void bfdd_client_register(struct stream *msg); static void bfdd_client_deregister(struct stream *msg); @@ -89,6 +89,7 @@ static void debug_printbpc(const char *func, unsigned int line, { char addr[3][128]; char timers[3][128]; + char cbit_str[10]; addr[0][0] = addr[1][0] = addr[2][0] = timers[0][0] = timers[1][0] = timers[2][0] = 0; @@ -117,9 +118,11 @@ static void debug_printbpc(const char *func, unsigned int line, snprintf(timers[2], sizeof(timers[2]), " detect-multiplier:%d", bpc->bpc_detectmultiplier); - log_debug("%s:%d: %s %s%s%s%s%s%s", func, line, + sprintf(cbit_str, "CB %x", bpc->bpc_cbit); + + log_debug("%s:%d: %s %s%s%s%s%s%s %s", func, line, bpc->bpc_mhop ? "multi-hop" : "single-hop", addr[0], addr[1], - addr[2], timers[0], timers[1], timers[2]); + addr[2], timers[0], timers[1], timers[2], cbit_str); } #define DEBUG_PRINTBPC(bpc) debug_printbpc(__FILE__, __LINE__, (bpc)) @@ -173,6 +176,7 @@ int ptm_bfd_notify(struct bfd_session *bs) * - AF_INET6: * - 16 bytes: ipv6 * - c: prefix length + * - c: cbit * * Commands: ZEBRA_BFD_DEST_REPLAY * @@ -182,7 +186,10 @@ int ptm_bfd_notify(struct bfd_session *bs) stream_reset(msg); /* TODO: VRF handling */ - zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT); + if (bs->vrf) + zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, bs->vrf->vrf_id); + else + zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT); /* This header will be handled by `zebra_ptm.c`. */ stream_putl(msg, ZEBRA_INTERFACE_BFD_DEST_UPDATE); @@ -216,6 +223,8 @@ int ptm_bfd_notify(struct bfd_session *bs) /* BFD source prefix information. */ _ptm_msg_address(msg, bs->key.family, &bs->key.local); + stream_putc(msg, bs->remote_cbit); + /* Write packet size. */ stream_putw_at(msg, 0, stream_get_endp(msg)); @@ -256,7 +265,7 @@ static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa) memset(sa, 0, sizeof(*sa)); } -static int _ptm_msg_read(struct stream *msg, int command, +static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id, struct bfd_peer_cfg *bpc, struct ptm_client **pc) { uint32_t pid; @@ -290,6 +299,7 @@ static int _ptm_msg_read(struct stream *msg, int command, * - 16 bytes: ipv6 address * - c: ifname length * - X bytes: interface name + * - c: bfd_cbit * * q(64), l(32), w(16), c(8) */ @@ -355,6 +365,20 @@ static int _ptm_msg_read(struct stream *msg, int command, bpc->bpc_localif[ifnamelen] = 0; } } + if (vrf_id != VRF_DEFAULT) { + struct vrf *vrf; + + vrf = vrf_lookup_by_id(vrf_id); + if (vrf) { + bpc->bpc_has_vrfname = true; + strlcpy(bpc->bpc_vrfname, vrf->name, sizeof(bpc->bpc_vrfname)); + } else { + log_error("ptm-read: vrf id %u could not be identified", vrf_id); + return -1; + } + } + + STREAM_GETC(msg, bpc->bpc_cbit); /* Sanity check: peer and local address must match IP types. */ if (bpc->bpc_local.sa_sin.sin_family != 0 @@ -370,7 +394,7 @@ static int _ptm_msg_read(struct stream *msg, int command, return -1; } -static void bfdd_dest_register(struct stream *msg) +static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id) { struct ptm_client *pc; struct ptm_client_notification *pcn; @@ -378,7 +402,7 @@ static void bfdd_dest_register(struct stream *msg) struct bfd_peer_cfg bpc; /* Read the client context and peer data. */ - if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_REGISTER, &bpc, &pc) == -1) + if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_REGISTER, vrf_id, &bpc, &pc) == -1) return; DEBUG_PRINTBPC(&bpc); @@ -408,7 +432,7 @@ static void bfdd_dest_register(struct stream *msg) ptm_bfd_notify(bs); } -static void bfdd_dest_deregister(struct stream *msg) +static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id) { struct ptm_client *pc; struct ptm_client_notification *pcn; @@ -416,7 +440,7 @@ static void bfdd_dest_deregister(struct stream *msg) struct bfd_peer_cfg bpc; /* Read the client context and peer data. */ - if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_DEREGISTER, &bpc, &pc) == -1) + if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_DEREGISTER, vrf_id, &bpc, &pc) == -1) return; DEBUG_PRINTBPC(&bpc); @@ -431,6 +455,10 @@ static void bfdd_dest_deregister(struct stream *msg) /* Unregister client peer notification. */ pcn = pcn_lookup(pc, bs); pcn_free(pcn); + if (bs->refcount || + BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) + return; + ptm_bfd_sess_del(&bpc); } /* @@ -483,9 +511,9 @@ static void bfdd_client_deregister(struct stream *msg) log_error("ptm-del-client: failed to deregister client"); } -static int bfdd_replay(int cmd, struct zclient *zc, uint16_t len, vrf_id_t vid) +static int bfdd_replay(ZAPI_CALLBACK_ARGS) { - struct stream *msg = zc->ibuf; + struct stream *msg = zclient->ibuf; uint32_t rcmd; STREAM_GETL(msg, rcmd); @@ -493,10 +521,10 @@ static int bfdd_replay(int cmd, struct zclient *zc, uint16_t len, vrf_id_t vid) switch (rcmd) { case ZEBRA_BFD_DEST_REGISTER: case ZEBRA_BFD_DEST_UPDATE: - bfdd_dest_register(msg); + bfdd_dest_register(msg, vrf_id); break; case ZEBRA_BFD_DEST_DEREGISTER: - bfdd_dest_deregister(msg); + bfdd_dest_deregister(msg, vrf_id); break; case ZEBRA_BFD_CLIENT_REGISTER: bfdd_client_register(msg); @@ -544,15 +572,21 @@ static void bfdd_sessions_enable_interface(struct interface *ifp) { struct bfd_session_observer *bso; struct bfd_session *bs; + struct vrf *vrf; TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) { + bs = bso->bso_bs; if (bso->bso_isinterface == false) continue; - /* Interface name mismatch. */ - bs = bso->bso_bs; if (strcmp(ifp->name, bs->key.ifname)) continue; + vrf = vrf_lookup_by_id(ifp->vrf_id); + if (!vrf) + continue; + if (bs->key.vrfname[0] && + strcmp(vrf->name, bs->key.vrfname)) + continue; /* Skip enabled sessions. */ if (bs->sock != -1) continue; @@ -582,13 +616,56 @@ static void bfdd_sessions_disable_interface(struct interface *ifp) /* Try to enable it. */ bfd_session_disable(bs); - TAILQ_INSERT_HEAD(&bglobal.bg_obslist, bso, bso_entry); } } -static int bfdd_interface_update(int cmd, struct zclient *zc, - uint16_t len __attribute__((__unused__)), - vrf_id_t vrfid) +void bfdd_sessions_enable_vrf(struct vrf *vrf) +{ + struct bfd_session_observer *bso; + struct bfd_session *bs; + + /* it may affect configs without interfaces */ + TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) { + bs = bso->bso_bs; + if (bs->vrf) + continue; + if (bs->key.vrfname[0] && + strcmp(vrf->name, bs->key.vrfname)) + continue; + /* need to update the vrf information on + * bs so that callbacks are handled + */ + bs->vrf = vrf; + /* Skip enabled sessions. */ + if (bs->sock != -1) + continue; + /* Try to enable it. */ + bfd_session_enable(bs); + } +} + +void bfdd_sessions_disable_vrf(struct vrf *vrf) +{ + struct bfd_session_observer *bso; + struct bfd_session *bs; + + TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) { + if (bso->bso_isinterface) + continue; + bs = bso->bso_bs; + if (bs->key.vrfname[0] && + strcmp(vrf->name, bs->key.vrfname)) + continue; + /* Skip disabled sessions. */ + if (bs->sock == -1) + continue; + + /* Try to enable it. */ + bfd_session_disable(bs); + } +} + +static int bfdd_interface_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -598,7 +675,7 @@ static int bfdd_interface_update(int cmd, struct zclient *zc, * rolling our own. */ if (cmd == ZEBRA_INTERFACE_ADD) { - ifp = zebra_interface_add_read(zc->ibuf, vrfid); + ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); if (ifp == NULL) return 0; @@ -607,7 +684,7 @@ static int bfdd_interface_update(int cmd, struct zclient *zc, } /* Update interface information. */ - ifp = zebra_interface_state_read(zc->ibuf, vrfid); + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); if (ifp == NULL) return 0; @@ -618,16 +695,12 @@ static int bfdd_interface_update(int cmd, struct zclient *zc, return 0; } -static int bfdd_interface_vrf_update(int command __attribute__((__unused__)), - struct zclient *zclient, - zebra_size_t length - __attribute__((__unused__)), - vrf_id_t vrfid) +static int bfdd_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t nvrfid; - ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrfid, &nvrfid); + ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, &nvrfid); if (ifp == NULL) return 0; @@ -662,14 +735,11 @@ static void bfdd_sessions_enable_address(struct connected *ifc) } } -static int bfdd_interface_address_update(int cmd, struct zclient *zc, - zebra_size_t len - __attribute__((__unused__)), - vrf_id_t vrfid) +static int bfdd_interface_address_update(ZAPI_CALLBACK_ARGS) { struct connected *ifc; - ifc = zebra_interface_address_read(cmd, zc->ibuf, vrfid); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -706,6 +776,20 @@ void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv) zclient->interface_address_delete = bfdd_interface_address_update; } +void bfdd_zclient_register(vrf_id_t vrf_id) +{ + if (!zclient || zclient->sock < 0) + return; + zclient_send_reg_requests(zclient, vrf_id); +} + +void bfdd_zclient_unregister(vrf_id_t vrf_id) +{ + if (!zclient || zclient->sock < 0) + return; + zclient_send_dereg_requests(zclient, vrf_id); +} + void bfdd_zclient_stop(void) { zclient_stop(zclient); diff --git a/bfdd/subdir.am b/bfdd/subdir.am index 334e974b048a..e88b982ec3bf 100644 --- a/bfdd/subdir.am +++ b/bfdd/subdir.am @@ -14,11 +14,9 @@ bfdd_libbfd_a_SOURCES = \ bfdd/bfd.c \ bfdd/bfdd_vty.c \ bfdd/bfd_packet.c \ - bfdd/bsd.c \ bfdd/config.c \ bfdd/control.c \ bfdd/event.c \ - bfdd/linux.c \ bfdd/log.c \ bfdd/ptm_adapter.c \ # end diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 05eeeca1564b..497fb0749e19 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -64,9 +64,9 @@ static void *baa_hash_alloc(void *p) return baa; } -unsigned int baa_hash_key(void *p) +unsigned int baa_hash_key(const void *p) { - struct bgp_advertise_attr *baa = (struct bgp_advertise_attr *)p; + const struct bgp_advertise_attr *baa = p; return attrhash_key_make(baa->attr); } @@ -241,9 +241,9 @@ void bgp_sync_init(struct peer *peer) FOREACH_AFI_SAFI (afi, safi) { sync = XCALLOC(MTYPE_BGP_SYNCHRONISE, sizeof(struct bgp_synchronize)); - BGP_ADV_FIFO_INIT(&sync->update); - BGP_ADV_FIFO_INIT(&sync->withdraw); - BGP_ADV_FIFO_INIT(&sync->withdraw_low); + bgp_adv_fifo_init(&sync->update); + bgp_adv_fifo_init(&sync->withdraw); + bgp_adv_fifo_init(&sync->withdraw_low); peer->sync[afi][safi] = sync; } } diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h index 9aa5a0eaff51..1b55b6e64b42 100644 --- a/bgpd/bgp_advertise.h +++ b/bgpd/bgp_advertise.h @@ -21,16 +21,11 @@ #ifndef _QUAGGA_BGP_ADVERTISE_H #define _QUAGGA_BGP_ADVERTISE_H -#include +#include "lib/typesafe.h" -struct update_subgroup; +PREDECL_DLIST(bgp_adv_fifo) -/* BGP advertise FIFO. */ -struct bgp_advertise_fifo { - struct bgp_advertise *next; - struct bgp_advertise *prev; - uint32_t count; -}; +struct update_subgroup; /* BGP advertise attribute. */ struct bgp_advertise_attr { @@ -46,7 +41,7 @@ struct bgp_advertise_attr { struct bgp_advertise { /* FIFO for advertisement. */ - struct bgp_advertise_fifo fifo; + struct bgp_adv_fifo_item fifo; /* Link list for same attribute advertise. */ struct bgp_advertise *next; @@ -65,6 +60,8 @@ struct bgp_advertise { struct bgp_path_info *pathi; }; +DECLARE_DLIST(bgp_adv_fifo, struct bgp_advertise, fifo) + /* BGP adjacency out. */ struct bgp_adj_out { /* RB Tree of adjacency entries */ @@ -110,9 +107,9 @@ struct bgp_adj_in { /* BGP advertisement list. */ struct bgp_synchronize { - struct bgp_advertise_fifo update; - struct bgp_advertise_fifo withdraw; - struct bgp_advertise_fifo withdraw_low; + struct bgp_adv_fifo_head update; + struct bgp_adv_fifo_head withdraw; + struct bgp_adv_fifo_head withdraw_low; }; /* BGP adjacency linked list. */ @@ -138,36 +135,6 @@ struct bgp_synchronize { #define BGP_ADJ_IN_ADD(N, A) BGP_PATH_INFO_ADD(N, A, adj_in) #define BGP_ADJ_IN_DEL(N, A) BGP_PATH_INFO_DEL(N, A, adj_in) -#define BGP_ADV_FIFO_ADD(F, N) \ - do { \ - FIFO_ADD((F), (N)); \ - (F)->count++; \ - } while (0) - -#define BGP_ADV_FIFO_DEL(F, N) \ - do { \ - FIFO_DEL((N)); \ - (F)->count--; \ - } while (0) - -#define BGP_ADV_FIFO_INIT(F) \ - do { \ - FIFO_INIT((F)); \ - (F)->count = 0; \ - } while (0) - -#define BGP_ADV_FIFO_COUNT(F) (F)->count - -#define BGP_ADV_FIFO_EMPTY(F) \ - (((struct bgp_advertise_fifo *)(F))->next \ - == (struct bgp_advertise *)(F)) - -#define BGP_ADV_FIFO_HEAD(F) \ - ((((struct bgp_advertise_fifo *)(F))->next \ - == (struct bgp_advertise *)(F)) \ - ? NULL \ - : (F)->next) - /* Prototypes. */ extern int bgp_adj_out_lookup(struct peer *, struct bgp_node *, uint32_t); extern void bgp_adj_in_set(struct bgp_node *, struct peer *, struct attr *, @@ -177,7 +144,7 @@ extern void bgp_adj_in_remove(struct bgp_node *, struct bgp_adj_in *); extern void bgp_sync_init(struct peer *); extern void bgp_sync_delete(struct peer *); -extern unsigned int baa_hash_key(void *p); +extern unsigned int baa_hash_key(const void *p); extern bool baa_hash_cmp(const void *p1, const void *p2); extern void bgp_advertise_add(struct bgp_advertise_attr *baa, struct bgp_advertise *adv); diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 92c37fabd234..05577cb8bdb0 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -2008,13 +2008,13 @@ struct aspath *aspath_str2aspath(const char *str) } /* Make hash value by raw aspath data. */ -unsigned int aspath_key_make(void *p) +unsigned int aspath_key_make(const void *p) { - struct aspath *aspath = (struct aspath *)p; + const struct aspath *aspath = p; unsigned int key = 0; if (!aspath->str) - aspath_str_update(aspath, false); + aspath_str_update((struct aspath *)aspath, false); key = jhash(aspath->str, aspath->str_len, 2334325); diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index be5725c1aea7..6f3d94cdb3f9 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -102,7 +102,7 @@ extern const char *aspath_print(struct aspath *); extern void aspath_print_vty(struct vty *, const char *, struct aspath *, const char *); extern void aspath_print_all_vty(struct vty *); -extern unsigned int aspath_key_make(void *); +extern unsigned int aspath_key_make(const void *); extern unsigned int aspath_get_first_as(struct aspath *); extern unsigned int aspath_get_last_as(struct aspath *); extern int aspath_loop_check(struct aspath *, as_t); diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 167ad89a5988..5a4105b4004f 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -51,7 +51,6 @@ #include "bgp_encap_types.h" #include "bgp_vnc_types.h" #endif -#include "bgp_encap_types.h" #include "bgp_evpn.h" #include "bgp_flowspec_private.h" #include "bgp_mac.h" @@ -140,7 +139,7 @@ int cluster_loop_check(struct cluster_list *cluster, struct in_addr originator) return 0; } -static unsigned int cluster_hash_key_make(void *p) +static unsigned int cluster_hash_key_make(const void *p) { const struct cluster_list *cluster = p; @@ -348,7 +347,7 @@ static void encap_unintern(struct bgp_attr_encap_subtlv **encapp, } } -static unsigned int encap_hash_key_make(void *p) +static unsigned int encap_hash_key_make(const void *p) { const struct bgp_attr_encap_subtlv *encap = p; @@ -433,7 +432,7 @@ void transit_unintern(struct transit *transit) } } -static unsigned int transit_hash_key_make(void *p) +static unsigned int transit_hash_key_make(const void *p) { const struct transit *transit = p; @@ -484,7 +483,7 @@ unsigned long int attr_unknown_count(void) return transit_hash->count; } -unsigned int attrhash_key_make(void *p) +unsigned int attrhash_key_make(const void *p) { const struct attr *attr = (struct attr *)p; uint32_t key = 0; @@ -1256,6 +1255,32 @@ static int bgp_attr_as4_path(struct bgp_attr_parser_args *args, return BGP_ATTR_PARSE_PROCEED; } +/* + * Check that the nexthop attribute is valid. + */ +bgp_attr_parse_ret_t +bgp_attr_nexthop_valid(struct peer *peer, struct attr *attr) +{ + in_addr_t nexthop_h; + + nexthop_h = ntohl(attr->nexthop.s_addr); + if ((IPV4_NET0(nexthop_h) || IPV4_NET127(nexthop_h) + || IPV4_CLASS_DE(nexthop_h)) + && !BGP_DEBUG(allow_martians, ALLOW_MARTIANS)) { + char buf[INET_ADDRSTRLEN]; + + inet_ntop(AF_INET, &attr->nexthop.s_addr, buf, + INET_ADDRSTRLEN); + flog_err(EC_BGP_ATTR_MARTIAN_NH, "Martian nexthop %s", + buf); + bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP); + return BGP_ATTR_PARSE_ERROR; + } + + return BGP_ATTR_PARSE_PROCEED; +} + /* Nexthop attribute. */ static bgp_attr_parse_ret_t bgp_attr_nexthop(struct bgp_attr_parser_args *args) { @@ -1263,8 +1288,6 @@ static bgp_attr_parse_ret_t bgp_attr_nexthop(struct bgp_attr_parser_args *args) struct attr *const attr = args->attr; const bgp_size_t length = args->length; - in_addr_t nexthop_h, nexthop_n; - /* Check nexthop attribute length. */ if (length != 4) { flog_err(EC_BGP_ATTR_LEN, @@ -1274,30 +1297,7 @@ static bgp_attr_parse_ret_t bgp_attr_nexthop(struct bgp_attr_parser_args *args) args->total); } - /* According to section 6.3 of RFC4271, syntactically incorrect NEXT_HOP - attribute must result in a NOTIFICATION message (this is implemented - below). - At the same time, semantically incorrect NEXT_HOP is more likely to - be just - logged locally (this is implemented somewhere else). The UPDATE - message - gets ignored in any of these cases. */ - nexthop_n = stream_get_ipv4(peer->curr); - nexthop_h = ntohl(nexthop_n); - if ((IPV4_NET0(nexthop_h) || IPV4_NET127(nexthop_h) - || IPV4_CLASS_DE(nexthop_h)) - && !BGP_DEBUG( - allow_martians, - ALLOW_MARTIANS)) /* loopbacks may be used in testing */ - { - char buf[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &nexthop_n, buf, INET_ADDRSTRLEN); - flog_err(EC_BGP_ATTR_MARTIAN_NH, "Martian nexthop %s", buf); - return bgp_attr_malformed( - args, BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, args->total); - } - - attr->nexthop.s_addr = nexthop_n; + attr->nexthop.s_addr = stream_get_ipv4(peer->curr); attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); return BGP_ATTR_PARSE_PROCEED; @@ -1956,6 +1956,10 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args) } + /* Get the tunnel type from encap extended community */ + bgp_attr_extcom_tunnel_type(attr, + (bgp_encap_types *)&attr->encap_tunneltype); + return BGP_ATTR_PARSE_PROCEED; } @@ -2681,6 +2685,26 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, return BGP_ATTR_PARSE_ERROR; } + /* + * RFC4271: If the NEXT_HOP attribute field is syntactically incorrect, + * then the Error Subcode MUST be set to Invalid NEXT_HOP Attribute. + * This is implemented below and will result in a NOTIFICATION. If the + * NEXT_HOP attribute is semantically incorrect, the error SHOULD be + * logged, and the route SHOULD be ignored. In this case, a NOTIFICATION + * message SHOULD NOT be sent. This is implemented elsewhere. + * + * RFC4760: An UPDATE message that carries no NLRI, other than the one + * encoded in the MP_REACH_NLRI attribute, SHOULD NOT carry the NEXT_HOP + * attribute. If such a message contains the NEXT_HOP attribute, the BGP + * speaker that receives the message SHOULD ignore this attribute. + */ + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) + && !CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI))) { + if (bgp_attr_nexthop_valid(peer, attr) < 0) { + return BGP_ATTR_PARSE_ERROR; + } + } + /* Check all mandatory well-known attributes are present */ if ((ret = bgp_attr_check(peer, attr)) < 0) { if (as4_path) @@ -2755,6 +2779,38 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, return BGP_ATTR_PARSE_PROCEED; } +/* + * Extract the tunnel type from extended community + */ +void bgp_attr_extcom_tunnel_type(struct attr *attr, + bgp_encap_types *tunnel_type) +{ + struct ecommunity *ecom; + int i; + if (!attr) + return; + + ecom = attr->ecommunity; + if (!ecom || !ecom->size) + return; + + for (i = 0; i < ecom->size; i++) { + uint8_t *pnt; + uint8_t type, sub_type; + + pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + type = pnt[0]; + sub_type = pnt[1]; + if (!(type == ECOMMUNITY_ENCODE_OPAQUE && + sub_type == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP)) + continue; + *tunnel_type = ((pnt[6] << 8) | pnt[7]); + return; + } + + return; +} + size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi, safi_t safi, struct bpacket_attr_vec_arr *vecarr, struct attr *attr) diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 6d5c647b21c2..1592a8df4eda 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -23,6 +23,7 @@ #include "mpls.h" #include "bgp_attr_evpn.h" +#include "bgpd/bgp_encap_types.h" /* Simple bit mapping. */ #define BITMAP_NBBY 8 @@ -282,7 +283,7 @@ extern bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *, extern void bgp_dump_routes_attr(struct stream *, struct attr *, struct prefix *); extern bool attrhash_cmp(const void *arg1, const void *arg2); -extern unsigned int attrhash_key_make(void *); +extern unsigned int attrhash_key_make(const void *); extern void attr_show_all(struct vty *); extern unsigned long int attr_count(void); extern unsigned long int attr_unknown_count(void); @@ -317,6 +318,9 @@ encap_tlv_dup(struct bgp_attr_encap_subtlv *orig); extern void bgp_attr_flush_encap(struct attr *attr); +extern void bgp_attr_extcom_tunnel_type(struct attr *attr, + bgp_encap_types *tunnel_type); + /** * Set of functions to encode MP_REACH_NLRI and MP_UNREACH_NLRI attributes. * Typical call sequence is to call _start(), followed by multiple _prefix(), @@ -344,6 +348,9 @@ extern void bgp_packet_mpunreach_prefix(struct stream *s, struct prefix *p, uint32_t, int, uint32_t, struct attr *); extern void bgp_packet_mpunreach_end(struct stream *s, size_t attrlen_pnt); +extern bgp_attr_parse_ret_t bgp_attr_nexthop_valid(struct peer *peer, + struct attr *attr); + static inline int bgp_rmap_nhop_changed(uint32_t out_rmap_flags, uint32_t in_rmap_flags) { diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index 663bc4894a84..57fef8e9139f 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -96,13 +96,12 @@ int bgp_bfd_is_peer_multihop(struct peer *peer) static void bgp_bfd_peer_sendmsg(struct peer *peer, int command) { struct bfd_info *bfd_info; - vrf_id_t vrf_id = VRF_DEFAULT; - int multihop; + int multihop, cbit = 0; + vrf_id_t vrf_id; bfd_info = (struct bfd_info *)peer->bfd_info; - if (peer->bgp->inst_type == BGP_INSTANCE_TYPE_VRF) - vrf_id = peer->bgp->vrf_id; + vrf_id = peer->bgp->vrf_id; if (command == ZEBRA_BFD_DEST_DEREGISTER) { multihop = @@ -113,20 +112,30 @@ static void bgp_bfd_peer_sendmsg(struct peer *peer, int command) if ((command == ZEBRA_BFD_DEST_REGISTER) && multihop) SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_TYPE_MULTIHOP); } + /* while graceful restart with fwd path preserved + * and bfd controlplane check not configured is not kept + * keep bfd independent controlplane bit set to 1 + */ + if (!bgp_flag_check(peer->bgp, BGP_FLAG_GRACEFUL_RESTART) + && !bgp_flag_check(peer->bgp, BGP_FLAG_GR_PRESERVE_FWD) + && !CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) + SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); + + cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); if (peer->su.sa.sa_family == AF_INET) bfd_peer_sendmsg( zclient, bfd_info, AF_INET, &peer->su.sin.sin_addr, (peer->su_local) ? &peer->su_local->sin.sin_addr : NULL, (peer->nexthop.ifp) ? peer->nexthop.ifp->name : NULL, - peer->ttl, multihop, command, 1, vrf_id); + peer->ttl, multihop, cbit, command, 1, vrf_id); else if (peer->su.sa.sa_family == AF_INET6) bfd_peer_sendmsg( zclient, bfd_info, AF_INET6, &peer->su.sin6.sin6_addr, (peer->su_local) ? &peer->su_local->sin6.sin6_addr : NULL, (peer->nexthop.ifp) ? peer->nexthop.ifp->name : NULL, - peer->ttl, multihop, command, 1, vrf_id); + peer->ttl, multihop, cbit, command, 1, vrf_id); } /* @@ -234,8 +243,7 @@ static void bgp_bfd_update_type(struct peer *peer) * bgp_bfd_dest_replay - Replay all the peers that have BFD enabled * to zebra */ -static int bgp_bfd_dest_replay(int command, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_bfd_dest_replay(ZAPI_CALLBACK_ARGS) { struct listnode *mnode, *node, *nnode; struct bgp *bgp; @@ -245,7 +253,7 @@ static int bgp_bfd_dest_replay(int command, struct zclient *client, zlog_debug("Zebra: BFD Dest replay request"); /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); /* Replay the peer, if BFD is enabled in BGP */ @@ -262,7 +270,8 @@ static int bgp_bfd_dest_replay(int command, struct zclient *client, * down the peer if the BFD session went down from * * up. */ -static void bgp_bfd_peer_status_update(struct peer *peer, int status) +static void bgp_bfd_peer_status_update(struct peer *peer, int status, + int remote_cbit) { struct bfd_info *bfd_info; int old_status; @@ -276,10 +285,30 @@ static void bgp_bfd_peer_status_update(struct peer *peer, int status) bfd_info->status = status; bfd_info->last_update = bgp_clock(); + if (status != old_status) { + if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS)) + zlog_debug("[%s]: BFD %s", peer->host, + bfd_get_status_str(status)); + } if ((status == BFD_STATUS_DOWN) && (old_status == BFD_STATUS_UP)) { + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE) && + CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE) && + !remote_cbit) { + zlog_info("%s BFD DOWN message ignored in the process" + " of graceful restart when C bit is cleared", + peer->host); + return; + } peer->last_reset = PEER_DOWN_BFD_DOWN; BGP_EVENT_ADD(peer, BGP_Stop); } + if ((status == BFD_STATUS_UP) && (old_status == BFD_STATUS_DOWN) + && peer->status != Established) { + if (!BGP_PEER_START_SUPPRESSED(peer)) { + bgp_fsm_event_update(peer, 1); + BGP_EVENT_ADD(peer, BGP_Start); + } + } } /* @@ -287,30 +316,33 @@ static void bgp_bfd_peer_status_update(struct peer *peer, int status) * has changed and bring down the peer * connectivity if the BFD session went down. */ -static int bgp_bfd_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_bfd_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct prefix dp; struct prefix sp; int status; + int remote_cbit; - ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status, vrf_id); + ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status, + &remote_cbit, vrf_id); if (BGP_DEBUG(zebra, ZEBRA)) { char buf[2][PREFIX2STR_BUFFER]; prefix2str(&dp, buf[0], sizeof(buf[0])); if (ifp) { zlog_debug( - "Zebra: vrf %u interface %s bfd destination %s %s", + "Zebra: vrf %u interface %s bfd destination %s %s %s", vrf_id, ifp->name, buf[0], - bfd_get_status_str(status)); + bfd_get_status_str(status), + remote_cbit ? "(cbit on)" : ""); } else { prefix2str(&sp, buf[1], sizeof(buf[1])); zlog_debug( - "Zebra: vrf %u source %s bfd destination %s %s", + "Zebra: vrf %u source %s bfd destination %s %s %s", vrf_id, buf[1], buf[0], - bfd_get_status_str(status)); + bfd_get_status_str(status), + remote_cbit ? "(cbit on)" : ""); } } @@ -342,7 +374,8 @@ static int bgp_bfd_dest_update(int command, struct zclient *zclient, if (ifp && (ifp == peer->nexthop.ifp)) { bgp_bfd_peer_status_update(peer, - status); + status, + remote_cbit); } else { if (!peer->su_local) continue; @@ -372,7 +405,8 @@ static int bgp_bfd_dest_update(int command, struct zclient *zclient, continue; bgp_bfd_peer_status_update(peer, - status); + status, + remote_cbit); } } } @@ -525,6 +559,9 @@ void bgp_bfd_peer_config_write(struct vty *vty, struct peer *peer, char *addr) if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_PARAM_CFG) && (bfd_info->type == BFD_TYPE_NOT_CONFIGURED)) vty_out(vty, " neighbor %s bfd\n", addr); + + if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) + vty_out(vty, " neighbor %s bfd check-control-plane-failure\n", addr); } /* @@ -635,6 +672,73 @@ DEFUN_HIDDEN (neighbor_bfd_type, return CMD_SUCCESS; } +static int bgp_bfd_set_check_controlplane_failure_peer(struct vty *vty, struct peer *peer, + const char *no) +{ + struct bfd_info *bfd_info; + + if (!peer->bfd_info) { + if (no) + return CMD_SUCCESS; + vty_out(vty, "%% Specify bfd command first\n"); + return CMD_WARNING_CONFIG_FAILED; + } + bfd_info = (struct bfd_info *)peer->bfd_info; + if (!no) { + if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) { + SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE); + bgp_bfd_update_peer(peer); + } + } else { + if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) { + UNSET_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE); + bgp_bfd_update_peer(peer); + } + } + return CMD_SUCCESS; +} + + +DEFUN (neighbor_bfd_check_controlplane_failure, + neighbor_bfd_check_controlplane_failure_cmd, + "[no] neighbor bfd check-control-plane-failure", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BFD support\n" + "Link dataplane status with BGP controlplane\n") +{ + const char *no = strmatch(argv[0]->text, "no") ? "no" : NULL; + int idx_peer = 0; + struct peer *peer; + struct peer_group *group; + struct listnode *node, *nnode; + int ret = CMD_SUCCESS; + + if (no) + idx_peer = 2; + else + idx_peer = 1; + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) { + vty_out(vty, "%% Specify remote-as or peer-group commands first\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (!peer->bfd_info) { + if (no) + return CMD_SUCCESS; + vty_out(vty, "%% Specify bfd command first\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + group = peer->group; + for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) + ret = bgp_bfd_set_check_controlplane_failure_peer(vty, peer, no); + } else + ret = bgp_bfd_set_check_controlplane_failure_peer(vty, peer, no); + return ret; + } + DEFUN (no_neighbor_bfd, no_neighbor_bfd_cmd, #if HAVE_BFDD > 0 @@ -709,6 +813,7 @@ void bgp_bfd_init(void) install_element(BGP_NODE, &neighbor_bfd_cmd); install_element(BGP_NODE, &neighbor_bfd_param_cmd); install_element(BGP_NODE, &neighbor_bfd_type_cmd); + install_element(BGP_NODE, &neighbor_bfd_check_controlplane_failure_cmd); install_element(BGP_NODE, &no_neighbor_bfd_cmd); install_element(BGP_NODE, &no_neighbor_bfd_type_cmd); } diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 7b64f349d233..b9a5784799c6 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -36,9 +36,9 @@ #include "bgpd/bgp_regex.h" #include "bgpd/bgp_clist.h" -static uint32_t bgp_clist_hash_key_community_list(void *data) +static uint32_t bgp_clist_hash_key_community_list(const void *data) { - struct community_list *cl = data; + struct community_list *cl = (struct community_list *) data; if (cl->name_hash) return cl->name_hash; @@ -1049,8 +1049,10 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name, /* Do not put duplicated community entry. */ if (community_list_dup_check(list, entry)) community_entry_free(entry); - else + else { community_list_entry_add(list, entry); + route_map_notify_dependencies(name, RMAP_EVENT_LLIST_ADDED); + } return 0; } @@ -1075,6 +1077,7 @@ int lcommunity_list_unset(struct community_list_handler *ch, const char *name, /* Delete all of entry belongs to this community-list. */ if (!str) { community_list_delete(cm, list); + route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED); return 0; } @@ -1100,6 +1103,7 @@ int lcommunity_list_unset(struct community_list_handler *ch, const char *name, return COMMUNITY_LIST_ERR_CANT_FIND_LIST; community_list_entry_delete(cm, list, entry); + route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED); return 0; } diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index 67cd2be21489..6fc52ff9e031 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -205,7 +205,6 @@ static void set_community_string(struct community *com, bool make_json) { int i; char *str; - char *pnt; int len; int first; uint32_t comval; @@ -297,7 +296,7 @@ static void set_community_string(struct community *com, bool make_json) } /* Allocate memory. */ - str = pnt = XMALLOC(MTYPE_COMMUNITY_STR, len); + str = XCALLOC(MTYPE_COMMUNITY_STR, len); first = 1; /* Fill in string. */ @@ -308,12 +307,11 @@ static void set_community_string(struct community *com, bool make_json) if (first) first = 0; else - *pnt++ = ' '; + strlcat(str, " ", len); switch (comval) { case COMMUNITY_INTERNET: - strcpy(pnt, "internet"); - pnt += strlen("internet"); + strlcat(str, "internet", len); if (make_json) { json_string = json_object_new_string("internet"); @@ -322,8 +320,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_GSHUT: - strcpy(pnt, "graceful-shutdown"); - pnt += strlen("graceful-shutdown"); + strlcat(str, "graceful-shutdown", len); if (make_json) { json_string = json_object_new_string( "gracefulShutdown"); @@ -332,8 +329,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_ACCEPT_OWN: - strcpy(pnt, "accept-own"); - pnt += strlen("accept-own"); + strlcat(str, "accept-own", len); if (make_json) { json_string = json_object_new_string( "acceptown"); @@ -342,8 +338,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4: - strcpy(pnt, "route-filter-translated-v4"); - pnt += strlen("route-filter-translated-v4"); + strlcat(str, "route-filter-translated-v4", len); if (make_json) { json_string = json_object_new_string( "routeFilterTranslatedV4"); @@ -352,8 +347,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_ROUTE_FILTER_v4: - strcpy(pnt, "route-filter-v4"); - pnt += strlen("route-filter-v4"); + strlcat(str, "route-filter-v4", len); if (make_json) { json_string = json_object_new_string( "routeFilterV4"); @@ -362,8 +356,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6: - strcpy(pnt, "route-filter-translated-v6"); - pnt += strlen("route-filter-translated-v6"); + strlcat(str, "route-filter-translated-v6", len); if (make_json) { json_string = json_object_new_string( "routeFilterTranslatedV6"); @@ -372,8 +365,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_ROUTE_FILTER_v6: - strcpy(pnt, "route-filter-v6"); - pnt += strlen("route-filter-v6"); + strlcat(str, "route-filter-v6", len); if (make_json) { json_string = json_object_new_string( "routeFilterV6"); @@ -382,8 +374,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_LLGR_STALE: - strcpy(pnt, "llgr-stale"); - pnt += strlen("llgr-stale"); + strlcat(str, "llgr-stale", len); if (make_json) { json_string = json_object_new_string( "llgrStale"); @@ -392,8 +383,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_NO_LLGR: - strcpy(pnt, "no-llgr"); - pnt += strlen("no-llgr"); + strlcat(str, "no-llgr", len); if (make_json) { json_string = json_object_new_string( "noLlgr"); @@ -402,8 +392,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_ACCEPT_OWN_NEXTHOP: - strcpy(pnt, "accept-own-nexthop"); - pnt += strlen("accept-own-nexthop"); + strlcat(str, "accept-own-nexthop", len); if (make_json) { json_string = json_object_new_string( "acceptownnexthop"); @@ -412,8 +401,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_BLACKHOLE: - strcpy(pnt, "blackhole"); - pnt += strlen("blackhole"); + strlcat(str, "blackhole", len); if (make_json) { json_string = json_object_new_string( "blackhole"); @@ -422,8 +410,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_NO_EXPORT: - strcpy(pnt, "no-export"); - pnt += strlen("no-export"); + strlcat(str, "no-export", len); if (make_json) { json_string = json_object_new_string("noExport"); @@ -432,8 +419,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_NO_ADVERTISE: - strcpy(pnt, "no-advertise"); - pnt += strlen("no-advertise"); + strlcat(str, "no-advertise", len); if (make_json) { json_string = json_object_new_string("noAdvertise"); @@ -442,8 +428,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_LOCAL_AS: - strcpy(pnt, "local-AS"); - pnt += strlen("local-AS"); + strlcat(str, "local-AS", len); if (make_json) { json_string = json_object_new_string("localAs"); json_object_array_add(json_community_list, @@ -451,8 +436,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_NO_PEER: - strcpy(pnt, "no-peer"); - pnt += strlen("no-peer"); + strlcat(str, "no-peer", len); if (make_json) { json_string = json_object_new_string("noPeer"); json_object_array_add(json_community_list, @@ -462,17 +446,17 @@ static void set_community_string(struct community *com, bool make_json) default: as = (comval >> 16) & 0xFFFF; val = comval & 0xFFFF; - sprintf(pnt, "%u:%d", as, val); + char buf[32]; + snprintf(buf, sizeof(buf), "%u:%d", as, val); + strlcat(str, buf, len); if (make_json) { - json_string = json_object_new_string(pnt); + json_string = json_object_new_string(buf); json_object_array_add(json_community_list, json_string); } - pnt += strlen(pnt); break; } } - *pnt = '\0'; if (make_json) { json_object_string_add(com->json, "string", str); @@ -574,7 +558,7 @@ char *community_str(struct community *com, bool make_json) /* Make hash value of community attribute. This function is used by hash package.*/ -unsigned int community_hash_make(struct community *com) +unsigned int community_hash_make(const struct community *com) { uint32_t *pnt = (uint32_t *)com->val; @@ -897,7 +881,7 @@ struct hash *community_hash(void) void community_init(void) { comhash = - hash_create((unsigned int (*)(void *))community_hash_make, + hash_create((unsigned int (*)(const void *))community_hash_make, (bool (*)(const void *, const void *))community_cmp, "BGP Community Hash"); } @@ -957,7 +941,7 @@ void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate, */ if (aggregate->community_hash == NULL) aggregate->community_hash = hash_create( - (unsigned int (*)(void *))community_hash_make, + (unsigned int (*)(const void *))community_hash_make, (bool (*)(const void *, const void *))community_cmp, "BGP Aggregator community hash"); diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h index 4ff4d214a560..f761a8f5e0f6 100644 --- a/bgpd/bgp_community.h +++ b/bgpd/bgp_community.h @@ -75,7 +75,7 @@ extern struct community *community_parse(uint32_t *, unsigned short); extern struct community *community_intern(struct community *); extern void community_unintern(struct community **); extern char *community_str(struct community *, bool make_json); -extern unsigned int community_hash_make(struct community *); +extern unsigned int community_hash_make(const struct community *); extern struct community *community_str2com(const char *); extern int community_match(const struct community *, const struct community *); extern bool community_cmp(const struct community *c1, diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 8ef398952d3a..76bd0e815e67 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -241,7 +241,7 @@ void ecommunity_unintern(struct ecommunity **ecom) } /* Utinity function to make hash key. */ -unsigned int ecommunity_hash_make(void *arg) +unsigned int ecommunity_hash_make(const void *arg) { const struct ecommunity *ecom = arg; int size = ecom->size * ECOMMUNITY_SIZE; diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 62b213775391..79be4ee4227d 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -22,6 +22,7 @@ #define _QUAGGA_BGP_ECOMMUNITY_H #include "bgpd/bgp_route.h" +#include "bgpd/bgpd.h" /* High-order octet of the Extended Communities type field. */ #define ECOMMUNITY_ENCODE_AS 0x00 @@ -162,7 +163,7 @@ extern struct ecommunity *ecommunity_uniq_sort(struct ecommunity *); extern struct ecommunity *ecommunity_intern(struct ecommunity *); extern bool ecommunity_cmp(const void *arg1, const void *arg2); extern void ecommunity_unintern(struct ecommunity **); -extern unsigned int ecommunity_hash_make(void *); +extern unsigned int ecommunity_hash_make(const void *); extern struct ecommunity *ecommunity_str2com(const char *, int, int); extern char *ecommunity_ecom2str(struct ecommunity *, int, int); extern void ecommunity_strfree(char **s); diff --git a/bgpd/bgp_errors.c b/bgpd/bgp_errors.c index 753ee6baf100..6e181697d64c 100644 --- a/bgpd/bgp_errors.c +++ b/bgpd/bgp_errors.c @@ -121,12 +121,6 @@ static struct log_ref ferr_bgp_warn[] = { .description = "BGP attempted to setup TCP MD5 configuration on the socket as per configuration but was unable to", .suggestion = "Please collect log files and open Issue", }, - { - .code = EC_BGP_NO_SOCKOPT_MARK, - .title = "Unable to set socket MARK option", - .description = "BGP attempted to set the SO_MARK option for a socket and was unable to do so", - .suggestion = "Please collect log files and open Issue", - }, { .code = EC_BGP_EVPN_PMSI_PRESENT, .title = "BGP Received a EVPN NLRI with PMSI included", diff --git a/bgpd/bgp_errors.h b/bgpd/bgp_errors.h index 13bd318e274c..39d043ff13c4 100644 --- a/bgpd/bgp_errors.h +++ b/bgpd/bgp_errors.h @@ -88,7 +88,6 @@ enum bgp_log_refs { EC_BGP_UPDATE_PACKET_LONG, EC_BGP_UNRECOGNIZED_CAPABILITY, EC_BGP_NO_TCP_MD5, - EC_BGP_NO_SOCKOPT_MARK, EC_BGP_EVPN_PMSI_PRESENT, EC_BGP_EVPN_VPN_VNI, EC_BGP_EVPN_ESI, diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 5d191a787ccf..c4b2a606c58e 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -83,9 +83,9 @@ static int evpn_vtep_ip_cmp(void *p1, void *p2) /* * Make hash key for ESI. */ -static unsigned int esi_hash_keymake(void *p) +static unsigned int esi_hash_keymake(const void *p) { - struct evpnes *pes = p; + const struct evpnes *pes = p; const void *pnt = (void *)pes->esi.val; return jhash(pnt, ESI_BYTES, 0xa5a5a55a); @@ -111,9 +111,9 @@ static bool esi_cmp(const void *p1, const void *p2) /* * Make vni hash key. */ -static unsigned int vni_hash_key_make(void *p) +static unsigned int vni_hash_key_make(const void *p) { - struct bgpevpn *vpn = p; + const struct bgpevpn *vpn = p; return (jhash_1word(vpn->vni, 0)); } @@ -143,10 +143,10 @@ static int vni_list_cmp(void *p1, void *p2) /* * Make vrf import route target hash key. */ -static unsigned int vrf_import_rt_hash_key_make(void *p) +static unsigned int vrf_import_rt_hash_key_make(const void *p) { - struct vrf_irt_node *irt = p; - char *pnt = irt->rt.val; + const struct vrf_irt_node *irt = p; + const char *pnt = irt->rt.val; return jhash(pnt, 8, 0x5abc1234); } @@ -259,10 +259,10 @@ static int is_vrf_present_in_irt_vrfs(struct list *vrfs, struct bgp *bgp_vrf) /* * Make import route target hash key. */ -static unsigned int import_rt_hash_key_make(void *p) +static unsigned int import_rt_hash_key_make(const void *p) { - struct irt_node *irt = p; - char *pnt = irt->rt.val; + const struct irt_node *irt = p; + const char *pnt = irt->rt.val; return jhash(pnt, 8, 0xdeadbeef); } @@ -2472,7 +2472,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, if (bgp_debug_zebra(NULL)) { zlog_debug( - "installing evpn prefix %s as ip prefix %s in vrf %s", + "import evpn prefix %s as ip prefix %s in vrf %s", prefix2str(evp, buf, sizeof(buf)), prefix2str(pp, buf1, sizeof(buf)), vrf_id_to_name(bgp_vrf->vrf_id)); @@ -4921,7 +4921,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, if (addpath_encoded) { /* When packet overflow occurs return immediately. */ if (pnt + BGP_ADDPATH_ID_LEN > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; addpath_id = ntohl(*((uint32_t *)pnt)); pnt += BGP_ADDPATH_ID_LEN; @@ -4929,14 +4929,14 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, /* All EVPN NLRI types start with type and length. */ if (pnt + 2 > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_EVPN_MISSING_TYPE; rtype = *pnt++; psize = *pnt++; /* When packet overflow occur return immediately. */ if (pnt + psize > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; switch (rtype) { case BGP_EVPN_MAC_IP_ROUTE: @@ -4947,7 +4947,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, EC_BGP_EVPN_FAIL, "%u:%s - Error in processing EVPN type-2 NLRI size %d", peer->bgp->vrf_id, peer->host, psize); - return -1; + return BGP_NLRI_PARSE_ERROR_EVPN_TYPE2_SIZE; } break; @@ -4959,7 +4959,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, EC_BGP_PKT_PROCESS, "%u:%s - Error in processing EVPN type-3 NLRI size %d", peer->bgp->vrf_id, peer->host, psize); - return -1; + return BGP_NLRI_PARSE_ERROR_EVPN_TYPE3_SIZE; } break; @@ -4971,7 +4971,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, EC_BGP_PKT_PROCESS, "%u:%s - Error in processing EVPN type-4 NLRI size %d", peer->bgp->vrf_id, peer->host, psize); - return -1; + return BGP_NLRI_PARSE_ERROR_EVPN_TYPE4_SIZE; } break; @@ -4983,7 +4983,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, EC_BGP_PKT_PROCESS, "%u:%s - Error in processing EVPN type-5 NLRI size %d", peer->bgp->vrf_id, peer->host, psize); - return -1; + return BGP_NLRI_PARSE_ERROR_EVPN_TYPE5_SIZE; } break; @@ -4994,9 +4994,9 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, /* Packet length consistency check. */ if (pnt != lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; - return 0; + return BGP_NLRI_PARSE_OK; } /* diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 4dccc89f5235..67b0079c37bf 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -681,7 +681,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, json_path = json_object_new_array(); if (detail) - route_vty_out_detail(vty, bgp, &rn->p, pi, + route_vty_out_detail(vty, bgp, rn, pi, AFI_L2VPN, SAFI_EVPN, json_path); else @@ -1010,14 +1010,17 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, struct bgp_path_info *pi; int rd_header; int header = 1; + char rd_str[BUFSIZ]; + char buf[BUFSIZ]; unsigned long output_count = 0; unsigned long total_count = 0; json_object *json = NULL; json_object *json_nroute = NULL; json_object *json_array = NULL; - json_object *json_scode = NULL; - json_object *json_ocode = NULL; + json_object *json_prefix_info = NULL; + + memset(rd_str, 0, BUFSIZ); bgp = bgp_get_evpn(); if (bgp == NULL) { @@ -1028,31 +1031,13 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, return CMD_WARNING; } - if (use_json) { - json_scode = json_object_new_object(); - json_ocode = json_object_new_object(); + if (use_json) json = json_object_new_object(); - json_nroute = json_object_new_object(); - - json_object_string_add(json_scode, "suppressed", "s"); - json_object_string_add(json_scode, "damped", "d"); - json_object_string_add(json_scode, "history", "h"); - json_object_string_add(json_scode, "valid", "*"); - json_object_string_add(json_scode, "best", ">"); - json_object_string_add(json_scode, "internal", "i"); - - json_object_string_add(json_ocode, "igp", "i"); - json_object_string_add(json_ocode, "egp", "e"); - json_object_string_add(json_ocode, "incomplete", "?"); - } for (rn = bgp_table_top(bgp->rib[afi][SAFI_EVPN]); rn; rn = bgp_route_next(rn)) { uint64_t tbl_ver; - if (use_json) - continue; /* XXX json TODO */ - if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) continue; @@ -1063,7 +1048,20 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, rd_header = 1; tbl_ver = table->version; - for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) + for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) { + if (use_json) { + json_array = json_object_new_array(); + json_prefix_info = json_object_new_object(); + + json_object_string_add(json_prefix_info, + "prefix", bgp_evpn_route2str( + (struct prefix_evpn *)&rm->p, buf, + BUFSIZ)); + + json_object_int_add(json_prefix_info, + "prefixLen", rm->p.prefixlen); + } + for (pi = bgp_node_get_bgp_path_info(rm); pi; pi = pi->next) { total_count++; @@ -1075,28 +1073,23 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, pi->peer->su_remote, su)) continue; } - if (header == 0) { + if (header) { if (use_json) { - if (option - == SHOW_DISPLAY_TAGS) { - json_object_int_add( - json, - "bgpTableVersion", - tbl_ver); - json_object_string_add( - json, - "bgpLocalRouterId", - inet_ntoa( - bgp->router_id)); - json_object_object_add( - json, - "bgpStatusCodes", - json_scode); - json_object_object_add( - json, - "bgpOriginCodes", - json_ocode); - } + json_object_int_add( + json, "bgpTableVersion", + tbl_ver); + json_object_string_add( + json, + "bgpLocalRouterId", + inet_ntoa( + bgp->router_id)); + json_object_int_add( + json, + "defaultLocPrf", + bgp->default_local_pref); + json_object_int_add( + json, "localAS", + bgp->as); } else { if (option == SHOW_DISPLAY_TAGS) vty_out(vty, @@ -1139,21 +1132,23 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, else if (type == RD_TYPE_IP) decode_rd_ip(pnt + 2, &rd_ip); if (use_json) { - char buffer[BUFSIZ]; + json_nroute = + json_object_new_object(); if (type == RD_TYPE_AS || type == RD_TYPE_AS4) - sprintf(buffer, "%u:%d", + sprintf(rd_str, "%u:%d", rd_as.as, rd_as.val); else if (type == RD_TYPE_IP) - sprintf(buffer, "%s:%d", + sprintf(rd_str, "%s:%d", inet_ntoa( rd_ip.ip), rd_ip.val); json_object_string_add( json_nroute, - "routeDistinguisher", - buffer); + "rd", + rd_str); + } else { vty_out(vty, "Route Distinguisher: "); @@ -1176,10 +1171,7 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, } rd_header = 0; } - if (use_json) - json_array = json_object_new_array(); - else - json_array = NULL; + if (option == SHOW_DISPLAY_TAGS) route_vty_out_tag(vty, &rm->p, pi, 0, SAFI_EVPN, @@ -1192,13 +1184,33 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, SAFI_EVPN, json_array); output_count++; } - /* XXX json */ + if (use_json) { + json_object_object_add(json_prefix_info, + "paths", json_array); + json_object_object_add(json_nroute, buf, + json_prefix_info); + } + } + + if (use_json) + json_object_object_add(json, rd_str, json_nroute); + } + + if (use_json) { + json_object_int_add(json, "numPrefix", output_count); + json_object_int_add(json, "totalPrefix", total_count); + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else { + if (output_count == 0) + vty_out(vty, "No prefixes displayed, %ld exist\n", + total_count); + else + vty_out(vty, + "\nDisplayed %ld out of %ld total prefixes\n", + output_count, total_count); } - if (output_count == 0) - vty_out(vty, "No prefixes displayed, %ld exist\n", total_count); - else - vty_out(vty, "\nDisplayed %ld out of %ld total prefixes\n", - output_count, total_count); return CMD_SUCCESS; } @@ -1542,14 +1554,15 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes, DEFUN(show_ip_bgp_l2vpn_evpn_all_overlay, show_ip_bgp_l2vpn_evpn_all_overlay_cmd, - "show [ip] bgp l2vpn evpn all overlay", + "show [ip] bgp l2vpn evpn all overlay [json]", SHOW_STR IP_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR "Display information about all EVPN NLRIs\n" - "Display BGP Overlay Information for prefixes\n") + "Display BGP Overlay Information for prefixes\n" + JSON_STR) { return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, NULL, SHOW_DISPLAY_OVERLAY, @@ -2077,7 +2090,7 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, &rn->p, pi, afi, safi, + route_vty_out_detail(vty, bgp, rn, pi, afi, safi, json_path); if (json) @@ -2147,7 +2160,7 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, &rn->p, pi, afi, safi, + route_vty_out_detail(vty, bgp, rn, pi, afi, safi, json_path); if (json) @@ -2254,7 +2267,7 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, &rn->p, pi, afi, safi, + route_vty_out_detail(vty, bgp, rn, pi, afi, safi, json_path); if (json) @@ -2359,7 +2372,7 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, &rn->p, pi, afi, safi, + route_vty_out_detail(vty, bgp, rn, pi, afi, safi, json_path); if (json) @@ -2509,7 +2522,7 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, if (detail) { route_vty_out_detail( - vty, bgp, &rn->p, pi, AFI_L2VPN, + vty, bgp, rn, pi, AFI_L2VPN, SAFI_EVPN, json_path); } else route_vty_out(vty, &rn->p, pi, 0, @@ -3259,9 +3272,6 @@ DEFPY(bgp_evpn_advertise_svi_ip_vni, if (!bgp) return CMD_WARNING; - if (!vpn) - return CMD_WARNING; - if (no) evpn_set_advertise_svi_macip(bgp, vpn, 0); else diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c index b262f6b56d70..d4f608d40feb 100644 --- a/bgpd/bgp_filter.c +++ b/bgpd/bgp_filter.c @@ -407,7 +407,7 @@ DEFUN(as_path, bgp_as_path_cmd, "Regular expression access list name\n" "Specify packets to reject\n" "Specify packets to forward\n" - "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n") + "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") { int idx = 0; enum as_filter_type type; @@ -475,7 +475,7 @@ ALIAS(as_path, ip_as_path_cmd, "Regular expression access list name\n" "Specify packets to reject\n" "Specify packets to forward\n" - "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n") + "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") DEFUN(no_as_path, no_bgp_as_path_cmd, "no bgp as-path access-list WORD LINE...", @@ -486,7 +486,7 @@ DEFUN(no_as_path, no_bgp_as_path_cmd, "Regular expression access list name\n" "Specify packets to reject\n" "Specify packets to forward\n" - "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n") + "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") { int idx = 0; enum as_filter_type type; @@ -563,7 +563,7 @@ ALIAS(no_as_path, no_ip_as_path_cmd, "Regular expression access list name\n" "Specify packets to reject\n" "Specify packets to forward\n" - "A regular-expression (1234567890_(^|[,{}() ]|$)) to match the BGP AS paths\n") + "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") DEFUN (no_as_path_all, no_bgp_as_path_all_cmd, diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index ab8bfcb770c7..9554638735f1 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -105,14 +105,14 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, if (afi == AFI_IP6) { flog_err(EC_LIB_DEVELOPMENT, "BGP flowspec IPv6 not supported"); - return -1; + return BGP_NLRI_PARSE_ERROR_FLOWSPEC_IPV6_NOT_SUPPORTED; } if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT) { flog_err(EC_BGP_FLOWSPEC_PACKET, "BGP flowspec nlri length maximum reached (%u)", packet->length); - return -1; + return BGP_NLRI_PARSE_ERROR_FLOWSPEC_NLRI_SIZELIMIT; } for (; pnt < lim; pnt += psize) { @@ -121,7 +121,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, /* All FlowSpec NLRI begin with length. */ if (pnt + 1 > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; psize = *pnt++; @@ -131,13 +131,13 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, EC_BGP_FLOWSPEC_PACKET, "Flowspec NLRI length inconsistent ( size %u seen)", psize); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; } if (bgp_fs_nlri_validate(pnt, psize) < 0) { flog_err( EC_BGP_FLOWSPEC_PACKET, "Bad flowspec format or NLRI options not supported"); - return -1; + return BGP_NLRI_PARSE_ERROR_FLOWSPEC_BAD_FORMAT; } p.family = AF_FLOWSPEC; p.prefixlen = 0; @@ -192,8 +192,8 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, flog_err(EC_BGP_FLOWSPEC_INSTALLATION, "Flowspec NLRI failed to be %s.", attr ? "added" : "withdrawn"); - return -1; + return BGP_NLRI_PARSE_ERROR; } } - return 0; + return BGP_NLRI_PARSE_OK; } diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 447d8da61372..dd765731dcab 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -949,9 +949,15 @@ void bgp_fsm_change_status(struct peer *peer, int status) else if ((peer->status == Established) && (status != Established)) bgp->established_peers--; - if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS)) - zlog_debug("%s : vrf %u, established_peers %u", __func__, - bgp->vrf_id, bgp->established_peers); + if (bgp_debug_neighbor_events(peer)) { + struct vrf *vrf = vrf_lookup_by_id(bgp->vrf_id); + + zlog_debug("%s : vrf %s(%u), Status: %s established_peers %u", __func__, + vrf ? vrf->name : "Unknown", bgp->vrf_id, + lookup_msg(bgp_status_msg, status, NULL), + bgp->established_peers); + } + /* Set to router ID to the value provided by RIB if there are no peers * in the established state and peer count did not change */ @@ -1115,8 +1121,6 @@ int bgp_stop(struct peer *peer) /* Reset peer synctime */ peer->synctime = 0; - - bgp_bfd_deregister_peer(peer); } /* stop keepalives */ @@ -1754,7 +1758,7 @@ static int bgp_fsm_exeption(struct peer *peer) return (bgp_stop(peer)); } -void bgp_fsm_nht_update(struct peer *peer, int valid) +void bgp_fsm_event_update(struct peer *peer, int valid) { if (!peer) return; @@ -1788,7 +1792,6 @@ void bgp_fsm_nht_update(struct peer *peer, int valid) } } - /* Finite State Machine structure */ static const struct { int (*func)(struct peer *); diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index d021c9884a55..3476a3c3a981 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -57,7 +57,7 @@ #define FSM_PEER_TRANSITIONED 3 /* Prototypes. */ -extern void bgp_fsm_nht_update(struct peer *, int valid); +extern void bgp_fsm_event_update(struct peer *peer, int valid); extern int bgp_event(struct thread *); extern int bgp_event_update(struct peer *, int event); extern int bgp_stop(struct peer *peer); diff --git a/bgpd/bgp_keepalives.c b/bgpd/bgp_keepalives.c index c2f0baff76ca..bec3bdcb8d0e 100644 --- a/bgpd/bgp_keepalives.c +++ b/bgpd/bgp_keepalives.c @@ -131,9 +131,9 @@ static bool peer_hash_cmp(const void *f, const void *s) return p1->peer == p2->peer; } -static unsigned int peer_hash_key(void *arg) +static unsigned int peer_hash_key(const void *arg) { - struct pkat *pkat = arg; + const struct pkat *pkat = arg; return (uintptr_t)pkat->peer; } diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c index a219c407dabd..951165084277 100644 --- a/bgpd/bgp_label.c +++ b/bgpd/bgp_label.c @@ -355,7 +355,7 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, /* When packet overflow occurs return immediately. */ if (pnt + BGP_ADDPATH_ID_LEN > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; addpath_id = ntohl(*((uint32_t *)pnt)); pnt += BGP_ADDPATH_ID_LEN; @@ -372,7 +372,7 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / L-U (prefix length %d exceeds packet size %u)", peer->host, prefixlen, (uint)(lim - pnt)); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; } /* Fill in the labels */ @@ -387,12 +387,12 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, peer->host, prefixlen); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_INVAL_NETWORK); - return -1; + return BGP_NLRI_PARSE_ERROR_LABEL_LENGTH; } if ((afi == AFI_IP && p.prefixlen > 32) || (afi == AFI_IP6 && p.prefixlen > 128)) - return -1; + return BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH; /* Fetch prefix from NLRI packet */ memcpy(&p.u.prefix, pnt + llen, psize - llen); @@ -463,8 +463,8 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / L-U (%zu data remaining after parsing)", peer->host, lim - pnt); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } - return 0; + return BGP_NLRI_PARSE_OK; } diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c index 69dd0f9daca6..71c0c8c7c6ff 100644 --- a/bgpd/bgp_labelpool.c +++ b/bgpd/bgp_labelpool.c @@ -25,7 +25,6 @@ #include "stream.h" #include "mpls.h" #include "vty.h" -#include "fifo.h" #include "linklist.h" #include "skiplist.h" #include "workqueue.h" @@ -50,34 +49,10 @@ static struct labelpool *lp; #define LP_CHUNK_SIZE 50 DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CHUNK, "BGP Label Chunk") -DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO") +DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO item") DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CB, "BGP Dynamic Label Assignment") DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CBQ, "BGP Dynamic Label Callback") -#define LABEL_FIFO_ADD(F, N) \ - do { \ - FIFO_ADD((F), (N)); \ - (F)->count++; \ - } while (0) - -#define LABEL_FIFO_DEL(F, N) \ - do { \ - FIFO_DEL((N)); \ - (F)->count--; \ - } while (0) - -#define LABEL_FIFO_INIT(F) \ - do { \ - FIFO_INIT((F)); \ - (F)->count = 0; \ - } while (0) - -#define LABEL_FIFO_COUNT(F) ((F)->count) - -#define LABEL_FIFO_EMPTY(F) FIFO_EMPTY(F) - -#define LABEL_FIFO_HEAD(F) ((F)->next == (F) ? NULL : (F)->next) - struct lp_chunk { uint32_t first; uint32_t last; @@ -98,15 +73,13 @@ struct lp_lcb { int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc); }; -/* XXX same first elements as "struct fifo" */ struct lp_fifo { - struct lp_fifo *next; - struct lp_fifo *prev; - - uint32_t count; + struct lp_fifo_item fifo; struct lp_lcb lcb; }; +DECLARE_LIST(lp_fifo, struct lp_fifo, fifo) + struct lp_cbq_item { int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc); int type; @@ -199,8 +172,7 @@ void bgp_lp_init(struct thread_master *master, struct labelpool *pool) lp->inuse = skiplist_new(0, NULL, NULL); lp->chunks = list_new(); lp->chunks->del = lp_chunk_free; - lp->requests = XCALLOC(MTYPE_BGP_LABEL_FIFO, sizeof(struct lp_fifo)); - LABEL_FIFO_INIT(lp->requests); + lp_fifo_init(&lp->requests); lp->callback_q = work_queue_new(master, "label callbacks"); lp->callback_q->spec.workfunc = lp_cbq_docallback; @@ -223,13 +195,9 @@ void bgp_lp_finish(void) list_delete(&lp->chunks); - while ((lf = LABEL_FIFO_HEAD(lp->requests))) { - - LABEL_FIFO_DEL(lp->requests, lf); + while ((lf = lp_fifo_pop(&lp->requests))) XFREE(MTYPE_BGP_LABEL_FIFO, lf); - } - XFREE(MTYPE_BGP_LABEL_FIFO, lp->requests); - lp->requests = NULL; + lp_fifo_fini(&lp->requests); work_queue_free_and_null(&lp->callback_q); @@ -385,9 +353,9 @@ void bgp_lp_get( sizeof(struct lp_fifo)); lf->lcb = *lcb; - LABEL_FIFO_ADD(lp->requests, lf); + lp_fifo_add_tail(&lp->requests, lf); - if (LABEL_FIFO_COUNT(lp->requests) > lp->pending_count) { + if (lp_fifo_count(&lp->requests) > lp->pending_count) { if (!zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE)) { lp->pending_count += LP_CHUNK_SIZE; return; @@ -441,11 +409,11 @@ void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last) lp->pending_count -= (last - first + 1); if (debug) { - zlog_debug("%s: %u pending requests", __func__, - LABEL_FIFO_COUNT(lp->requests)); + zlog_debug("%s: %zu pending requests", __func__, + lp_fifo_count(&lp->requests)); } - while ((lf = LABEL_FIFO_HEAD(lp->requests))) { + while ((lf = lp_fifo_first(&lp->requests))) { struct lp_lcb *lcb; void *labelid = lf->lcb.labelid; @@ -504,7 +472,7 @@ void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last) work_queue_add(lp->callback_q, q); finishedrequest: - LABEL_FIFO_DEL(lp->requests, lf); + lp_fifo_del(&lp->requests, lf); XFREE(MTYPE_BGP_LABEL_FIFO, lf); } } @@ -533,7 +501,7 @@ void bgp_lp_event_zebra_up(void) /* * Get label chunk allocation request dispatched to zebra */ - labels_needed = LABEL_FIFO_COUNT(lp->requests) + + labels_needed = lp_fifo_count(&lp->requests) + skiplist_count(lp->inuse); /* round up */ @@ -588,7 +556,7 @@ void bgp_lp_event_zebra_up(void) sizeof(struct lp_fifo)); lf->lcb = *lcb; - LABEL_FIFO_ADD(lp->requests, lf); + lp_fifo_add_tail(&lp->requests, lf); } skiplist_delete_first(lp->inuse); diff --git a/bgpd/bgp_labelpool.h b/bgpd/bgp_labelpool.h index 0507e65489e4..eaa3fce20b10 100644 --- a/bgpd/bgp_labelpool.h +++ b/bgpd/bgp_labelpool.h @@ -31,11 +31,13 @@ #define LP_TYPE_VRF 0x00000001 #define LP_TYPE_BGP_LU 0x00000002 +PREDECL_LIST(lp_fifo) + struct labelpool { struct skiplist *ledger; /* all requests */ struct skiplist *inuse; /* individual labels */ struct list *chunks; /* granted by zebra */ - struct lp_fifo *requests; /* blocked on zebra */ + struct lp_fifo_head requests; /* blocked on zebra */ struct work_queue *callback_q; uint32_t pending_count; /* requested from zebra */ }; diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c index 44766c9b6e30..098374fa9f7b 100644 --- a/bgpd/bgp_lcommunity.c +++ b/bgpd/bgp_lcommunity.c @@ -301,7 +301,7 @@ char *lcommunity_str(struct lcommunity *lcom, bool make_json) } /* Utility function to make hash key. */ -unsigned int lcommunity_hash_make(void *arg) +unsigned int lcommunity_hash_make(const void *arg) { const struct lcommunity *lcom = arg; int size = lcom_length(lcom); diff --git a/bgpd/bgp_lcommunity.h b/bgpd/bgp_lcommunity.h index aa4e8c69fe47..a512395492f9 100644 --- a/bgpd/bgp_lcommunity.h +++ b/bgpd/bgp_lcommunity.h @@ -63,7 +63,7 @@ extern struct lcommunity *lcommunity_uniq_sort(struct lcommunity *); extern struct lcommunity *lcommunity_intern(struct lcommunity *); extern bool lcommunity_cmp(const void *arg1, const void *arg2); extern void lcommunity_unintern(struct lcommunity **); -extern unsigned int lcommunity_hash_make(void *); +extern unsigned int lcommunity_hash_make(const void *); extern struct hash *lcommunity_hash(void); extern struct lcommunity *lcommunity_str2com(const char *); extern int lcommunity_match(const struct lcommunity *, diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c index 49b585402090..f19453fecb68 100644 --- a/bgpd/bgp_mac.c +++ b/bgpd/bgp_mac.c @@ -40,9 +40,9 @@ struct bgp_self_mac { struct list *ifp_list; }; -static unsigned int bgp_mac_hash_key_make(void *data) +static unsigned int bgp_mac_hash_key_make(const void *data) { - struct bgp_self_mac *bsm = data; + const struct bgp_self_mac *bsm = data; return jhash(&bsm->macaddr, ETH_ALEN, 0xa5a5dead); } diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index adba73e40465..abd8586f4c71 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -70,10 +70,6 @@ static const struct option longopts[] = { {"bgp_port", required_argument, NULL, 'p'}, {"listenon", required_argument, NULL, 'l'}, -#if CONFDATE > 20190521 - CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") -#endif - {"retain", no_argument, NULL, 'r'}, {"no_kernel", no_argument, NULL, 'n'}, {"skip_runas", no_argument, NULL, 'S'}, {"ecmp", required_argument, NULL, 'e'}, @@ -367,10 +363,7 @@ FRR_DAEMON_INFO(bgpd, BGP, .vty_port = BGP_VTY_PORT, .privs = &bgpd_privs, .yang_modules = bgpd_yang_modules, .n_yang_modules = array_size(bgpd_yang_modules), ) -#if CONFDATE > 20190521 -CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") -#endif -#define DEPRECATED_OPTIONS "r" +#define DEPRECATED_OPTIONS "" /* Main routine of bgpd. Treatment of argument and start bgp finite state machine is handled at here. */ diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index d5b3d6b1974d..648c3be47e07 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -294,6 +294,10 @@ static struct bgp_path_info_mpath * bgp_path_info_mpath_get(struct bgp_path_info *path) { struct bgp_path_info_mpath *mpath; + + if (!path) + return NULL; + if (!path->mpath) { mpath = bgp_path_info_mpath_new(); if (!mpath) @@ -523,6 +527,7 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, list_delete_node(mp_list, mp_node); bgp_path_info_mpath_dequeue(cur_mpath); if ((mpath_count < maxpaths) + && prev_mpath && bgp_path_info_nexthop_cmp(prev_mpath, cur_mpath)) { bgp_path_info_mpath_enqueue(prev_mpath, diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 4c4659ad5439..6eddd0e1e359 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -140,7 +140,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, /* When packet overflow occurs return immediately. */ if (pnt + BGP_ADDPATH_ID_LEN > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; addpath_id = ntohl(*((uint32_t *)pnt)); pnt += BGP_ADDPATH_ID_LEN; @@ -156,7 +156,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / VPN (prefix length %d less than VPN min length)", peer->host, prefixlen); - return -1; + return BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH; } /* sanity check against packet data */ @@ -165,7 +165,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / VPN (prefix length %d exceeds packet size %u)", peer->host, prefixlen, (uint)(lim - pnt)); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; } /* sanity check against storage for the IP address portion */ @@ -176,7 +176,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, peer->host, prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8, sizeof(p.u)); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } /* Sanity check against max bitlen of the address family */ @@ -187,7 +187,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, peer->host, prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8, p.family, prefix_blen(&p)); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } /* Copy label to prefix. */ @@ -245,7 +245,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / VPN (%zu data remaining after parsing)", peer->host, lim - pnt); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } return 0; @@ -319,9 +319,6 @@ void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi) bgp->name_pretty, bgp->vrf_id); } - if (label == BGP_PREVENT_VRF_2_VRF_LEAK) - label = MPLS_LABEL_NONE; - zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP); bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label; } diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 6a5c2c4b38f9..8e18ed75295c 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -588,8 +588,6 @@ static int bgp_update_source(struct peer *peer) return ret; } -#define DATAPLANE_MARK 254 /* main table ID */ - /* BGP try to connect to the peer. */ int bgp_connect(struct peer *peer) { @@ -619,10 +617,6 @@ int bgp_connect(struct peer *peer) sockopt_reuseaddr(peer->fd); sockopt_reuseport(peer->fd); - if (sockopt_mark_default(peer->fd, DATAPLANE_MARK, &bgpd_privs) < 0) - flog_warn(EC_BGP_NO_SOCKOPT_MARK, - "Unable to set mark on FD for peer %s, err=%s", - peer->host, safe_strerror(errno)); #ifdef IPTOS_PREC_INTERNETCONTROL frr_elevate_privs(&bgpd_privs) { diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index de97b73c7210..a8c507832cce 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -114,7 +114,7 @@ static void bgp_tip_hash_free(void *addr) XFREE(MTYPE_TIP_ADDR, addr); } -static unsigned int bgp_tip_hash_key_make(void *p) +static unsigned int bgp_tip_hash_key_make(const void *p) { const struct tip_addr *addr = p; @@ -237,7 +237,7 @@ static void bgp_address_hash_free(void *data) XFREE(MTYPE_BGP_ADDR, addr); } -static unsigned int bgp_address_hash_key_make(void *p) +static unsigned int bgp_address_hash_key_make(const void *p) { const struct bgp_addr *addr = p; @@ -505,6 +505,77 @@ int bgp_multiaccess_check_v4(struct in_addr nexthop, struct peer *peer) return (ret); } +int bgp_multiaccess_check_v6(struct in6_addr nexthop, struct peer *peer) +{ + struct bgp_node *rn1; + struct bgp_node *rn2; + struct prefix p; + int ret; + + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_BITLEN; + p.u.prefix6 = nexthop; + + rn1 = bgp_node_match(peer->bgp->connected_table[AFI_IP6], &p); + if (!rn1) + return 0; + + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_BITLEN; + p.u.prefix6 = peer->su.sin6.sin6_addr; + + rn2 = bgp_node_match(peer->bgp->connected_table[AFI_IP6], &p); + if (!rn2) { + bgp_unlock_node(rn1); + return 0; + } + + ret = (rn1 == rn2) ? 1 : 0; + + bgp_unlock_node(rn1); + bgp_unlock_node(rn2); + + return ret; +} + +int bgp_subgrp_multiaccess_check_v6(struct in6_addr nexthop, + struct update_subgroup *subgrp) +{ + struct bgp_node *rn1 = NULL, *rn2 = NULL; + struct peer_af *paf = NULL; + struct prefix p = {0}, np = {0}; + struct bgp *bgp = NULL; + + np.family = AF_INET6; + np.prefixlen = IPV6_MAX_BITLEN; + np.u.prefix6 = nexthop; + + p.family = AF_INET; + p.prefixlen = IPV6_MAX_BITLEN; + + bgp = SUBGRP_INST(subgrp); + rn1 = bgp_node_match(bgp->connected_table[AFI_IP6], &np); + if (!rn1) + return 0; + + SUBGRP_FOREACH_PEER (subgrp, paf) { + + p.u.prefix6 = paf->peer->su.sin6.sin6_addr; + rn2 = bgp_node_match(bgp->connected_table[AFI_IP6], &p); + if (rn1 == rn2) { + bgp_unlock_node(rn1); + bgp_unlock_node(rn2); + return 1; + } + + if (rn2) + bgp_unlock_node(rn2); + } + + bgp_unlock_node(rn1); + return 0; +} + int bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, struct update_subgroup *subgrp) { diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index f06fae5706a9..d35f1ad5207d 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -74,11 +74,19 @@ struct tip_addr { int refcnt; }; +struct bgp_addrv6 { + struct in6_addr addrv6; + struct list *ifp_name_list; +}; + extern void bgp_connected_add(struct bgp *bgp, struct connected *c); extern void bgp_connected_delete(struct bgp *bgp, struct connected *c); extern int bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, struct update_subgroup *subgrp); -extern int bgp_multiaccess_check_v4(struct in_addr, struct peer *); +extern int bgp_subgrp_multiaccess_check_v6(struct in6_addr nexthop, + struct update_subgroup *subgrp); +extern int bgp_multiaccess_check_v4(struct in_addr nexthop, struct peer *peer); +extern int bgp_multiaccess_check_v6(struct in6_addr nexthop, struct peer *peer); extern int bgp_config_write_scan_time(struct vty *); extern int bgp_nexthop_self(struct bgp *, struct in_addr); extern struct bgp_nexthop_cache *bnc_new(void); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 6e85abc8dfc8..fdfa15b445f7 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -474,8 +474,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) continue; for (oldnh = bnc->nexthop; oldnh; oldnh = oldnh->next) - if (nexthop_same_no_recurse(oldnh, nexthop) && - nexthop_labels_match(oldnh, nexthop)) + if (nexthop_same(oldnh, nexthop)) break; if (!oldnh) @@ -793,7 +792,7 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) if (BGP_DEBUG(nht, NHT)) zlog_debug("%s: Updating peer (%s) status with NHT", __FUNCTION__, peer->host); - bgp_fsm_nht_update(peer, bgp_isvalid_nexthop(bnc)); + bgp_fsm_event_update(peer, bgp_isvalid_nexthop(bnc)); SET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); } diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 7b76d7e83e88..655a4745cba0 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -308,7 +308,7 @@ int bgp_nlri_parse(struct peer *peer, struct attr *attr, case SAFI_FLOWSPEC: return bgp_nlri_parse_flowspec(peer, attr, packet, mp_withdraw); } - return -1; + return BGP_NLRI_PARSE_ERROR; } /* @@ -709,12 +709,15 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code, XMALLOC(MTYPE_TMP, bgp_notify.length * 3); for (i = 0; i < bgp_notify.length; i++) if (first) { - sprintf(c, " %02x", data[i]); - strcat(bgp_notify.data, c); + snprintf(c, sizeof(c), " %02x", + data[i]); + strlcat(bgp_notify.data, c, + bgp_notify.length); } else { first = 1; - sprintf(c, "%02x", data[i]); - strcpy(bgp_notify.data, c); + snprintf(c, sizeof(c), "%02x", data[i]); + strlcpy(bgp_notify.data, c, + bgp_notify.length); } } bgp_notify_print(peer, &bgp_notify, "sending"); @@ -1533,6 +1536,17 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) nlris[NLRI_UPDATE].nlri = stream_pnt(s); nlris[NLRI_UPDATE].length = update_len; stream_forward_getp(s, update_len); + + if (CHECK_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI))) { + /* + * We skipped nexthop attribute validation earlier so + * validate the nexthop now. + */ + if (bgp_attr_nexthop_valid(peer, &attr) < 0) { + bgp_attr_unintern_sub(&attr); + return BGP_Stop; + } + } } if (BGP_DEBUG(update, UPDATE_IN)) @@ -1568,10 +1582,11 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) nlri_ret = bgp_nlri_parse(peer, &attr, &nlris[i], 1); break; default: - nlri_ret = -1; + nlri_ret = BGP_NLRI_PARSE_ERROR; } - if (nlri_ret < 0) { + if (nlri_ret < BGP_NLRI_PARSE_OK + && nlri_ret != BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW) { flog_err(EC_BGP_UPDATE_RCV, "%s [Error] Error parsing NLRI", peer->host); if (peer->status == Established) @@ -1688,14 +1703,16 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) XMALLOC(MTYPE_TMP, bgp_notify.length * 3); for (i = 0; i < bgp_notify.length; i++) if (first) { - sprintf(c, " %02x", + snprintf(c, sizeof(c), " %02x", stream_getc(peer->curr)); - strcat(bgp_notify.data, c); + strlcat(bgp_notify.data, c, + bgp_notify.length); } else { first = 1; - sprintf(c, "%02x", - stream_getc(peer->curr)); - strcpy(bgp_notify.data, c); + snprintf(c, sizeof(c), "%02x", + stream_getc(peer->curr)); + strlcpy(bgp_notify.data, c, + bgp_notify.length); } bgp_notify.raw_data = (uint8_t *)peer->notify.data; } @@ -2287,6 +2304,9 @@ int bgp_process_packet(struct thread *thread) __FUNCTION__, peer->host); break; default: + /* Suppress uninitialized variable warning */ + mprc = 0; + (void)mprc; /* * The message type should have been sanitized before * we ever got here. Receipt of a message with an diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 0fddfa75a1e8..5eef6ac6ccc3 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -964,9 +964,9 @@ static void *bgp_pbr_match_entry_alloc_intern(void *arg) return new; } -uint32_t bgp_pbr_match_hash_key(void *arg) +uint32_t bgp_pbr_match_hash_key(const void *arg) { - struct bgp_pbr_match *pbm = (struct bgp_pbr_match *)arg; + const struct bgp_pbr_match *pbm = arg; uint32_t key; key = jhash_1word(pbm->vrf_id, 0x4312abde); @@ -1019,9 +1019,9 @@ bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) return true; } -uint32_t bgp_pbr_rule_hash_key(void *arg) +uint32_t bgp_pbr_rule_hash_key(const void *arg) { - struct bgp_pbr_rule *pbr = (struct bgp_pbr_rule *)arg; + const struct bgp_pbr_rule *pbr = arg; uint32_t key; key = prefix_hash_key(&pbr->src); @@ -1057,12 +1057,12 @@ bool bgp_pbr_rule_hash_equal(const void *arg1, const void *arg2) return true; } -uint32_t bgp_pbr_match_entry_hash_key(void *arg) +uint32_t bgp_pbr_match_entry_hash_key(const void *arg) { - struct bgp_pbr_match_entry *pbme; + const struct bgp_pbr_match_entry *pbme; uint32_t key; - pbme = (struct bgp_pbr_match_entry *)arg; + pbme = arg; key = prefix_hash_key(&pbme->src); key = jhash_1word(prefix_hash_key(&pbme->dst), key); key = jhash(&pbme->dst_port_min, 2, key); @@ -1111,12 +1111,12 @@ bool bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2) return true; } -uint32_t bgp_pbr_action_hash_key(void *arg) +uint32_t bgp_pbr_action_hash_key(const void *arg) { - struct bgp_pbr_action *pbra; + const struct bgp_pbr_action *pbra; uint32_t key; - pbra = (struct bgp_pbr_action *)arg; + pbra = arg; key = jhash_1word(pbra->table_id, 0x4312abde); key = jhash_1word(pbra->fwmark, key); return key; diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index f7fddac7fbf7..b368d8892def 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -273,16 +273,16 @@ extern struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id, extern void bgp_pbr_cleanup(struct bgp *bgp); extern void bgp_pbr_init(struct bgp *bgp); -extern uint32_t bgp_pbr_rule_hash_key(void *arg); +extern uint32_t bgp_pbr_rule_hash_key(const void *arg); extern bool bgp_pbr_rule_hash_equal(const void *arg1, const void *arg2); -extern uint32_t bgp_pbr_action_hash_key(void *arg); +extern uint32_t bgp_pbr_action_hash_key(const void *arg); extern bool bgp_pbr_action_hash_equal(const void *arg1, const void *arg2); -extern uint32_t bgp_pbr_match_entry_hash_key(void *arg); +extern uint32_t bgp_pbr_match_entry_hash_key(const void *arg); extern bool bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2); -extern uint32_t bgp_pbr_match_hash_key(void *arg); +extern uint32_t bgp_pbr_match_hash_key(const void *arg); extern bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 7850666085cc..4de73f124419 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -434,7 +434,8 @@ void bgp_path_info_path_with_addpath_rx_str(struct bgp_path_info *pi, char *buf) static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, struct bgp_path_info *exist, int *paths_eq, struct bgp_maxpaths_cfg *mpath_cfg, int debug, - char *pfx_buf, afi_t afi, safi_t safi) + char *pfx_buf, afi_t afi, safi_t safi, + enum bgp_path_selection_reason *reason) { struct attr *newattr, *existattr; bgp_peer_sort_t new_sort; @@ -463,6 +464,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, /* 0. Null check. */ if (new == NULL) { + *reason = bgp_path_selection_none; if (debug) zlog_debug("%s: new is NULL", pfx_buf); return 0; @@ -472,6 +474,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, bgp_path_info_path_with_addpath_rx_str(new, new_buf); if (exist == NULL) { + *reason = bgp_path_selection_first; if (debug) zlog_debug("%s: %s is the initial bestpath", pfx_buf, new_buf); @@ -514,6 +517,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (newattr->sticky && !existattr->sticky) { + *reason = bgp_path_selection_evpn_sticky_mac; if (debug) zlog_debug( "%s: %s wins over %s due to sticky MAC flag", @@ -522,6 +526,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (!newattr->sticky && existattr->sticky) { + *reason = bgp_path_selection_evpn_sticky_mac; if (debug) zlog_debug( "%s: %s loses to %s due to sticky MAC flag", @@ -534,6 +539,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, exist_mm_seq = mac_mobility_seqnum(existattr); if (new_mm_seq > exist_mm_seq) { + *reason = bgp_path_selection_evpn_seq; if (debug) zlog_debug( "%s: %s wins over %s due to MM seq %u > %u", @@ -543,6 +549,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (new_mm_seq < exist_mm_seq) { + *reason = bgp_path_selection_evpn_seq; if (debug) zlog_debug( "%s: %s loses to %s due to MM seq %u < %u", @@ -557,6 +564,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, */ nh_cmp = bgp_path_info_nexthop_cmp(new, exist); if (nh_cmp < 0) { + *reason = bgp_path_selection_evpn_lower_ip; if (debug) zlog_debug( "%s: %s wins over %s due to same MM seq %u and lower IP %s", @@ -565,6 +573,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, return 1; } if (nh_cmp > 0) { + *reason = bgp_path_selection_evpn_lower_ip; if (debug) zlog_debug( "%s: %s loses to %s due to same MM seq %u and higher IP %s", @@ -579,6 +588,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, exist_weight = existattr->weight; if (new_weight > exist_weight) { + *reason = bgp_path_selection_weight; if (debug) zlog_debug("%s: %s wins over %s due to weight %d > %d", pfx_buf, new_buf, exist_buf, new_weight, @@ -587,6 +597,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (new_weight < exist_weight) { + *reason = bgp_path_selection_weight; if (debug) zlog_debug("%s: %s loses to %s due to weight %d < %d", pfx_buf, new_buf, exist_buf, new_weight, @@ -603,6 +614,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, exist_pref = existattr->local_pref; if (new_pref > exist_pref) { + *reason = bgp_path_selection_local_pref; if (debug) zlog_debug( "%s: %s wins over %s due to localpref %d > %d", @@ -612,6 +624,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (new_pref < exist_pref) { + *reason = bgp_path_selection_local_pref; if (debug) zlog_debug( "%s: %s loses to %s due to localpref %d < %d", @@ -627,6 +640,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, */ if (!(new->sub_type == BGP_ROUTE_NORMAL || new->sub_type == BGP_ROUTE_IMPORTED)) { + *reason = bgp_path_selection_local_route; if (debug) zlog_debug( "%s: %s wins over %s due to preferred BGP_ROUTE type", @@ -636,6 +650,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, if (!(exist->sub_type == BGP_ROUTE_NORMAL || exist->sub_type == BGP_ROUTE_IMPORTED)) { + *reason = bgp_path_selection_local_route; if (debug) zlog_debug( "%s: %s loses to %s due to preferred BGP_ROUTE type", @@ -655,6 +670,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, aspath_hops += aspath_count_confeds(newattr->aspath); if (aspath_hops < (exist_hops + exist_confeds)) { + *reason = bgp_path_selection_confed_as_path; if (debug) zlog_debug( "%s: %s wins over %s due to aspath (with confeds) hopcount %d < %d", @@ -665,6 +681,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (aspath_hops > (exist_hops + exist_confeds)) { + *reason = bgp_path_selection_confed_as_path; if (debug) zlog_debug( "%s: %s loses to %s due to aspath (with confeds) hopcount %d > %d", @@ -677,6 +694,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, int newhops = aspath_count_hops(newattr->aspath); if (newhops < exist_hops) { + *reason = bgp_path_selection_as_path; if (debug) zlog_debug( "%s: %s wins over %s due to aspath hopcount %d < %d", @@ -686,6 +704,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (newhops > exist_hops) { + *reason = bgp_path_selection_as_path; if (debug) zlog_debug( "%s: %s loses to %s due to aspath hopcount %d > %d", @@ -698,6 +717,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, /* 5. Origin check. */ if (newattr->origin < existattr->origin) { + *reason = bgp_path_selection_origin; if (debug) zlog_debug("%s: %s wins over %s due to ORIGIN %s < %s", pfx_buf, new_buf, exist_buf, @@ -707,6 +727,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (newattr->origin > existattr->origin) { + *reason = bgp_path_selection_origin; if (debug) zlog_debug("%s: %s loses to %s due to ORIGIN %s > %s", pfx_buf, new_buf, exist_buf, @@ -732,6 +753,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, exist_med = bgp_med_value(exist->attr, bgp); if (new_med < exist_med) { + *reason = bgp_path_selection_med; if (debug) zlog_debug( "%s: %s wins over %s due to MED %d < %d", @@ -741,6 +763,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (new_med > exist_med) { + *reason = bgp_path_selection_med; if (debug) zlog_debug( "%s: %s loses to %s due to MED %d > %d", @@ -756,6 +779,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, if (new_sort == BGP_PEER_EBGP && (exist_sort == BGP_PEER_IBGP || exist_sort == BGP_PEER_CONFED)) { + *reason = bgp_path_selection_peer; if (debug) zlog_debug( "%s: %s wins over %s due to eBGP peer > iBGP peer", @@ -765,6 +789,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, if (exist_sort == BGP_PEER_EBGP && (new_sort == BGP_PEER_IBGP || new_sort == BGP_PEER_CONFED)) { + *reason = bgp_path_selection_peer; if (debug) zlog_debug( "%s: %s loses to %s due to iBGP peer < eBGP peer", @@ -834,6 +859,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) { if (new_sort == BGP_PEER_CONFED && exist_sort == BGP_PEER_IBGP) { + *reason = bgp_path_selection_confed; if (debug) zlog_debug( "%s: %s wins over %s due to confed-external peer > confed-internal peer", @@ -843,6 +869,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, if (exist_sort == BGP_PEER_CONFED && new_sort == BGP_PEER_IBGP) { + *reason = bgp_path_selection_confed; if (debug) zlog_debug( "%s: %s loses to %s due to confed-internal peer < confed-external peer", @@ -918,6 +945,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, "%s: %s loses to %s after IGP metric comparison", pfx_buf, new_buf, exist_buf); } + *reason = bgp_path_selection_igp_metric; return ret; } @@ -928,6 +956,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, if (!bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID) && new_sort == BGP_PEER_EBGP && exist_sort == BGP_PEER_EBGP) { if (CHECK_FLAG(new->flags, BGP_PATH_SELECTED)) { + *reason = bgp_path_selection_older; if (debug) zlog_debug( "%s: %s wins over %s due to oldest external", @@ -936,6 +965,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (CHECK_FLAG(exist->flags, BGP_PATH_SELECTED)) { + *reason = bgp_path_selection_older; if (debug) zlog_debug( "%s: %s loses to %s due to oldest external", @@ -959,6 +989,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, exist_id.s_addr = exist->peer->remote_id.s_addr; if (ntohl(new_id.s_addr) < ntohl(exist_id.s_addr)) { + *reason = bgp_path_selection_router_id; if (debug) zlog_debug( "%s: %s wins over %s due to Router-ID comparison", @@ -967,6 +998,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (ntohl(new_id.s_addr) > ntohl(exist_id.s_addr)) { + *reason = bgp_path_selection_router_id; if (debug) zlog_debug( "%s: %s loses to %s due to Router-ID comparison", @@ -979,6 +1011,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, exist_cluster = BGP_CLUSTER_LIST_LENGTH(exist->attr); if (new_cluster < exist_cluster) { + *reason = bgp_path_selection_cluster_length; if (debug) zlog_debug( "%s: %s wins over %s due to CLUSTER_LIST length %d < %d", @@ -988,6 +1021,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (new_cluster > exist_cluster) { + *reason = bgp_path_selection_cluster_length; if (debug) zlog_debug( "%s: %s loses to %s due to CLUSTER_LIST length %d > %d", @@ -1001,6 +1035,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, * valid peer information (as the connection may or may not be up). */ if (CHECK_FLAG(exist->flags, BGP_PATH_STALE)) { + *reason = bgp_path_selection_stale; if (debug) zlog_debug( "%s: %s wins over %s due to latter path being STALE", @@ -1009,6 +1044,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (CHECK_FLAG(new->flags, BGP_PATH_STALE)) { + *reason = bgp_path_selection_stale; if (debug) zlog_debug( "%s: %s loses to %s due to former path being STALE", @@ -1017,14 +1053,19 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } /* locally configured routes to advertise do not have su_remote */ - if (new->peer->su_remote == NULL) + if (new->peer->su_remote == NULL) { + *reason = bgp_path_selection_local_configured; return 0; - if (exist->peer->su_remote == NULL) + } + if (exist->peer->su_remote == NULL) { + *reason = bgp_path_selection_local_configured; return 1; + } ret = sockunion_cmp(new->peer->su_remote, exist->peer->su_remote); if (ret == 1) { + *reason = bgp_path_selection_neighbor_ip; if (debug) zlog_debug( "%s: %s loses to %s due to Neighor IP comparison", @@ -1033,6 +1074,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (ret == -1) { + *reason = bgp_path_selection_neighbor_ip; if (debug) zlog_debug( "%s: %s wins over %s due to Neighor IP comparison", @@ -1040,6 +1082,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, return 1; } + *reason = bgp_path_selection_default; if (debug) zlog_debug("%s: %s wins over %s due to nothing left to compare", pfx_buf, new_buf, exist_buf); @@ -1053,12 +1096,13 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, * This version is compatible with */ int bgp_path_info_cmp_compatible(struct bgp *bgp, struct bgp_path_info *new, struct bgp_path_info *exist, char *pfx_buf, - afi_t afi, safi_t safi) + afi_t afi, safi_t safi, + enum bgp_path_selection_reason *reason) { int paths_eq; int ret; ret = bgp_path_info_cmp(bgp, new, exist, &paths_eq, NULL, 0, pfx_buf, - afi, safi); + afi, safi, reason); if (paths_eq) ret = 0; @@ -1219,20 +1263,6 @@ static int bgp_input_modifier(struct peer *peer, struct prefix *p, } } - /* RFC 8212 to prevent route leaks. - * This specification intends to improve this situation by requiring the - * explicit configuration of both BGP Import and Export Policies for any - * External BGP (EBGP) session such as customers, peers, or - * confederation boundaries for all enabled address families. Through - * codification of the aforementioned requirement, operators will - * benefit from consistent behavior across different BGP - * implementations. - */ - if (peer->bgp->ebgp_requires_policy - == DEFAULT_EBGP_POLICY_ENABLED) - if (!bgp_inbound_policy_exists(peer, filter)) - return RMAP_DENY; - /* Route map apply. */ if (rmap) { memset(&rmap_path, 0, sizeof(struct bgp_path_info)); @@ -1781,6 +1811,10 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, peer->rmap_type = 0; if (ret == RMAP_DENYMATCH) { + if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) + zlog_debug("%s [Update:SEND] %s is filtered by route-map", + peer->host, prefix2str(p, buf, sizeof(buf))); + bgp_attr_flush(attr); return 0; } @@ -1854,13 +1888,28 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, * Note: 3rd party nexthop currently implemented for * IPv4 only. */ - if (!bgp_subgrp_multiaccess_check_v4(piattr->nexthop, - subgrp)) + if ((p->family == AF_INET) && + (!bgp_subgrp_multiaccess_check_v4( + piattr->nexthop, + subgrp))) subgroup_announce_reset_nhop( (peer_cap_enhe(peer, afi, safi) ? AF_INET6 : p->family), - attr); + attr); + + if ((p->family == AF_INET6) && + (!bgp_subgrp_multiaccess_check_v6( + piattr->mp_nexthop_global, + subgrp))) + subgroup_announce_reset_nhop( + (peer_cap_enhe(peer, afi, safi) + ? AF_INET6 + : p->family), + attr); + + + } else if (CHECK_FLAG(pi->flags, BGP_PATH_ANNC_NH_SELF)) { /* * This flag is used for leaked vpn-vrf routes @@ -1965,7 +2014,8 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, if (bgp_path_info_cmp( bgp, pi2, new_select, &paths_eq, mpath_cfg, debug, - pfx_buf, afi, safi)) { + pfx_buf, afi, safi, + &rn->reason)) { bgp_path_info_unset_flag( rn, new_select, BGP_PATH_DMED_SELECTED); @@ -2038,7 +2088,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, bgp_path_info_unset_flag(rn, pi, BGP_PATH_DMED_CHECK); if (bgp_path_info_cmp(bgp, pi, new_select, &paths_eq, mpath_cfg, - debug, pfx_buf, afi, safi)) { + debug, pfx_buf, afi, safi, &rn->reason)) { new_select = pi; } } @@ -2094,7 +2144,8 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, } bgp_path_info_cmp(bgp, pi, new_select, &paths_eq, - mpath_cfg, debug, pfx_buf, afi, safi); + mpath_cfg, debug, pfx_buf, afi, safi, + &rn->reason); if (paths_eq) { if (debug) @@ -3049,6 +3100,22 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, goto filtered; } + /* RFC 8212 to prevent route leaks. + * This specification intends to improve this situation by requiring the + * explicit configuration of both BGP Import and Export Policies for any + * External BGP (EBGP) session such as customers, peers, or + * confederation boundaries for all enabled address families. Through + * codification of the aforementioned requirement, operators will + * benefit from consistent behavior across different BGP + * implementations. + */ + if (peer->bgp->ebgp_requires_policy == DEFAULT_EBGP_POLICY_ENABLED) + if (!bgp_inbound_policy_exists(peer, + &peer->filter[afi][safi])) { + reason = "inbound policy missing"; + goto filtered; + } + bgp_attr_dup(&new_attr, attr); /* Apply incoming route-map. @@ -4340,7 +4407,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, /* When packet overflow occurs return immediately. */ if (pnt + BGP_ADDPATH_ID_LEN > lim) - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; addpath_id = ntohl(*((uint32_t *)pnt)); pnt += BGP_ADDPATH_ID_LEN; @@ -4358,7 +4425,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error (wrong prefix length %d for afi %u)", peer->host, p.prefixlen, packet->afi); - return -1; + return BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH; } /* Packet size overflow check. */ @@ -4370,7 +4437,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error (prefix length %d overflows packet)", peer->host, p.prefixlen); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; } /* Defensive coding, double-check the psize fits in a struct @@ -4380,7 +4447,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error (prefix length %d too large for prefix storage %zu)", peer->host, p.prefixlen, sizeof(p.u)); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } /* Fetch prefix from NLRI packet. */ @@ -4445,10 +4512,14 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, BGP_ROUTE_NORMAL, NULL, NULL, 0, NULL); - /* Address family configuration mismatch or maximum-prefix count - overflow. */ + /* Do not send BGP notification twice when maximum-prefix count + * overflow. */ + if (CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) + return BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW; + + /* Address family configuration mismatch. */ if (ret < 0) - return -1; + return BGP_NLRI_PARSE_ERROR_ADDRESS_FAMILY; } /* Packet length consistency check. */ @@ -4457,10 +4528,10 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error (prefix length mismatch with total length)", peer->host); - return -1; + return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } - return 0; + return BGP_NLRI_PARSE_OK; } static struct bgp_static *bgp_static_new(void) @@ -6859,6 +6930,13 @@ static void route_vty_short_status_out(struct vty *vty, vty_out(vty, " "); } +static char *bgp_nexthop_fqdn(struct peer *peer) +{ + if (peer->hostname && bgp_flag_check(peer->bgp, BGP_FLAG_SHOW_HOSTNAME)) + return peer->hostname; + return NULL; +} + /* called from terminal list command */ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_path_info *path, int display, safi_t safi, @@ -6875,6 +6953,7 @@ void route_vty_out(struct vty *vty, struct prefix *p, bool nexthop_othervrf = false; vrf_id_t nexthop_vrfid = VRF_DEFAULT; const char *nexthop_vrfname = VRF_DEFAULT_NAME; + char *nexthop_fqdn = bgp_nexthop_fqdn(path->peer); if (json_paths) json_path = json_object_new_object(); @@ -6970,42 +7049,58 @@ void route_vty_out(struct vty *vty, struct prefix *p, if (json_paths) { json_nexthop_global = json_object_new_object(); - json_object_string_add(json_nexthop_global, "afi", - (af == AF_INET) ? "ip" : "ipv6"); - json_object_string_add(json_nexthop_global, - (af == AF_INET) ? "ip" : "ipv6", - nexthop); + json_object_string_add( + json_nexthop_global, "afi", + nexthop_fqdn ? "fqdn" + : (af == AF_INET) ? "ip" : "ipv6"); + json_object_string_add( + json_nexthop_global, + nexthop_fqdn ? "fqdn" + : (af == AF_INET) ? "ip" : "ipv6", + nexthop_fqdn ? nexthop_fqdn : nexthop); json_object_boolean_true_add(json_nexthop_global, "used"); } else - vty_out(vty, "%s%s", nexthop, vrf_id_str); + vty_out(vty, "%s%s", + nexthop_fqdn ? nexthop_fqdn : nexthop, + vrf_id_str); } else if (safi == SAFI_EVPN) { if (json_paths) { json_nexthop_global = json_object_new_object(); - json_object_string_add(json_nexthop_global, "ip", - inet_ntoa(attr->nexthop)); + json_object_string_add( + json_nexthop_global, + nexthop_fqdn ? "fqdn" : "ip", + nexthop_fqdn ? nexthop_fqdn + : inet_ntoa(attr->nexthop)); json_object_string_add(json_nexthop_global, "afi", "ipv4"); json_object_boolean_true_add(json_nexthop_global, "used"); } else - vty_out(vty, "%-16s%s", inet_ntoa(attr->nexthop), + vty_out(vty, "%-16s%s", + nexthop_fqdn ?: inet_ntoa(attr->nexthop), vrf_id_str); } else if (safi == SAFI_FLOWSPEC) { if (attr->nexthop.s_addr != 0) { if (json_paths) { json_nexthop_global = json_object_new_object(); json_object_string_add( - json_nexthop_global, "ip", - inet_ntoa(attr->nexthop)); + json_nexthop_global, + nexthop_fqdn ? "fqdn" : "ip", + nexthop_fqdn + ? nexthop_fqdn + : inet_ntoa(attr->nexthop)); json_object_string_add(json_nexthop_global, "afi", "ipv4"); json_object_boolean_true_add( json_nexthop_global, "used"); } else { - vty_out(vty, "%-16s", inet_ntoa(attr->nexthop)); + vty_out(vty, "%-16s", + nexthop_fqdn + ? nexthop_fqdn + : inet_ntoa(attr->nexthop)); } } } else if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) { @@ -7014,12 +7109,19 @@ void route_vty_out(struct vty *vty, struct prefix *p, if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) json_object_string_add( - json_nexthop_global, "ip", - inet_ntoa(attr->mp_nexthop_global_in)); + json_nexthop_global, + nexthop_fqdn ? "fqdn" : "ip", + nexthop_fqdn + ? nexthop_fqdn + : inet_ntoa( + attr->mp_nexthop_global_in)); else json_object_string_add( - json_nexthop_global, "ip", - inet_ntoa(attr->nexthop)); + json_nexthop_global, + nexthop_fqdn ? "fqdn" : "ip", + nexthop_fqdn + ? nexthop_fqdn + : inet_ntoa(attr->nexthop)); json_object_string_add(json_nexthop_global, "afi", "ipv4"); @@ -7029,7 +7131,9 @@ void route_vty_out(struct vty *vty, struct prefix *p, char buf[BUFSIZ]; snprintf(buf, sizeof(buf), "%s%s", - inet_ntoa(attr->nexthop), vrf_id_str); + nexthop_fqdn ? nexthop_fqdn + : inet_ntoa(attr->nexthop), + vrf_id_str); vty_out(vty, "%-16s", buf); } } @@ -7042,9 +7146,13 @@ void route_vty_out(struct vty *vty, struct prefix *p, if (json_paths) { json_nexthop_global = json_object_new_object(); json_object_string_add( - json_nexthop_global, "ip", - inet_ntop(AF_INET6, &attr->mp_nexthop_global, - buf, BUFSIZ)); + json_nexthop_global, + nexthop_fqdn ? "fqdn" : "ip", + nexthop_fqdn + ? nexthop_fqdn + : inet_ntop(AF_INET6, + &attr->mp_nexthop_global, + buf, BUFSIZ)); json_object_string_add(json_nexthop_global, "afi", "ipv6"); json_object_string_add(json_nexthop_global, "scope", @@ -7056,10 +7164,14 @@ void route_vty_out(struct vty *vty, struct prefix *p, || (path->peer->conf_if)) { json_nexthop_ll = json_object_new_object(); json_object_string_add( - json_nexthop_ll, "ip", - inet_ntop(AF_INET6, - &attr->mp_nexthop_local, buf, - BUFSIZ)); + json_nexthop_ll, + nexthop_fqdn ? "fqdn" : "ip", + nexthop_fqdn + ? nexthop_fqdn + : inet_ntop( + AF_INET6, + &attr->mp_nexthop_local, + buf, BUFSIZ)); json_object_string_add(json_nexthop_ll, "afi", "ipv6"); json_object_string_add(json_nexthop_ll, "scope", @@ -7098,10 +7210,12 @@ void route_vty_out(struct vty *vty, struct prefix *p, } else { len = vty_out( vty, "%s%s", - inet_ntop( - AF_INET6, - &attr->mp_nexthop_local, - buf, BUFSIZ), + nexthop_fqdn + ? nexthop_fqdn + : inet_ntop( + AF_INET6, + &attr->mp_nexthop_local, + buf, BUFSIZ), vrf_id_str); len = 16 - len; @@ -7113,10 +7227,13 @@ void route_vty_out(struct vty *vty, struct prefix *p, } else { len = vty_out( vty, "%s%s", - inet_ntop(AF_INET6, - &attr->mp_nexthop_global, buf, - BUFSIZ), - vrf_id_str); + nexthop_fqdn + ? nexthop_fqdn + : inet_ntop( + AF_INET6, + &attr->mp_nexthop_global, + buf, BUFSIZ), + vrf_id_str); len = 16 - len; if (len < 1) @@ -7515,21 +7632,26 @@ void route_vty_out_overlay(struct vty *vty, struct prefix *p, json_object *json_paths) { struct attr *attr; - char buf[BUFSIZ]; + char buf[BUFSIZ] = {0}; json_object *json_path = NULL; - - if (json_paths) - json_path = json_object_new_object(); + json_object *json_nexthop = NULL; + json_object *json_overlay = NULL; if (!path->extra) return; + if (json_paths) { + json_path = json_object_new_object(); + json_overlay = json_object_new_object(); + json_nexthop = json_object_new_object(); + } + /* short status lead text */ route_vty_short_status_out(vty, path, json_path); /* print prefix and mask */ if (!display) - route_vty_out_route(p, vty, NULL); + route_vty_out_route(p, vty, json_path); else vty_out(vty, "%*s", 17, " "); @@ -7541,35 +7663,69 @@ void route_vty_out_overlay(struct vty *vty, struct prefix *p, switch (af) { case AF_INET: - vty_out(vty, "%-16s", - inet_ntop(af, &attr->mp_nexthop_global_in, buf, - BUFSIZ)); + inet_ntop(af, &attr->mp_nexthop_global_in, buf, BUFSIZ); + if (!json_path) { + vty_out(vty, "%-16s", buf); + } else { + json_object_string_add(json_nexthop, "ip", buf); + + json_object_string_add(json_nexthop, "afi", + "ipv4"); + + json_object_object_add(json_path, "nexthop", + json_nexthop); + } break; case AF_INET6: - vty_out(vty, "%s(%s)", - inet_ntop(af, &attr->mp_nexthop_global, buf, - BUFSIZ), - inet_ntop(af, &attr->mp_nexthop_local, buf1, - BUFSIZ)); + inet_ntop(af, &attr->mp_nexthop_global, buf, BUFSIZ); + inet_ntop(af, &attr->mp_nexthop_local, buf1, BUFSIZ); + if (!json_path) { + vty_out(vty, "%s(%s)", buf, buf1); + } else { + json_object_string_add(json_nexthop, + "ipv6Global", buf); + + json_object_string_add(json_nexthop, + "ipv6LinkLocal", buf1); + + json_object_string_add(json_nexthop, "afi", + "ipv6"); + + json_object_object_add(json_path, "nexthop", + json_nexthop); + } break; default: - vty_out(vty, "?"); + if (!json_path) { + vty_out(vty, "?"); + } else { + json_object_string_add(json_nexthop, "Error", + "Unsupported address-family"); + } } char *str = esi2str(&(attr->evpn_overlay.eth_s_id)); - vty_out(vty, "%s", str); + if (!json_path) + vty_out(vty, "%s", str); + else + json_object_string_add(json_overlay, "esi", str); + XFREE(MTYPE_TMP, str); if (is_evpn_prefix_ipaddr_v4((struct prefix_evpn *)p)) { - vty_out(vty, "/%s", - inet_ntoa(attr->evpn_overlay.gw_ip.ipv4)); + inet_ntop(AF_INET, &(attr->evpn_overlay.gw_ip.ipv4), + buf, BUFSIZ); } else if (is_evpn_prefix_ipaddr_v6((struct prefix_evpn *)p)) { - vty_out(vty, "/%s", - inet_ntop(AF_INET6, - &(attr->evpn_overlay.gw_ip.ipv6), buf, - BUFSIZ)); + inet_ntop(AF_INET6, &(attr->evpn_overlay.gw_ip.ipv6), + buf, BUFSIZ); } + + if (!json_path) + vty_out(vty, "/%s", buf); + else + json_object_string_add(json_overlay, "gw", buf); + if (attr->ecommunity) { char *mac = NULL; struct ecommunity_val *routermac = ecommunity_lookup( @@ -7578,13 +7734,25 @@ void route_vty_out_overlay(struct vty *vty, struct prefix *p, if (routermac) mac = ecom_mac2str((char *)routermac->val); if (mac) { - vty_out(vty, "/%s", (char *)mac); + if (!json_path) { + vty_out(vty, "/%s", (char *)mac); + } else { + json_object_string_add(json_overlay, + "rmac", mac); + } XFREE(MTYPE_TMP, mac); } } - vty_out(vty, "\n"); - } + if (!json_path) { + vty_out(vty, "\n"); + } else { + json_object_object_add(json_path, "overlay", + json_overlay); + + json_object_array_add(json_paths, json_path); + } + } } /* dampening route */ @@ -7812,9 +7980,83 @@ static void route_vty_out_tx_ids(struct vty *vty, } } -void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, - struct bgp_path_info *path, afi_t afi, safi_t safi, - json_object *json_paths) +static const char *bgp_path_selection_reason2str( + enum bgp_path_selection_reason reason) +{ + switch (reason) { + case bgp_path_selection_none: + return "Nothing to Select"; + break; + case bgp_path_selection_first: + return "First path received"; + break; + case bgp_path_selection_evpn_sticky_mac: + return "EVPN Sticky Mac"; + break; + case bgp_path_selection_evpn_seq: + return "EVPN sequence number"; + break; + case bgp_path_selection_evpn_lower_ip: + return "EVPN lower IP"; + break; + case bgp_path_selection_weight: + return "Weight"; + break; + case bgp_path_selection_local_pref: + return "Local Pref"; + break; + case bgp_path_selection_local_route: + return "Local Route"; + break; + case bgp_path_selection_confed_as_path: + return "Confederation based AS Path"; + break; + case bgp_path_selection_as_path: + return "AS Path"; + break; + case bgp_path_selection_origin: + return "Origin"; + break; + case bgp_path_selection_med: + return "MED"; + break; + case bgp_path_selection_peer: + return "Peer Type"; + break; + case bgp_path_selection_confed: + return "Confed Peer Type"; + break; + case bgp_path_selection_igp_metric: + return "IGP Metric"; + break; + case bgp_path_selection_older: + return "Older Path"; + break; + case bgp_path_selection_router_id: + return "Router ID"; + break; + case bgp_path_selection_cluster_length: + return "Cluser length"; + break; + case bgp_path_selection_stale: + return "Path Staleness"; + break; + case bgp_path_selection_local_configured: + return "Locally configured route"; + break; + case bgp_path_selection_neighbor_ip: + return "Neighbor IP"; + break; + case bgp_path_selection_default: + return "Nothing left to compare"; + break; + } + return "Invalid (internal error)"; +} + +void route_vty_out_detail(struct vty *vty, struct bgp *bgp, + struct bgp_node *bn, struct bgp_path_info *path, + afi_t afi, safi_t safi, json_object *json_paths) { char buf[INET6_ADDRSTRLEN]; char buf1[BUFSIZ]; @@ -7844,6 +8086,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, bool nexthop_self = CHECK_FLAG(path->flags, BGP_PATH_ANNC_NH_SELF) ? true : false; int i; + char *nexthop_fqdn = bgp_nexthop_fqdn(path->peer); if (json_paths) { json_path = json_object_new_object(); @@ -7854,7 +8097,8 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, if (!json_paths && safi == SAFI_EVPN) { char tag_buf[30]; - bgp_evpn_route2str((struct prefix_evpn *)p, buf2, sizeof(buf2)); + bgp_evpn_route2str((struct prefix_evpn *)&bn->p, + buf2, sizeof(buf2)); vty_out(vty, " Route %s", buf2); tag_buf[0] = '\0'; if (path->extra && path->extra->num_labels) { @@ -7969,8 +8213,8 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, /* Line2 display Next-hop, Neighbor, Router-id */ /* Display the nexthop */ - if ((p->family == AF_INET || p->family == AF_ETHERNET - || p->family == AF_EVPN) + if ((bn->p.family == AF_INET || bn->p.family == AF_ETHERNET + || bn->p.family == AF_EVPN) && (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN || !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { @@ -7978,21 +8222,33 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, || safi == SAFI_EVPN) { if (json_paths) json_object_string_add( - json_nexthop_global, "ip", - inet_ntoa( - attr->mp_nexthop_global_in)); + json_nexthop_global, + nexthop_fqdn ? "fqdn" : "ip", + nexthop_fqdn + ? nexthop_fqdn + : inet_ntoa( + attr->mp_nexthop_global_in)); else vty_out(vty, " %s", - inet_ntoa( - attr->mp_nexthop_global_in)); + nexthop_fqdn + ? nexthop_fqdn + : inet_ntoa( + attr->mp_nexthop_global_in)); } else { if (json_paths) json_object_string_add( - json_nexthop_global, "ip", - inet_ntoa(attr->nexthop)); + json_nexthop_global, + nexthop_fqdn ? "fqdn" : "ip", + nexthop_fqdn + ? nexthop_fqdn + : inet_ntoa( + attr->nexthop)); else vty_out(vty, " %s", - inet_ntoa(attr->nexthop)); + nexthop_fqdn + ? nexthop_fqdn + : inet_ntoa( + attr->nexthop)); } if (json_paths) @@ -8001,19 +8257,28 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, } else { if (json_paths) { json_object_string_add( - json_nexthop_global, "ip", - inet_ntop(AF_INET6, - &attr->mp_nexthop_global, buf, - INET6_ADDRSTRLEN)); + json_nexthop_global, + nexthop_fqdn ? "fqdn" : "ip", + nexthop_fqdn + ? nexthop_fqdn + : inet_ntop( + AF_INET6, + &attr->mp_nexthop_global, + buf, + INET6_ADDRSTRLEN)); json_object_string_add(json_nexthop_global, "afi", "ipv6"); json_object_string_add(json_nexthop_global, "scope", "global"); } else { vty_out(vty, " %s", - inet_ntop(AF_INET6, - &attr->mp_nexthop_global, buf, - INET6_ADDRSTRLEN)); + nexthop_fqdn + ? nexthop_fqdn + : inet_ntop( + AF_INET6, + &attr->mp_nexthop_global, + buf, + INET6_ADDRSTRLEN)); } } @@ -8052,7 +8317,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, if (path->peer == bgp->peer_self) { if (safi == SAFI_EVPN - || (p->family == AF_INET + || (bn->p.family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { if (json_paths) json_object_string_add( @@ -8195,10 +8460,15 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, if (json_paths) { json_nexthop_ll = json_object_new_object(); json_object_string_add( - json_nexthop_ll, "ip", - inet_ntop(AF_INET6, - &attr->mp_nexthop_local, buf, - INET6_ADDRSTRLEN)); + json_nexthop_ll, + nexthop_fqdn ? "fqdn" : "ip", + nexthop_fqdn + ? nexthop_fqdn + : inet_ntop( + AF_INET6, + &attr->mp_nexthop_local, + buf, + INET6_ADDRSTRLEN)); json_object_string_add(json_nexthop_ll, "afi", "ipv6"); json_object_string_add(json_nexthop_ll, "scope", @@ -8406,8 +8676,14 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, json_object_new_object(); json_object_boolean_true_add(json_bestpath, "overall"); - } else + json_object_string_add(json_bestpath, + "selectionReason", + bgp_path_selection_reason2str(bn->reason)); + } else { vty_out(vty, ", best"); + vty_out(vty, " (%s)", + bgp_path_selection_reason2str(bn->reason)); + } } if (json_bestpath) @@ -9008,6 +9284,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, unsigned long i; for (i = 0; i < *json_header_depth; ++i) vty_out(vty, " } "); + vty_out(vty, "\n"); } } else { if (is_last) { @@ -9265,12 +9542,14 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, vty_out(vty, "Paths: (%d available", count); if (best) { vty_out(vty, ", best #%d", best); - if (safi == SAFI_UNICAST) - vty_out(vty, ", table %s", - (bgp->inst_type - == BGP_INSTANCE_TYPE_DEFAULT) - ? VRF_DEFAULT_NAME - : bgp->name); + if (safi == SAFI_UNICAST) { + if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) + vty_out(vty, ", table %s", + VRF_DEFAULT_NAME); + else + vty_out(vty, ", vrf %s", + bgp->name); + } } else vty_out(vty, ", no best path"); @@ -9418,7 +9697,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, BGP_PATH_MULTIPATH) || CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)))) - route_vty_out_detail(vty, bgp, &rm->p, + route_vty_out_detail(vty, bgp, rm, pi, AFI_IP, safi, json_paths); } @@ -9462,7 +9741,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, pi->flags, BGP_PATH_SELECTED)))) route_vty_out_detail( - vty, bgp, &rn->p, pi, + vty, bgp, rn, pi, afi, safi, json_paths); } } @@ -9934,7 +10213,7 @@ DEFUN (show_ip_bgp_regexp, BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR "Display routes matching the AS path regular expression\n" - "A regular-expression to match the BGP AS paths\n") + "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") { afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; @@ -12096,30 +12375,9 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, p = &rn->p; - /* "network" configuration display. */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO) && afi == AFI_IP) { - uint32_t destination; - struct in_addr netmask; - - destination = ntohl(p->u.prefix4.s_addr); - masklen2ip(p->prefixlen, &netmask); - vty_out(vty, " network %s", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN)); - - if ((IN_CLASSC(destination) && p->prefixlen == 24) - || (IN_CLASSB(destination) && p->prefixlen == 16) - || (IN_CLASSA(destination) && p->prefixlen == 8) - || p->u.prefix4.s_addr == 0) { - /* Natural mask is not display. */ - } else - vty_out(vty, " mask %s", inet_ntoa(netmask)); - } else { - vty_out(vty, " network %s/%d", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - p->prefixlen); - } + vty_out(vty, " network %s/%d", + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); if (bgp_static->label_index != BGP_INVALID_LABEL_INDEX) vty_out(vty, " label-index %u", @@ -12143,20 +12401,9 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, p = &rn->p; - if (bgp_option_check(BGP_OPT_CONFIG_CISCO) && afi == AFI_IP) { - struct in_addr netmask; - - masklen2ip(p->prefixlen, &netmask); - vty_out(vty, " aggregate-address %s %s", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - inet_ntoa(netmask)); - } else { - vty_out(vty, " aggregate-address %s/%d", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - p->prefixlen); - } + vty_out(vty, " aggregate-address %s/%d", + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); if (bgp_aggregate->as_set) vty_out(vty, " as-set"); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index a559a6b58cb4..f71508445097 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -73,6 +73,24 @@ enum bgp_show_adj_route_type { */ #define BGP_MAX_LABELS 2 +/* Error codes for handling NLRI */ +#define BGP_NLRI_PARSE_OK 0 +#define BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW -1 +#define BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW -2 +#define BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH -3 +#define BGP_NLRI_PARSE_ERROR_PACKET_LENGTH -4 +#define BGP_NLRI_PARSE_ERROR_LABEL_LENGTH -5 +#define BGP_NLRI_PARSE_ERROR_EVPN_MISSING_TYPE -6 +#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE2_SIZE -7 +#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE3_SIZE -8 +#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE4_SIZE -9 +#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE5_SIZE -10 +#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_IPV6_NOT_SUPPORTED -11 +#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_NLRI_SIZELIMIT -12 +#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_BAD_FORMAT -13 +#define BGP_NLRI_PARSE_ERROR_ADDRESS_FAMILY -14 +#define BGP_NLRI_PARSE_ERROR -32 + /* Ancillary information to struct bgp_path_info, * used for uncommonly used data (aggregation, MPLS, etc.) * and lazily allocated to save memory. @@ -570,7 +588,8 @@ extern void bgp_path_info_restore(struct bgp_node *rn, extern int bgp_path_info_cmp_compatible(struct bgp *bgp, struct bgp_path_info *new, struct bgp_path_info *exist, - char *pfx_buf, afi_t afi, safi_t safi); + char *pfx_buf, afi_t afi, safi_t safi, + enum bgp_path_selection_reason *reason); extern void bgp_attr_add_gshut_community(struct attr *attr); extern void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, @@ -586,7 +605,8 @@ extern void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, struct prefix_rd *prd, afi_t afi, safi_t safi, json_object *json); extern void route_vty_out_detail(struct vty *vty, struct bgp *bgp, - struct prefix *p, struct bgp_path_info *path, + struct bgp_node *bn, + struct bgp_path_info *path, afi_t afi, safi_t safi, json_object *json_paths); extern int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index c276f5ef7b87..30e512c53eed 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -28,7 +28,7 @@ #include "plist.h" #include "memory.h" #include "log.h" -#include "lua.h" +#include "frrlua.h" #ifdef HAVE_LIBPCREPOSIX #include #else @@ -1382,6 +1382,8 @@ static route_map_result_t route_match_interface(void *rule, { struct interface *ifp; struct bgp_path_info *path; + struct bgp_node *rn; + struct bgp_table *table; if (type == RMAP_BGP) { path = object; @@ -1389,7 +1391,13 @@ static route_map_result_t route_match_interface(void *rule, if (!path || !path->attr) return RMAP_NOMATCH; - ifp = if_lookup_by_name_all_vrf((char *)rule); + /* get vrf_id of current bgp instance */ + rn = path->net; + assert(rn && bgp_node_table(rn)); + table = bgp_node_table(rn); + assert(table && table->bgp); + + ifp = if_lookup_by_name((char *)rule, table->bgp->vrf_id); if (ifp == NULL || ifp->ifindex != path->attr->nh_ifindex) return RMAP_NOMATCH; @@ -3016,7 +3024,7 @@ static int bgp_route_match_add(struct vty *vty, const char *command, int retval = CMD_SUCCESS; int ret; - ret = route_map_add_match(index, command, arg); + ret = route_map_add_match(index, command, arg, type); switch (ret) { case RMAP_RULE_MISSING: vty_out(vty, "%% BGP Can't find rule.\n"); @@ -3090,8 +3098,6 @@ static void bgp_route_map_process_peer(const char *rmap_name, struct route_map *map, struct peer *peer, int afi, int safi, int route_update) { - - int update; struct bgp_filter *filter; if (!peer || !rmap_name) @@ -3102,52 +3108,16 @@ static void bgp_route_map_process_peer(const char *rmap_name, * in is for non-route-server clients, * out is for all peers */ - if (!CHECK_FLAG(peer->flags, PEER_FLAG_RSERVER_CLIENT)) { - if (filter->map[RMAP_IN].name - && (strcmp(rmap_name, filter->map[RMAP_IN].name) == 0)) { - filter->map[RMAP_IN].map = map; - - if (route_update && peer->status == Established) { - if (CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SOFT_RECONFIG)) { - if (bgp_debug_update(peer, NULL, NULL, - 1)) - zlog_debug( - "Processing route_map %s update on " - "peer %s (inbound, soft-reconfig)", - rmap_name, peer->host); - - bgp_soft_reconfig_in(peer, afi, safi); - } else if ( - CHECK_FLAG(peer->cap, - PEER_CAP_REFRESH_OLD_RCV) - || CHECK_FLAG( - peer->cap, - PEER_CAP_REFRESH_NEW_RCV)) { - - if (bgp_debug_update(peer, NULL, NULL, - 1)) - zlog_debug( - "Processing route_map %s update on " - "peer %s (inbound, route-refresh)", - rmap_name, peer->host); - bgp_route_refresh_send(peer, afi, safi, - 0, 0, 0); - } - } - } - } - - if (CHECK_FLAG(peer->flags, PEER_FLAG_RSERVER_CLIENT)) { - update = 0; + if (filter->map[RMAP_IN].name + && (strcmp(rmap_name, filter->map[RMAP_IN].name) == 0)) { + filter->map[RMAP_IN].map = map; - if (update && route_update && peer->status == Established) { + if (route_update && peer->status == Established) { if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) { if (bgp_debug_update(peer, NULL, NULL, 1)) zlog_debug( - "Processing route_map %s update on " - "peer %s (import, soft-reconfig)", + "Processing route_map %s update on peer %s (inbound, soft-reconfig)", rmap_name, peer->host); bgp_soft_reconfig_in(peer, afi, safi); @@ -3157,13 +3127,11 @@ static void bgp_route_map_process_peer(const char *rmap_name, PEER_CAP_REFRESH_NEW_RCV)) { if (bgp_debug_update(peer, NULL, NULL, 1)) zlog_debug( - "Processing route_map %s update on " - "peer %s (import, route-refresh)", + "Processing route_map %s update on peer %s (inbound, route-refresh)", rmap_name, peer->host); bgp_route_refresh_send(peer, afi, safi, 0, 0, 0); } - /* DD: Else, what else do we do ? Reset peer ? */ } } @@ -3447,7 +3415,7 @@ static void bgp_route_map_delete(const char *rmap_name) route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED); } -static void bgp_route_map_event(route_map_event_t event, const char *rmap_name) +static void bgp_route_map_event(const char *rmap_name) { if (route_map_mark_updated(rmap_name) == 0) bgp_route_map_mark_update(rmap_name); @@ -4262,10 +4230,10 @@ DEFUN (set_community, str = community_str(com, false); if (additive) { - argstr = XCALLOC(MTYPE_TMP, - strlen(str) + strlen(" additive") + 1); - strcpy(argstr, str); - strcpy(argstr + strlen(str), " additive"); + size_t argstr_sz = strlen(str) + strlen(" additive") + 1; + argstr = XCALLOC(MTYPE_TMP, argstr_sz); + strlcpy(argstr, str, argstr_sz); + strlcat(argstr, " additive", argstr_sz); ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), "community", argstr); XFREE(MTYPE_TMP, argstr); diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index c63d4f9ad2fc..063772399055 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -737,28 +737,27 @@ static int rpki_validate_prefix(struct peer *peer, struct attr *attr, prefix->prefixlen, &result); // Print Debug output - prefix_string = - inet_ntop(prefix->family, &prefix->u.prefix, buf, BUFSIZ); + prefix_string = prefix2str(prefix, buf, sizeof(buf)); switch (result) { case BGP_PFXV_STATE_VALID: RPKI_DEBUG( - "Validating Prefix %s/%hhu from asn %u Result: VALID", - prefix_string, prefix->prefixlen, as_number); + "Validating Prefix %s from asn %u Result: VALID", + prefix_string, as_number); return RPKI_VALID; case BGP_PFXV_STATE_NOT_FOUND: RPKI_DEBUG( - "Validating Prefix %s/%hhu from asn %u Result: NOT FOUND", - prefix_string, prefix->prefixlen, as_number); + "Validating Prefix %s from asn %u Result: NOT FOUND", + prefix_string, as_number); return RPKI_NOTFOUND; case BGP_PFXV_STATE_INVALID: RPKI_DEBUG( - "Validating Prefix %s/%hhu from asn %u Result: INVALID", - prefix_string, prefix->prefixlen, as_number); + "Validating Prefix %s from asn %u Result: INVALID", + prefix_string, as_number); return RPKI_INVALID; default: RPKI_DEBUG( - "Validating Prefix %s/%hhu from asn %u Result: CANNOT VALIDATE", - prefix_string, prefix->prefixlen, as_number); + "Validating Prefix %s from asn %u Result: CANNOT VALIDATE", + prefix_string, as_number); break; } return 0; @@ -1112,7 +1111,7 @@ DEFPY (rpki_cache, vty_out(vty, "ssh sockets are not supported. " "Please recompile rtrlib and frr with ssh support. " - "If you want to use it"); + "If you want to use it\n"); #endif } else { // use tcp connection return_value = add_tcp_cache(cache, tcpport, preference); @@ -1253,6 +1252,7 @@ DEFUN (show_rpki_cache_server, cache->tr_config.tcp_config->host, cache->tr_config.tcp_config->port); +#if defined(FOUND_SSH) } else if (cache->type == SSH) { vty_out(vty, "host: %s port: %d username: %s " @@ -1264,6 +1264,7 @@ DEFUN (show_rpki_cache_server, ->server_hostkey_path, cache->tr_config.ssh_config ->client_privkey_path); +#endif } } @@ -1402,7 +1403,8 @@ DEFUN (match_rpki, VTY_DECLVAR_CONTEXT(route_map_index, index); int ret; - ret = route_map_add_match(index, "rpki", argv[2]->arg); + ret = route_map_add_match(index, "rpki", argv[2]->arg, + RMAP_EVENT_MATCH_ADDED); if (ret) { switch (ret) { case RMAP_RULE_MISSING: diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index 040e83a8cd9e..b3542e7848a9 100644 --- a/bgpd/bgp_table.h +++ b/bgpd/bgp_table.h @@ -42,6 +42,31 @@ struct bgp_table { uint64_t version; }; +enum bgp_path_selection_reason { + bgp_path_selection_none, + bgp_path_selection_first, + bgp_path_selection_evpn_sticky_mac, + bgp_path_selection_evpn_seq, + bgp_path_selection_evpn_lower_ip, + bgp_path_selection_weight, + bgp_path_selection_local_pref, + bgp_path_selection_local_route, + bgp_path_selection_confed_as_path, + bgp_path_selection_as_path, + bgp_path_selection_origin, + bgp_path_selection_med, + bgp_path_selection_peer, + bgp_path_selection_confed, + bgp_path_selection_igp_metric, + bgp_path_selection_older, + bgp_path_selection_router_id, + bgp_path_selection_cluster_length, + bgp_path_selection_stale, + bgp_path_selection_local_configured, + bgp_path_selection_neighbor_ip, + bgp_path_selection_default, +}; + struct bgp_node { /* * CAUTION @@ -72,6 +97,8 @@ struct bgp_node { #define BGP_NODE_REGISTERED_FOR_LABEL (1 << 3) struct bgp_addpath_node_data tx_addpath; + + enum bgp_path_selection_reason reason; }; /* diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index 49a435120d57..d0be2471af02 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -83,9 +83,9 @@ static void sync_init(struct update_subgroup *subgrp) { subgrp->sync = XCALLOC(MTYPE_BGP_SYNCHRONISE, sizeof(struct bgp_synchronize)); - BGP_ADV_FIFO_INIT(&subgrp->sync->update); - BGP_ADV_FIFO_INIT(&subgrp->sync->withdraw); - BGP_ADV_FIFO_INIT(&subgrp->sync->withdraw_low); + bgp_adv_fifo_init(&subgrp->sync->update); + bgp_adv_fifo_init(&subgrp->sync->withdraw); + bgp_adv_fifo_init(&subgrp->sync->withdraw_low); subgrp->hash = hash_create(baa_hash_key, baa_hash_cmp, "BGP SubGroup Hash"); @@ -288,7 +288,7 @@ static void *updgrp_hash_alloc(void *p) * 16. Local-as should match, if configured. * ) */ -static unsigned int updgrp_hash_key_make(void *p) +static unsigned int updgrp_hash_key_make(const void *p) { const struct update_group *updgrp; const struct peer *peer; diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h index 6b3bf9d1f77f..bb547454f21f 100644 --- a/bgpd/bgp_updgrp.h +++ b/bgpd/bgp_updgrp.h @@ -56,6 +56,7 @@ #define PEER_UPDGRP_AF_FLAGS \ (PEER_FLAG_SEND_COMMUNITY | PEER_FLAG_SEND_EXT_COMMUNITY \ + | PEER_FLAG_SEND_LARGE_COMMUNITY \ | PEER_FLAG_DEFAULT_ORIGINATE | PEER_FLAG_REFLECTOR_CLIENT \ | PEER_FLAG_RSERVER_CLIENT | PEER_FLAG_NEXTHOP_SELF \ | PEER_FLAG_NEXTHOP_UNCHANGED | PEER_FLAG_FORCE_NEXTHOP_SELF \ @@ -590,9 +591,9 @@ static inline void bgp_announce_peer(struct peer *peer) */ static inline int advertise_list_is_empty(struct update_subgroup *subgrp) { - if (!BGP_ADV_FIFO_EMPTY(&subgrp->sync->update) - || !BGP_ADV_FIFO_EMPTY(&subgrp->sync->withdraw) - || !BGP_ADV_FIFO_EMPTY(&subgrp->sync->withdraw_low)) { + if (bgp_adv_fifo_count(&subgrp->sync->update) + || bgp_adv_fifo_count(&subgrp->sync->withdraw) + || bgp_adv_fifo_count(&subgrp->sync->withdraw_low)) { return 0; } diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 3870df593f51..b64c51f34140 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -422,7 +422,7 @@ bgp_advertise_clean_subgroup(struct update_subgroup *subgrp, struct bgp_advertise *adv; struct bgp_advertise_attr *baa; struct bgp_advertise *next; - struct bgp_advertise_fifo *fhead; + struct bgp_adv_fifo_head *fhead; adv = adj->adv; baa = adv->baa; @@ -444,7 +444,7 @@ bgp_advertise_clean_subgroup(struct update_subgroup *subgrp, /* Unlink myself from advertisement FIFO. */ - BGP_ADV_FIFO_DEL(fhead, adv); + bgp_adv_fifo_del(fhead, adv); /* Free memory. */ bgp_advertise_free(adj->adv); @@ -507,7 +507,7 @@ void bgp_adj_out_set_subgroup(struct bgp_node *rn, * If the update adv list is empty, trigger the member peers' * mrai timers so the socket writes can happen. */ - if (BGP_ADV_FIFO_EMPTY(&subgrp->sync->update)) { + if (!bgp_adv_fifo_count(&subgrp->sync->update)) { struct peer_af *paf; SUBGRP_FOREACH_PEER (subgrp, paf) { @@ -515,7 +515,7 @@ void bgp_adj_out_set_subgroup(struct bgp_node *rn, } } - BGP_ADV_FIFO_ADD(&subgrp->sync->update, &adv->fifo); + bgp_adv_fifo_add_tail(&subgrp->sync->update, adv); subgrp->version = max(subgrp->version, rn->version); } @@ -550,11 +550,11 @@ void bgp_adj_out_unset_subgroup(struct bgp_node *rn, /* Note if we need to trigger a packet write */ trigger_write = - BGP_ADV_FIFO_EMPTY(&subgrp->sync->withdraw); + !bgp_adv_fifo_count(&subgrp->sync->withdraw); /* Add to synchronization entry for withdraw * announcement. */ - BGP_ADV_FIFO_ADD(&subgrp->sync->withdraw, &adv->fifo); + bgp_adv_fifo_add_tail(&subgrp->sync->withdraw, adv); if (trigger_write) subgroup_trigger_write(subgrp); diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 66e306cba23d..688abae0e41a 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -554,9 +554,9 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt, mod_v6nhg = &peer->nexthop.v6_global; gnh_modified = 1; } else if ( - peer->sort == BGP_PEER_EBGP - && !CHECK_FLAG( - vec->flags, + (peer->sort == BGP_PEER_EBGP) + && (!bgp_multiaccess_check_v6(v6nhglobal, peer)) + && !CHECK_FLAG(vec->flags, BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED) && !peer_af_flag_check( peer, nhafi, paf->safi, @@ -664,11 +664,11 @@ int subgroup_packets_to_build(struct update_subgroup *subgrp) if (!subgrp) return 0; - adv = BGP_ADV_FIFO_HEAD(&subgrp->sync->withdraw); + adv = bgp_adv_fifo_first(&subgrp->sync->withdraw); if (adv) return 1; - adv = BGP_ADV_FIFO_HEAD(&subgrp->sync->update); + adv = bgp_adv_fifo_first(&subgrp->sync->update); if (adv) return 1; @@ -725,7 +725,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) addpath_encode = bgp_addpath_encode_tx(peer, afi, safi); addpath_overhead = addpath_encode ? BGP_ADDPATH_ID_LEN : 0; - adv = BGP_ADV_FIFO_HEAD(&subgrp->sync->update); + adv = bgp_adv_fifo_first(&subgrp->sync->update); while (adv) { assert(adv->rn); rn = adv->rn; @@ -966,7 +966,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) addpath_encode = bgp_addpath_encode_tx(peer, afi, safi); addpath_overhead = addpath_encode ? BGP_ADDPATH_ID_LEN : 0; - while ((adv = BGP_ADV_FIFO_HEAD(&subgrp->sync->withdraw)) != NULL) { + while ((adv = bgp_adv_fifo_first(&subgrp->sync->withdraw)) != NULL) { assert(adv->rn); adj = adv->adj; rn = adv->rn; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 9004926dee5e..43d0d9352049 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -119,6 +119,7 @@ static enum node_type bgp_node_type(afi_t afi, safi_t safi) case AFI_L2VPN: return BGP_EVPN_NODE; break; + case AFI_UNSPEC: case AFI_MAX: // We should never be here but to clarify the switch statement.. return BGP_IPV4_NODE; @@ -216,6 +217,10 @@ int argv_find_and_parse_afi(struct cmd_token **argv, int argc, int *index, ret = 1; if (afi) *afi = AFI_IP6; + } else if (argv_find(argv, argc, "l2vpn", index)) { + ret = 1; + if (afi) + *afi = AFI_L2VPN; } return ret; } @@ -259,6 +264,10 @@ int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, ret = 1; if (safi) *safi = SAFI_MPLS_VPN; + } else if (argv_find(argv, argc, "evpn", index)) { + ret = 1; + if (safi) + *safi = SAFI_EVPN; } else if (argv_find(argv, argc, "flowspec", index)) { ret = 1; if (safi) @@ -295,6 +304,7 @@ int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, * afi -> The parsed afi if it was included in the show command, returned here * safi -> The parsed safi if it was included in the show command, returned here * bgp -> Pointer to the bgp data structure we need to fill in. + * use_json -> json is configured or not * * The function returns the correct location in the parse tree for the * last token found. @@ -329,8 +339,17 @@ int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty, else { *bgp = bgp_lookup_by_name(vrf_name); if (!*bgp) { - if (use_json) - vty_out(vty, "{}\n"); + if (use_json) { + json_object *json = NULL; + json = json_object_new_object(); + json_object_string_add( + json, "warning", + "View/Vrf is unknown"); + vty_out(vty, "%s\n", + json_object_to_json_string_ext(json, + JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else vty_out(vty, "View/Vrf %s is unknown\n", vrf_name); @@ -341,8 +360,17 @@ int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty, } else { *bgp = bgp_get_default(); if (!*bgp) { - if (use_json) - vty_out(vty, "{}\n"); + if (use_json) { + json_object *json = NULL; + json = json_object_new_object(); + json_object_string_add( + json, "warning", + "Default BGP instance not found"); + vty_out(vty, "%s\n", + json_object_to_json_string_ext(json, + JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else vty_out(vty, "Default BGP instance not found\n"); @@ -795,41 +823,6 @@ static void bgp_clear_star_soft_out(struct vty *vty, const char *name) #include "bgpd/bgp_vty_clippy.c" #endif -/* BGP global configuration. */ -#if (CONFDATE > 20190601) -CPP_NOTICE("bgpd: time to remove deprecated bgp multiple-instance") -CPP_NOTICE("This includes BGP_OPT_MULTIPLE_INSTANCE") -#endif -DEFUN_HIDDEN (bgp_multiple_instance_func, - bgp_multiple_instance_cmd, - "bgp multiple-instance", - BGP_STR - "Enable bgp multiple instance\n") -{ - bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE); - return CMD_SUCCESS; -} - -DEFUN_HIDDEN (no_bgp_multiple_instance, - no_bgp_multiple_instance_cmd, - "no bgp multiple-instance", - NO_STR - BGP_STR - "BGP multiple instance\n") -{ - int ret; - - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please let the developers know\n"); - zlog_info("Deprecated option: `bgp multiple-instance` being used"); - ret = bgp_option_unset(BGP_OPT_MULTIPLE_INSTANCE); - if (ret < 0) { - vty_out(vty, "%% There are more than two BGP instances\n"); - return CMD_WARNING_CONFIG_FAILED; - } - return CMD_SUCCESS; -} - DEFUN_HIDDEN (bgp_local_mac, bgp_local_mac_cmd, "bgp local-mac vni " CMD_VNI_RANGE " mac WORD seq (0-4294967295)", @@ -911,44 +904,6 @@ DEFUN_HIDDEN (no_bgp_local_mac, return CMD_SUCCESS; } -#if (CONFDATE > 20190601) -CPP_NOTICE("bgpd: time to remove deprecated cli bgp config-type cisco") -CPP_NOTICE("This includes BGP_OPT_CISCO_CONFIG") -#endif -DEFUN_HIDDEN (bgp_config_type, - bgp_config_type_cmd, - "bgp config-type ", - BGP_STR - "Configuration type\n" - "cisco\n" - "zebra\n") -{ - int idx = 0; - if (argv_find(argv, argc, "cisco", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please let the developers know!\n"); - zlog_info("Deprecated option: `bgp config-type cisco` being used"); - bgp_option_set(BGP_OPT_CONFIG_CISCO); - } else - bgp_option_unset(BGP_OPT_CONFIG_CISCO); - - return CMD_SUCCESS; -} - -DEFUN_HIDDEN (no_bgp_config_type, - no_bgp_config_type_cmd, - "no bgp config-type []", - NO_STR - BGP_STR - "Display configuration type\n" - "cisco\n" - "zebra\n") -{ - bgp_option_unset(BGP_OPT_CONFIG_CISCO); - return CMD_SUCCESS; -} - - DEFUN (no_synchronization, no_synchronization_cmd, "no synchronization", @@ -1109,10 +1064,25 @@ DEFUN (no_router_bgp, } if (bgp->l3vni) { - vty_out(vty, "%% Please unconfigure l3vni %u", + vty_out(vty, "%% Please unconfigure l3vni %u\n", bgp->l3vni); return CMD_WARNING_CONFIG_FAILED; } + + /* Cannot delete default instance if vrf instances exist */ + if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { + struct listnode *node; + struct bgp *tmp_bgp; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) { + if (tmp_bgp->inst_type + == BGP_INSTANCE_TYPE_VRF) { + vty_out(vty, + "%% Cannot delete default BGP instance. Dependent VRF instances exist\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + } } bgp_delete(bgp); @@ -2123,28 +2093,6 @@ DEFUN (no_bgp_fast_external_failover, return CMD_SUCCESS; } -/* "bgp enforce-first-as" configuration. */ -#if CONFDATE > 20190517 -CPP_NOTICE("bgpd: remove deprecated '[no] bgp enforce-first-as' commands") -#endif - -DEFUN_HIDDEN (bgp_enforce_first_as, - bgp_enforce_first_as_cmd, - "[no] bgp enforce-first-as", - NO_STR - BGP_STR - "Enforce the first AS for EBGP routes\n") -{ - VTY_DECLVAR_CONTEXT(bgp, bgp); - - if (strmatch(argv[0]->text, "no")) - bgp_flag_unset(bgp, BGP_FLAG_ENFORCE_FIRST_AS); - else - bgp_flag_set(bgp, BGP_FLAG_ENFORCE_FIRST_AS); - - return CMD_SUCCESS; -} - /* "bgp bestpath compare-routerid" configuration. */ DEFUN (bgp_bestpath_compare_router_id, bgp_bestpath_compare_router_id_cmd, @@ -3943,6 +3891,13 @@ ALIAS_HIDDEN(neighbor_nexthop_self_force, "Disable the next hop calculation for this neighbor\n" "Set the next hop to self for reflected routes\n") +ALIAS_HIDDEN(neighbor_nexthop_self_force, + neighbor_nexthop_self_all_hidden_cmd, + "neighbor next-hop-self all", + NEIGHBOR_STR NEIGHBOR_ADDR_STR2 + "Disable the next hop calculation for this neighbor\n" + "Set the next hop to self for reflected routes\n") + DEFUN (no_neighbor_nexthop_self, no_neighbor_nexthop_self_cmd, "no neighbor next-hop-self", @@ -3984,6 +3939,13 @@ ALIAS_HIDDEN(no_neighbor_nexthop_self_force, "Disable the next hop calculation for this neighbor\n" "Set the next hop to self for reflected routes\n") +ALIAS_HIDDEN(no_neighbor_nexthop_self_force, + no_neighbor_nexthop_self_all_hidden_cmd, + "no neighbor next-hop-self all", + NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 + "Disable the next hop calculation for this neighbor\n" + "Set the next hop to self for reflected routes\n") + /* neighbor as-override */ DEFUN (neighbor_as_override, neighbor_as_override_cmd, @@ -7454,11 +7416,6 @@ DEFUN (show_bgp_views, struct listnode *node; struct bgp *bgp; - if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - vty_out(vty, "BGP Multiple Instance is not enabled\n"); - return CMD_WARNING; - } - vty_out(vty, "Defined BGP views:\n"); for (ALL_LIST_ELEMENTS_RO(inst, node, bgp)) { /* Skip VRFs. */ @@ -7489,11 +7446,6 @@ DEFUN (show_bgp_vrfs, json_object *json_vrfs = NULL; int count = 0; - if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - vty_out(vty, "BGP Multiple Instance is not enabled\n"); - return CMD_WARNING; - } - if (uj) { json = json_object_new_object(); json_vrfs = json_object_new_object(); @@ -10897,11 +10849,11 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, if (p->password) vty_out(vty, "Peer Authentication Enabled\n"); - vty_out(vty, "Read thread: %s Write thread: %s\n", + vty_out(vty, "Read thread: %s Write thread: %s FD used: %d\n", p->t_read ? "on" : "off", CHECK_FLAG(p->thread_flags, PEER_THREAD_WRITES_ON) ? "on" - : "off"); + : "off", p->fd); } if (p->notify.code == BGP_NOTIFY_OPEN_ERR @@ -12869,14 +12821,6 @@ void bgp_vty_init(void) install_default(BGP_EVPN_NODE); install_default(BGP_EVPN_VNI_NODE); - /* "bgp multiple-instance" commands. */ - install_element(CONFIG_NODE, &bgp_multiple_instance_cmd); - install_element(CONFIG_NODE, &no_bgp_multiple_instance_cmd); - - /* "bgp config-type" commands. */ - install_element(CONFIG_NODE, &bgp_config_type_cmd); - install_element(CONFIG_NODE, &no_bgp_config_type_cmd); - /* "bgp local-mac" hidden commands. */ install_element(CONFIG_NODE, &bgp_local_mac_cmd); install_element(CONFIG_NODE, &no_bgp_local_mac_cmd); @@ -13002,9 +12946,6 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_fast_external_failover_cmd); install_element(BGP_NODE, &no_bgp_fast_external_failover_cmd); - /* "bgp enforce-first-as" commands */ - install_element(BGP_NODE, &bgp_enforce_first_as_cmd); - /* "bgp bestpath compare-routerid" commands */ install_element(BGP_NODE, &bgp_bestpath_compare_router_id_cmd); install_element(BGP_NODE, &no_bgp_bestpath_compare_router_id_cmd); @@ -13234,6 +13175,8 @@ void bgp_vty_init(void) /* "neighbor next-hop-self force" commands. */ install_element(BGP_NODE, &neighbor_nexthop_self_force_hidden_cmd); install_element(BGP_NODE, &no_neighbor_nexthop_self_force_hidden_cmd); + install_element(BGP_NODE, &neighbor_nexthop_self_all_hidden_cmd); + install_element(BGP_NODE, &no_neighbor_nexthop_self_all_hidden_cmd); install_element(BGP_IPV4_NODE, &neighbor_nexthop_self_force_cmd); install_element(BGP_IPV4_NODE, &no_neighbor_nexthop_self_force_cmd); install_element(BGP_IPV4M_NODE, &neighbor_nexthop_self_force_cmd); @@ -14580,7 +14523,7 @@ static int lcommunity_list_unset_vty(struct vty *vty, int argc, vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); vty_out(vty, "if you are using this please migrate to the below command.\n"); vty_out(vty, "'no bgp large-community-list <(1-99)|(100-500)|standard|expanded> '\n"); - zlog_warn("Deprecated option: 'no ip large-community-list <(1-99)|(100-500)|standard|expanded> ' being used"); + zlog_warn("Deprecated option: 'no ip large-community-list <(1-99)|(100-500)|standard|expanded> ' being used"); } argv_find(argv, argc, "permit", &idx); argv_find(argv, argc, "deny", &idx); @@ -14935,14 +14878,16 @@ static void lcommunity_list_show(struct vty *vty, struct community_list *list) if (entry == list->head) { if (all_digit(list->name)) vty_out(vty, "Large community %s list %s\n", - entry->style == EXTCOMMUNITY_LIST_STANDARD + entry->style == + LARGE_COMMUNITY_LIST_STANDARD ? "standard" : "(expanded) access", list->name); else vty_out(vty, "Named large community %s list %s\n", - entry->style == EXTCOMMUNITY_LIST_STANDARD + entry->style == + LARGE_COMMUNITY_LIST_STANDARD ? "standard" : "expanded", list->name); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index e42d6ee26035..45ce8ef6e3c7 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -83,8 +83,7 @@ static inline int bgp_install_info_to_zebra(struct bgp *bgp) int zclient_num_connects; /* Router-id update message from zebra. */ -static int bgp_router_id_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_router_id_update(ZAPI_CALLBACK_ARGS) { struct prefix router_id; @@ -101,17 +100,15 @@ static int bgp_router_id_update(int command, struct zclient *zclient, } /* Nexthop update message from zebra. */ -static int bgp_read_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_read_nexthop_update(ZAPI_CALLBACK_ARGS) { - bgp_parse_nexthop_update(command, vrf_id); + bgp_parse_nexthop_update(cmd, vrf_id); return 0; } -static int bgp_read_import_check_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_read_import_check_update(ZAPI_CALLBACK_ARGS) { - bgp_parse_nexthop_update(command, vrf_id); + bgp_parse_nexthop_update(cmd, vrf_id); return 0; } @@ -206,8 +203,7 @@ static void bgp_nbr_connected_delete(struct bgp *bgp, struct nbr_connected *ifc, } /* Inteface addition message from zebra. */ -static int bgp_interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct bgp *bgp; @@ -229,8 +225,7 @@ static int bgp_interface_add(int command, struct zclient *zclient, return 0; } -static int bgp_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_delete(ZAPI_CALLBACK_ARGS) { struct stream *s; struct interface *ifp; @@ -255,8 +250,7 @@ static int bgp_interface_delete(int command, struct zclient *zclient, return 0; } -static int bgp_interface_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_up(ZAPI_CALLBACK_ARGS) { struct stream *s; struct interface *ifp; @@ -290,8 +284,7 @@ static int bgp_interface_up(int command, struct zclient *zclient, return 0; } -static int bgp_interface_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_down(ZAPI_CALLBACK_ARGS) { struct stream *s; struct interface *ifp; @@ -350,15 +343,14 @@ static int bgp_interface_down(int command, struct zclient *zclient, return 0; } -static int bgp_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct bgp *bgp; bgp = bgp_lookup_by_vrf_id(vrf_id); - ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -388,15 +380,14 @@ static int bgp_interface_address_add(int command, struct zclient *zclient, return 0; } -static int bgp_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct bgp *bgp; bgp = bgp_lookup_by_vrf_id(vrf_id); - ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -417,13 +408,12 @@ static int bgp_interface_address_delete(int command, struct zclient *zclient, return 0; } -static int bgp_interface_nbr_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_nbr_address_add(ZAPI_CALLBACK_ARGS) { struct nbr_connected *ifc = NULL; struct bgp *bgp; - ifc = zebra_interface_nbr_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_nbr_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -444,15 +434,12 @@ static int bgp_interface_nbr_address_add(int command, struct zclient *zclient, return 0; } -static int bgp_interface_nbr_address_delete(int command, - struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static int bgp_interface_nbr_address_delete(ZAPI_CALLBACK_ARGS) { struct nbr_connected *ifc = NULL; struct bgp *bgp; - ifc = zebra_interface_nbr_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_nbr_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -476,8 +463,7 @@ static int bgp_interface_nbr_address_delete(int command, } /* VRF update for an interface. */ -static int bgp_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t new_vrf_id; @@ -532,8 +518,7 @@ static int bgp_interface_vrf_update(int command, struct zclient *zclient, } /* Zebra route add and delete treatment. */ -static int zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int zebra_read_route(ZAPI_CALLBACK_ARGS) { enum nexthop_types_t nhtype; struct zapi_route api; @@ -562,7 +547,7 @@ static int zebra_read_route(int command, struct zclient *zclient, ifindex = api.nexthops[0].ifindex; nhtype = api.nexthops[0].type; - add = (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD); + add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD); if (add) { /* * The ADD message is actually an UPDATE and there is no @@ -1450,15 +1435,29 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, for (i = 0; i < api.nexthop_num; i++) { api_nh = &api.nexthops[i]; - if (api_nh->type == NEXTHOP_TYPE_IFINDEX) + switch (api_nh->type) { + case NEXTHOP_TYPE_IFINDEX: nh_buf[0] = '\0'; - else { - if (api_nh->type == NEXTHOP_TYPE_IPV4) - nh_family = AF_INET; - else - nh_family = AF_INET6; + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + nh_family = AF_INET; inet_ntop(nh_family, &api_nh->gate, nh_buf, sizeof(nh_buf)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + nh_family = AF_INET6; + inet_ntop(nh_family, &api_nh->gate, nh_buf, + sizeof(nh_buf)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + strlcpy(nh_buf, "blackhole", sizeof(nh_buf)); + break; + default: + /* Note: add new nexthop case */ + assert(0); + break; } label_buf[0] = '\0'; @@ -2101,8 +2100,7 @@ int bgp_zebra_dup_addr_detection(struct bgp *bgp) return zclient_send_message(zclient); } -static int rule_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int rule_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t seqno, priority, unique; enum zapi_rule_notify_owner note; @@ -2171,8 +2169,7 @@ static int rule_notify_owner(int command, struct zclient *zclient, return 0; } -static int ipset_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ipset_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t unique; enum zapi_ipset_notify_owner note; @@ -2217,8 +2214,7 @@ static int ipset_notify_owner(int command, struct zclient *zclient, return 0; } -static int ipset_entry_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ipset_entry_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t unique; char ipset_name[ZEBRA_IPSET_NAME_SIZE]; @@ -2275,8 +2271,7 @@ static int ipset_entry_notify_owner(int command, struct zclient *zclient, return 0; } -static int iptable_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int iptable_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t unique; enum zapi_iptable_notify_owner note; @@ -2322,7 +2317,7 @@ static int iptable_notify_owner(int command, struct zclient *zclient, /* this function is used to forge ip rule, * - either for iptable/ipset using fwmark id - * - or for sample ip rule command + * - or for sample ip rule cmd */ static void bgp_encode_pbr_rule_action(struct stream *s, struct bgp_pbr_action *pbra, @@ -2460,7 +2455,7 @@ static void bgp_zebra_connected(struct zclient *zclient) bgp_zebra_instance_register(bgp); /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, bgp->vrf_id); /* tell label pool that zebra is connected */ bgp_lp_event_zebra_up(); @@ -2470,8 +2465,7 @@ static void bgp_zebra_connected(struct zclient *zclient) */ } -static int bgp_zebra_process_local_es(int cmd, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_zebra_process_local_es(ZAPI_CALLBACK_ARGS) { esi_t esi; struct bgp *bgp = NULL; @@ -2504,8 +2498,7 @@ static int bgp_zebra_process_local_es(int cmd, struct zclient *zclient, return 0; } -static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_zebra_process_local_l3vni(ZAPI_CALLBACK_ARGS) { int filter = 0; char buf[ETHER_ADDR_STRLEN]; @@ -2545,8 +2538,7 @@ static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient, return 0; } -static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS) { struct stream *s; vni_t vni; @@ -2557,7 +2549,7 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, s = zclient->ibuf; vni = stream_getl(s); - if (command == ZEBRA_VNI_ADD) { + if (cmd == ZEBRA_VNI_ADD) { vtep_ip.s_addr = stream_get_ipv4(s); stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t)); mcast_grp.s_addr = stream_get_ipv4(s); @@ -2569,11 +2561,11 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s", - (command == ZEBRA_VNI_ADD) ? "add" : "del", + (cmd == ZEBRA_VNI_ADD) ? "add" : "del", vrf_id_to_name(vrf_id), vni, vrf_id_to_name(tenant_vrf_id)); - if (command == ZEBRA_VNI_ADD) + if (cmd == ZEBRA_VNI_ADD) return bgp_evpn_local_vni_add( bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id, tenant_vrf_id, mcast_grp); @@ -2581,8 +2573,7 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, return bgp_evpn_local_vni_del(bgp, vni); } -static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_zebra_process_local_macip(ZAPI_CALLBACK_ARGS) { struct stream *s; vni_t vni; @@ -2605,7 +2596,7 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, && ipa_len != IPV6_MAX_BYTELEN) { flog_err(EC_BGP_MACIP_LEN, "%u:Recv MACIP %s with invalid IP addr length %d", - vrf_id, (command == ZEBRA_MACIP_ADD) ? "Add" : "Del", + vrf_id, (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", ipa_len); return -1; } @@ -2615,7 +2606,7 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 : IPADDR_V6; stream_get(&ip.ip.addr, s, ipa_len); } - if (command == ZEBRA_MACIP_ADD) { + if (cmd == ZEBRA_MACIP_ADD) { flags = stream_getc(s); seqnum = stream_getl(s); } else { @@ -2628,21 +2619,19 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%u:Recv MACIP %s flags 0x%x MAC %s IP %s VNI %u seq %u state %d", - vrf_id, (command == ZEBRA_MACIP_ADD) ? "Add" : "Del", + vrf_id, (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags, prefix_mac2str(&mac, buf, sizeof(buf)), ipaddr2str(&ip, buf1, sizeof(buf1)), vni, seqnum, state); - if (command == ZEBRA_MACIP_ADD) + if (cmd == ZEBRA_MACIP_ADD) return bgp_evpn_local_macip_add(bgp, vni, &mac, &ip, flags, seqnum); else return bgp_evpn_local_macip_del(bgp, vni, &mac, &ip, state); } -static void bgp_zebra_process_local_ip_prefix(int cmd, struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static void bgp_zebra_process_local_ip_prefix(ZAPI_CALLBACK_ARGS) { struct stream *s = NULL; struct bgp *bgp_vrf = NULL; @@ -2682,11 +2671,7 @@ static void bgp_zebra_process_local_ip_prefix(int cmd, struct zclient *zclient, } } -static void bgp_zebra_process_label_chunk( - int cmd, - struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static void bgp_zebra_process_label_chunk(ZAPI_CALLBACK_ARGS) { struct stream *s = NULL; uint8_t response_keep; @@ -2989,6 +2974,9 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); api_nh = &api.nexthops[0]; + api.distance = ZEBRA_EBGP_DISTANCE_DEFAULT; + SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); + /* redirect IP */ if (nh->gate.ipv4.s_addr) { char buff[PREFIX_STRLEN]; @@ -3015,7 +3003,7 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, /* create default route with interface * with nexthop-vrf */ - ifp = if_lookup_by_name_all_vrf(vrf->name); + ifp = if_lookup_by_name(vrf->name, nh->vrf_id); if (!ifp) return; api_nh->vrf_id = nh->vrf_id; diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index b2925cd51228..02eda7a430c4 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -171,8 +171,6 @@ int bgp_option_set(int flag) { switch (flag) { case BGP_OPT_NO_FIB: - case BGP_OPT_MULTIPLE_INSTANCE: - case BGP_OPT_CONFIG_CISCO: case BGP_OPT_NO_LISTEN: case BGP_OPT_NO_ZEBRA: SET_FLAG(bm->options, flag); @@ -186,13 +184,9 @@ int bgp_option_set(int flag) int bgp_option_unset(int flag) { switch (flag) { - case BGP_OPT_MULTIPLE_INSTANCE: - if (listcount(bm->bgp) > 1) - return BGP_ERR_MULTIPLE_INSTANCE_USED; /* Fall through. */ case BGP_OPT_NO_ZEBRA: case BGP_OPT_NO_FIB: - case BGP_OPT_CONFIG_CISCO: UNSET_FLAG(bm->options, flag); break; default: @@ -812,9 +806,9 @@ int peer_cmp(struct peer *p1, struct peer *p2) return sockunion_cmp(&p1->su, &p2->su); } -static unsigned int peer_hash_key_make(void *p) +static unsigned int peer_hash_key_make(const void *p) { - struct peer *peer = p; + const struct peer *peer = p; return sockunion_hash(&peer->su); } @@ -1205,21 +1199,18 @@ struct peer *peer_new(struct bgp *bgp) /* Set default flags. */ FOREACH_AFI_SAFI (afi, safi) { - if (!bgp_option_check(BGP_OPT_CONFIG_CISCO)) { - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SEND_COMMUNITY); - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SEND_EXT_COMMUNITY); - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SEND_LARGE_COMMUNITY); - - SET_FLAG(peer->af_flags_invert[afi][safi], - PEER_FLAG_SEND_COMMUNITY); - SET_FLAG(peer->af_flags_invert[afi][safi], - PEER_FLAG_SEND_EXT_COMMUNITY); - SET_FLAG(peer->af_flags_invert[afi][safi], - PEER_FLAG_SEND_LARGE_COMMUNITY); - } + SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY); + SET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_SEND_LARGE_COMMUNITY); + + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_COMMUNITY); + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_LARGE_COMMUNITY); peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE; } @@ -2232,6 +2223,8 @@ int peer_delete(struct peer *peer) SET_FLAG(peer->flags, PEER_FLAG_DELETE); + bgp_bfd_deregister_peer(peer); + /* If this peer belongs to peer group, clear up the relationship. */ if (peer->group) { @@ -3172,40 +3165,21 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, struct vrf *vrf = NULL; /* Multiple instance check. */ - if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - if (name) - bgp = bgp_lookup_by_name(name); - else - bgp = bgp_get_default(); - - /* Already exists. */ - if (bgp) { - if (bgp->as != *as) { - *as = bgp->as; - return BGP_ERR_INSTANCE_MISMATCH; - } - if (bgp->inst_type != inst_type) - return BGP_ERR_INSTANCE_MISMATCH; - *bgp_val = bgp; - return 0; - } - } else { - /* BGP instance name can not be specified for single instance. - */ - if (name) - return BGP_ERR_MULTIPLE_INSTANCE_NOT_SET; - - /* Get default BGP structure if exists. */ + if (name) + bgp = bgp_lookup_by_name(name); + else bgp = bgp_get_default(); - if (bgp) { - if (bgp->as != *as) { - *as = bgp->as; - return BGP_ERR_AS_MISMATCH; - } - *bgp_val = bgp; - return 0; + /* Already exists. */ + if (bgp) { + if (bgp->as != *as) { + *as = bgp->as; + return BGP_ERR_INSTANCE_MISMATCH; } + if (bgp->inst_type != inst_type) + return BGP_ERR_INSTANCE_MISMATCH; + *bgp_val = bgp; + return 0; } bgp = bgp_create(as, name, inst_type); @@ -6186,8 +6160,15 @@ int peer_route_map_set(struct peer *peer, afi_t afi, safi_t safi, int direct, /* Set configuration on peer. */ filter = &peer->filter[afi][safi]; - if (filter->map[direct].name) + if (filter->map[direct].name) { + /* If the neighbor is configured with the same route-map + * again then, ignore the duplicate configuration. + */ + if (strcmp(filter->map[direct].name, name) == 0) + return 0; + XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name); + } route_map_counter_decrement(filter->map[direct].map); filter->map[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name); filter->map[direct].map = route_map; @@ -7310,45 +7291,19 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, flag_slcomm = peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY); - if (!bgp_option_check(BGP_OPT_CONFIG_CISCO)) { - if (flag_scomm && flag_secomm && flag_slcomm) { - vty_out(vty, " no neighbor %s send-community all\n", - addr); - } else { - if (flag_scomm) - vty_out(vty, - " no neighbor %s send-community\n", - addr); - if (flag_secomm) - vty_out(vty, - " no neighbor %s send-community extended\n", - addr); - - if (flag_slcomm) - vty_out(vty, - " no neighbor %s send-community large\n", - addr); - } + if (flag_scomm && flag_secomm && flag_slcomm) { + vty_out(vty, " no neighbor %s send-community all\n", addr); } else { - if (flag_scomm && flag_secomm && flag_slcomm) { - vty_out(vty, " neighbor %s send-community all\n", + if (flag_scomm) + vty_out(vty, " no neighbor %s send-community\n", addr); + if (flag_secomm) + vty_out(vty, + " no neighbor %s send-community extended\n", addr); - } else if (flag_scomm && flag_secomm) { - vty_out(vty, " neighbor %s send-community both\n", + + if (flag_slcomm) + vty_out(vty, " no neighbor %s send-community large\n", addr); - } else { - if (flag_scomm) - vty_out(vty, " neighbor %s send-community\n", - addr); - if (flag_secomm) - vty_out(vty, - " neighbor %s send-community extended\n", - addr); - if (flag_slcomm) - vty_out(vty, - " neighbor %s send-community large\n", - addr); - } } /* Default information */ @@ -7550,12 +7505,6 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, vty_endframe(vty, " exit-address-family\n"); } -/* clang-format off */ -#if CONFDATE > 20190517 -CPP_NOTICE("bgpd: remove 'bgp enforce-first-as' config migration from bgp_config_write") -#endif -/* clang-format on */ - int bgp_config_write(struct vty *vty) { int write = 0; @@ -7565,18 +7514,6 @@ int bgp_config_write(struct vty *vty) struct listnode *node, *nnode; struct listnode *mnode, *mnnode; - /* BGP Multiple instance. */ - if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - vty_out(vty, "no bgp multiple-instance\n"); - write++; - } - - /* BGP Config type. */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) { - vty_out(vty, "bgp config-type cisco\n"); - write++; - } - if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER) vty_out(vty, "bgp route-map delay-timer %u\n", bm->rmap_update_timer); @@ -7591,33 +7528,15 @@ int bgp_config_write(struct vty *vty) if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) continue; - /* Migrate deprecated 'bgp enforce-first-as' - * config to 'neighbor * enforce-first-as' configs - */ - if (bgp_flag_check(bgp, BGP_FLAG_ENFORCE_FIRST_AS)) { - for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) - peer_flag_set(peer, PEER_FLAG_ENFORCE_FIRST_AS); - bgp_flag_unset(bgp, BGP_FLAG_ENFORCE_FIRST_AS); - } - /* Router bgp ASN */ vty_out(vty, "router bgp %u", bgp->as); - if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - if (bgp->name) - vty_out(vty, " %s %s", - (bgp->inst_type - == BGP_INSTANCE_TYPE_VIEW) - ? "view" - : "vrf", - bgp->name); - } + if (bgp->name) + vty_out(vty, " %s %s", + (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) + ? "view" : "vrf", bgp->name); vty_out(vty, "\n"); - /* No Synchronization */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) - vty_out(vty, " no synchronization\n"); - /* BGP fast-external-failover. */ if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) vty_out(vty, " no bgp fast-external-failover\n"); @@ -7831,10 +7750,6 @@ int bgp_config_write(struct vty *vty) if (bgp->autoshutdown) vty_out(vty, " bgp default shutdown\n"); - /* No auto-summary */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) - vty_out(vty, " no auto-summary\n"); - /* IPv4 unicast configuration. */ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); @@ -7910,9 +7825,6 @@ void bgp_master_init(struct thread_master *master) bf_init(bm->rd_idspace, UINT16_MAX); bf_assign_zero_index(bm->rd_idspace); - /* Enable multiple instances by default. */ - bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE); - /* mpls label dynamic allocation pool */ bgp_lp_init(bm->master, &bm->labelpool); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index b0f65675342d..518f1f6714fd 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -134,10 +134,8 @@ struct bgp_master { /* Various BGP global configuration. */ uint8_t options; #define BGP_OPT_NO_FIB (1 << 0) -#define BGP_OPT_MULTIPLE_INSTANCE (1 << 1) -#define BGP_OPT_CONFIG_CISCO (1 << 2) -#define BGP_OPT_NO_LISTEN (1 << 3) -#define BGP_OPT_NO_ZEBRA (1 << 4) +#define BGP_OPT_NO_LISTEN (1 << 1) +#define BGP_OPT_NO_ZEBRA (1 << 2) uint64_t updgrp_idspace; uint64_t subgrp_idspace; @@ -348,7 +346,6 @@ struct bgp { #define BGP_FLAG_MED_CONFED (1 << 3) #define BGP_FLAG_NO_DEFAULT_IPV4 (1 << 4) #define BGP_FLAG_NO_CLIENT_TO_CLIENT (1 << 5) -#define BGP_FLAG_ENFORCE_FIRST_AS (1 << 6) #define BGP_FLAG_COMPARE_ROUTER_ID (1 << 7) #define BGP_FLAG_ASPATH_IGNORE (1 << 8) #define BGP_FLAG_IMPORT_CHECK (1 << 9) diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index 2220f0ed9abf..cad33404fad3 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -2208,24 +2208,6 @@ void vnc_routemap_update(struct bgp *bgp, const char *unused) vnc_zlog_debug_verbose("%s done", __func__); } -#if 0 /* superseded */ -static void vnc_routemap_event(route_map_event_t type, /* ignored */ - const char *rmap_name) /* ignored */ -{ - struct listnode *mnode, *mnnode; - struct bgp *bgp; - - vnc_zlog_debug_verbose("%s(event type=%d)", __func__, type); - if (bm->bgp == NULL) /* may be called during cleanup */ - return; - - for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) - vnc_routemap_update(bgp, rmap_name); - - vnc_zlog_debug_verbose("%s: done", __func__); -} -#endif - /*------------------------------------------------------------------------- * nve-group *-----------------------------------------------------------------------*/ @@ -3699,10 +3681,6 @@ bgp_rfapi_get_ecommunity_by_lni_label(struct bgp *bgp, uint32_t is_import, void bgp_rfapi_cfg_init(void) { - /* main bgpd code does not use this hook, but vnc does */ - /* superseded by bgp_route_map_process_update_cb() */ - /* bgp_route_map_event_hook_add(vnc_routemap_event); */ - install_node(&bgp_vnc_defaults_node, NULL); install_node(&bgp_vnc_nve_group_node, NULL); install_node(&bgp_vrf_policy_node, NULL); diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index 568f8d68e863..583adcda77cc 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -374,42 +374,15 @@ int rfapiGetVncLifetime(struct attr *attr, uint32_t *lifetime) return ENOENT; } -/* - * Extract the tunnel type from the extended community - */ -int rfapiGetTunnelType(struct attr *attr, bgp_encap_types *type) -{ - *type = BGP_ENCAP_TYPE_MPLS; /* default to MPLS */ - if (attr && attr->ecommunity) { - struct ecommunity *ecom = attr->ecommunity; - int i; - - for (i = 0; i < (ecom->size * ECOMMUNITY_SIZE); - i += ECOMMUNITY_SIZE) { - uint8_t *ep; - - ep = ecom->val + i; - if (ep[0] == ECOMMUNITY_ENCODE_OPAQUE - && ep[1] == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) { - *type = (ep[6] << 8) + ep[7]; - return 0; - } - } - } - - return ENOENT; -} - - /* * Look for UN address in Encap attribute */ int rfapiGetVncTunnelUnAddr(struct attr *attr, struct prefix *p) { struct bgp_attr_encap_subtlv *pEncap; - bgp_encap_types tun_type; + bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS;/*Default tunnel type*/ - rfapiGetTunnelType(attr, &tun_type); + bgp_attr_extcom_tunnel_type(attr, &tun_type); if (tun_type == BGP_ENCAP_TYPE_MPLS) { if (!p) return 0; @@ -1170,6 +1143,9 @@ static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1, break; } + memset(&pfx_un1, 0, sizeof(pfx_un1)); + memset(&pfx_un2, 0, sizeof(pfx_un2)); + /* * UN address comparisons */ @@ -1211,7 +1187,7 @@ static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1, } } - if (!pfx_un1.family || !pfx_un2.family) + if (pfx_un1.family == 0 || pfx_un2.family == 0) return 0; if (pfx_un1.family != pfx_un2.family) @@ -1350,7 +1326,7 @@ rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix, } if (bpi->attr) { - bgp_encap_types tun_type; + bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS; /*Default*/ new->prefix.cost = rfapiRfpCost(bpi->attr); struct bgp_attr_encap_subtlv *pEncap; @@ -1390,7 +1366,7 @@ rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix, } } - rfapiGetTunnelType(bpi->attr, &tun_type); + bgp_attr_extcom_tunnel_type(bpi->attr, &tun_type); if (tun_type == BGP_ENCAP_TYPE_MPLS) { struct prefix p; /* MPLS carries UN address in next hop */ @@ -2011,11 +1987,14 @@ static void rfapiBgpInfoAttachSorted(struct agg_node *rn, for (prev = NULL, next = rn->info; next; prev = next, next = next->next) { + enum bgp_path_selection_reason reason; + if (!bgp || (!CHECK_FLAG(info_new->flags, BGP_PATH_REMOVED) && CHECK_FLAG(next->flags, BGP_PATH_REMOVED)) || bgp_path_info_cmp_compatible(bgp, info_new, next, - pfx_buf, afi, safi) + pfx_buf, afi, safi, + &reason) == -1) { /* -1 if 1st is better */ break; } @@ -4135,6 +4114,9 @@ static void rfapiProcessPeerDownRt(struct peer *peer, timer_service_func = rfapiWithdrawTimerEncap; break; default: + /* Suppress uninitialized variable warning */ + rt = NULL; + timer_service_func = NULL; assert(0); } diff --git a/bgpd/rfapi/rfapi_private.h b/bgpd/rfapi/rfapi_private.h index 87d9a32f67c2..ff1cf7ef427d 100644 --- a/bgpd/rfapi/rfapi_private.h +++ b/bgpd/rfapi/rfapi_private.h @@ -306,8 +306,6 @@ extern int rfapiCliGetPrefixAddr(struct vty *vty, const char *str, extern int rfapiGetVncLifetime(struct attr *attr, uint32_t *lifetime); -extern int rfapiGetTunnelType(struct attr *attr, bgp_encap_types *type); - extern int rfapiGetVncTunnelUnAddr(struct attr *attr, struct prefix *p); extern int rfapi_reopen(struct rfapi_descriptor *rfd, struct bgp *bgp); diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index ea82c254bcec..46161b4f382d 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -1020,7 +1020,7 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, struct prefix pfx_vn; uint8_t cost; uint32_t lifetime; - bgp_encap_types tun_type; + bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS;/*Default tunnel type*/ char buf_pfx[BUFSIZ]; char buf_ntop[BUFSIZ]; @@ -1055,7 +1055,7 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, BUFSIZ)); } - rfapiGetTunnelType(bpi->attr, &tun_type); + bgp_attr_extcom_tunnel_type(bpi->attr, &tun_type); /* * VN addr */ diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c index b08e92296225..481500dfb445 100644 --- a/bgpd/rfapi/vnc_zebra.c +++ b/bgpd/rfapi/vnc_zebra.c @@ -344,8 +344,7 @@ static void vnc_redistribute_withdraw(struct bgp *bgp, afi_t afi, uint8_t type) * * Assumes 1 nexthop */ -static int vnc_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int vnc_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; int add; @@ -357,7 +356,7 @@ static int vnc_zebra_read_route(int command, struct zclient *zclient, if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) return 0; - add = (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD); + add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD); if (add) vnc_redistribute_add(&api.prefix, api.metric, api.type); else diff --git a/configure.ac b/configure.ac index 9ae196fcb19e..77d5ee155558 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ([2.60]) -AC_INIT([frr], [7.1-dev], [https://github.com/frrouting/frr/issues]) +AC_INIT([frr], [7.2-dev], [https://github.com/frrouting/frr/issues]) PACKAGE_URL="https://frrouting.org/" AC_SUBST([PACKAGE_URL]) PACKAGE_FULLNAME="FRRouting" @@ -126,12 +126,15 @@ dnl Check CC and friends dnl -------------------- dnl note orig_cflags is also used further down orig_cflags="$CFLAGS" +orig_cxxflags="$CXXFLAGS" AC_LANG([C]) AC_PROG_CC AC_PROG_CPP +AC_PROG_CXX AM_PROG_CC_C_O dnl remove autoconf default "-g -O2" CFLAGS="$orig_cflags" +CXXFLAGS="$orig_cxxflags" AC_PROG_CC_C99 dnl NB: see C11 below @@ -180,15 +183,17 @@ AC_DEFUN([AC_LINK_IFELSE_FLAGS], [{ AC_LINK_IFELSE( [$3], [ - AC_MSG_RESULT([yes]) CFLAGS="$ac_cflags_save" LIBS="$ac_libs_save" - $5 + m4_default([$5], [ + AC_MSG_RESULT([yes]) + ]) ], [ - AC_MSG_RESULT([no]) CFLAGS="$ac_cflags_save" LIBS="$ac_libs_save" - $4 + m4_default([$4], [ + AC_MSG_RESULT([no]) + ]) ]) AC_LANG_POP([C]) }]) @@ -219,9 +224,12 @@ elif test "x${enable_dev_build}" = "xyes"; then AC_C_FLAG([-O0]) fi if test "x${enable_lua}" = "xyes"; then - AC_CHECK_LIB([lua], [lua_newstate], - [LIBS="$LIBS -llua"]) - AC_DEFINE([HAVE_LUA], [1], [Lua enabled for development]) + AX_PROG_LUA([5.3]) + AX_LUA_HEADERS + AX_LUA_LIBS([ + AC_DEFINE([HAVE_LUA], [1], [Have support for Lua interpreter]) + LIBS="$LIBS $LUA_LIB" + ]) fi else if test "x${enable_lua}" = "xyes"; then @@ -437,6 +445,8 @@ AC_ARG_ENABLE([fabricd], AS_HELP_STRING([--disable-fabricd], [do not build fabricd])) AC_ARG_ENABLE([bgp-announce], AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement])) +AC_ARG_ENABLE([vrrpd], + AS_HELP_STRING([--disable-vrrpd], [do not build vrrpd])) AC_ARG_ENABLE([bgp-vnc], AS_HELP_STRING([--disable-bgp-vnc],[turn off BGP VNC support])) AC_ARG_ENABLE([snmp], @@ -447,6 +457,8 @@ AC_ARG_ENABLE([confd], AS_HELP_STRING([--enable-confd=ARG], [enable confd integration])) AC_ARG_ENABLE([sysrepo], AS_HELP_STRING([--enable-sysrepo], [enable sysrepo integration])) +AC_ARG_ENABLE([grpc], + AS_HELP_STRING([--enable-grpc], [enable the gRPC northbound plugin])) AC_ARG_ENABLE([zeromq], AS_HELP_STRING([--enable-zeromq], [enable ZeroMQ handler (libfrrzmq)])) AC_ARG_WITH([libpam], @@ -599,92 +611,30 @@ AM_CONDITIONAL([FPM], [test "x$enable_fpm" = "xyes"]) # Python for clippy # -AC_DEFUN([FRR_PYTHON_CHECK_WORKING], [ - AC_MSG_CHECKING([whether we found a working Python version]) - AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ -#include -#if PY_VERSION_HEX < 0x02070000 -#error python too old -#endif -int main(void); -], -[ -{ - Py_Initialize(); - return 0; -} -])], [ - # some python installs are missing the zlib dependency... - PYTHON_LIBS="${PYTHON_LIBS} -lz" - AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ -#include -#if PY_VERSION_HEX < 0x02070000 -#error python too old -#endif -int main(void); -], -[ -{ - Py_Initialize(); - return 0; -} -])], [ - m4_if([$1], [], [ - PYTHONCONFIG="" - unset PYTHON_LIBS - unset PYTHON_CFLAGS - ], [$1]) - ]) - ]) -]) - AS_IF([test "$host" = "$build"], [ - PYTHONCONFIG="" - - # ordering: - # 1. try python3, but respect the user's preference on which minor ver - # 2. try python, which might be py3 or py2 again on the user's preference - # 3. try python2 (can really only be 2.7 but eh) - # 4. try 3.6 > 3.5 > 3.4 > 3.3 > 3.2 > 2.7 through pkg-config (no user pref) - # - # (AX_PYTHON_DEVEL has no clue about py3 vs py2) - # (AX_PYTHON does not do what we need) - - AC_CHECK_TOOLS([PYTHONCONFIG], [ \ - python3-config \ - python-config \ - python2-config \ - python3.6-config \ - python3.5-config \ - python3.4-config \ - python3.3-config \ - python3.2-config \ - python2.7-config ]) - if test -n "$PYTHONCONFIG"; then - PYTHON_CFLAGS="`\"${PYTHONCONFIG}\" --includes`" - PYTHON_LIBS="`\"${PYTHONCONFIG}\" --ldflags`" - - FRR_PYTHON_CHECK_WORKING([]) - fi + FRR_PYTHON_DEV +], [ + FRR_PYTHON +]) - if test -z "$PYTHONCONFIG"; then - PKG_CHECK_MODULES([PYTHON], [python-3.6], [], [ - PKG_CHECK_MODULES([PYTHON], [python-3.5], [], [ - PKG_CHECK_MODULES([PYTHON], [python-3.4], [], [ - PKG_CHECK_MODULES([PYTHON], [python-3.3], [], [ - PKG_CHECK_MODULES([PYTHON], [python-3.2], [], [ - PKG_CHECK_MODULES([PYTHON], [python-2.7], [], [ - AC_MSG_FAILURE([could not find python-config or pkg-config python, please install Python development files from libpython-dev or similar]) - ])])])])])]) +FRR_PYTHON_MODULES([pytest]) +if test "${enable_doc}" != "no"; then + FRR_PYTHON_MODULES([sphinx], , [ + if test "${enable_doc}" = "yes"; then + AC_MSG_ERROR([Documentation was explicitly requested with --enable-doc but sphinx is not available for $PYTHON. Please disable docs or install sphinx.]) + fi + ]) +fi +AM_CONDITIONAL([DOC], [test "${enable_doc}" != "no" -a "$frr_py_mod_sphinx" != "false"]) +AM_CONDITIONAL([DOC_HTML], [test "${enable_doc_html}" = "yes"]) - FRR_PYTHON_CHECK_WORKING([ - AC_MSG_FAILURE([could not find python-config or pkg-config python, please install Python development files from libpython-dev or similar]) - ]) - fi +FRR_PYTHON_MOD_EXEC([sphinx], [--version], [ + PYSPHINX="-m sphinx" +], [ + PYSPHINX="-c 'import sys; from sphinx import main; sys.exit(main(sys.argv))'" ]) -AC_SUBST([PYTHON_CFLAGS]) -AC_SUBST([PYTHON_LIBS]) +AC_SUBST([PYSPHINX]) # # Logic for protobuf support. @@ -926,6 +876,80 @@ AC_CHECK_HEADERS([pthread_np.h],,, [ ]) AC_CHECK_FUNCS([pthread_setname_np pthread_set_name_np]) +needsync=true + +AS_IF([$needsync], [ + dnl Linux + AC_MSG_CHECKING([for Linux futex() support]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include + +int main(void); +], +[ +{ + return syscall(SYS_futex, NULL, FUTEX_WAIT, 0, NULL, NULL, 0); +} +])], [ + AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_SYNC_LINUX_FUTEX,,Have Linux futex support) + needsync=false + ], [ + AC_MSG_RESULT([no]) + ]) +]) + +AS_IF([$needsync], [ + dnl FreeBSD + AC_MSG_CHECKING([for FreeBSD _umtx_op() support]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#include +#include +#include +#include +int main(void); +], +[ +{ + return _umtx_op(NULL, UMTX_OP_WAIT_UINT, 0, NULL, NULL); +} +])], [ + AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_SYNC_UMTX_OP,,Have FreeBSD _umtx_op() support) + needsync=false + ], [ + AC_MSG_RESULT([no]) + ]) +]) + +AS_IF([$needsync], [ + dnl OpenBSD patch (not upstream at the time of writing this) + dnl https://marc.info/?l=openbsd-tech&m=147299508409549&w=2 + AC_MSG_CHECKING([for OpenBSD futex() support]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#include +int main(void); +], +[ +{ + return futex(NULL, FUTEX_WAIT, 0, NULL, NULL, 0); +} +])], [ + AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_SYNC_OPENBSD_FUTEX,,Have OpenBSD futex support) + needsync=false + ], [ + AC_MSG_RESULT([no]) + ]) +]) + dnl Utility macro to avoid retyping includes all the time m4_define([FRR_INCLUDES], [#ifdef SUNOS_5 @@ -1423,16 +1447,6 @@ FRR_INCLUDES #endif ])dnl -dnl disable doc check -AC_CHECK_PROGS([SPHINXBUILD], [sphinx-build sphinx-build3 sphinx-build2], [/bin/false]) -if test "$SPHINXBUILD" = "/bin/false"; then - if test "${enable_doc}" = "yes"; then - AC_MSG_ERROR([Documentation was explicitly requested with --enable-doc but sphinx-build is not available. Please disable docs or install sphinx.]) - fi -fi -AM_CONDITIONAL([DOC], [test "${enable_doc}" != "no" -a "$SPHINXBUILD" != "/bin/false"]) -AM_CONDITIONAL([DOC_HTML], [test "${enable_doc_html}" = "yes"]) - dnl -------------------- dnl Daemon disable check dnl -------------------- @@ -1520,6 +1534,7 @@ AM_CONDITIONAL([PBRD], [test "${enable_pbrd}" != "no"]) AM_CONDITIONAL([SHARPD], [test "${enable_sharpd}" = "yes"]) AM_CONDITIONAL([STATICD], [test "${enable_staticd}" != "no"]) AM_CONDITIONAL([FABRICD], [test "${enable_fabricd}" != "no"]) +AM_CONDITIONAL([VRRPD], [test "${enable_vrrpd}" != "no"]) if test "${enable_bgp_announce}" = "no";then AC_DEFINE([DISABLE_BGP_ANNOUNCE], [1], [Disable BGP installation to zebra]) @@ -1581,6 +1596,7 @@ int main(void); return 0; } ])], [ + AC_MSG_RESULT([no]) AC_MSG_ERROR([--enable-snmp given but not usable])]) case "${enable_snmp}" in yes) @@ -1616,24 +1632,6 @@ AC_CHECK_MEMBER([struct lyd_node.priv], [], [ ], [[#include ]]) CFLAGS="$ac_cflags_save" -ac_libs_save="$LIBS" -LIBS="$LIBS $LIBYANG_LIBS" -AC_CHECK_FUNC([ly_register_types], [ - libyang_ext_builtin=true - AC_DEFINE([LIBYANG_EXT_BUILTIN], [1], [have ly_register_types()]) -], [ - libyang_ext_builtin=false - AC_MSG_WARN([===== old libyang (before 0.16.74) detected =====]) - AC_MSG_WARN([The available version of libyang does not seem to support]) - AC_MSG_WARN([built-in YANG extension modules. This will cause "make check"]) - AC_MSG_WARN([to fail and may create installation and version mismatch issues.]) - AC_MSG_WARN([Support for the old mechanism will be removed at some point.]) - AC_MSG_WARN([Please update libyang to version 0.16.74 or newer.]) - AC_MSG_WARN([===== old libyang (before 0.16.74) detected =====]) -]) -AM_CONDITIONAL([LIBYANG_EXT_BUILTIN], [$libyang_ext_builtin]) -LIBS="$ac_libs_save" - dnl --------------- dnl configuration rollbacks dnl --------------- @@ -1678,6 +1676,25 @@ if test "$enable_sysrepo" = "yes"; then fi AM_CONDITIONAL([SYSREPO], [test "x$enable_sysrepo" = "xyes"]) +dnl --------------- +dnl gRPC +dnl --------------- +if test "$enable_grpc" = "yes"; then + PKG_CHECK_MODULES([GRPC], [grpc grpc++ protobuf], [ + AC_CHECK_PROGS([PROTOC], [protoc], [/bin/false]) + if test "$PROTOC" = "/bin/false"; then + AC_MSG_FAILURE([grpc requested but protoc not found.]) + fi + + AC_DEFINE([HAVE_GRPC], [1], [Enable the gRPC northbound plugin]) + GRPC=true + ], [ + GRPC=false + AC_MSG_ERROR([grpc/grpc++ were not found on your system.]) + ]) +fi +AM_CONDITIONAL([GRPC], [test "x$enable_grpc" = "xyes"]) + dnl --------------- dnl math dnl --------------- @@ -2116,10 +2133,10 @@ fi AC_MSG_RESULT([${frr_statedir}]) AC_SUBST([frr_statedir]) -AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir/ldpd.sock"], [ldpd control socket]) -AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir/zserv.api"], [zebra api socket]) -AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir/bfdd.sock"], [bfdd control socket]) -AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir"], [daemon vty directory]) +AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir%s%s/ldpd.sock"], [ldpd control socket]) +AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra api socket]) +AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket]) +AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory]) AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory]) dnl autoconf does this, but it does it too late... @@ -2270,7 +2287,9 @@ zebra protobuf enabled : ${enable_protobuf:-no} The above user and group must have read/write access to the state file directory and to the config files in the config file directory." -if test "${enable_doc}" != "no";then - AS_IF([test "$SPHINXBUILD" = /bin/false], - AC_MSG_WARN([sphinx-build is missing but required to build documentation])) +if test "${enable_doc}" != "no" -a "$frr_py_mod_sphinx" = false; then + AC_MSG_WARN([sphinx is missing but required to build documentation]) +fi +if test "$frr_py_mod_pytest" = false; then + AC_MSG_WARN([pytest is missing, unit tests cannot be performed]) fi diff --git a/debian/copyright b/debian/copyright index 61d87260d82e..d1f28a65a2f0 100644 --- a/debian/copyright +++ b/debian/copyright @@ -324,19 +324,6 @@ Copyright: Copyright (c) 2006, 2007 Pierre-Yves Ritschard Copyright (c) 2006, 2007, 2008 Reyk Floeter -Files: isisd/dict.* -Copyright: Copyright (C) 1997 Kaz Kylheku -License: custom-BSD-like - All rights are reserved by the author, with the following exceptions: - Permission is granted to freely reproduce and distribute this software, - possibly in exchange for a fee, provided that this copyright notice appears - intact. Permission is also granted to adapt this software to produce - derivative works, as long as the modified versions carry this copyright - notice and additional notices stating that the work has been modified. - This source code may be translated into executable form and incorporated - into proprietary software; there is no requirement for such software to - contain a copyright notice related to this source. - Files: qpb/qpb.proto fpm/fpm.proto License: ISC Copyright: Copyright (C) 2016 Sproute Networks, Inc. diff --git a/doc/developer/building-frr-for-centos6.rst b/doc/developer/building-frr-for-centos6.rst index f1ec2ad3ea0d..ee0ffc2bf7d8 100644 --- a/doc/developer/building-frr-for-centos6.rst +++ b/doc/developer/building-frr-for-centos6.rst @@ -163,10 +163,9 @@ an example.) --disable-ldpd \ --enable-fpm \ --with-pkg-git-version \ - --with-pkg-extra-version=-MyOwnFRRVersion \ - SPHINXBUILD=sphinx-build2.7 + --with-pkg-extra-version=-MyOwnFRRVersion make - make check PYTHON=/usr/bin/python2.7 + make check sudo make install Create empty FRR configuration files diff --git a/doc/developer/building-frr-for-centos7.rst b/doc/developer/building-frr-for-centos7.rst index ea3c44478c02..67f71bc3a5b8 100644 --- a/doc/developer/building-frr-for-centos7.rst +++ b/doc/developer/building-frr-for-centos7.rst @@ -70,7 +70,8 @@ an example.) --disable-ldpd \ --enable-fpm \ --with-pkg-git-version \ - --with-pkg-extra-version=-MyOwnFRRVersion + --with-pkg-extra-version=-MyOwnFRRVersion \ + SPHINXBUILD=/usr/bin/sphinx-build make make check sudo make install diff --git a/doc/developer/building-frr-for-debian8.rst b/doc/developer/building-frr-for-debian8.rst index 9c6e48a6f83c..76f927853d60 100644 --- a/doc/developer/building-frr-for-debian8.rst +++ b/doc/developer/building-frr-for-debian8.rst @@ -16,14 +16,15 @@ Add packages: :: sudo apt-get install git autoconf automake libtool make \ - libreadline-dev texinfo libjson-c-dev pkg-config bison flex python-pip \ - libc-ares-dev python3-dev python3-sphinx build-essential libsystemd-dev + libreadline-dev texinfo libjson-c-dev pkg-config bison flex python3-pip \ + libc-ares-dev python3-dev python3-sphinx build-essential libsystemd-dev \ + libsnmp-dev Install newer pytest (>3.0) from pip :: - sudo pip install pytest + sudo pip3 install pytest .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-debian9.rst b/doc/developer/building-frr-for-debian9.rst index 39e7488cd710..e58c59f451ac 100644 --- a/doc/developer/building-frr-for-debian9.rst +++ b/doc/developer/building-frr-for-debian9.rst @@ -9,9 +9,9 @@ Add packages: :: sudo apt-get install git autoconf automake libtool make \ - libreadline-dev texinfo libjson-c-dev pkg-config bison flex python-pip \ - libc-ares-dev python3-dev python-pytest python3-sphinx build-essential \ - libsystemd-dev + libreadline-dev texinfo libjson-c-dev pkg-config bison flex \ + libc-ares-dev python3-dev python3-pytest python3-sphinx build-essential \ + libsnmp-dev libsystemd-dev .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-fedora.rst b/doc/developer/building-frr-for-fedora.rst index 204c185f5659..d11da2d647cb 100644 --- a/doc/developer/building-frr-for-fedora.rst +++ b/doc/developer/building-frr-for-fedora.rst @@ -13,8 +13,8 @@ Installing Dependencies sudo dnf install git autoconf automake libtool make \ readline-devel texinfo net-snmp-devel groff pkgconfig json-c-devel \ - pam-devel pytest bison flex c-ares-devel python3-devel python2-sphinx \ - perl-core patch + pam-devel python3-pytest bison flex c-ares-devel python3-devel \ + python3-sphinx perl-core patch .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-freebsd10.rst b/doc/developer/building-frr-for-freebsd10.rst index 86c44f4d90cf..e85cb8005351 100644 --- a/doc/developer/building-frr-for-freebsd10.rst +++ b/doc/developer/building-frr-for-freebsd10.rst @@ -17,7 +17,7 @@ is first package install and asked) :: pkg install git autoconf automake libtool gmake json-c pkgconf \ - bison flex py27-pytest c-ares python3 py-sphinx + bison flex py36-pytest c-ares python3.6 py36-sphinx Make sure there is no /usr/bin/flex preinstalled (and use the newly installed in /usr/local/bin): (FreeBSD frequently provides a older flex diff --git a/doc/developer/building-frr-for-freebsd11.rst b/doc/developer/building-frr-for-freebsd11.rst index 5e56c8cd7a80..b97538b763c1 100644 --- a/doc/developer/building-frr-for-freebsd11.rst +++ b/doc/developer/building-frr-for-freebsd11.rst @@ -17,7 +17,7 @@ is first package install and asked) .. code-block:: shell pkg install git autoconf automake libtool gmake json-c pkgconf \ - bison flex py27-pytest c-ares python3 py36-sphinx texinfo + bison flex py36-pytest c-ares python3.6 py36-sphinx texinfo Make sure there is no /usr/bin/flex preinstalled (and use the newly installed in /usr/local/bin): (FreeBSD frequently provides a older flex diff --git a/doc/developer/building-frr-for-freebsd9.rst b/doc/developer/building-frr-for-freebsd9.rst index 59241d1d1376..1e9774979525 100644 --- a/doc/developer/building-frr-for-freebsd9.rst +++ b/doc/developer/building-frr-for-freebsd9.rst @@ -17,8 +17,8 @@ is first package install and asked) :: pkg install -y git autoconf automake libtool gmake \ - pkgconf texinfo json-c bison flex py27-pytest c-ares \ - python3 py-sphinx libexecinfo + pkgconf texinfo json-c bison flex py36-pytest c-ares \ + python3 py36-sphinx libexecinfo Make sure there is no /usr/bin/flex preinstalled (and use the newly installed in /usr/local/bin): (FreeBSD frequently provides a older flex diff --git a/doc/developer/building-frr-for-netbsd6.rst b/doc/developer/building-frr-for-netbsd6.rst index 49091c49b400..e50d11130a17 100644 --- a/doc/developer/building-frr-for-netbsd6.rst +++ b/doc/developer/building-frr-for-netbsd6.rst @@ -23,7 +23,7 @@ Add packages: :: sudo pkg_add git autoconf automake libtool gmake openssl \ - pkg-config json-c python27 py27-test python35 py-sphinx + pkg-config json-c py36-test python36 py36-sphinx Install SSL Root Certificates (for git https access): @@ -33,13 +33,6 @@ Install SSL Root Certificates (for git https access): sudo touch /etc/openssl/openssl.cnf sudo mozilla-rootcerts install -Select default Python and py.test - -:: - - sudo ln -s /usr/pkg/bin/python2.7 /usr/bin/python - sudo ln -s /usr/pkg/bin/py.test-2.7 /usr/bin/py.test - .. include:: building-libyang.rst Get FRR, compile it and install it (from Git) diff --git a/doc/developer/building-frr-for-netbsd7.rst b/doc/developer/building-frr-for-netbsd7.rst index 64c462a5c8b7..32d1145edce7 100644 --- a/doc/developer/building-frr-for-netbsd7.rst +++ b/doc/developer/building-frr-for-netbsd7.rst @@ -14,7 +14,7 @@ Install required packages :: sudo pkgin install git autoconf automake libtool gmake openssl \ - pkg-config json-c python27 py27-test python35 py-sphinx + pkg-config json-c python36 py36-test py36-sphinx Install SSL Root Certificates (for git https access): @@ -24,13 +24,6 @@ Install SSL Root Certificates (for git https access): sudo touch /etc/openssl/openssl.cnf sudo mozilla-rootcerts install -Select default Python and py.test - -:: - - sudo ln -s /usr/pkg/bin/python2.7 /usr/bin/python - sudo ln -s /usr/pkg/bin/py.test-2.7 /usr/bin/py.test - .. include:: building-libyang.rst Get FRR, compile it and install it (from Git) diff --git a/doc/developer/building-frr-for-ubuntu1404.rst b/doc/developer/building-frr-for-ubuntu1404.rst index 6e2765c1c86c..569b3bded1c5 100644 --- a/doc/developer/building-frr-for-ubuntu1404.rst +++ b/doc/developer/building-frr-for-ubuntu1404.rst @@ -12,7 +12,7 @@ Installing Dependencies apt-get update apt-get install \ git autoconf automake libtool make libreadline-dev texinfo \ - pkg-config libpam0g-dev libjson-c-dev bison flex python-pytest \ + pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \ libc-ares-dev python3-dev python3-sphinx install-info build-essential \ libsnmp-dev perl diff --git a/doc/developer/building-frr-for-ubuntu1604.rst b/doc/developer/building-frr-for-ubuntu1604.rst index a9a0a2f733aa..03852a62aa34 100644 --- a/doc/developer/building-frr-for-ubuntu1604.rst +++ b/doc/developer/building-frr-for-ubuntu1604.rst @@ -12,7 +12,7 @@ Installing Dependencies apt-get update apt-get install \ git autoconf automake libtool make libreadline-dev texinfo \ - pkg-config libpam0g-dev libjson-c-dev bison flex python-pytest \ + pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \ libc-ares-dev python3-dev libsystemd-dev python-ipaddress python3-sphinx \ install-info build-essential libsystemd-dev libsnmp-dev perl diff --git a/doc/developer/building-frr-for-ubuntu1804.rst b/doc/developer/building-frr-for-ubuntu1804.rst index 1320bda5777b..96c0efe02a77 100644 --- a/doc/developer/building-frr-for-ubuntu1804.rst +++ b/doc/developer/building-frr-for-ubuntu1804.rst @@ -12,7 +12,7 @@ Installing Dependencies sudo apt update sudo apt-get install \ git autoconf automake libtool make libreadline-dev texinfo \ - pkg-config libpam0g-dev libjson-c-dev bison flex python-pytest \ + pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \ libc-ares-dev python3-dev libsystemd-dev python-ipaddress python3-sphinx \ install-info build-essential libsystemd-dev libsnmp-dev perl @@ -104,6 +104,10 @@ And load the kernel modules on the running system: sudo modprobe mpls-router mpls-iptunnel +If the above command returns an error, you may need to install the appropriate +or latest linux-modules-extra--generic package. For example +``apt-get install linux-modules-extra-`uname -r`-generic`` + Enable MPLS Forwarding """""""""""""""""""""" diff --git a/doc/developer/library.rst b/doc/developer/library.rst index 77b2f229b785..4ba0c0ebc6cd 100644 --- a/doc/developer/library.rst +++ b/doc/developer/library.rst @@ -7,8 +7,9 @@ Library Facilities (libfrr) .. toctree:: :maxdepth: 2 - logging memtypes + lists + logging hooks cli modules diff --git a/doc/developer/lists.rst b/doc/developer/lists.rst new file mode 100644 index 000000000000..fc47a67e426f --- /dev/null +++ b/doc/developer/lists.rst @@ -0,0 +1,626 @@ +List implementations +==================== + +.. note:: + + The term *list* is used generically for lists, skiplists, trees and hash + tables in this document. + +Common list interface +--------------------- + +FRR includes a set of list-like data structure implementations with abstracted +common APIs. The purpose of this is easily allow swapping out one +data structure for another while also making the code easier to read and write. +There is one API for unsorted lists and a similar but not identical API for +sorted lists - and heaps use a middle ground of both. + +For unsorted lists, the following implementations exist: + +- single-linked list with tail pointer (e.g. STAILQ in BSD) + +- double-linked list + +- atomic single-linked list with tail pointer + + +Being partially sorted, the oddball structure: + +- an 8-ary heap + + +For sorted lists, these data structures are implemented: + +- single-linked list + +- atomic single-linked list + +- skiplist + +- red-black tree (based on OpenBSD RB_TREE) + +- hash table (note below) + +Except for hash tables, each of the sorted data structures has a variant with +unique and non-unique list items. Hash tables always require unique items +and mostly follow the "sorted" API but use the hash value as sorting +key. Also, iterating while modifying does not work with hash tables. +Conversely, the heap always has non-unique items, but iterating while modifying +doesn't work either. + + +The following sorted structures are likely to be implemented at some point +in the future: + +- atomic skiplist + +- atomic hash table (note below) + + +The APIs are all designed to be as type-safe as possible. This means that +there will be a compiler warning when an item doesn't match the list, or +the return value has a different type, or other similar situations. **You +should never use casts with these APIs.** If a cast is neccessary in relation +to these APIs, there is probably something wrong with the overall design. + +Only the following pieces use dynamically allocated memory: + +- the hash table itself is dynamically grown and shrunk + +- skiplists store up to 4 next pointers inline but will dynamically allocate + memory to hold an item's 5th up to 16th next pointer (if they exist) + +- the heap uses a dynamically grown and shrunk array of items + +Cheat sheet +----------- + +Available types: + +:: + + DECLARE_LIST + DECLARE_ATOMLIST + DECLARE_DLIST + + DECLARE_HEAP + + DECLARE_SORTLIST_UNIQ + DECLARE_SORTLIST_NONUNIQ + DECLARE_ATOMLIST_UNIQ + DECLARE_ATOMLIST_NONUNIQ + DECLARE_SKIPLIST_UNIQ + DECLARE_SKIPLIST_NONUNIQ + DECLARE_RBTREE_UNIQ + DECLARE_RBTREE_NONUNIQ + + DECLARE_HASH + +Functions provided: + ++------------------------------------+------+------+------+---------+------------+ +| Function | LIST | HEAP | HASH | \*_UNIQ | \*_NONUNIQ | ++====================================+======+======+======+=========+============+ +| _init, _fini | yes | yes | yes | yes | yes | ++------------------------------------+------+------+------+---------+------------+ +| _first, _next, _next_safe | yes | yes | yes | yes | yes | ++------------------------------------+------+------+------+---------+------------+ +| _add_head, _add_tail, _add_after | yes | -- | -- | -- | -- | ++------------------------------------+------+------+------+---------+------------+ +| _add | -- | yes | yes | yes | yes | ++------------------------------------+------+------+------+---------+------------+ +| _del, _pop | yes | yes | yes | yes | yes | ++------------------------------------+------+------+------+---------+------------+ +| _find | -- | -- | yes | yes | -- | ++------------------------------------+------+------+------+---------+------------+ +| _find_lt, _find_gteq | -- | -- | -- | yes | yes | ++------------------------------------+------+------+------+---------+------------+ +| use with frr_each() macros | yes | yes | yes | yes | yes | ++------------------------------------+------+------+------+---------+------------+ + + + +Datastructure type setup +------------------------ + +Each of the data structures has a ``PREDECL_*`` and a ``DECLARE_*`` macro to +set up an "instantiation" of the list. This works somewhat similar to C++ +templating, though much simpler. + +**In all following text, the Z prefix is replaced with a name choosen +for the instance of the datastructure.** + +The common setup pattern will look like this: + +.. code-block:: c + + #include + + PREDECL_XXX(Z) + struct item { + int otherdata; + struct Z_item mylistitem; + } + + struct Z_head mylisthead; + + /* unsorted: */ + DECLARE_XXX(Z, struct item, mylistitem) + + /* sorted, items that compare as equal cannot be added to list */ + int compare_func(const struct item *a, const struct item *b); + DECLARE_XXX_UNIQ(Z, struct item, mylistitem, compare_func) + + /* sorted, items that compare as equal can be added to list */ + int compare_func(const struct item *a, const struct item *b); + DECLARE_XXX_NONUNIQ(Z, struct item, mylistitem, compare_func) + + /* hash tables: */ + int compare_func(const struct item *a, const struct item *b); + uint32_t hash_func(const struct item *a); + DECLARE_XXX(Z, struct item, mylistitem, compare_func, hash_func) + +``XXX`` is replaced with the name of the data structure, e.g. ``SKIPLIST`` +or ``ATOMLIST``. The ``DECLARE_XXX`` invocation can either occur in a `.h` +file (if the list needs to be accessed from several C files) or it can be +placed in a `.c` file (if the list is only accessed from that file.) The +``PREDECL_XXX`` invocation defines the ``struct Z_item`` and ``struct +Z_head`` types and must therefore occur before these are used. + +To switch between compatible data structures, only these two lines need to be +changes. To switch to a data structure with a different API, some source +changes are necessary. + +Common iteration macros +----------------------- + +The following iteration macros work across all data structures: + +.. c:function:: frr_each(Z, &head, item) + + Equivalent to: + + .. code-block:: c + + for (item = Z_first(&head); item; item = Z_next(&head, item)) + + Note that this will fail if the list is modified while being iterated + over. + +.. c:function:: frr_each_safe(Z, &head, item) + + Same as the previous, but the next element is pre-loaded into a "hidden" + variable (named ``Z_safe``.) Equivalent to: + + .. code-block:: c + + for (item = Z_first(&head); item; item = next) { + next = Z_next_safe(&head, item); + ... + } + + .. warning:: + + Iterating over hash tables while adding or removing items is not + possible. The iteration position will be corrupted when the hash + tables is resized while iterating. This will cause items to be + skipped or iterated over twice. + +.. c:function:: frr_each_from(Z, &head, item, from) + + Iterates over the list, starting at item ``from``. This variant is "safe" + as in the previous macro. Equivalent to: + + .. code-block:: c + + for (item = from; item; item = from) { + from = Z_next_safe(&head, item); + ... + } + + .. note:: + + The ``from`` variable is written to. This is intentional - you can + resume iteration after breaking out of the loop by keeping the ``from`` + value persistent and reusing it for the next loop. + +Common API +---------- + +The following documentation assumes that a list has been defined using +``Z`` as the name, and ``itemtype`` being the type of the list items (e.g. +``struct item``.) + +.. c:function:: void Z_init(struct Z_head *) + + Initializes the list for use. For most implementations, this just sets + some values. Hash tables are the only implementation that allocates + memory in this call. + +.. c:function:: void Z_fini(struct Z_head *) + + Reverse the effects of :c:func:`Z_init()`. The list must be empty + when this function is called. + + .. warning:: + + This function may ``assert()`` if the list is not empty. + +.. c:function:: size_t Z_count(struct Z_head *) + + Returns the number of items in a structure. All structures store a + counter in their `Z_head` so that calling this function completes + in O(1). + + .. note:: + + For atomic lists with concurrent access, the value will already be + outdated by the time this function returns and can therefore only be + used as an estimate. + +.. c:function:: itemtype *Z_first(struct Z_head *) + + Returns the first item in the structure, or ``NULL`` if the structure is + empty. This is O(1) for all data structures except red-black trees + where it is O(log n). + +.. c:function:: itemtype *Z_pop(struct Z_head *) + + Remove and return the first item in the structure, or ``NULL`` if the + structure is empty. Like :c:func:`Z_first`, this is O(1) for all + data structures except red-black trees where it is O(log n) again. + + This function can be used to build queues (with unsorted structures) or + priority queues (with sorted structures.) + + Another common pattern is deleting all list items: + + .. code-block:: c + + while ((item = Z_pop(head))) + item_free(item); + + .. note:: + + This function can - and should - be used with hash tables. It is not + affected by the "modification while iterating" problem. To remove + all items from a hash table, use the loop demonstrated above. + +.. c:function:: itemtype *Z_next(struct Z_head *, itemtype *prev) + + Return the item that follows after ``prev``, or ``NULL`` if ``prev`` is + the last item. + + .. warning:: + + ``prev`` must not be ``NULL``! Use :c:func:`Z_next_safe()` if + ``prev`` might be ``NULL``. + +.. c:function:: itemtype *Z_next_safe(struct Z_head *, itemtype *prev) + + Same as :c:func:`Z_next()`, except that ``NULL`` is returned if + ``prev`` is ``NULL``. + +.. c:function:: itemtype *Z_del(struct Z_head *, itemtype *item) + + Remove ``item`` from the list and return it. + + .. note:: + + This function's behaviour is undefined if ``item`` is not actually + on the list. Some structures return ``NULL`` in this case while others + return ``item``. The function may also call ``assert()`` (but most + don't.) + +.. todo:: + + ``Z_del_after()`` / ``Z_del_hint()``? + +API for unsorted structures +--------------------------- + +Since the insertion position is not pre-defined for unsorted data, there +are several functions exposed to insert data: + +.. note:: + + ``item`` must not be ``NULL`` for any of the following functions. + +.. c:function:: DECLARE_XXX(Z, type, field) + + :param listtype XXX: ``LIST``, ``DLIST`` or ``ATOMLIST`` to select a data + structure implementation. + :param token Z: Gives the name prefix that is used for the functions + created for this instantiation. ``DECLARE_XXX(foo, ...)`` + gives ``struct foo_item``, ``foo_add_head()``, ``foo_count()``, etc. Note + that this must match the value given in ``PREDECL_XXX(foo)``. + :param typename type: Specifies the data type of the list items, e.g. + ``struct item``. Note that ``struct`` must be added here, it is not + automatically added. + :param token field: References a struct member of ``type`` that must be + typed as ``struct foo_item``. This struct member is used to + store "next" pointers or other data structure specific data. + +.. c:function:: void Z_add_head(struct Z_head *, itemtype *item) + + Insert an item at the beginning of the structure, before the first item. + This is an O(1) operation for non-atomic lists. + +.. c:function:: void Z_add_tail(struct Z_head *, itemtype *item) + + Insert an item at the end of the structure, after the last item. + This is also an O(1) operation for non-atomic lists. + +.. c:function:: void Z_add_after(struct Z_head *, itemtype *after, itemtype *item) + + Insert ``item`` behind ``after``. If ``after`` is ``NULL``, the item is + inserted at the beginning of the list as with :c:func:`Z_add_head`. + This is also an O(1) operation for non-atomic lists. + + A common pattern is to keep a "previous" pointer around while iterating: + + .. code-block:: c + + itemtype *prev = NULL, *item; + + frr_each_safe(Z, head, item) { + if (something) { + Z_add_after(head, prev, item); + break; + } + prev = item; + } + + .. todo:: + + maybe flip the order of ``item`` & ``after``? + ``Z_add_after(head, item, after)`` + +API for sorted structures +------------------------- + +Sorted data structures do not need to have an insertion position specified, +therefore the insertion calls are different from unsorted lists. Also, +sorted lists can be searched for a value. + +.. c:function:: DECLARE_XXX_UNIQ(Z, type, field, compare_func) + + :param listtype XXX: One of the following: + ``SORTLIST`` (single-linked sorted list), ``SKIPLIST`` (skiplist), + ``RBTREE`` (RB-tree) or ``ATOMSORT`` (atomic single-linked list). + :param token Z: Gives the name prefix that is used for the functions + created for this instantiation. ``DECLARE_XXX(foo, ...)`` + gives ``struct foo_item``, ``foo_add()``, ``foo_count()``, etc. Note + that this must match the value given in ``PREDECL_XXX(foo)``. + :param typename type: Specifies the data type of the list items, e.g. + ``struct item``. Note that ``struct`` must be added here, it is not + automatically added. + :param token field: References a struct member of ``type`` that must be + typed as ``struct foo_item``. This struct member is used to + store "next" pointers or other data structure specific data. + :param funcptr compare_func: Item comparison function, must have the + following function signature: + ``int function(const itemtype *, const itemtype*)``. This function + may be static if the list is only used in one file. + +.. c:function:: DECLARE_XXX_NONUNIQ(Z, type, field, compare_func) + + Same as above, but allow adding multiple items to the list that compare + as equal in ``compare_func``. Ordering between these items is undefined + and depends on the list implementation. + +.. c:function:: itemtype *Z_add(struct Z_head *, itemtype *item) + + Insert an item at the appropriate sorted position. If another item exists + in the list that compares as equal (``compare_func()`` == 0), ``item`` is + not inserted into the list and the already-existing item in the list is + returned. Otherwise, on successful insertion, ``NULL`` is returned. + + For ``_NONUNIQ`` lists, this function always returns NULL since ``item`` + can always be successfully added to the list. + +.. c:function:: itemtype *Z_find(struct Z_head *, const itemtype *ref) + + Search the list for an item that compares equal to ``ref``. If no equal + item is found, return ``NULL``. + + This function is likely used with a temporary stack-allocated value for + ``ref`` like so: + + .. code-block:: c + + itemtype searchfor = { .foo = 123 }; + + itemtype *item = Z_find(head, &searchfor); + + .. note:: + + The ``Z_find()`` function is only available for lists that contain + unique items (i.e. ``DECLARE_XXX_UNIQ``.) This is because on a list + containing non-unique items, more than one item may compare as equal to + the item that is searched for. + +.. c:function:: itemtype *Z_find_gteq(struct Z_head *, const itemtype *ref) + + Search the list for an item that compares greater or equal to + ``ref``. See :c:func:`Z_find()` above. + +.. c:function:: itemtype *Z_find_lt(struct Z_head *, const itemtype *ref) + + Search the list for an item that compares less than + ``ref``. See :c:func:`Z_find()` above. + + +API for hash tables +------------------- + +.. c:function:: DECLARE_XXX(Z, type, field, compare_func, hash_func) + + :param listtype XXX: Only ``HASH`` is currently available. + :param token Z: Gives the name prefix that is used for the functions + created for this instantiation. ``DECLARE_XXX(foo, ...)`` + gives ``struct foo_item``, ``foo_add()``, ``foo_count()``, etc. Note + that this must match the value given in ``PREDECL_XXX(foo)``. + :param typename type: Specifies the data type of the list items, e.g. + ``struct item``. Note that ``struct`` must be added here, it is not + automatically added. + :param token field: References a struct member of ``type`` that must be + typed as ``struct foo_item``. This struct member is used to + store "next" pointers or other data structure specific data. + :param funcptr compare_func: Item comparison function, must have the + following function signature: + ``int function(const itemtype *, const itemtype*)``. This function + may be static if the list is only used in one file. For hash tables, + this function is only used to check for equality, the ordering is + ignored. + :param funcptr hash_func: Hash calculation function, must have the + following function signature: + ``uint32_t function(const itemtype *)``. The hash value for items + stored in a hash table is cached in each item, so this value need not + be cached by the user code. + + .. warning:: + + Items that compare as equal cannot be inserted. Refer to the notes + about sorted structures in the previous section. + +.. c:function:: void Z_init_size(struct Z_head *, size_t size) + + Same as :c:func:`Z_init()` but preset the minimum hash table to + ``size``. + +Hash tables also support :c:func:`Z_add()` and :c:func:`Z_find()` with +the same semantics as noted above. :c:func:`Z_find_gteq()` and +:c:func:`Z_find_lt()` are **not** provided for hash tables. + + +API for heaps +------------- + +Heaps provide the same API as the sorted data structures, except: + +* none of the find functions (:c:func:`Z_find()`, :c:func:`Z_find_gteq()` + or :c:func:`Z_find_lt()`) are available. +* iterating over the heap yields the items in semi-random order, only the + first item is guaranteed to be in order and actually the "lowest" item + on the heap. Being a heap, only the rebalancing performed on removing the + first item (either through :c:func:`Z_pop()` or :c:func:`Z_del()`) causes + the new lowest item to bubble up to the front. +* all heap modifications are O(log n). However, cacheline efficiency and + latency is likely quite a bit better than with other data structures. + +Atomic lists +------------ + +`atomlist.h` provides an unsorted and a sorted atomic single-linked list. +Since atomic memory accesses can be considerably slower than plain memory +accessses (depending on the CPU type), these lists should only be used where +neccessary. + +The following guarantees are provided regarding concurrent access: + +- the operations are lock-free but not wait-free. + + Lock-free means that it is impossible for all threads to be blocked. Some + thread will always make progress, regardless of what other threads do. (This + even includes a random thread being stopped by a debugger in a random + location.) + + Wait-free implies that the time any single thread might spend in one of the + calls is bounded. This is not provided here since it is not normally + relevant to practical operations. What this means is that if some thread is + hammering a particular list with requests, it is possible that another + thread is blocked for an extended time. The lock-free guarantee still + applies since the hammering thread is making progress. + +- without a RCU mechanism in place, the point of contention for atomic lists + is memory deallocation. As it is, **a rwlock is required for correct + operation**. The *read* lock must be held for all accesses, including + reading the list, adding items to the list, and removing items from the + list. The *write* lock must be acquired and released before deallocating + any list element. If this is not followed, an use-after-free can occur + as a MT race condition when an element gets deallocated while another + thread is accessing the list. + + .. note:: + + The *write* lock does not need to be held for deleting items from the + list, and there should not be any instructions between the + ``pthread_rwlock_wrlock`` and ``pthread_rwlock_unlock``. The write lock + is used as a sequence point, not as an exclusion mechanism. + +- insertion operations are always safe to do with the read lock held. + Added items are immediately visible after the insertion call returns and + should not be touched anymore. + +- when removing a *particular* (pre-determined) item, the caller must ensure + that no other thread is attempting to remove that same item. If this cannot + be guaranteed by architecture, a separate lock might need to be added. + +- concurrent `pop` calls are always safe to do with only the read lock held. + This does not fall under the previous rule since the `pop` call will select + the next item if the first is already being removed by another thread. + + **Deallocation locking still applies.** Assume another thread starts + reading the list, but gets task-switched by the kernel while reading the + first item. `pop` will happily remove and return that item. If it is + deallocated without acquiring and releasing the write lock, the other thread + will later resume execution and try to access the now-deleted element. + +- the list count should be considered an estimate. Since there might be + concurrent insertions or removals in progress, it might already be outdated + by the time the call returns. No attempt is made to have it be correct even + for a nanosecond. + +Overall, atomic lists are well-suited for MT queues; concurrent insertion, +iteration and removal operations will work with the read lock held. + +Code snippets +^^^^^^^^^^^^^ + +Iteration: + +.. code-block:: c + + struct item *i; + + pthread_rwlock_rdlock(&itemhead_rwlock); + frr_each(itemlist, &itemhead, i) { + /* lock must remain held while iterating */ + ... + } + pthread_rwlock_unlock(&itemhead_rwlock); + +Head removal (pop) and deallocation: + +.. code-block:: c + + struct item *i; + + pthread_rwlock_rdlock(&itemhead_rwlock); + i = itemlist_pop(&itemhead); + pthread_rwlock_unlock(&itemhead_rwlock); + + /* i might still be visible for another thread doing an + * frr_each() (but won't be returned by another pop()) */ + ... + + pthread_rwlock_wrlock(&itemhead_rwlock); + pthread_rwlock_unlock(&itemhead_rwlock); + /* i now guaranteed to be gone from the list. + * note nothing between wrlock() and unlock() */ + XFREE(MTYPE_ITEM, i); + +FRR lists +--------- + +.. TODO:: + + document + +BSD lists +--------- + +.. TODO:: + + refer to external docs diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst index 1dc188515827..e393fe6fba30 100644 --- a/doc/developer/logging.rst +++ b/doc/developer/logging.rst @@ -6,6 +6,114 @@ to log, what level to log it at, and when to log it. Here is a list of recommendations for these decisions. +printfrr() +---------- + +``printfrr()`` is FRR's modified version of ``printf()``, designed to make +life easier when printing nontrivial datastructures. The following variants +are available: + +.. c:function:: ssize_t snprintfrr(char *buf, size_t len, const char *fmt, ...) +.. c:function:: ssize_t vsnprintfrr(char *buf, size_t len, const char *fmt, va_list) + + These correspond to ``snprintf``/``vsnprintf``. If you pass NULL for buf + or 0 for len, no output is written but the return value is still calculated. + + The return value is always the full length of the output, unconstrained by + `len`. It does **not** include the terminating ``\0`` character. A + malformed format string can result in a ``-1`` return value. + +.. c:function:: ssize_t csnprintfrr(char *buf, size_t len, const char *fmt, ...) +.. c:function:: ssize_t vcsnprintfrr(char *buf, size_t len, const char *fmt, va_list) + + Same as above, but the ``c`` stands for "continue" or "concatenate". The + output is appended to the string instead of overwriting it. + +.. c:function:: char *asprintfrr(struct memtype *mt, const char *fmt, ...) +.. c:function:: char *vasprintfrr(struct memtype *mt, const char *fmt, va_list) + + These functions allocate a dynamic buffer (using MTYPE `mt`) and print to + that. If the format string is malformed, they return a copy of the format + string, so the return value is always non-NULL and always dynamically + allocated with `mt`. + +.. c:function:: char *asnprintfrr(struct memtype *mt, char *buf, size_t len, const char *fmt, ...) +.. c:function:: char *vasnprintfrr(struct memtype *mt, char *buf, size_t len, const char *fmt, va_list) + + This variant tries to use the static buffer provided, but falls back to + dynamic allocation if it is insufficient. + + The return value can be either `buf` or a newly allocated string using + `mt`. You MUST free it like this:: + + char *ret = asnprintfrr(MTYPE_FOO, buf, sizeof(buf), ...); + if (ret != buf) + XFREE(MTYPE_FOO, ret); + +Extensions +^^^^^^^^^^ + +``printfrr()`` format strings can be extended with suffixes after `%p` or +`%d`. The following extended format specifiers are available: + ++-----------+--------------------------+----------------------------------------------+ +| Specifier | Argument | Output | ++===========+==========================+==============================================+ +| ``%Lu`` | ``uint64_t`` | ``12345`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%Ld`` | ``int64_t`` | ``-12345`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pI4`` | ``struct in_addr *`` | ``1.2.3.4`` | +| | | | +| | ``in_addr_t *`` | | ++-----------+--------------------------+----------------------------------------------+ +| ``%pI6`` | ``struct in6_addr *`` | ``fe80::1234`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pFX`` | ``struct prefix *`` | ``fe80::1234/64`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pSG4`` | ``struct prefix_sg *`` | ``(*,1.2.3.4)`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pRN`` | ``struct route_node *`` | ``192.168.1.0/24`` (dst-only node) | +| | | | +| | | ``2001:db8::/32 from fe80::/64`` (SADR node) | ++-----------+--------------------------+----------------------------------------------+ +| ``%pNHv`` | ``struct nexthop *`` | ``1.2.3.4, via eth0`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pNHs`` | ``struct nexthop *`` | ``1.2.3.4 if 15`` | ++-----------+--------------------------+----------------------------------------------+ + +Printf features like field lengths can be used normally with these extensions, +e.g. ``%-15pI4`` works correctly. + +The extension specifier after ``%p`` or ``%d`` is always an uppercase letter; +by means of established pattern uppercase letters and numbers form the type +identifier which may be followed by lowercase flags. + +You can grep the FRR source for ``printfrr_ext_autoreg`` to see all extended +printers and what exactly they do. More printers are likely to be added as +needed/useful, so the list above may become outdated. + +``%Ld`` is not an "extension" for printfrr; it's wired directly into the main +printf logic. + +.. note:: + + The ``zlog_*``/``flog_*`` and ``vty_out`` functions all use printfrr + internally, so these extensions are available there. However, they are + **not** available when calling ``snprintf`` directly. You need to call + ``snprintfrr`` instead. + +AS-Safety +^^^^^^^^^ + +``printfrr()`` are AS-Safe under the following conditions: + +* the ``[v]as[n]printfrr`` variants are not AS-Safe (allocating memory) +* floating point specifiers are not AS-Safe (system printf is used for these) +* the positional ``%1$d`` syntax should not be used (8 arguments are supported + while AS-Safe) +* extensions are only AS-Safe if their printer is AS-Safe + Errors and warnings ------------------- diff --git a/doc/developer/ospf-api.rst b/doc/developer/ospf-api.rst index f4f38a63e904..41c31b29c3dd 100644 --- a/doc/developer/ospf-api.rst +++ b/doc/developer/ospf-api.rst @@ -4,76 +4,76 @@ OSPF API Documentation Disclaimer ---------- -The OSPF daemon contains an API for application access to the LSA -database. This API was created by Ralph Keller, originally as patch for -Zebra. Unfortunately, the page containing documentation of the API is no -longer online. This page is an attempt to recreate documentation for the -API (with lots of help of the WayBackMachine) +The OSPF daemon contains an API for application access to the LSA database. +This API and documentation was created by Ralph Keller, originally as patch for +Zebra. Unfortunately, the page containing documentation for the API is no +longer online. This page is an attempt to recreate documentation for the API +(with lots of help from the WayBackMachine). + +Ralph has kindly licensed this documentation under GPLv2+. Please preserve the +acknowledgements at the bottom of this document. Introduction ------------ -This page describes an API that allows external applications to access -the link-state database (LSDB) of the OSPF daemon. The implementation is -based on the OSPF code from FRRouting (forked from Quagga and formerly -Zebra) routing protocol suite and is subject to the GNU General Public -License. The OSPF API provides you with the following functionality: - -- Retrieval of the full or partial link-state database of the OSPF - daemon. This allows applications to obtain an exact copy of the LSDB - including router LSAs, network LSAs and so on. Whenever a new LSA - arrives at the OSPF daemon, the API module immediately informs the - application by sending a message. This way, the application is always - synchronized with the LSDB of the OSPF daemon. -- Origination of own opaque LSAs (of type 9, 10, or 11) which are then - distributed transparently to other routers within the flooding scope - and received by other applications through the OSPF API. - -Opaque LSAs, which are described in RFC 2370 , allow you to distribute -application-specific information within a network using the OSPF -protocol. The information contained in opaque LSAs is transparent for -the routing process but it can be processed by other modules such as -traffic engineering (e.g., MPLS-TE). +This page describes an API that allows external applications to access the +link-state database (LSDB) of the OSPF daemon. The implementation is based on +the OSPF code from FRRouting (forked from Quagga and formerly Zebra) routing +protocol suite and is subject to the GNU General Public License. The OSPF API +provides you with the following functionality: + +- Retrieval of the full or partial link-state database of the OSPF daemon. + This allows applications to obtain an exact copy of the LSDB including router + LSAs, network LSAs and so on. Whenever a new LSA arrives at the OSPF daemon, + the API module immediately informs the application by sending a message. This + way, the application is always synchronized with the LSDB of the OSPF daemon. +- Origination of own opaque LSAs (of type 9, 10, or 11) which are then + distributed transparently to other routers within the flooding scope and + received by other applications through the OSPF API. + +Opaque LSAs, which are described in :rfc:`2370`, allow you to distribute +application-specific information within a network using the OSPF protocol. The +information contained in opaque LSAs is transparent for the routing process but +it can be processed by other modules such as traffic engineering (e.g., +MPLS-TE). Architecture ------------ -The following picture depicts the architecture of the Quagga/Zebra -protocol suite. The OSPF daemon is extended with opaque LSA capabilities -and an API for external applications. The OSPF core module executes the -OSPF protocol by discovering neighbors and exchanging neighbor state. -The opaque module, implemented by Masahiko Endo, provides functions to -exchange opaque LSAs between routers. Opaque LSAs can be generated by -several modules such as the MPLS-TE module or the API server module. -These modules then invoke the opaque module to flood their data to -neighbors within the flooding scope. - -The client, which is an application potentially running on a different -node than the OSPF daemon, links against the OSPF API client library. -This client library establishes a socket connection with the API server -module of the OSPF daemon and uses this connection to retrieve LSAs and -originate opaque LSAs. +The following picture depicts the architecture of the Quagga/Zebra protocol +suite. The OSPF daemon is extended with opaque LSA capabilities and an API for +external applications. The OSPF core module executes the OSPF protocol by +discovering neighbors and exchanging neighbor state. The opaque module, +implemented by Masahiko Endo, provides functions to exchange opaque LSAs +between routers. Opaque LSAs can be generated by several modules such as the +MPLS-TE module or the API server module. These modules then invoke the opaque +module to flood their data to neighbors within the flooding scope. + +The client, which is an application potentially running on a different node +than the OSPF daemon, links against the OSPF API client library. This client +library establishes a socket connection with the API server module of the OSPF +daemon and uses this connection to retrieve LSAs and originate opaque LSAs. .. figure:: ../figures/ospf_api_architecture.png :alt: image image -The OSPF API server module works like any other internal opaque module -(such as the MPLS-TE module), but listens to connections from external -applications that want to communicate with the OSPF daemon. The API -server module can handle multiple clients concurrently. +The OSPF API server module works like any other internal opaque module (such as +the MPLS-TE module), but listens to connections from external applications that +want to communicate with the OSPF daemon. The API server module can handle +multiple clients concurrently. -One of the main objectives of the implementation is to make as little -changes to the existing Zebra code as possible. +One of the main objectives of the implementation is to make as little changes +to the existing Zebra code as possible. Installation & Configuration ---------------------------- -Download FRRouting and unpack +Download FRRouting and unpack it. -Configure your frr version (note that --enable-opaque-lsa also enables -the ospfapi server and ospfclient). +Configure and build FRR (note that ``--enable-opaque-lsa`` also enables the +ospfapi server and ospfclient). :: @@ -83,8 +83,8 @@ the ospfapi server and ospfclient). This should also compile the client library and sample application in ospfclient. -Make sure that you have enabled opaque LSAs in your configuration. Add -the ospf opaque-lsa statement to your ospfd.conf: +Make sure that you have enabled opaque LSAs in your configuration. Add the +``ospf opaque-lsa`` statement to your :file:`ospfd.conf`: :: @@ -108,171 +108,165 @@ Usage ----- In the following we describe how you can use the sample application to -originate opaque LSAs. The sample application first registers with the -OSPF daemon the opaque type it wants to inject and then waits until the -OSPF daemon is ready to accept opaque LSAs of that type. Then the client -application originates an opaque LSA, waits 10 seconds and then updates -the opaque LSA with new opaque data. After another 20 seconds, the -client application deletes the opaque LSA from the LSDB. If the clients -terminates unexpectedly, the OSPF API module will remove all the opaque -LSAs that the application registered. Since the opaque LSAs are flooded -to other routers, we will see the opaque LSAs in all routers according -to the flooding scope of the opaque LSA. +originate opaque LSAs. The sample application first registers with the OSPF +daemon the opaque type it wants to inject and then waits until the OSPF daemon +is ready to accept opaque LSAs of that type. Then the client application +originates an opaque LSA, waits 10 seconds and then updates the opaque LSA with +new opaque data. After another 20 seconds, the client application deletes the +opaque LSA from the LSDB. If the clients terminates unexpectedly, the OSPF API +module will remove all the opaque LSAs that the application registered. Since +the opaque LSAs are flooded to other routers, we will see the opaque LSAs in +all routers according to the flooding scope of the opaque LSA. We have a very simple demo setup, just two routers connected with an ATM -point-to-point link. Start the modified OSPF daemons on two adjacent -routers. First run on msr2: +point-to-point link. Start the modified OSPF daemons on two adjacent routers. +First run on msr2: -:: +.. code-block:: console - > msr2:/home/keller/ospfapi/zebra/ospfd# ./ospfd -f /usr/local/etc/ospfd.conf + # ./ospfd --apiserver -f /usr/local/etc/ospfd.conf And on the neighboring router msr3: -:: +.. code-block:: console - > msr3:/home/keller/ospfapi/zebra/ospfd# ./ospfd -f /usr/local/etc/ospfd.conf + # ./ospfd --apiserver -f /usr/local/etc/ospfd.conf Now the two routers form adjacency and start exchanging their databases. Looking at the OSPF daemon of msr2 (or msr3), you see this: -:: +.. code-block:: console - ospfd> show ip ospf database + ospfd> show ip ospf database - OSPF Router with ID (10.0.0.1) + OSPF Router with ID (10.0.0.1) - Router Link States (Area 0.0.0.1) + Router Link States (Area 0.0.0.1) - Link ID ADV Router Age Seq# CkSum Link count - 10.0.0.1 10.0.0.1 55 0x80000003 0xc62f 2 - 10.0.0.2 10.0.0.2 55 0x80000003 0xe3e4 3 + Link ID ADV Router Age Seq# CkSum Link count + 10.0.0.1 10.0.0.1 55 0x80000003 0xc62f 2 + 10.0.0.2 10.0.0.2 55 0x80000003 0xe3e4 3 - Net Link States (Area 0.0.0.1) + Net Link States (Area 0.0.0.1) - Link ID ADV Router Age Seq# CkSum - 10.0.0.2 10.0.0.2 60 0x80000001 0x5fcb + Link ID ADV Router Age Seq# CkSum + 10.0.0.2 10.0.0.2 60 0x80000001 0x5fcb Now we start the sample main application that originates an opaque LSA. -:: +.. code-block:: console - > cd ospfapi/apiclient - > ./main msr2 10 250 20 0.0.0.0 0.0.0.1 + # cd ospfapi/apiclient + # ./main msr2 10 250 20 0.0.0.0 0.0.0.1 -This originates an opaque LSA of type 10 (area local), with opaque type -250 (experimental), opaque id of 20 (chosen arbitrarily), interface -address 0.0.0.0 (which is used only for opaque LSAs type 9), and area -0.0.0.1 +This originates an opaque LSA of type 10 (area local), with opaque type 250 +(experimental), opaque id of 20 (chosen arbitrarily), interface address 0.0.0.0 +(which is used only for opaque LSAs type 9), and area 0.0.0.1 Again looking at the OSPF database you see: -:: +.. code-block:: console - ospfd> show ip ospf database + ospfd> show ip ospf database - OSPF Router with ID (10.0.0.1) + OSPF Router with ID (10.0.0.1) - Router Link States (Area 0.0.0.1) + Router Link States (Area 0.0.0.1) - Link ID ADV Router Age Seq# CkSum Link count - 10.0.0.1 10.0.0.1 437 0x80000003 0xc62f 2 - 10.0.0.2 10.0.0.2 437 0x80000003 0xe3e4 3 + Link ID ADV Router Age Seq# CkSum Link count + 10.0.0.1 10.0.0.1 437 0x80000003 0xc62f 2 + 10.0.0.2 10.0.0.2 437 0x80000003 0xe3e4 3 - Net Link States (Area 0.0.0.1) + Net Link States (Area 0.0.0.1) - Link ID ADV Router Age Seq# CkSum - 10.0.0.2 10.0.0.2 442 0x80000001 0x5fcb + Link ID ADV Router Age Seq# CkSum + 10.0.0.2 10.0.0.2 442 0x80000001 0x5fcb - Area-Local Opaque-LSA (Area 0.0.0.1) + Area-Local Opaque-LSA (Area 0.0.0.1) - Opaque-Type/Id ADV Router Age Seq# CkSum - 250.0.0.20 10.0.0.1 0 0x80000001 0x58a6 <=== opaque LSA + Opaque-Type/Id ADV Router Age Seq# CkSum + 250.0.0.20 10.0.0.1 0 0x80000001 0x58a6 <=== opaque LSA You can take a closer look at this opaque LSA: -:: +.. code-block:: console - ospfd> show ip ospf database opaque-area + ospfd> show ip ospf database opaque-area - OSPF Router with ID (10.0.0.1) + OSPF Router with ID (10.0.0.1) - Area-Local Opaque-LSA (Area 0.0.0.1) + Area-Local Opaque-LSA (Area 0.0.0.1) - LS age: 4 - Options: 66 - LS Type: Area-Local Opaque-LSA - Link State ID: 250.0.0.20 (Area-Local Opaque-Type/ID) - Advertising Router: 10.0.0.1 - LS Seq Number: 80000001 - Checksum: 0x58a6 - Length: 24 - Opaque-Type 250 (Private/Experimental) - Opaque-ID 0x14 - Opaque-Info: 4 octets of data - Added using OSPF API: 4 octets of opaque data - Opaque data: 1 0 0 0 <==== counter is 1 + LS age: 4 + Options: 66 + LS Type: Area-Local Opaque-LSA + Link State ID: 250.0.0.20 (Area-Local Opaque-Type/ID) + Advertising Router: 10.0.0.1 + LS Seq Number: 80000001 + Checksum: 0x58a6 + Length: 24 + Opaque-Type 250 (Private/Experimental) + Opaque-ID 0x14 + Opaque-Info: 4 octets of data + Added using OSPF API: 4 octets of opaque data + Opaque data: 1 0 0 0 <==== counter is 1 -Note that the main application updates the opaque LSA after 10 seconds, -then it looks as follows: +Note that the main application updates the opaque LSA after 10 seconds, then it +looks as follows: -:: +.. code-block:: console - ospfd> show ip ospf database opaque-area + ospfd> show ip ospf database opaque-area - OSPF Router with ID (10.0.0.1) + OSPF Router with ID (10.0.0.1) - Area-Local Opaque-LSA (Area 0.0.0.1) + Area-Local Opaque-LSA (Area 0.0.0.1) - LS age: 1 - Options: 66 - LS Type: Area-Local Opaque-LSA - Link State ID: 250.0.0.20 (Area-Local Opaque-Type/ID) - Advertising Router: 10.0.0.1 - LS Seq Number: 80000002 - Checksum: 0x59a3 - Length: 24 - Opaque-Type 250 (Private/Experimental) - Opaque-ID 0x14 - Opaque-Info: 4 octets of data - Added using OSPF API: 4 octets of opaque data - Opaque data: 2 0 0 0 <==== counter is now 2 + LS age: 1 + Options: 66 + LS Type: Area-Local Opaque-LSA + Link State ID: 250.0.0.20 (Area-Local Opaque-Type/ID) + Advertising Router: 10.0.0.1 + LS Seq Number: 80000002 + Checksum: 0x59a3 + Length: 24 + Opaque-Type 250 (Private/Experimental) + Opaque-ID 0x14 + Opaque-Info: 4 octets of data + Added using OSPF API: 4 octets of opaque data + Opaque data: 2 0 0 0 <==== counter is now 2 -Note that the payload of the opaque LSA has changed as you can see -above. +Note that the payload of the opaque LSA has changed as you can see above. -Then, again after another 20 seconds, the opaque LSA is flushed from the -LSDB. +Then, again after another 20 seconds, the opaque LSA is flushed from the LSDB. Important note: ^^^^^^^^^^^^^^^ In order to originate an opaque LSA, there must be at least one active -opaque-capable neighbor. Thus, you cannot originate opaque LSAs of no -neighbors are present. If you try to originate even so no neighbor is -ready, you will receive a not ready error message. The reason for this -restriction is that it might be possible that some routers have an -identical opaque LSA from a previous origination in their LSDB that -unfortunately could not be flushed due to a crash, and now if the router -comes up again and starts originating a new opaque LSA, the new opaque -LSA is considered older since it has a lower sequence number and is -ignored by other routers (that consider the stalled opaque LSA as more -recent). However, if the originating router first synchronizes the -database before originating opaque LSAs, it will detect the older opaque -LSA and can flush it first. +opaque-capable neighbor. Thus, you cannot originate opaque LSAs if no neighbors +are present. If you try to originate when no neighbors are ready, you will +receive a not ready error message. The reason for this restriction is that it +might be possible that some routers have an identical opaque LSA from a +previous origination in their LSDB that unfortunately could not be flushed due +to a crash, and now if the router comes up again and starts originating a new +opaque LSA, the new opaque LSA is considered older since it has a lower +sequence number and is ignored by other routers (that consider the stalled +opaque LSA as more recent). However, if the originating router first +synchronizes the database before originating opaque LSAs, it will detect the +older opaque LSA and can flush it first. Protocol and Message Formats ---------------------------- -If you are developing your own client application and you don't want to -make use of the client library (due to the GNU license restriction or -whatever reason), you can implement your own client-side message -handling. The OSPF API uses two connections between the client and the -OSPF API server: One connection is used for a synchronous request /reply -protocol and another connection is used for asynchronous notifications -(e.g., LSA update, neighbor status change). +If you are developing your own client application and you don't want to make +use of the client library (due to the GNU license restriction or whatever +reason), you can implement your own client-side message handling. The OSPF API +uses two connections between the client and the OSPF API server: One connection +is used for a synchronous request /reply protocol and another connection is +used for asynchronous notifications (e.g., LSA update, neighbor status change). Each message begins with the following header: @@ -326,8 +320,8 @@ The synchronous requests and replies have the following message formats: image -The origin field allows to select according to the following types of -origins: +The origin field allows origin-based filtering using the following origin +types: +-------------------------+---------+ | Origin | Value | @@ -339,7 +333,7 @@ origins: | ANY\_ORIGIN | 2 | +-------------------------+---------+ -The reply message has on of the following error codes: +The reply message has one of the following error codes: +--------------------------+---------+ | Error code | Value | @@ -372,16 +366,18 @@ The asynchronous notifications have the following message formats: image + +.. Do not delete these acknowledgements! + Original Acknowledgments from Ralph Keller ------------------------------------------ -I would like to thank Masahiko Endo, the author of the opaque LSA -extension module, for his great support. His wonderful ASCII graphs -explaining the internal workings of this code, and his invaluable input -proved to be crucial in designing a useful API for accessing the link -state database of the OSPF daemon. Once, he even decided to take the -plane from Tokyo to Zurich so that we could actually meet and have -face-to-face discussions, which was a lot of fun. Clearly, without -Masahiko no API would ever be completed. I also would like to thank -Daniel Bauer who wrote an opaque LSA implementation too and was willing +I would like to thank Masahiko Endo, the author of the opaque LSA extension +module, for his great support. His wonderful ASCII graphs explaining the +internal workings of this code, and his invaluable input proved to be crucial +in designing a useful API for accessing the link state database of the OSPF +daemon. Once, he even decided to take the plane from Tokyo to Zurich so that we +could actually meet and have face-to-face discussions, which was a lot of fun. +Clearly, without Masahiko no API would ever be completed. I also would like to +thank Daniel Bauer who wrote an opaque LSA implementation too and was willing to test the OSPF API code in one of his projects. diff --git a/doc/developer/packaging-redhat.rst b/doc/developer/packaging-redhat.rst index f6b9931156a9..d3440461485c 100644 --- a/doc/developer/packaging-redhat.rst +++ b/doc/developer/packaging-redhat.rst @@ -32,7 +32,7 @@ Tested on CentOS 6, CentOS 7 and Fedora 24. cd frr ./bootstrap.sh - ./configure --with-pkg-extra-version=-MyRPMVersion SPHINXBUILD=sphinx-build2.7 + ./configure --with-pkg-extra-version=-MyRPMVersion make dist .. note:: diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index 7ae48881abb8..996f12d47f0e 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -30,6 +30,7 @@ dev_RSTFILES = \ doc/developer/include-compile.rst \ doc/developer/index.rst \ doc/developer/library.rst \ + doc/developer/lists.rst \ doc/developer/logging.rst \ doc/developer/maintainer-release-build.rst \ doc/developer/memtypes.rst \ @@ -39,7 +40,7 @@ dev_RSTFILES = \ doc/developer/ospf-sr.rst \ doc/developer/ospf.rst \ doc/developer/packaging-debian.rst \ - doc/developer/packaging-redhat.rst + doc/developer/packaging-redhat.rst \ doc/developer/packaging.rst \ doc/developer/testing.rst \ doc/developer/topotests-snippets.rst \ diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index 605b9c9a0cfe..e12bc37256b8 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -105,6 +105,8 @@ Execute all tests with output to console py.test -s -v --tb=no +The above command must be executed from inside the topotests directory. + All test\_\* scripts in subdirectories are detected and executed (unless disabled in ``pytest.ini`` file). @@ -119,6 +121,13 @@ Execute single test cd test_to_be_run ./test_to_be_run.py +For example, and assuming you are inside the frr directory: + +.. code:: shell + + cd tests/topotests/bgp_l3vpn_to_bgp_vrf + ./test_bgp_l3vpn_to_bgp_vrf.py + For further options, refer to pytest documentation. Test will set exit code which can be used with ``git bisect``. @@ -136,30 +145,23 @@ the following env variable can be set:: export TOPOTESTS_CHECK_STDERR=Yes -(The value doesn't matter at this time. The check is if the env variable exists -or not) There is no pass/fail on this reporting. The Output will be reported to -the console:: - - export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_" - -This will enable the check and output to console and the writing of the -information to files with the given prefix (followed by testname), ie -:file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case of a memory -leak. +(The value doesn't matter at this time. The check is whether the env +variable exists or not.) There is no pass/fail on this reporting; the +Output will be reported to the console. Collect Memory Leak Information ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -FRR processes have the capabilities to report remaining memory allocations upon -exit. To enable the reporting of the memory, define an environment variable +FRR processes can report unfreed memory allocations upon exit. To +enable the reporting of memory leaks, define an environment variable ``TOPOTESTS_CHECK_MEMLEAK`` with the file prefix, i.e.:: export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_" -This will enable the check and output to console and the writing of the -information to files with the given prefix (followed by testname), ie -:file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case of a memory -leak. +This will enable the check and output to console and the writing of +the information to files with the given prefix (followed by testname), +ie :file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case +of a memory leak. Running Topotests with AddressSanitizer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -180,13 +182,12 @@ If found, then this is added with context (calling test) to Compiling for GCC AddressSanitizer requires to use ``gcc`` as a linker as well (instead of ``ld``). Here is a suggest way to compile frr with AddressSanitizer -for ``stable/3.0`` branch: +for ``master`` branch: .. code:: shell git clone https://github.com/FRRouting/frr.git cd frr - git checkout stable/3.0 ./bootstrap.sh export CC=gcc export CFLAGS="-O1 -g -fsanitize=address -fno-omit-frame-pointer" @@ -199,7 +200,8 @@ for ``stable/3.0`` branch: --enable-exampledir=/usr/lib/frr/examples \ --with-moduledir=/usr/lib/frr/modules \ --enable-multipath=0 --enable-rtadv \ - --enable-tcp-zebra --enable-fpm --enable-pimd + --enable-tcp-zebra --enable-fpm --enable-pimd \ + --enable-sharpd make sudo make install # Create symlink for vtysh, so topotest finds it in /usr/lib/frr diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index b3b3a47cb03c..07c43ac2de70 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -73,14 +73,20 @@ Releases FRR employs a ``..`` versioning scheme. ``MAJOR`` - Significant new features or multiple minor features. The addition of a new - routing protocol or daemon would fall under this class. + Significant new features or multiple minor features. This should mostly + cover any kind of disruptive change that is visible or "risky" to operators. + New features or protocols do not necessarily trigger this. (This was changed + for FRR 7.x after feedback from users that the pace of major version number + increments was too high.) ``MINOR`` - Small features, e.g. options for automatic BGP shutdown. + General incremental development releases, excluding "major" changes + mentioned above. Not necessarily fully backwards compatible, as smaller + (but still visible) changes or deprecated feature removals may still happen. + However, there shouldn't be any huge "surprises" between minor releases. ``BUGFIX`` - Fixes for actual bugs and/or security issues. + Fixes for actual bugs and/or security issues. Fully compatible. We will pull a new development branch for the next release every 4 months. The current schedule is Feb/June/October 1. The decision for a ``MAJOR/MINOR`` @@ -362,6 +368,19 @@ Documentation should be written in reStructuredText. Sphinx extensions may be utilized but pure ReST is preferred where possible. See :ref:`documentation`. +Use of C++ +---------- + +While C++ is not accepted for core components of FRR, extensions, modules or +other distinct components may want to use C++ and include FRR header files. +There is no requirement on contributors to work to retain C++ compatibility, +but fixes for C++ compatibility are welcome. + +This implies that the burden of work to keep C++ compatibility is placed with +the people who need it, and they may provide it at their leisure to the extent +it is useful to them. So, if only a subset of header files, or even parts of +a header file are made available to C++, this is perfectly fine. + Code Reviews ============ @@ -750,7 +769,8 @@ developer will use this convention to allow control of their debugs. Static Analysis and Sanitizers ------------------------------ -Clang/LLVM comes with a variety of tools that can be used to help find bugs in FRR. +Clang/LLVM and GCC come with a variety of tools that can be used to help find +bugs in FRR. clang-analyze This is a static analyzer that scans the source code looking for patterns @@ -794,11 +814,31 @@ All of the above tools are available in the Clang/LLVM toolchain since 3.4. AddressSanitizer and ThreadSanitizer are available in recent versions of GCC, but are no longer actively maintained. MemorySanitizer is not available in GCC. +.. note:: + + The different Sanitizers are mostly incompatible with each other. Please + refer to GCC/LLVM documentation for details. + Additionally, the FRR codebase is regularly scanned with Coverity. Unfortunately Coverity does not have the ability to handle scanning pull requests, but after code is merged it will send an email notifying project members with Coverity access of newly introduced defects. +Executing non-installed dynamic binaries +---------------------------------------- + +Since FRR uses the GNU autotools build system, it inherits its shortcomings. +To execute a binary directly from the build tree under a wrapper like +`valgrind`, `gdb` or `strace`, use:: + + ./libtool --mode=execute valgrind [--valgrind-opts] zebra/zebra [--zebra-opts] + +While replacing valgrind/zebra as needed. The `libtool` script is found in +the root of the build directory after `./configure` has completed. Its purpose +is to correctly set up `LD_LIBRARY_PATH` so that libraries from the build tree +are used. (On some systems, `libtool` is also available from PATH, but this is +not always the case.) + CLI changes ----------- diff --git a/doc/extra/spelling_wordlist.txt b/doc/extra/spelling_wordlist.txt index 29445929625e..271f5e49f11f 100644 --- a/doc/extra/spelling_wordlist.txt +++ b/doc/extra/spelling_wordlist.txt @@ -80,6 +80,9 @@ IP iptables ipv IPv +IPvX +IPv4 +IPv6 isis isisd lan @@ -99,6 +102,8 @@ LSAs Masaki Mbit Mbits +macvlan +macvlans mib motd mpls @@ -227,6 +232,7 @@ VN VNC vrf vrfs +vrrp vty Vty vtysh diff --git a/doc/manpages/common-options.rst b/doc/manpages/common-options.rst index a5977a6ebbe5..a47a233c08eb 100644 --- a/doc/manpages/common-options.rst +++ b/doc/manpages/common-options.rst @@ -126,6 +126,7 @@ These following options control the daemon's VTY (interactive command line) inte staticd 2616 bfdd 2617 fabricd 2618 + vrrpd 2619 Port 2607 is used for ospfd's Opaque LSA API. diff --git a/doc/manpages/conf.py b/doc/manpages/conf.py index 46240de1c0a1..e7813d817654 100644 --- a/doc/manpages/conf.py +++ b/doc/manpages/conf.py @@ -334,6 +334,7 @@ ('frr', 'frr', 'a systemd interaction script', [], 1), ('bfdd', 'bfdd', fwfrr.format("a bfd"), [], 8), ('fabricd', 'fabricd', fwfrr.format("an OpenFabric "), [], 8), + ('vrrpd', 'vrrpd', fwfrr.format("a VRRP"), [], 8), ] # -- Options for Texinfo output ------------------------------------------- diff --git a/doc/manpages/defines.rst b/doc/manpages/defines.rst index cdf5e1967e47..2a6a9fd1bdf8 100644 --- a/doc/manpages/defines.rst +++ b/doc/manpages/defines.rst @@ -1,3 +1,3 @@ .. |synopsis-options| replace:: [-d|-t|-dt] [-C] [-f config-file] [-i pid-file] [-z zclient-path] [-u user] [-g group] [-A vty-addr] [-P vty-port] [-M module[:options]] [-N pathspace] [--vty_socket vty-path] [--moduledir module-path] .. |synopsis-options-hv| replace:: [-h] [-v] -.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), staticd(8), fabricd(8), mtracebis(8) +.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), staticd(8), fabricd(8), vrrpd(8), mtracebis(8) diff --git a/doc/manpages/index.rst b/doc/manpages/index.rst index 053555c4e427..40f06efdfeb5 100644 --- a/doc/manpages/index.rst +++ b/doc/manpages/index.rst @@ -26,4 +26,5 @@ watchfrr zebra vtysh + vrrpd frr diff --git a/doc/manpages/ospfd.rst b/doc/manpages/ospfd.rst index 6e4d093f69aa..951a0229aa19 100644 --- a/doc/manpages/ospfd.rst +++ b/doc/manpages/ospfd.rst @@ -22,6 +22,10 @@ OPTIONS available for the |DAEMON| command: .. include:: common-options.rst +.. option:: -a, --apiserver + + Enable the OSPF API server. + FILES ===== diff --git a/doc/manpages/subdir.am b/doc/manpages/subdir.am index a4457c9c4553..19d2d8d6ae4a 100644 --- a/doc/manpages/subdir.am +++ b/doc/manpages/subdir.am @@ -30,6 +30,7 @@ man_RSTFILES = \ doc/manpages/zebra.rst \ doc/manpages/bfdd.rst \ doc/manpages/bfd-options.rst \ + doc/manpages/vrrpd.rst \ # end EXTRA_DIST += $(man_RSTFILES) diff --git a/doc/manpages/vrrpd.rst b/doc/manpages/vrrpd.rst new file mode 100644 index 000000000000..0e73b07cdad3 --- /dev/null +++ b/doc/manpages/vrrpd.rst @@ -0,0 +1,40 @@ +***** +VRRPD +***** + +.. include:: defines.rst +.. |DAEMON| replace:: vrrpd + +SYNOPSIS +======== +|DAEMON| |synopsis-options-hv| + +|DAEMON| |synopsis-options| + +DESCRIPTION +=========== +|DAEMON| is a routing component that works with the FRRouting routing engine. +It implements the Virtual Router Redundancy Protocol. Support for both VRRPv2 +and VRRPv3 is present. + +OPTIONS +======= +OPTIONS available for the |DAEMON| command: + +.. include:: common-options.rst + +FILES +===== + +|INSTALL_PREFIX_SBIN|/|DAEMON| + The default location of the |DAEMON| binary. + +|INSTALL_PREFIX_ETC|/|DAEMON|.conf + The default location of the |DAEMON| config file. + +$(PWD)/|DAEMON|.log + If the |DAEMON| process is configured to output logs to a file, then you + will find this file in the directory where you started |DAEMON|. + +.. include:: epilogue.rst + diff --git a/doc/subdir.am b/doc/subdir.am index 7d3792bf2bd2..a1297a4f810d 100644 --- a/doc/subdir.am +++ b/doc/subdir.am @@ -4,7 +4,6 @@ # You can set these variables from the command line. SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build PAPER ?= # Internal variables. @@ -32,20 +31,20 @@ am__v_MAKEINFO_1 = doc/%/_build/.doctrees/environment.pickle: $(AM_V_SPHINX) ( \ subdoc="$@"; subdoc="$${subdoc#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ - $(SPHINXBUILD) -a -q -b text -d "$${subdoc}/_build/.doctrees" \ + $(PYTHON) $(PYSPHINX) -a -q -b text -d "$${subdoc}/_build/.doctrees" \ $(ALLSPHINXOPTS) "$(top_srcdir)/$${subdoc}" "$${subdoc}/_build/text" \ ) doc/%/_build/html/.buildinfo: doc/%/_build/.doctrees/environment.pickle $(AM_V_SPHINX) ( \ subdoc="$@"; subdoc="$${subdoc#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ - $(SPHINXBUILD) -q -b html -d "$${subdoc}/_build/.doctrees" \ + $(PYTHON) $(PYSPHINX) -q -b html -d "$${subdoc}/_build/.doctrees" \ $(ALLSPHINXOPTS) "$(top_srcdir)/$${subdoc}" "$${subdoc}/_build/html" \ ) .PRECIOUS: doc/%/_build/texinfo/frr.texi doc/%/_build/texinfo/frr.texi: doc/%/_build/.doctrees/environment.pickle $(AM_V_SPHINX) ( \ subdoc="$@"; subdoc="$${subdoc#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ - $(SPHINXBUILD) -q -b texinfo -d "$${subdoc}/_build/.doctrees" \ + $(PYTHON) $(PYSPHINX) -q -b texinfo -d "$${subdoc}/_build/.doctrees" \ $(ALLSPHINXOPTS) "$(top_srcdir)/$${subdoc}" "$${subdoc}/_build/texinfo" \ ) doc/%/_build/texinfo/frr.info: doc/%/_build/texinfo/frr.texi @@ -54,7 +53,7 @@ doc/%/_build/man/man.stamp: doc/%/_build/.doctrees/environment.pickle $(AM_V_SPHINX) ( \ subdoc="$@"; subdoc="$${subdoc#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ $(MKDIR_P) "$${subdoc}/_build/man"; touch $@.tmp; \ - $(SPHINXBUILD) -a -q -b man -d "$${subdoc}/_build/.doctrees" \ + $(PYTHON) $(PYSPHINX) -a -q -b man -d "$${subdoc}/_build/.doctrees" \ $(ALLSPHINXOPTS) "$(top_srcdir)/$${subdoc}" "$${subdoc}/_build/man" && \ mv -f $@.tmp $@ \ ) @@ -80,7 +79,7 @@ $(M_SPHINXTARGETS): doc/%/_build/.doctrees/environment.pickle builder="$${target##*/}"; \ subdoc="$${target#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ rm -rf "$@"; \ - $(SPHINXBUILD) -q -b $${builder} -d $${subdoc}/_build/.doctrees \ + $(PYTHON) $(PYSPHINX) -q -b $${builder} -d $${subdoc}/_build/.doctrees \ $(ALLSPHINXOPTS) $(top_srcdir)/$${subdoc} $@ \ ) diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 8fbea29ee7ec..3d3a75d4b1b4 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -180,13 +180,14 @@ Basic Config Commands In this example, the precision is set to provide timestamps with millisecond accuracy. -.. index:: log commands -.. clicmd:: log commands +.. index:: [no] log commands +.. clicmd:: [no] log commands This command enables the logging of all commands typed by a user to all enabled log destinations. The note that logging includes full command lines, - including passwords. Once set, command logging can only be turned off by - restarting the daemon. + including passwords. If the daemon startup option `--command-log-always` + is used to start the daemon then this command is turned on by default + and cannot be turned off and the [no] form of the command is dissallowed. .. index:: service password-encryption .. clicmd:: service password-encryption @@ -287,8 +288,8 @@ Terminal Mode Commands Write current configuration to configuration file. -.. index:: configure terminal -.. clicmd:: configure terminal +.. index:: configure [terminal] +.. clicmd:: configure [terminal] Change to configuration mode. This command is the first step to configuration. @@ -414,6 +415,22 @@ Terminal Mode Commands (view) show [ip] bgp l2vpn evpn all overlay ... +.. _common-show-commands: + +.. index:: show thread cpu +.. clicmd:: show thread cpu [r|w|t|e|x] + + This command displays system run statistics for all the different event + types. If no options is specified all different run types are displayed + together. Additionally you can ask to look at (r)ead, (w)rite, (t)imer, + (e)vent and e(x)ecute thread event types. + +.. index:: show thread poll +.. clicmd:: show thread poll + + This command displays FRR's poll data. It allows a glimpse into how + we are setting each individual fd for the poll command at that point + in time. .. _common-invocation-options: @@ -462,10 +479,23 @@ These options apply to all |PACKAGE_NAME| daemons. Set the user and group to run as. +.. option:: -N + + Set the namespace that the daemon will run in. A "/" will + be added to all files that use the statedir. If you have "/var/run/frr" + as the default statedir then it will become "/var/run/frr/". + .. option:: -v, --version Print program version. +.. option:: --command-log-always + + Cause the daemon to always log commands entered to the specified log file. + This also makes the `no log commands` command dissallowed. Enabling this + is suggested if you have need to track what the operator is doing on + this router. + .. option:: --log When initializing the daemon, setup the log to go to either stdout, diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index 986d1494a547..32ca5707d245 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -47,6 +47,9 @@ may also be specified (:ref:`common-invocation-options`). #define BFDD_CONTROL_SOCKET "|INSTALL_PREFIX_STATE|/bfdd.sock" + This option overrides the location addition that the -N option provides + to the bfdd.sock + .. _bfd-commands: @@ -72,8 +75,7 @@ BFDd Commands peer listener to and the address we should use to send the packets. This option is mandatory for IPv6. - `interface` selects which interface we should use. This option - conflicts with `vrf`. + `interface` selects which interface we should use. `vrf` selects which domain we want to use. @@ -82,13 +84,13 @@ BFDd Commands Stops and removes the selected peer. -.. index:: show bfd peers [json] -.. clicmd:: show bfd peers [json] +.. index:: show bfd [vrf NAME] peers [json] +.. clicmd:: show bfd [vrf NAME] peers [json] Show all configured BFD peers information and current status. -.. index:: show bfd peer $peer [{multihop|local-address $local|interface IFNAME$ifname|vrf NAME$vrfname}]> [json] -.. clicmd:: show bfd peer $peer [{multihop|local-address $local|interface IFNAME$ifname|vrf NAME$vrfname}]> [json] +.. index:: show bfd [vrf NAME$vrfname] peer $peer [{multihop|local-address $local|interface IFNAME$ifname}]> [json] +.. clicmd:: show bfd [vrf NAME$vrfname] peer $peer [{multihop|local-address $local|interface IFNAME$ifname}]> [json] Show status for a specific BFD peer. @@ -175,6 +177,21 @@ The following commands are available inside the BGP configuration node. Removes any notification registration for this neighbor. +.. index:: neighbor bfd check-control-plane-failure +.. clicmd:: neighbor bfd check-control-plane-failure + + Allow to write CBIT independence in BFD outgoing packets. Also allow to + read both C-BIT value of BFD and lookup BGP peer status. This command is + useful when a BFD down event is caught, while the BGP peer requested that + local BGP keeps the remote BGP entries as staled if such issue is detected. + This is the case when graceful restart is enabled, and it is wished to + ignore the BD event while waiting for the remote router to restart. + +.. index:: no neighbor bfd check-control-plane-failure +.. clicmd:: no neighbor bfd check-control-plane-failure + + Disallow to write CBIT independence in BFD outgoing packets. Also disallow + to ignore BFD down notification. This is the default behaviour. .. _bfd-ospf-peer-config: @@ -296,6 +313,11 @@ Here are the available peer configurations: shutdown ! + ! configure a peer on an interface from a separate vrf + peer 192.168.0.5 interface eth1 vrf vrf2 + no shutdown + ! + ! remove a peer no peer 192.168.0.3 vrf foo diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 35e42d95cb51..768f22c87320 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -942,14 +942,18 @@ Configuring Peers .. index:: [no] neighbor PEER maximum-prefix NUMBER .. clicmd:: [no] neighbor PEER maximum-prefix NUMBER -.. index:: [no] neighbor PEER local-as AS-NUMBER no-prepend -.. clicmd:: [no] neighbor PEER local-as AS-NUMBER no-prepend + Sets a maximum number of prefixes we can receive from a given peer. If this + number is exceeded, the BGP session will be destroyed. -.. index:: [no] neighbor PEER local-as AS-NUMBER no-prepend replace-as -.. clicmd:: [no] neighbor PEER local-as AS-NUMBER no-prepend replace-as + In practice, it is generally preferable to use a prefix-list to limit what + prefixes are received from the peer instead of using this knob. Tearing down + the BGP session when a limit is exceeded is far more destructive than merely + rejecting undesired prefixes. The prefix-list method is also much more + granular and offers much smarter matching criterion than number of received + prefixes, making it more suited to implementing policy. -.. index:: [no] neighbor PEER local-as AS-NUMBER -.. clicmd:: [no] neighbor PEER local-as AS-NUMBER +.. index:: [no] neighbor PEER local-as AS-NUMBER [no-prepend] [replace-as] +.. clicmd:: [no] neighbor PEER local-as AS-NUMBER [no-prepend] [replace-as] Specify an alternate AS for this BGP process when interacting with the specified peer. With no modifiers, the specified local-as is prepended to @@ -1858,11 +1862,11 @@ address-family: .. index:: label vpn export (0..1048575)|auto .. clicmd:: label vpn export (0..1048575)|auto - Specifies an optional MPLS label to be attached to a route exported from the - current unicast VRF to VPN. If label is specified as ``auto``, the label - value is automatically assigned from a pool maintained by the zebra - daemon. If zebra is not running, automatic label assignment will not - complete, which will block corresponding route export. + Enables an MPLS label to be attached to a route exported from the current + unicast VRF to VPN. If the value specified is ``auto``, the label value is + automatically assigned from a pool maintained by the Zebra daemon. If Zebra + is not running, or if this command is not configured, automatic label + assignment will not complete, which will block corresponding route export. .. index:: no label vpn export [(0..1048575)|auto] .. clicmd:: no label vpn export [(0..1048575)|auto] diff --git a/doc/user/index.rst b/doc/user/index.rst index 4c218c658028..4e14de673799 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -56,6 +56,7 @@ Protocols sharp static vnc + vrrp ######## Appendix diff --git a/doc/user/installation.rst b/doc/user/installation.rst index 964297292f2c..6438c1141305 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -347,6 +347,27 @@ compile directory: ./configure --with-libyang-pluginsdir="`pwd`/yang/libyang_plugins/.libs" \ --with-yangmodelsdir="`pwd`/yang" +Python dependency, documentation and tests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +FRR's documentation and basic unit tests heavily use code written in Python. +Additionally, FRR ships Python extensions written in C which are used during +its build process. + +To this extent, FRR needs the following: + +* an installation of CPython, preferably version 3.2 or newer (2.7 works but + is end of life and will stop working at some point.) +* development files (mostly headers) for that version of CPython +* an installation of `sphinx` for that version of CPython, to build the + documentation +* an installation of `pytest` for that version of CPython, to run the unit + tests + +The `sphinx` and `pytest` dependencies can be avoided by not building +documentation / not running ``make check``, but the CPython dependency is a +hard dependency of the FRR build process (for the `clippy` tool.) + .. _least-privilege-support: Least-Privilege Support diff --git a/doc/user/ldpd.rst b/doc/user/ldpd.rst index 85d280343d68..977195d6a796 100644 --- a/doc/user/ldpd.rst +++ b/doc/user/ldpd.rst @@ -24,6 +24,12 @@ Running Ldpd The *ldpd* daemon can be invoked with any of the common options (:ref:`common-invocation-options`). +..option:: --ctl_socket + + This option allows you to override the path to the ldpd.sock file + used to control this daemon. If specified this option overrides + the -N option path addition. + The *zebra* daemon must be running before *ldpd* is invoked. Configuration of *ldpd* is done in its configuration file diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 71bc04772021..6413c6299531 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -15,8 +15,8 @@ OSPF6 router .. index:: router ospf6 .. clicmd:: router ospf6 -.. index:: router-id A.B.C.D -.. clicmd:: router-id A.B.C.D +.. index:: ospf6 router-id A.B.C.D +.. clicmd:: ospf6 router-id A.B.C.D Set router's Router-ID. diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index ad0a8639a34a..83e14d474f0a 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -26,7 +26,7 @@ Configuring OSPF .. option:: -a, --apiserver - Enable the OSPF API server + Enable the OSPF API server. This is required to use ``ospfclient``. *ospfd* must acquire interface information from *zebra* in order to function. Therefore *zebra* must be running before invoking *ospfd*. Also, if *zebra* is diff --git a/doc/user/pim.rst b/doc/user/pim.rst index e385df3b6d37..e8c74f7b87d4 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -166,6 +166,20 @@ is in a vrf, enter the interface command with the vrf keyword at the end. Turns on BFD support for PIM for this interface. +.. index:: ip pim bsm +.. clicmd:: ip pim bsm + + Tell pim that we would like to use this interface to process bootstrap + messages. This is enabled by default. 'no' form of this command is used to + restrict bsm messages on this interface. + +.. index:: ip pim unicast-bsm +.. clicmd:: ip pim unicast-bsm + + Tell pim that we would like to allow interface to process unicast bootstrap + messages. This is enabled by default. 'no' form of this command is used to + restrict processing of unicast bsm messages on this interface. + .. index:: ip pim drpriority (1-4294967295) .. clicmd:: ip pim drpriority (1-4294967295) @@ -218,6 +232,19 @@ is in a vrf, enter the interface command with the vrf keyword at the end. or IGMP report is received on this interface and the Group is denied by the prefix-list, PIM will ignore the join or report. +.. index:: ip igmp last-member-query-count (1-7) +.. clicmd:: ip igmp last-member-query-count (1-7) + + Set the IGMP last member query count. The default value is 2. 'no' form of + this command is used to to configure back to the default value. + +.. index:: ip igmp last-member-query-interval (1-255) +.. clicmd:: ip igmp last-member-query-interval (1-255) + + Set the IGMP last member query interval in deciseconds. The default value is + 10 deciseconds. 'no' form of this command is used to to configure back to the + default value. + .. _pim-multicast-rib-insertion: PIM Multicast RIB insertion: @@ -308,6 +335,12 @@ cause great confusion. Display information about installed into the kernel S,G mroutes and in addition display data about packet flow for the mroutes. +.. index:: show ip mroute summary +.. clicmd:: show ip mroute summary + + Display total number of S,G mroutes and number of S,G mroutes installed + into the kernel. + .. index:: show ip pim assert .. clicmd:: show ip pim assert @@ -407,6 +440,21 @@ cause great confusion. Display upstream information for S,G's and the RPF data associated with them. +.. index:: show ip pim bsr +.. clicmd:: show ip pim bsr + + Display current bsr, its uptime and last received bsm age. + +.. index:: show ip pim bsrp-info +.. clicmd:: show ip pim bsrp-info + + Display group-to-rp mappings received from E-BSR. + +.. index:: show ip pim bsm-database +.. clicmd:: show ip pim bsm-database + + Display all fragments ofstored bootstrap message in user readable format. + .. index:: show ip rpf .. clicmd:: show ip rpf @@ -470,6 +518,11 @@ the config was written out. This traces pim code and how it is running. +.. index:: debug pim bsm +.. clicmd:: debug pim bsm + + This turns on debugging for BSR message processing. + .. index:: debug pim zebra .. clicmd:: debug pim zebra @@ -494,6 +547,13 @@ Clear commands reset various variables. Reset multicast routes. +.. index:: clear ip mroute [vrf NAME] count +.. clicmd:: clear ip mroute [vrf NAME] count + + When this command is issued, reset the counts of data shown for + packet count, byte count and wrong interface to 0 and start count + up from this spot. + .. index:: clear ip pim interfaces .. clicmd:: clear ip pim interfaces @@ -506,20 +566,25 @@ Clear commands reset various variables. PIM EVPN configuration ====================== -To use PIM in the underlay for overlay BUM forwarding associate a -multicast group with the L2 VNI. The actual configuration is based -on your distribution. Here is an ifupdown2 example - -auto vx-10100 -iface vx-10100 - vxlan-id 10100 - bridge-access 100 - vxlan-local-tunnelip 27.0.0.11 - vxlan-mcastgrp 239.1.1.100 >>>>>>>> -PIM will see the vxlan configuration and auto configure state to properly -forward BUM traffic. - -PIM also needs to be configured in the underlay to allow the BUM MDT to -be setup. This is existing PIM configuration - +To use PIM in the underlay for overlay BUM forwarding associate a multicast +group with the L2 VNI. The actual configuration is based on your distribution. +Here is an ifupdown2 example:: + + auto vx-10100 + iface vx-10100 + vxlan-id 10100 + bridge-access 100 + vxlan-local-tunnelip 27.0.0.11 + vxlan-mcastgrp 239.1.1.100 + +.. note:: + + PIM will see the ``vxlan-mcastgrp`` configuration and auto configure state + to properly forward BUM traffic. + +PIM also needs to be configured in the underlay to allow the BUM MDT to be +setup. This is existing PIM configuration: + - Enable pim on the underlay L3 interface via the "ip pim" command. - Configure RPs for the BUM multicast group range. - Ensure the PIM is enabled on the lo of the VTEPs and the RP. diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst index 295a26fda9f7..ca6b46d3cff2 100644 --- a/doc/user/rpki.rst +++ b/doc/user/rpki.rst @@ -188,10 +188,6 @@ Validating BGP Updates Create a clause for a route map to match prefixes with the specified RPKI state. - **Note** that the matching of invalid prefixes requires that invalid - prefixes are considered for best path selection, i.e., - ``bgp bestpath prefix-validate disallow-invalid`` is not enabled. - In the following example, the router prefers valid routes over invalid prefixes because invalid routes have a lower local preference. diff --git a/doc/user/setup.rst b/doc/user/setup.rst index ffefe3790577..2cdd3a7c7fd3 100644 --- a/doc/user/setup.rst +++ b/doc/user/setup.rst @@ -6,8 +6,8 @@ Basic Setup After installing FRR, some basic configuration must be completed before it is ready to use. -Daemons File ------------- +Daemons Configuration File +-------------------------- After a fresh install, starting FRR will do nothing. This is because daemons must be explicitly enabled by editing a file in your configuration directory. This file is usually located at :file:`/etc/frr/daemons` and determines which @@ -34,19 +34,6 @@ systemd. The file initially looks like this: bfdd=no fabricd=no -To enable a particular daemon, simply change the corresponding 'no' to 'yes'. -Subsequent service restarts should start the daemon. - -Daemons Configuration File --------------------------- -There is another file that controls the default options passed to daemons when -starting FRR as a service. This file is located in your configuration -directory, usually at :file:`/etc/frr/daemons`. - -This file has several parts. Here is an example: - -:: - # # If this option is set the /etc/init.d/frr script automatically loads # the config via "vtysh -b" when the servers are started. @@ -71,6 +58,7 @@ This file has several parts. Here is an example: bfdd_options=" --daemon -A 127.0.0.1" fabricd_options=" --daemon -A 127.0.0.1" + #MAX_FDS=1024 # The list of daemons to watch is automatically generated by the init script. #watchfrr_options="" @@ -83,6 +71,13 @@ This file has several parts. Here is an example: Breaking this file down: +:: + + bgpd=yes + +To enable a particular daemon, simply change the corresponding 'no' to 'yes'. +Subsequent service restarts should start the daemon. + :: vtysh_enable=yes @@ -91,6 +86,16 @@ As the comment says, this causes :ref:`VTYSH ` to apply configuration when starting the daemons. This is useful for a variety of reasons touched on in the VTYSH documentation and should generally be enabled. +:: + + MAX_FDS=1024 + +This allows the operator to control the number of open file descriptors +each daemon is allowed to start with. The current assumed value on +most operating systems is 1024. If the operator plans to run bgp with +several thousands of peers than this is where we would modify FRR to +allow this to happen. + :: zebra_options=" -s 90000000 --daemon -A 127.0.0.1" diff --git a/doc/user/static.rst b/doc/user/static.rst index 1705b6379e8a..09bdc9cbea0e 100644 --- a/doc/user/static.rst +++ b/doc/user/static.rst @@ -123,7 +123,7 @@ but this time, the route command will apply to the VRF. .. code-block:: frr # case with VRF - configure terminal + configure vrf r1-cust1 ip route 10.0.0.0/24 10.0.0.2 exit-vrf diff --git a/doc/user/subdir.am b/doc/user/subdir.am index 08b5dc954c5f..1e4d86c722f5 100644 --- a/doc/user/subdir.am +++ b/doc/user/subdir.am @@ -37,6 +37,7 @@ user_RSTFILES = \ doc/user/snmptrap.rst \ doc/user/static.rst \ doc/user/vnc.rst \ + doc/user/vrrp.rst \ doc/user/vtysh.rst \ doc/user/zebra.rst \ doc/user/bfd.rst \ diff --git a/doc/user/vrrp.rst b/doc/user/vrrp.rst new file mode 100644 index 000000000000..a2dd950987d7 --- /dev/null +++ b/doc/user/vrrp.rst @@ -0,0 +1,506 @@ +.. _vrrp: + +**** +VRRP +**** + +:abbr:`VRRP` stands for Virtual Router Redundancy Protocol. This protocol is +used to allow multiple backup routers on the same segment to take over +operation of each others' IP addresses if the primary router fails. This is +typically used to provide fault-tolerant gateways to hosts on the segment. + +FRR implements VRRPv2 (:rfc:`3768`) and VRRPv3 (:rfc:`5798`). For VRRPv2, no +authentication methods are supported; these are deprecated in the VRRPv2 +specification as they do not provide any additional security over the base +protocol. + +.. note:: + + - VRRP is supported on Linux 5.1+ + - VRRP does not implement Accept_Mode + +.. _vrrp-starting: + +Starting VRRP +============= + +The configuration file for *vrrpd* is :file:`vrrpd.conf`. The typical location +of :file:`vrrpd.conf` is |INSTALL_PREFIX_ETC|/vrrpd.conf. + +If using integrated config, then :file:`vrrpd.conf` need not be present and +:file:`frr.conf` is read instead. + +.. program:: vrrpd + +:abbr:`VRRP` supports all the common FRR daemon start options which are +documented elsewhere. + +.. _vrrp-protocol-overview: + +Protocol Overview +================= + +From :rfc:`5798`: + + VRRP specifies an election protocol that dynamically assigns responsibility + for a virtual router to one of the VRRP routers on a LAN. The VRRP router + controlling the IPv4 or IPv6 address(es) associated with a virtual router is + called the Master, and it forwards packets sent to these IPv4 or IPv6 + addresses. VRRP Master routers are configured with virtual IPv4 or IPv6 + addresses, and VRRP Backup routers infer the address family of the virtual + addresses being carried based on the transport protocol. Within a VRRP + router, the virtual routers in each of the IPv4 and IPv6 address families + are a domain unto themselves and do not overlap. The election process + provides dynamic failover in the forwarding responsibility should the Master + become unavailable. For IPv4, the advantage gained from using VRRP is a + higher-availability default path without requiring configuration of dynamic + routing or router discovery protocols on every end-host. For IPv6, the + advantage gained from using VRRP for IPv6 is a quicker switchover to Backup + routers than can be obtained with standard IPv6 Neighbor Discovery + mechanisms. + +VRRP accomplishes these goals primarily by using a virtual MAC address shared +between the physical routers participating in a VRRP virtual router. This +reduces churn in the neighbor tables of hosts and downstream switches and makes +router failover theoretically transparent to these devices. + +FRR implements the election protocol and handles changing the operating system +interface configuration in response to protocol state changes. + +As a consequence of the shared virtual MAC requirement, VRRP is currently +supported only on Linux, as Linux is the only operating system that provides +the necessary features in its network stack to make implementing this protocol +feasible. + +When a VRRP router is acting as the Master router, FRR allows the interface(s) +with the backed-up IP addresses to remain up and functional. When the router +transitions to Backup state, these interfaces are set into ``protodown`` mode. +This is an interface mode that is functionally equivalent to ``NO-CARRIER``. +Physical drivers typically use this state indication to drop traffic on an +interface. In the case of VRRP, the interfaces in question are macvlan devices, +which are virtual interfaces. Since the IP addresses managed by VRRP are on +these interfaces, this has the same effect as removing these addresses from the +interface, but is implemented as a state flag. + +.. _vrrp-configuration: + +Configuring VRRP +================ + +VRRP is configured on a per-interface basis, with some global defaults +accessible outside the interface context. + +.. _vrrp-system-configuration: + +System Configuration +-------------------- + +FRR's VRRP implementation uses Linux macvlan devices to to implement the shared +virtual MAC feature of the protocol. Currently, it does not create those system +interfaces - they must be configured outside of FRR before VRRP can be enabled +on them. + +Each interface on which VRRP will be enabled must have at least one macvlan +device configured with the virtual MAC and placed in the proper operation mode. +The addresses backed up by VRRP are assigned to these interfaces. + +Suppose you have an interface ``eth0`` with the following configuration: + +.. code-block:: console + + $ ip link show eth0 + 2: eth0: mtu 1500 qdisc fq_codel state UP group default qlen 1000 + link/ether 02:17:45:00:aa:aa brd ff:ff:ff:ff:ff:ff + inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic eth0 + valid_lft 72532sec preferred_lft 72532sec + inet 10.0.2.16/24 brd 10.0.2.255 scope global dynamic eth0 + valid_lft 72532sec preferred_lft 72532sec + inet6 fe80::17:45ff:fe00:aaaa/64 scope link + valid_lft forever preferred_lft forever + +Suppose the address you want to back up is ``10.0.2.16``, and will be managed +by the virtual router with id ``5``. A macvlan device with the appropriate MAC +address must be created before VRRP can begin to operate. + +If you are using ``ifupdown2``, the configuration is as follows: + +.. code-block:: console + + iface eth0 + ... + vrrp 5 10.0.2.16/24 2001:0db8::0370:7334/64 + +Applying this configuration with ``ifreload -a`` will create the appropriate +macvlan device. If you are using ``iproute2``, the equivalent configuration is: + +.. code-block:: console + + ip link add vrrp4-2-1 link eth0 addrgenmode random type macvlan mode bridge + ip link set dev vrrp4-2-1 address 00:00:5e:00:01:05 + ip addr add 10.0.2.16/24 dev vrrp4-2-1 + ip link set dev vrrp4-2-1 up + + ip link add vrrp6-2-1 link eth0 addrgenmode random type macvlan mode bridge + ip link set dev vrrp4-2-1 address 00:00:5e:00:02:05 + ip addr add 2001:db8::370:7334/64 dev vrrp6-2-1 + ip link set dev vrrp6-2-1 up + +In either case, the created interfaces will look like this: + +.. code-block:: console + + $ ip addr show vrrp4-2-1 + 5: vrrp4-2-1@eth0: mtu 1500 qdisc noqueue state UP group default qlen 1000 + link/ether 00:00:5e:00:01:05 brd ff:ff:ff:ff:ff:ff + inet 10.0.2.16/24 scope global vrrp4-2-1 + valid_lft forever preferred_lft forever + inet6 fe80::dc56:d11a:e69d:ea72/64 scope link stable-privacy + valid_lft forever preferred_lft forever + + $ ip addr show vrrp6-2-1 + 8: vrrp6-2-1@eth0: mtu 1500 qdisc noqueue state UP group default qlen 1000 + link/ether 00:00:5e:00:02:05 brd ff:ff:ff:ff:ff:ff + inet6 2001:db8::370:7334/64 scope global + valid_lft forever preferred_lft forever + inet6 fe80::f8b7:c9dd:a1e8:9844/64 scope link stable-privacy + valid_lft forever preferred_lft forever + +Using ``vrrp4-2-1`` as an example, a few things to note about this interface: + +- It is slaved to ``eth0``; any packets transmitted on this interface will + egress via ``eth0`` +- Its MAC address is set to the VRRP IPv4 virtual MAC specified by the RFC for + :abbr:`VRID (Virtual Router ID)` ``5`` +- The link local address on the interface is not derived from the interface + MAC + +First to note is that packets transmitted on this interface will egress via +``eth0``, but with their Ethernet source MAC set to the VRRP virtual MAC. This +is how FRR's VRRP implementation accomplishes the virtual MAC requirement on +real hardware. + +Ingress traffic is a more complicated matter. Macvlan devices have multiple +operating modes that change how ingress traffic is handled. Of relevance to +FRR's implementation are the ``bridge`` and ``private`` modes. In ``private`` +mode, any ingress traffic on ``eth0`` (in our example) with a source MAC +address equal to the MAC address on any of ``eth0``'s macvlan devices will be +placed *only* on that macvlan device. This curious behavior is undesirable, +since FRR's implementation of VRRP needs to be able to receive advertisements +from neighbors while in Backup mode - i.e., while its macvlan devices are in +``protodown on``. If the macvlan devices are instead set to ``bridge`` mode, +all ingress traffic shows up on all interfaces - including ``eth0`` - +regardless of source MAC or any other factor. Consequently, macvlans used by +FRR for VRRP must be set to ``bridge`` mode or the protocol will not function +correctly. + +As for the MAC address assigned to this interface, the last byte of the address +holds the :abbr:`VRID (Virtual Router Identifier)`, in this case ``0x05``. The +second to last byte is ``0x01``, as specified by the RFC for IPv4 operation. +The IPv6 MAC address is be identical except that the second to last byte is +defined to be ``0x02``. Two things to note from this arrangement: + +1. There can only be up to 255 unique Virtual Routers on an interface (only 1 + byte is available for the VRID) +2. IPv4 and IPv6 addresses must be assigned to different macvlan devices, + because they have different MAC addresses + +Finally, take note of the generated IPv6 link local address on the interface. +For interfaces on which VRRP will operate in IPv6 mode, this link local +*cannot* be derived using the usual EUI-64 method. This is because VRRP +advertisements are sent from the link local address of this interface, and VRRP +uses the source address of received advertisements as part of its election +algorithm. If the IPv6 link local of a router is equivalent to the IPv6 link +local in a received advertisement, this can cause both routers to assume the +Master role (very bad). ``ifupdown`` knows to set the ``addrgenmode`` of the +interface properly, but when using ``iproute2`` to create the macvlan devices, +you must be careful to manually specify ``addrgenmode random``. + +A brief note on the Backup state +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is worth noting here that an alternate choice for the implementation of the +Backup state, such as removing all the IP addresses assigned to the macvlan +device or deleting their local routes instead of setting the device into +``protodown on``, would allow the protocol to function regardless of whether +the macvlan device(s) are set to ``private`` or ``bridge`` mode. Indeed, the +strange behavior of the kernel macvlan driver in ``private`` mode, whereby it +performs what may be thought of as a sort of interface-level layer 2 "NAT" +based on source MAC, can be traced back to a patch clearly designed to +accommodate a VRRP implementation from a different vendor. However, the +``protodown`` based implementation allows for a configuration model in which +FRR does not dynamically manage the addresses assigned on a system, but instead +just manages interface state. Such a scenario was in mind when this protocol +implementation was initially built, which is why the other choices are not +currently present. Since support for placing macvlan devices into ``protodown`` +was not added to Linux until version 5.1, this also explains the relatively +restrictive kernel versioning requirement. + +In the future other methods of implementing Backup state may be added along +with a configuration knob to choose between them. + +.. _vrrp-interface-configuration: + +Interface Configuration +----------------------- + +Continuing with the example from the previous section, we assume the macvlan +interfaces have been properly configured with the proper MAC addresses and the +IPvX addresses assigned. + +In FRR, a possible VRRPv3 configuration for this interface is: + +.. code-block:: frr + + interface eth0 + vrrp 5 version 3 + vrrp 5 priority 200 + vrrp 5 advertisement-interval 1500 + vrrp 5 ip 10.0.2.16 + vrrp 5 ipv6 2001:0db8::0370:7334 + +VRRP will activate as soon as the first IPvX address configuration line is +encountered. If you do not want this behavior, use the :clicmd:`vrrp (1-255) +shutdown` command, and apply the ``no`` form when you are ready to activate +VRRP. + +At this point executing ``show vrrp`` will display the following: + +.. code-block:: console + + ubuntu-bionic# show vrrp + + Virtual Router ID 5 + Protocol Version 3 + Autoconfigured Yes + Shutdown No + Interface eth0 + VRRP interface (v4) vrrp4-2-5 + VRRP interface (v6) vrrp6-2-5 + Primary IP (v4) 10.0.2.15 + Primary IP (v6) fe80::9b91:7155:bf6a:d386 + Virtual MAC (v4) 00:00:5e:00:01:05 + Virtual MAC (v6) 00:00:5e:00:02:05 + Status (v4) Master + Status (v6) Master + Priority 200 + Effective Priority (v4) 200 + Effective Priority (v6) 200 + Preempt Mode Yes + Accept Mode Yes + Advertisement Interval 1500 ms + Master Advertisement Interval (v4) 1000 ms + Master Advertisement Interval (v6) 1000 ms + Advertisements Tx (v4) 14 + Advertisements Tx (v6) 14 + Advertisements Rx (v4) 0 + Advertisements Rx (v6) 0 + Gratuitous ARP Tx (v4) 1 + Neigh. Adverts Tx (v6) 1 + State transitions (v4) 2 + State transitions (v6) 2 + Skew Time (v4) 210 ms + Skew Time (v6) 210 ms + Master Down Interval (v4) 3210 ms + Master Down Interval (v6) 3210 ms + IPv4 Addresses 1 + .................................. 10.0.2.16 + IPv6 Addresses 1 + .................................. 2001:db8::370:7334 + +At this point, VRRP has sent gratuitous ARP requests for the IPv4 address, +Unsolicited Neighbor Advertisements for the IPv6 address, and has asked Zebra +to send Router Advertisements on its behalf. It is also transmitting VRRPv3 +advertisements on the macvlan interfaces. + +The Primary IP fields are of some interest, as the behavior may be +counterintuitive. These fields show the source address used for VRRP +advertisements. Although VRRPv3 advertisements are always transmitted on the +macvlan interfaces, in the IPv4 case the source address is set to the primary +IPv4 address on the base interface, ``eth0`` in this case. This is a protocol +requirement, and IPv4 VRRP will not function unless the base interface has an +IPv4 address assigned. In the IPv6 case the link local of the macvlan interface +is used. + +If any misconfiguration errors are detected, VRRP for the misconfigured address +family will not come up and the configuration issue will be logged to FRR's +configured logging destination. + +Per the RFC, IPv4 and IPv6 virtual routers are independent of each other. For +instance, it is possible for the IPv4 router to be in Backup state while the +IPv6 router is in Master state; or for either to be completely inoperative +while the other is operative, etc. Instances sharing the same base interface +and VRID are shown together in the show output for conceptual convenience. + +To complete your VRRP deployment, configure other routers on the segment with +the exact same system and FRR configuration as shown above. Provided each +router receives the others' VRRP advertisements, the Master election protocol +will run, one Master will be elected, and the other routers will place their +macvlan interfaces into ``protodown on`` until Master fails or priority values +are changed to favor another router. + +Switching the protocol version to VRRPv2 is accomplished simply by changing +``version 3`` to ``version 2`` in the VRID configuration line. Note that VRRPv2 +does not support IPv6, so any IPv6 configuration will be rejected by FRR when +using VRRPv2. + +.. note:: + + All VRRP routers initially start in Backup state, and wait for the + calculated Master Down Interval to pass before they assume Master status. + This prevents downstream neighbor table churn if another router is already + Master with higher priority, meaning this box will ultimately assume Backup + status once the first advertisement is received. However, if the calculated + Master Down Interval is high and this router is configured such that it will + ultimately assume Master status, then it will take a while for this to + happen. This is a known issue. + + +All interface configuration commands are documented below. + +.. index:: [no] vrrp (1-255) [version (2-3)] +.. clicmd:: [no] vrrp (1-255) [version (2-3)] + + Create a VRRP router with the specified VRID on the interface. Optionally + specify the protocol version. If the protocol version is not specified, the + default is VRRPv3. + +.. index:: [no] vrrp (1-255) advertisement-interval (10-40950) +.. clicmd:: [no] vrrp (1-255) advertisement-interval (10-40950) + + Set the advertisement interval. This is the interval at which VRRP + advertisements will be sent. Values are given in milliseconds, but must be + multiples of 10, as VRRP itself uses centiseconds. + +.. index:: [no] vrrp (1-255) ip A.B.C.D +.. clicmd:: [no] vrrp (1-255) ip A.B.C.D + + Add an IPv4 address to the router. This address must already be configured + on the appropriate macvlan device. Adding an IP address to the router will + implicitly activate the router; see :clicmd:`[no] vrrp (1-255) shutdown` to + override this behavior. + +.. index:: [no] vrrp (1-255) ipv6 X:X::X:X +.. clicmd:: [no] vrrp (1-255) ipv6 X:X::X:X + + Add an IPv6 address to the router. This address must already be configured + on the appropriate macvlan device. Adding an IP address to the router will + implicitly activate the router; see :clicmd:`[no] vrrp (1-255) shutdown` to + override this behavior. + + This command will fail if the protocol version is set to VRRPv2, as VRRPv2 + does not support IPv6. + +.. index:: [no] vrrp (1-255) preempt +.. clicmd:: [no] vrrp (1-255) preempt + + Toggle preempt mode. When enabled, preemption allows Backup routers with + higher priority to take over Master status from the existing Master. Enabled + by default. + +.. index:: [no] vrrp (1-255) priority (1-254) +.. clicmd:: [no] vrrp (1-255) priority (1-254) + + Set the router priority. The router with the highest priority is elected as + the Master. If all routers in the VRRP virtual router are configured with + the same priority, the router with the highest primary IP address is elected + as the Master. Priority value 255 is reserved for the acting Master router. + +.. index:: [no] vrrp (1-255) shutdown +.. clicmd:: [no] vrrp (1-255) shutdown + + Place the router into administrative shutdown. VRRP will not activate for + this router until this command is removed with the ``no`` form. + +.. _vrrp-global-configuration: + +Global Configuration +-------------------- + +Show commands, global defaults and debugging configuration commands. + +.. index:: show vrrp [interface INTERFACE] [(1-255)] [json] +.. clicmd:: show vrrp [interface INTERFACE] [(1-255)] [json] + + Shows VRRP status for some or all configured VRRP routers. Specifying an + interface will only show routers configured on that interface. Specifying a + VRID will only show routers with that VRID. Specifying ``json`` will dump + each router state in a JSON array. + +.. index:: debug vrrp [{protocol|autoconfigure|packets|sockets|ndisc|arp|zebra}] +.. clicmd:: debug vrrp [{protocol|autoconfigure|packets|sockets|ndisc|arp|zebra}] + + Toggle debugging logs for some or all components of VRRP. + + protocol + Logs state changes, election protocol decisions, and interface status + changes. + + autoconfigure + Logs actions taken by the autoconfiguration procedures. See + :ref:`vrrp-autoconfiguration`. + + packets + Logs details of ingress and egress packets. Includes packet decodes and + hex dumps. + + sockets + Logs details of socket configuration and initialization. + + ndisc + Logs actions taken by the Neighbor Discovery component of VRRP. + + arp + Logs actions taken by the ARP component of VRRP. + + zebra + Logs communications with Zebra. + +.. index:: [no] vrrp default +.. clicmd:: [no] vrrp default + + Configure defaults for new VRRP routers. These values will not affect + already configured VRRP routers, but will be applied to newly configured + ones. + +.. _vrrp-autoconfiguration: + +Autoconfiguration +----------------- + +In light of the complicated configuration required on the base system before +VRRP can be enabled, FRR has the ability to automatically configure VRRP +sessions by inspecting the interfaces present on the system. Since it is quite +unlikely that macvlan devices with VRRP virtual MACs will exist on systems not +using VRRP, this can be a convenient shortcut to automatically generate FRR +configuration. + +After configuring the interfaces as described in +:ref:`vrrp-system-configuration`, and configuring any defaults you may want, +execute the following command: + +.. index:: [no] vrrp autoconfigure [version (2-3)] +.. clicmd:: [no] vrrp autoconfigure [version (2-3)] + + Generates VRRP configuration based on the interface configuration on the + base system. Any existing interfaces that are configured properly for VRRP - + i.e. have the correct MAC address, link local address (when required), IPv4 + and IPv6 addresses - are used to create a VRRP router on their parent + interfaces, with VRRP IPvX addresses taken from the addresses assigned to + the macvlan devices. The generated configuration appears in the output of + ``show run``, which can then be modified as needed and written to the config + file. The ``version`` parameter controls the protocol version; if using + VRRPv2, keep in mind that IPv6 is not supported and will not be configured. + +The following configuration is then generated for you: + +.. code-block:: frr + + interface eth0 + vrrp 5 + vrrp 5 ip 10.0.2.16 + vrrp 5 ipv6 2001:db8::370:7334 + +VRRP is automatically activated. Global defaults, if set, are applied. + +You can then edit this configuration with **vtysh** as needed, and commit it by +writing to the configuration file. diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index a7bf0c74da0c..eefc5802a2b3 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -23,9 +23,12 @@ Besides the common invocation options (:ref:`common-invocation-options`), the Runs in batch mode. *zebra* parses configuration file and terminates immediately. -.. option:: -k, --keep_kernel +.. option:: -K TIME, --graceful_restart TIME - When zebra starts up, don't delete old self inserted routes. + If this option is specified, the graceful restart time is TIME seconds. + Zebra, when started, will read in routes. Those routes that Zebra + identifies that it was the originator of will be swept in TIME seconds. + If no time is specified then we will sweep those routes immediately. .. option:: -r, --retain @@ -55,6 +58,12 @@ Besides the common invocation options (:ref:`common-invocation-options`), the .. seealso:: :ref:`zebra-vrf` +.. option:: -z , --socket + + If this option is supplied on the cli, the path to the zebra + control socket(zapi), is used. This option overrides a -N + option if handed to it on the cli. + .. option:: --v6-rr-semantics The linux kernel is receiving the ability to use the same route @@ -268,14 +277,6 @@ Link Parameters Commands for InterASv2 link in OSPF (RFC5392). Note that this option is not yet supported for ISIS (RFC5316). -.. index:: table TABLENO -.. clicmd:: table TABLENO - - Select the primary kernel routing table to be used. This only works for - kernels supporting multiple routing tables (like GNU/Linux 2.2.x and later). - After setting TABLENO with this command, static routes defined after this - are added to the specified table. - .. index:: ip nht resolve-via-default .. clicmd:: ip nht resolve-via-default @@ -693,6 +694,40 @@ replaces the information sent in the first message. If the connection to the FPM goes down for some reason, zebra sends the FPM a complete copy of the forwarding table(s) when it reconnects. +.. _zebra-dplane: + +Dataplane Commands +================== + +The zebra dataplane subsystem provides a framework for FIB +programming. Zebra uses the dataplane to program the local kernel as +it makes changes to objects such as IP routes, MPLS LSPs, and +interface IP addresses. The dataplane runs in its own pthread, in +order to off-load work from the main zebra pthread. + + +.. index:: show zebra dplane [detailed] +.. clicmd:: show zebra dplane [detailed] + + Display statistics about the updates and events passing through the + dataplane subsystem. + + +.. index:: show zebra dplane providers +.. clicmd:: show zebra dplane providers + + Display information about the running dataplane plugins that are + providing updates to a FIB. By default, the local kernel plugin is + present. + + +.. index:: zebra dplane limit [NUMBER] +.. clicmd:: zebra dplane limit [NUMBER] + + Configure the limit on the number of pending updates that are + waiting to be processed by the dataplane pthread. + + zebra Terminal Mode Commands ============================ @@ -746,6 +781,22 @@ zebra Terminal Mode Commands Display various statistics related to the installation and deletion of routes, neighbor updates, and LSP's into the kernel. +.. index:: show zebra client [summary] +.. clicmd:: show zebra client [summary] + + Display statistics about clients that are connected to zebra. This is + useful for debugging and seeing how much data is being passed between + zebra and it's clients. If the summary form of the command is choosen + a table is displayed with shortened information. + +.. index:: show zebra router table summary +.. clicmd:: show zebra router table summary + + Display summarized data about tables created, their afi/safi/tableid + and how many routes each table contains. Please note this is the + total number of route nodes in the table. Which will be higher than + the actual number of routes that are held. + .. index:: show zebra fpm stats .. clicmd:: show zebra fpm stats diff --git a/eigrpd/eigrp_routemap.c b/eigrpd/eigrp_routemap.c index ee8d5f758270..f080ba48769f 100644 --- a/eigrpd/eigrp_routemap.c +++ b/eigrpd/eigrp_routemap.c @@ -136,7 +136,7 @@ static int eigrp_route_match_add(struct vty *vty, struct route_map_index *index, const char *command, const char *arg) { int ret; - ret = route_map_add_match(index, command, arg); + ret = route_map_add_match(index, command, arg, type); switch (ret) { case RMAP_RULE_MISSING: vty_out(vty, "%% Can't find rule.\n"); @@ -764,7 +764,7 @@ static struct route_map_rule_cmd route_set_tag_cmd = { DEFUN (match_metric, match_metric_cmd, - "match metric <0-4294967295>", + "match metric (0-4294967295)", MATCH_STR "Match metric of route\n" "Metric value\n") @@ -787,7 +787,7 @@ DEFUN (no_match_metric, } ALIAS(no_match_metric, no_match_metric_val_cmd, - "no match metric <0-4294967295>", NO_STR MATCH_STR + "no match metric (0-4294967295)", NO_STR MATCH_STR "Match metric of route\n" "Metric value\n") @@ -822,7 +822,7 @@ ALIAS(no_match_interface, no_match_interface_val_cmd, "no match interface WORD", DEFUN (match_ip_next_hop, match_ip_next_hop_cmd, - "match ip next-hop (<1-199>|<1300-2699>|WORD)", + "match ip next-hop ((1-199)|(1300-2699)|WORD)", MATCH_STR IP_STR "Match next-hop address of route\n" @@ -850,7 +850,7 @@ DEFUN (no_match_ip_next_hop, } ALIAS(no_match_ip_next_hop, no_match_ip_next_hop_val_cmd, - "no match ip next-hop (<1-199>|<1300-2699>|WORD)", NO_STR MATCH_STR IP_STR + "no match ip next-hop ((1-199)|(1300-2699)|WORD)", NO_STR MATCH_STR IP_STR "Match next-hop address of route\n" "IP access-list number\n" "IP access-list number (expanded range)\n" @@ -895,7 +895,7 @@ ALIAS(no_match_ip_next_hop_prefix_list, DEFUN (match_ip_address, match_ip_address_cmd, - "match ip address (<1-199>|<1300-2699>|WORD)", + "match ip address ((1-199)|(1300-2699)|WORD)", MATCH_STR IP_STR "Match address of route\n" @@ -922,7 +922,7 @@ DEFUN (no_match_ip_address, } ALIAS(no_match_ip_address, no_match_ip_address_val_cmd, - "no match ip address (<1-199>|<1300-2699>|WORD)", NO_STR MATCH_STR IP_STR + "no match ip address ((1-199)|(1300-2699)|WORD)", NO_STR MATCH_STR IP_STR "Match address of route\n" "IP access-list number\n" "IP access-list number (expanded range)\n" @@ -966,7 +966,7 @@ ALIAS(no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_val_cmd, DEFUN (match_tag, match_tag_cmd, - "match tag <0-65535>", + "match tag (0-65535)", MATCH_STR "Match tag of route\n" "Metric value\n") @@ -987,7 +987,7 @@ DEFUN (no_match_tag, return eigrp_route_match_delete(vty, vty->index, "tag", argv[0]); } -ALIAS(no_match_tag, no_match_tag_val_cmd, "no match tag <0-65535>", +ALIAS(no_match_tag, no_match_tag_val_cmd, "no match tag (0-65535)", NO_STR MATCH_STR "Match tag of route\n" "Metric value\n") @@ -996,7 +996,7 @@ ALIAS(no_match_tag, no_match_tag_val_cmd, "no match tag <0-65535>", DEFUN (set_metric, set_metric_cmd, - "set metric <0-4294967295>", + "set metric (0-4294967295)", SET_STR "Metric value for destination routing protocol\n" "Metric value\n") @@ -1022,7 +1022,7 @@ DEFUN (no_set_metric, } ALIAS(no_set_metric, no_set_metric_val_cmd, - "no set metric (<0-4294967295>|<+/-metric>)", NO_STR SET_STR + "no set metric ((0-4294967295)|<+/-metric>)", NO_STR SET_STR "Metric value for destination routing protocol\n" "Metric value\n" "Add or subtract metric\n") @@ -1069,7 +1069,7 @@ ALIAS(no_set_ip_nexthop, no_set_ip_nexthop_val_cmd, DEFUN (set_tag, set_tag_cmd, - "set tag <0-65535>", + "set tag (0-65535)", SET_STR "Tag value for routing protocol\n" "Tag value\n") @@ -1090,7 +1090,7 @@ DEFUN (no_set_tag, return eigrp_route_set_delete(vty, vty->index, "tag", argv[0]); } -ALIAS(no_set_tag, no_set_tag_val_cmd, "no set tag <0-65535>", NO_STR SET_STR +ALIAS(no_set_tag, no_set_tag_val_cmd, "no set tag (0-65535)", NO_STR SET_STR "Tag value for routing protocol\n" "Tag value\n") diff --git a/eigrpd/eigrp_vty.c b/eigrpd/eigrp_vty.c index fc5bdbdbc5f5..1a1634ca912b 100644 --- a/eigrpd/eigrp_vty.c +++ b/eigrpd/eigrp_vty.c @@ -370,7 +370,7 @@ DEFUN (eigrp_metric_weights, DEFUN (no_eigrp_metric_weights, no_eigrp_metric_weights_cmd, - "no metric weights <0-255> <0-255> <0-255> <0-255> <0-255>", + "no metric weights (0-255) (0-255) (0-255) (0-255) (0-255)", NO_STR "Modify metrics and parameters for advertisement\n" "Modify metric coefficients\n" @@ -1209,7 +1209,7 @@ DEFUN (eigrp_maximum_paths, DEFUN (no_eigrp_maximum_paths, no_eigrp_maximum_paths_cmd, - "no maximum-paths <1-32>", + "no maximum-paths (1-32)", NO_STR "Forward packets over multiple paths\n" "Number of paths\n") diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c index dc1ae675b04c..0a74e86263a3 100644 --- a/eigrpd/eigrp_zebra.c +++ b/eigrpd/eigrp_zebra.c @@ -53,21 +53,15 @@ #include "eigrpd/eigrp_topology.h" #include "eigrpd/eigrp_fsm.h" -static int eigrp_interface_add(int, struct zclient *, zebra_size_t, vrf_id_t); -static int eigrp_interface_delete(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int eigrp_interface_address_add(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); -static int eigrp_interface_address_delete(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); -static int eigrp_interface_state_up(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); -static int eigrp_interface_state_down(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); +static int eigrp_interface_add(ZAPI_CALLBACK_ARGS); +static int eigrp_interface_delete(ZAPI_CALLBACK_ARGS); +static int eigrp_interface_address_add(ZAPI_CALLBACK_ARGS); +static int eigrp_interface_address_delete(ZAPI_CALLBACK_ARGS); +static int eigrp_interface_state_up(ZAPI_CALLBACK_ARGS); +static int eigrp_interface_state_down(ZAPI_CALLBACK_ARGS); static struct interface *zebra_interface_if_lookup(struct stream *); -static int eigrp_zebra_read_route(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); +static int eigrp_zebra_read_route(ZAPI_CALLBACK_ARGS); /* Zebra structure to hold current status. */ struct zclient *zclient = NULL; @@ -77,8 +71,7 @@ extern struct thread_master *master; struct in_addr router_id_zebra; /* Router-id update message from zebra. */ -static int eigrp_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct eigrp *eigrp; struct prefix router_id; @@ -94,8 +87,7 @@ static int eigrp_router_id_update_zebra(int command, struct zclient *zclient, return 0; } -static int eigrp_zebra_route_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_zebra_route_notify_owner(ZAPI_CALLBACK_ARGS) { struct prefix p; enum zapi_route_notify_owner note; @@ -134,8 +126,7 @@ void eigrp_zebra_init(void) /* Zebra route add and delete treatment. */ -static int eigrp_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; struct eigrp *eigrp; @@ -150,9 +141,9 @@ static int eigrp_zebra_read_route(int command, struct zclient *zclient, if (eigrp == NULL) return 0; - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { - } else /* if (command == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ + } else /* if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ { } @@ -160,8 +151,7 @@ static int eigrp_zebra_read_route(int command, struct zclient *zclient, } /* Inteface addition message from zebra. */ -static int eigrp_interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct eigrp_interface *ei; @@ -180,8 +170,7 @@ static int eigrp_interface_add(int command, struct zclient *zclient, return 0; } -static int eigrp_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -211,12 +200,11 @@ static int eigrp_interface_delete(int command, struct zclient *zclient, return 0; } -static int eigrp_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -233,14 +221,13 @@ static int eigrp_interface_address_add(int command, struct zclient *zclient, return 0; } -static int eigrp_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; struct interface *ifp; struct eigrp_interface *ei; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -266,8 +253,7 @@ static int eigrp_interface_address_delete(int command, struct zclient *zclient, return 0; } -static int eigrp_interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_state_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -323,8 +309,7 @@ static int eigrp_interface_state_up(int command, struct zclient *zclient, return 0; } -static int eigrp_interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_state_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; diff --git a/grpc/Makefile b/grpc/Makefile new file mode 100644 index 000000000000..8748286629a1 --- /dev/null +++ b/grpc/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. grpc/libfrrgrpc_pb.la +%: ALWAYS + @$(MAKE) -s -C .. grpc/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/grpc/frr-northbound.proto b/grpc/frr-northbound.proto new file mode 100644 index 000000000000..d070d715e804 --- /dev/null +++ b/grpc/frr-northbound.proto @@ -0,0 +1,412 @@ +// +// Copyright (C) 2019 NetDEF, Inc. +// Renato Westphal +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; see the file COPYING; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// + +syntax = "proto3"; + +package frr; + +// Service specification for the FRR northbound interface. +service Northbound { + // Retrieve the capabilities supported by the target. + rpc GetCapabilities(GetCapabilitiesRequest) returns (GetCapabilitiesResponse) {} + + // Retrieve configuration data, state data or both from the target. + rpc Get(GetRequest) returns (stream GetResponse) {} + + // Create a new candidate configuration and return a reference to it. The + // created candidate is a copy of the running configuration. + rpc CreateCandidate(CreateCandidateRequest) returns (CreateCandidateResponse) {} + + // Delete a candidate configuration. + rpc DeleteCandidate(DeleteCandidateRequest) returns (DeleteCandidateResponse) {} + + // Update a candidate configuration by rebasing the changes on top of the + // latest running configuration. Resolve conflicts automatically by giving + // preference to the changes done in the candidate configuration. + rpc UpdateCandidate(UpdateCandidateRequest) returns (UpdateCandidateResponse) {} + + // Edit a candidate configuration. All changes are discarded if any error + // happens. + rpc EditCandidate(EditCandidateRequest) returns (EditCandidateResponse) {} + + // Load configuration data into a candidate configuration. Both merge and + // replace semantics are supported. + rpc LoadToCandidate(LoadToCandidateRequest) returns (LoadToCandidateResponse) {} + + // Create a new configuration transaction using a two-phase commit protocol. + rpc Commit(CommitRequest) returns (CommitResponse) {} + + // List the metadata of all configuration transactions recorded in the + // transactions database. + rpc ListTransactions(ListTransactionsRequest) returns (stream ListTransactionsResponse) {} + + // Fetch a configuration (identified by its transaction ID) from the + // transactions database. + rpc GetTransaction(GetTransactionRequest) returns (GetTransactionResponse) {} + + // Lock the running configuration, preventing other users from changing it. + rpc LockConfig(LockConfigRequest) returns (LockConfigResponse) {} + + // Unlock the running configuration. + rpc UnlockConfig(UnlockConfigRequest) returns (UnlockConfigResponse) {} + + // Execute a YANG RPC. + rpc Execute(ExecuteRequest) returns (ExecuteResponse) {} +} + +// ----------------------- Parameters and return types ------------------------- + +// +// RPC: GetCapabilities() +// +message GetCapabilitiesRequest { + // Empty. +} + +message GetCapabilitiesResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + + // FRR version. + string frr_version = 1; + + // Indicates whether FRR was compiled with support for configuration + // rollbacks or not (--enable-config-rollbacks). + bool rollback_support = 2; + + // Supported schema modules. + repeated ModuleData supported_modules = 3; + + // Supported encodings. + repeated Encoding supported_encodings = 4; +} + +// +// RPC: Get() +// +message GetRequest { + // Type of elements within the data tree. + enum DataType { + // All data elements. + ALL = 0; + + // Config elements. + CONFIG = 1; + + // State elements. + STATE = 2; + } + + // The type of data being requested. + DataType type = 1; + + // Encoding to be used. + Encoding encoding = 2; + + // Include implicit default nodes. + bool with_defaults = 3; + + // Paths requested by the client. + repeated string path = 4; +} + +message GetResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::INVALID_ARGUMENT: Invalid YANG data path. + + // Timestamp in nanoseconds since Epoch. + int64 timestamp = 1; + + // The requested data. + DataTree data = 2; +} + +// +// RPC: CreateCandidate() +// +message CreateCandidateRequest { + // Empty. +} + +message CreateCandidateResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::RESOURCE_EXHAUSTED: can't create candidate + // configuration. + + // Handle to the new created candidate configuration. + uint32 candidate_id = 1; +} + +// +// RPC: DeleteCandidate() +// +message DeleteCandidateRequest { + // Candidate configuration to delete. + uint32 candidate_id = 1; +} + +message DeleteCandidateResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::NOT_FOUND: Candidate wasn't found. +} + +// +// RPC: UpdateCandidate() +// +message UpdateCandidateRequest { + // Candidate configuration to update. + uint32 candidate_id = 1; +} + +message UpdateCandidateResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::NOT_FOUND: Candidate wasn't found. +} + +// +// RPC: EditCandidate() +// +message EditCandidateRequest { + // Candidate configuration that is going to be edited. + uint32 candidate_id = 1; + + // Data elements to be created or updated. + repeated PathValue update = 2; + + // Paths to be deleted from the data tree. + repeated PathValue delete = 3; +} + +message EditCandidateResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::NOT_FOUND: Candidate wasn't found. + // - grpc::StatusCode::INVALID_ARGUMENT: An error occurred while editing the + // candidate configuration. +} + +// +// RPC: LoadToCandidate() +// +message LoadToCandidateRequest { + enum LoadType { + // Merge the data tree into the candidate configuration. + MERGE = 0; + + // Replace the candidate configuration by the provided data tree. + REPLACE = 1; + } + + // Candidate configuration that is going to be edited. + uint32 candidate_id = 1; + + // Load operation to apply. + LoadType type = 2; + + // Configuration data. + DataTree config = 3; +} + +message LoadToCandidateResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::INVALID_ARGUMENT: An error occurred while performing + // the load operation. +} + +// +// RPC: Commit() +// +message CommitRequest { + enum Phase { + // Validate if the configuration changes are valid (phase 0). + VALIDATE = 0; + + // Prepare resources to apply the configuration changes (phase 1). + PREPARE = 1; + + // Release previously allocated resources (phase 2). + ABORT = 2; + + // Apply the configuration changes (phase 2). + APPLY = 3; + + // All of the above (VALIDATE + PREPARE + ABORT/APPLY). + // + // This option can't be used to implement network-wide transactions, + // since they require the manager entity to take into account the results + // of the preparation phase of multiple managed devices. + ALL = 4; + } + + // Candidate configuration that is going to be committed. + uint32 candidate_id = 1; + + // Transaction phase. + Phase phase = 2; + + // Assign a comment to this commit. + string comment = 3; +} + +message CommitResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::FAILED_PRECONDITION: misuse of the two-phase commit + // protocol. + // - grpc::StatusCode::INVALID_ARGUMENT: Validation error. + // - grpc::StatusCode::RESOURCE_EXHAUSTED: Failure to allocate resource. + + // ID of the created configuration transaction (when the phase is APPLY + // or ALL). + uint32 transaction_id = 1; +} + +// +// RPC: ListTransactions() +// +message ListTransactionsRequest { + // Empty. +} + +message ListTransactionsResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + + // Transaction ID. + uint32 id = 1; + + // Client that committed the transaction. + string client = 2; + + // Date and time the transaction was committed. + string date = 3; + + // Comment assigned to the transaction. + string comment = 4; +} + +// +// RPC: GetTransaction() +// +message GetTransactionRequest { + // Transaction to retrieve. + uint32 transaction_id = 1; + + // Encoding to be used. + Encoding encoding = 2; + + // Include implicit default nodes. + bool with_defaults = 3; +} + +message GetTransactionResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::NOT_FOUND: Transaction wasn't found in the transactions + // database. + + DataTree config = 1; +} + +// +// RPC: LockConfig() +// +message LockConfigRequest { + // Empty. +} + +message LockConfigResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::FAILED_PRECONDITION: Running configuration is + // locked already. +} + +// +// RPC: UnlockConfig() +// +message UnlockConfigRequest { + // Empty. +} + +message UnlockConfigResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::FAILED_PRECONDITION: Running configuration isn't + // locked. +} + +// +// RPC: Execute() +// +message ExecuteRequest { + // Path of the YANG RPC or YANG Action. + string path = 1; + + // Input parameters. + repeated PathValue input = 2; +} + +message ExecuteResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + + // Output parameters. + repeated PathValue output = 1; +} + +// -------------------------------- Definitions -------------------------------- + +// YANG module. +message ModuleData { + // Name of the YANG module; + string name = 1; + + // Organization publishing the module. + string organization = 2; + + // Latest revision of the module; + string revision = 3; +} + +// Supported encodings for YANG instance data. +enum Encoding { + JSON = 0; + XML = 1; +} + +// Path-value pair representing a data element. +message PathValue { + // YANG data path. + string path = 1; + + // Data value. + string value = 2; +} + +// YANG instance data. +message DataTree { + Encoding encoding = 1; + string data = 2; +} diff --git a/grpc/subdir.am b/grpc/subdir.am new file mode 100644 index 000000000000..3fb163fccff2 --- /dev/null +++ b/grpc/subdir.am @@ -0,0 +1,30 @@ +if GRPC +lib_LTLIBRARIES += grpc/libfrrgrpc_pb.la +endif + +grpc_libfrrgrpc_pb_la_LDFLAGS = -version-info 0:0:0 +grpc_libfrrgrpc_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(GRPC_CXXFLAGS) + +nodist_grpc_libfrrgrpc_pb_la_SOURCES = \ + grpc/frr-northbound.pb.cc \ + grpc/frr-northbound.grpc.pb.cc \ + # end + +CLEANFILES += \ + grpc/frr-northbound.pb.cc \ + grpc/frr-northbound.pb.h \ + grpc/frr-northbound.grpc.pb.cc \ + grpc/frr-northbound.grpc.pb.h \ + # end + +EXTRA_DIST += grpc/frr-northbound.proto + +AM_V_PROTOC = $(am__v_PROTOC_$(V)) +am__v_PROTOC_ = $(am__v_PROTOC_$(AM_DEFAULT_VERBOSITY)) +am__v_PROTOC_0 = @echo " PROTOC" $@; +am__v_PROTOC_1 = + +.proto.pb.cc: + $(AM_V_PROTOC)$(PROTOC) -I$(top_srcdir) --cpp_out=$(top_srcdir) $(top_srcdir)/$^ +.proto.grpc.pb.cc: + $(AM_V_PROTOC)$(PROTOC) -I$(top_srcdir) --grpc_out=$(top_srcdir) --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` $(top_srcdir)/$^ diff --git a/isisd/dict.c b/isisd/dict.c deleted file mode 100644 index d91f05d25442..000000000000 --- a/isisd/dict.c +++ /dev/null @@ -1,1510 +0,0 @@ -/* - * Dictionary Abstract Data Type - * Copyright (C) 1997 Kaz Kylheku - * - * Free Software License: - * - * All rights are reserved by the author, with the following exceptions: - * Permission is granted to freely reproduce and distribute this software, - * possibly in exchange for a fee, provided that this copyright notice appears - * intact. Permission is also granted to adapt this software to produce - * derivative works, as long as the modified versions carry this copyright - * notice and additional notices stating that the work has been modified. - * This source code may be translated into executable form and incorporated - * into proprietary software; there is no requirement for such software to - * contain a copyright notice related to this source. - */ - -#include "zebra.h" -#include "zassert.h" -#include "memory.h" -#include "isis_memory.h" -#include "dict.h" - -/* - * These macros provide short convenient names for structure members, - * which are embellished with dict_ prefixes so that they are - * properly confined to the documented namespace. It's legal for a - * program which uses dict to define, for instance, a macro called ``parent''. - * Such a macro would interfere with the dnode_t struct definition. - * In general, highly portable and reusable C modules which expose their - * structures need to confine structure member names to well-defined spaces. - * The resulting identifiers aren't necessarily convenient to use, nor - * readable, in the implementation, however! - */ - -#define left dict_left -#define right dict_right -#define parent dict_parent -#define color dict_color -#define key dict_key -#define data dict_data - -#define nilnode dict_nilnode -#define nodecount dict_nodecount -#define maxcount dict_maxcount -#define compare dict_compare -#define allocnode dict_allocnode -#define freenode dict_freenode -#define context dict_context -#define dupes dict_dupes - -#define dictptr dict_dictptr - -#define dict_root(D) ((D)->nilnode.left) -#define dict_nil(D) (&(D)->nilnode) -#define DICT_DEPTH_MAX 64 - -static dnode_t *dnode_alloc(void *context); -static void dnode_free(dnode_t *node, void *context); - -/* - * Perform a ``left rotation'' adjustment on the tree. The given node P and - * its right child C are rearranged so that the P instead becomes the left - * child of C. The left subtree of C is inherited as the new right subtree - * for P. The ordering of the keys within the tree is thus preserved. - */ - -static void rotate_left(dnode_t *upper) -{ - dnode_t *lower, *lowleft, *upparent; - - lower = upper->right; - upper->right = lowleft = lower->left; - lowleft->parent = upper; - - lower->parent = upparent = upper->parent; - - /* don't need to check for root node here because root->parent is - the sentinel nil node, and root->parent->left points back to root */ - - if (upper == upparent->left) { - upparent->left = lower; - } else { - assert(upper == upparent->right); - upparent->right = lower; - } - - lower->left = upper; - upper->parent = lower; -} - -/* - * This operation is the ``mirror'' image of rotate_left. It is - * the same procedure, but with left and right interchanged. - */ - -static void rotate_right(dnode_t *upper) -{ - dnode_t *lower, *lowright, *upparent; - - lower = upper->left; - upper->left = lowright = lower->right; - lowright->parent = upper; - - lower->parent = upparent = upper->parent; - - if (upper == upparent->right) { - upparent->right = lower; - } else { - assert(upper == upparent->left); - upparent->left = lower; - } - - lower->right = upper; - upper->parent = lower; -} - -/* - * Do a postorder traversal of the tree rooted at the specified - * node and free everything under it. Used by dict_free(). - */ - -static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil) -{ - if (node == nil) - return; - free_nodes(dict, node->left, nil); - free_nodes(dict, node->right, nil); - dict->freenode(node, dict->context); -} - -/* - * This procedure performs a verification that the given subtree is a binary - * search tree. It performs an inorder traversal of the tree using the - * dict_next() successor function, verifying that the key of each node is - * strictly lower than that of its successor, if duplicates are not allowed, - * or lower or equal if duplicates are allowed. This function is used for - * debugging purposes. - */ - -static int verify_bintree(dict_t *dict) -{ - dnode_t *first, *next; - - first = dict_first(dict); - - if (dict->dupes) { - while (first && (next = dict_next(dict, first))) { - if (dict->compare(first->key, next->key) > 0) - return 0; - first = next; - } - } else { - while (first && (next = dict_next(dict, first))) { - if (dict->compare(first->key, next->key) >= 0) - return 0; - first = next; - } - } - return 1; -} - - -/* - * This function recursively verifies that the given binary subtree satisfies - * three of the red black properties. It checks that every red node has only - * black children. It makes sure that each node is either red or black. And it - * checks that every path has the same count of black nodes from root to leaf. - * It returns the blackheight of the given subtree; this allows blackheights to - * be computed recursively and compared for left and right siblings for - * mismatches. It does not check for every nil node being black, because there - * is only one sentinel nil node. The return value of this function is the - * black height of the subtree rooted at the node ``root'', or zero if the - * subtree is not red-black. - */ - -#ifdef EXTREME_DICT_DEBUG -static unsigned int verify_redblack(dnode_t *nil, dnode_t *root) -{ - unsigned height_left, height_right; - - if (root != nil) { - height_left = verify_redblack(nil, root->left); - height_right = verify_redblack(nil, root->right); - if (height_left == 0 || height_right == 0) - return 0; - if (height_left != height_right) - return 0; - if (root->color == dnode_red) { - if (root->left->color != dnode_black) - return 0; - if (root->right->color != dnode_black) - return 0; - return height_left; - } - if (root->color != dnode_black) - return 0; - return height_left + 1; - } - return 1; -} -#endif - -/* - * Compute the actual count of nodes by traversing the tree and - * return it. This could be compared against the stored count to - * detect a mismatch. - */ - -#ifdef EXTREME_DICT_DEBUG -static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root) -{ - if (root == nil) - return 0; - else - return 1 + verify_node_count(nil, root->left) - + verify_node_count(nil, root->right); -} -#endif - -/* - * Verify that the tree contains the given node. This is done by - * traversing all of the nodes and comparing their pointers to the - * given pointer. Returns 1 if the node is found, otherwise - * returns zero. It is intended for debugging purposes. - */ - -static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node) -{ - if (root != nil) { - return root == node - || verify_dict_has_node(nil, root->left, node) - || verify_dict_has_node(nil, root->right, node); - } - return 0; -} - - -/* - * Dynamically allocate and initialize a dictionary object. - */ - -dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp) -{ - dict_t *new = XCALLOC(MTYPE_ISIS_DICT, sizeof(dict_t)); - - new->compare = comp; - new->allocnode = dnode_alloc; - new->freenode = dnode_free; - new->context = NULL; - new->nodecount = 0; - new->maxcount = maxcount; - new->nilnode.left = &new->nilnode; - new->nilnode.right = &new->nilnode; - new->nilnode.parent = &new->nilnode; - new->nilnode.color = dnode_black; - new->dupes = 0; - - return new; -} - -/* - * Select a different set of node allocator routines. - */ - -void dict_set_allocator(dict_t *dict, dnode_alloc_t al, dnode_free_t fr, - void *context) -{ - assert(dict_count(dict) == 0); - assert((al == NULL && fr == NULL) || (al != NULL && fr != NULL)); - - dict->allocnode = al ? al : dnode_alloc; - dict->freenode = fr ? fr : dnode_free; - dict->context = context; -} - -/* - * Free a dynamically allocated dictionary object. Removing the nodes - * from the tree before deleting it is required. - */ - -void dict_destroy(dict_t *dict) -{ - assert(dict_isempty(dict)); - XFREE(MTYPE_ISIS_DICT, dict); -} - -/* - * Free all the nodes in the dictionary by using the dictionary's - * installed free routine. The dictionary is emptied. - */ - -void dict_free_nodes(dict_t *dict) -{ - dnode_t *nil = dict_nil(dict), *root = dict_root(dict); - free_nodes(dict, root, nil); - dict->nodecount = 0; - dict->nilnode.left = &dict->nilnode; - dict->nilnode.right = &dict->nilnode; -} - -/* - * Obsolescent function, equivalent to dict_free_nodes - */ - -void dict_free(dict_t *dict) -{ - dict_free_nodes(dict); -} - -/* - * Initialize a user-supplied dictionary object. - */ - -dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp) -{ - dict->compare = comp; - dict->allocnode = dnode_alloc; - dict->freenode = dnode_free; - dict->context = NULL; - dict->nodecount = 0; - dict->maxcount = maxcount; - dict->nilnode.left = &dict->nilnode; - dict->nilnode.right = &dict->nilnode; - dict->nilnode.parent = &dict->nilnode; - dict->nilnode.color = dnode_black; - dict->dupes = 0; - return dict; -} - -/* - * Initialize a dictionary in the likeness of another dictionary - */ - -void dict_init_like(dict_t *dict, const dict_t *template) -{ - dict->compare = template->compare; - dict->allocnode = template->allocnode; - dict->freenode = template->freenode; - dict->context = template->context; - dict->nodecount = 0; - dict->maxcount = template->maxcount; - dict->nilnode.left = &dict->nilnode; - dict->nilnode.right = &dict->nilnode; - dict->nilnode.parent = &dict->nilnode; - dict->nilnode.color = dnode_black; - dict->dupes = template->dupes; - - assert(dict_similar(dict, template)); -} - -/* - * Remove all nodes from the dictionary (without freeing them in any way). - */ - -static void dict_clear(dict_t *dict) -{ - dict->nodecount = 0; - dict->nilnode.left = &dict->nilnode; - dict->nilnode.right = &dict->nilnode; - dict->nilnode.parent = &dict->nilnode; - assert(dict->nilnode.color == dnode_black); -} - - -/* - * Verify the integrity of the dictionary structure. This is provided for - * debugging purposes, and should be placed in assert statements. Just because - * this function succeeds doesn't mean that the tree is not corrupt. Certain - * corruptions in the tree may simply cause undefined behavior. - */ - -int dict_verify(dict_t *dict) -{ -#ifdef EXTREME_DICT_DEBUG - dnode_t *nil = dict_nil(dict), *root = dict_root(dict); - - /* check that the sentinel node and root node are black */ - if (root->color != dnode_black) - return 0; - if (nil->color != dnode_black) - return 0; - if (nil->right != nil) - return 0; - /* nil->left is the root node; check that its parent pointer is nil */ - if (nil->left->parent != nil) - return 0; - /* perform a weak test that the tree is a binary search tree */ - if (!verify_bintree(dict)) - return 0; - /* verify that the tree is a red-black tree */ - if (!verify_redblack(nil, root)) - return 0; - if (verify_node_count(nil, root) != dict_count(dict)) - return 0; -#endif - return 1; -} - -/* - * Determine whether two dictionaries are similar: have the same comparison and - * allocator functions, and same status as to whether duplicates are allowed. - */ - -int dict_similar(const dict_t *left, const dict_t *right) -{ - if (left->compare != right->compare) - return 0; - - if (left->allocnode != right->allocnode) - return 0; - - if (left->freenode != right->freenode) - return 0; - - if (left->context != right->context) - return 0; - - if (left->dupes != right->dupes) - return 0; - - return 1; -} - -/* - * Locate a node in the dictionary having the given key. - * If the node is not found, a null a pointer is returned (rather than - * a pointer that dictionary's nil sentinel node), otherwise a pointer to the - * located node is returned. - */ - -dnode_t *dict_lookup(dict_t *dict, const void *key) -{ - dnode_t *root = dict_root(dict); - dnode_t *nil = dict_nil(dict); - dnode_t *saved; - int result; - - /* simple binary search adapted for trees that contain duplicate keys */ - - while (root != nil) { - result = dict->compare(key, root->key); - if (result < 0) - root = root->left; - else if (result > 0) - root = root->right; - else { - if (!dict->dupes) { /* no duplicates, return match - */ - return root; - } else { /* could be dupes, find leftmost one */ - do { - saved = root; - root = root->left; - while (root != nil - && dict->compare(key, root->key)) - root = root->right; - } while (root != nil); - return saved; - } - } - } - - return NULL; -} - -/* - * Look for the node corresponding to the lowest key that is equal to or - * greater than the given key. If there is no such node, return null. - */ - -dnode_t *dict_lower_bound(dict_t *dict, const void *key) -{ - dnode_t *root = dict_root(dict); - dnode_t *nil = dict_nil(dict); - dnode_t *tentative = 0; - - while (root != nil) { - int result = dict->compare(key, root->key); - - if (result > 0) { - root = root->right; - } else if (result < 0) { - tentative = root; - root = root->left; - } else { - if (!dict->dupes) { - return root; - } else { - tentative = root; - root = root->left; - } - } - } - - return tentative; -} - -/* - * Look for the node corresponding to the greatest key that is equal to or - * lower than the given key. If there is no such node, return null. - */ - -dnode_t *dict_upper_bound(dict_t *dict, const void *key) -{ - dnode_t *root = dict_root(dict); - dnode_t *nil = dict_nil(dict); - dnode_t *tentative = 0; - - while (root != nil) { - int result = dict->compare(key, root->key); - - if (result < 0) { - root = root->left; - } else if (result > 0) { - tentative = root; - root = root->right; - } else { - if (!dict->dupes) { - return root; - } else { - tentative = root; - root = root->right; - } - } - } - - return tentative; -} - -/* - * Insert a node into the dictionary. The node should have been - * initialized with a data field. All other fields are ignored. - * The behavior is undefined if the user attempts to insert into - * a dictionary that is already full (for which the dict_isfull() - * function returns true). - */ - -void dict_insert(dict_t *dict, dnode_t *node, const void *key) -{ - dnode_t *where = dict_root(dict), *nil = dict_nil(dict); - dnode_t *parent = nil, *uncle, *grandpa; - int result = -1; - - node->key = key; - - assert(!dict_isfull(dict)); - assert(!dict_contains(dict, node)); - assert(!dnode_is_in_a_dict(node)); - - /* basic binary tree insert */ - - while (where != nil) { - parent = where; - result = dict->compare(key, where->key); - /* trap attempts at duplicate key insertion unless it's - * explicitly allowed */ - assert(dict->dupes || result != 0); - if (result < 0) - where = where->left; - else - where = where->right; - } - - assert(where == nil); - - if (result < 0) - parent->left = node; - else - parent->right = node; - - node->parent = parent; - node->left = nil; - node->right = nil; - - dict->nodecount++; - - /* red black adjustments */ - - node->color = dnode_red; - - while (parent->color == dnode_red) { - grandpa = parent->parent; - if (parent == grandpa->left) { - uncle = grandpa->right; - if (uncle->color - == dnode_red) { /* red parent, red uncle */ - parent->color = dnode_black; - uncle->color = dnode_black; - grandpa->color = dnode_red; - node = grandpa; - parent = grandpa->parent; - } else { /* red parent, black uncle */ - if (node == parent->right) { - rotate_left(parent); - parent = node; - assert(grandpa == parent->parent); - /* rotation between parent and child - * preserves grandpa */ - } - parent->color = dnode_black; - grandpa->color = dnode_red; - rotate_right(grandpa); - break; - } - } else { /* symmetric cases: parent == parent->parent->right */ - uncle = grandpa->left; - if (uncle->color == dnode_red) { - parent->color = dnode_black; - uncle->color = dnode_black; - grandpa->color = dnode_red; - node = grandpa; - parent = grandpa->parent; - } else { - if (node == parent->left) { - rotate_right(parent); - parent = node; - assert(grandpa == parent->parent); - } - parent->color = dnode_black; - grandpa->color = dnode_red; - rotate_left(grandpa); - break; - } - } - } - - dict_root(dict)->color = dnode_black; - - assert(dict_verify(dict)); -} - -/* - * Delete the given node from the dictionary. If the given node does not belong - * to the given dictionary, undefined behavior results. A pointer to the - * deleted node is returned. - */ - -dnode_t *dict_delete(dict_t *dict, dnode_t *delete) -{ - dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent; - - /* basic deletion */ - - assert(!dict_isempty(dict)); - assert(dict_contains(dict, delete)); - - /* - * If the node being deleted has two children, then we replace it with - * its - * successor (i.e. the leftmost node in the right subtree.) By doing - * this, - * we avoid the traditional algorithm under which the successor's key - * and - * value *only* move to the deleted node and the successor is spliced - * out - * from the tree. We cannot use this approach because the user may hold - * pointers to the successor, or nodes may be inextricably tied to some - * other structures by way of embedding, etc. So we must splice out the - * node we are given, not some other node, and must not move contents - * from - * one node to another behind the user's back. - */ - - if (delete->left != nil && delete->right != nil) { - dnode_t *next = dict_next(dict, delete); - assert(next); - dnode_t *nextparent = next->parent; - dnode_color_t nextcolor = next->color; - - assert(next != nil); - assert(next->parent != nil); - assert(next->left == nil); - - /* - * First, splice out the successor from the tree completely, by - * moving up its right child into its place. - */ - - child = next->right; - child->parent = nextparent; - - if (nextparent->left == next) { - nextparent->left = child; - } else { - assert(nextparent->right == next); - nextparent->right = child; - } - - /* - * Now that the successor has been extricated from the tree, - * install it - * in place of the node that we want deleted. - */ - - next->parent = delparent; - next->left = delete->left; - next->right = delete->right; - next->left->parent = next; - next->right->parent = next; - next->color = delete->color; - delete->color = nextcolor; - - if (delparent->left == delete) { - delparent->left = next; - } else { - assert(delparent->right == delete); - delparent->right = next; - } - - } else { - assert(delete != nil); - assert(delete->left == nil || delete->right == nil); - - child = (delete->left != nil) ? delete->left : delete->right; - - child->parent = delparent = delete->parent; - - if (delete == delparent->left) { - delparent->left = child; - } else { - assert(delete == delparent->right); - delparent->right = child; - } - } - - delete->parent = NULL; - delete->right = NULL; - delete->left = NULL; - - dict->nodecount--; - - assert(verify_bintree(dict)); - - /* red-black adjustments */ - - if (delete->color == dnode_black) { - dnode_t *parent, *sister; - - dict_root(dict)->color = dnode_red; - - while (child->color == dnode_black) { - parent = child->parent; - if (child == parent->left) { - sister = parent->right; - assert(sister != nil); - if (sister->color == dnode_red) { - sister->color = dnode_black; - parent->color = dnode_red; - rotate_left(parent); - sister = parent->right; - assert(sister != nil); - } - if (sister->left->color == dnode_black - && sister->right->color == dnode_black) { - sister->color = dnode_red; - child = parent; - } else { - if (sister->right->color - == dnode_black) { - assert(sister->left->color - == dnode_red); - sister->left->color = - dnode_black; - sister->color = dnode_red; - rotate_right(sister); - sister = parent->right; - assert(sister != nil); - } - sister->color = parent->color; - sister->right->color = dnode_black; - parent->color = dnode_black; - rotate_left(parent); - break; - } - } else { /* symmetric case: child == - child->parent->right */ - assert(child == parent->right); - sister = parent->left; - assert(sister != nil); - if (sister->color == dnode_red) { - sister->color = dnode_black; - parent->color = dnode_red; - rotate_right(parent); - sister = parent->left; - assert(sister != nil); - } - if (sister->right->color == dnode_black - && sister->left->color == dnode_black) { - sister->color = dnode_red; - child = parent; - } else { - if (sister->left->color - == dnode_black) { - assert(sister->right->color - == dnode_red); - sister->right->color = - dnode_black; - sister->color = dnode_red; - rotate_left(sister); - sister = parent->left; - assert(sister != nil); - } - sister->color = parent->color; - sister->left->color = dnode_black; - parent->color = dnode_black; - rotate_right(parent); - break; - } - } - } - - child->color = dnode_black; - dict_root(dict)->color = dnode_black; - } - - assert(dict_verify(dict)); - - return delete; -} - -/* - * Allocate a node using the dictionary's allocator routine, give it - * the data item. - */ - -int dict_alloc_insert(dict_t *dict, const void *key, void *data) -{ - dnode_t *node = dict->allocnode(dict->context); - - if (node) { - dnode_init(node, data); - dict_insert(dict, node, key); - return 1; - } - return 0; -} - -void dict_delete_free(dict_t *dict, dnode_t *node) -{ - dict_delete(dict, node); - dict->freenode(node, dict->context); -} - -/* - * Return the node with the lowest (leftmost) key. If the dictionary is empty - * (that is, dict_isempty(dict) returns 1) a null pointer is returned. - */ - -dnode_t *dict_first(dict_t *dict) -{ - dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left; - - if (root != nil) - while ((left = root->left) != nil) - root = left; - - return (root == nil) ? NULL : root; -} - -/* - * Return the node with the highest (rightmost) key. If the dictionary is empty - * (that is, dict_isempty(dict) returns 1) a null pointer is returned. - */ - -dnode_t *dict_last(dict_t *dict) -{ - dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right; - - if (root != nil) - while ((right = root->right) != nil) - root = right; - - return (root == nil) ? NULL : root; -} - -/* - * Return the given node's successor node---the node which has the - * next key in the the left to right ordering. If the node has - * no successor, a null pointer is returned rather than a pointer to - * the nil node. - */ - -dnode_t *dict_next(dict_t *dict, dnode_t *curr) -{ - dnode_t *nil = dict_nil(dict), *parent, *left; - - if (curr->right != nil) { - curr = curr->right; - while ((left = curr->left) != nil) - curr = left; - return curr; - } - - parent = curr->parent; - - while (parent != nil && curr == parent->right) { - curr = parent; - parent = curr->parent; - } - - return (parent == nil) ? NULL : parent; -} - -/* - * Return the given node's predecessor, in the key order. - * The nil sentinel node is returned if there is no predecessor. - */ - -dnode_t *dict_prev(dict_t *dict, dnode_t *curr) -{ - dnode_t *nil = dict_nil(dict), *parent, *right; - - if (curr->left != nil) { - curr = curr->left; - while ((right = curr->right) != nil) - curr = right; - return curr; - } - - parent = curr->parent; - - while (parent != nil && curr == parent->left) { - curr = parent; - parent = curr->parent; - } - - return (parent == nil) ? NULL : parent; -} - -void dict_allow_dupes(dict_t *dict) -{ - dict->dupes = 1; -} - -#undef dict_count -#undef dict_isempty -#undef dict_isfull -#undef dnode_get -#undef dnode_put -#undef dnode_getkey - -dictcount_t dict_count(dict_t *dict) -{ - return dict->nodecount; -} - -int dict_isempty(dict_t *dict) -{ - return dict->nodecount == 0; -} - -int dict_isfull(dict_t *dict) -{ - return dict->nodecount == dict->maxcount; -} - -int dict_contains(dict_t *dict, dnode_t *node) -{ - return verify_dict_has_node(dict_nil(dict), dict_root(dict), node); -} - -static dnode_t *dnode_alloc(void *context) -{ - return XCALLOC(MTYPE_ISIS_DICT_NODE, sizeof(dnode_t)); -} - -static void dnode_free(dnode_t *node, void *context) -{ - XFREE(MTYPE_ISIS_DICT_NODE, node); -} - -dnode_t *dnode_create(void *data) -{ - dnode_t *new = XCALLOC(MTYPE_ISIS_DICT_NODE, sizeof(dnode_t)); - - new->data = data; - new->parent = NULL; - new->left = NULL; - new->right = NULL; - - return new; -} - -dnode_t *dnode_init(dnode_t *dnode, void *data) -{ - dnode->data = data; - dnode->parent = NULL; - dnode->left = NULL; - dnode->right = NULL; - return dnode; -} - -void dnode_destroy(dnode_t *dnode) -{ - assert(!dnode_is_in_a_dict(dnode)); - XFREE(MTYPE_ISIS_DICT_NODE, dnode); -} - -void *dnode_get(dnode_t *dnode) -{ - return dnode->data; -} - -const void *dnode_getkey(dnode_t *dnode) -{ - return dnode->key; -} - -void dnode_put(dnode_t *dnode, void *data) -{ - dnode->data = data; -} - -int dnode_is_in_a_dict(dnode_t *dnode) -{ - return (dnode->parent && dnode->left && dnode->right); -} - -void dict_process(dict_t *dict, void *context, dnode_process_t function) -{ - dnode_t *node = dict_first(dict), *next; - - while (node != NULL) { - /* check for callback function deleting */ - /* the next node from under us */ - assert(dict_contains(dict, node)); - next = dict_next(dict, node); - function(dict, node, context); - node = next; - } -} - -static void load_begin_internal(dict_load_t *load, dict_t *dict) -{ - load->dictptr = dict; - load->nilnode.left = &load->nilnode; - load->nilnode.right = &load->nilnode; -} - -void dict_load_begin(dict_load_t *load, dict_t *dict) -{ - assert(dict_isempty(dict)); - load_begin_internal(load, dict); -} - -void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key) -{ - dict_t *dict = load->dictptr; - dnode_t *nil = &load->nilnode; - - assert(!dnode_is_in_a_dict(newnode)); - assert(dict->nodecount < dict->maxcount); - -#ifndef NDEBUG - if (dict->nodecount > 0) { - if (dict->dupes) - assert(dict->compare(nil->left->key, key) <= 0); - else - assert(dict->compare(nil->left->key, key) < 0); - } -#endif - - newnode->key = key; - nil->right->left = newnode; - nil->right = newnode; - newnode->left = nil; - dict->nodecount++; -} - -void dict_load_end(dict_load_t *load) -{ - dict_t *dict = load->dictptr; - dnode_t *tree[DICT_DEPTH_MAX] = {0}; - dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, - *next; - dnode_t *complete = 0; - dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount; - dictcount_t botrowcount; - unsigned baselevel = 0, level = 0, i; - - assert(dnode_red == 0 && dnode_black == 1); - - while (fullcount >= nodecount && fullcount) - fullcount >>= 1; - - botrowcount = nodecount - fullcount; - - for (curr = loadnil->left; curr != loadnil; curr = next) { - next = curr->left; - - if (complete == NULL && botrowcount-- == 0) { - assert(baselevel == 0); - assert(level == 0); - baselevel = level = 1; - complete = tree[0]; - - if (complete != NULL) { - tree[0] = 0; - complete->right = dictnil; - while (tree[level] != NULL) { - tree[level]->right = complete; - complete->parent = tree[level]; - complete = tree[level]; - tree[level++] = 0; - } - } - } - - if (complete == NULL) { - curr->left = dictnil; - curr->right = dictnil; - curr->color = level % 2; - complete = curr; - - assert(level == baselevel); - while (tree[level] != NULL) { - tree[level]->right = complete; - complete->parent = tree[level]; - complete = tree[level]; - tree[level++] = 0; - } - } else { - curr->left = complete; - curr->color = (level + 1) % 2; - complete->parent = curr; - tree[level] = curr; - complete = 0; - level = baselevel; - } - } - - if (complete == NULL) - complete = dictnil; - - for (i = 0; i < DICT_DEPTH_MAX; i++) { - if (tree[i] != NULL) { - tree[i]->right = complete; - complete->parent = tree[i]; - complete = tree[i]; - } - } - - dictnil->color = dnode_black; - dictnil->right = dictnil; - complete->parent = dictnil; - complete->color = dnode_black; - dict_root(dict) = complete; - - assert(dict_verify(dict)); -} - -void dict_merge(dict_t *dest, dict_t *source) -{ - dict_load_t load; - dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source); - - assert(dict_similar(dest, source)); - - if (source == dest) - return; - - dest->nodecount = 0; - load_begin_internal(&load, dest); - - for (;;) { - if (leftnode != NULL && rightnode != NULL) { - if (dest->compare(leftnode->key, rightnode->key) < 0) - goto copyleft; - else - goto copyright; - } else if (leftnode != NULL) { - goto copyleft; - } else if (rightnode != NULL) { - goto copyright; - } else { - assert(leftnode == NULL && rightnode == NULL); - break; - } - - copyleft : { - dnode_t *next = dict_next(dest, leftnode); -#ifndef NDEBUG - leftnode->left = - NULL; /* suppress assertion in dict_load_next */ -#endif - dict_load_next(&load, leftnode, leftnode->key); - leftnode = next; - continue; - } - - copyright : { - dnode_t *next = dict_next(source, rightnode); -#ifndef NDEBUG - rightnode->left = NULL; -#endif - dict_load_next(&load, rightnode, rightnode->key); - rightnode = next; - continue; - } - } - - dict_clear(source); - dict_load_end(&load); -} - -#ifdef KAZLIB_TEST_MAIN - -#include -#include -#include -#include - -typedef char input_t[256]; - -static int tokenize(char *string, ...) -{ - char **tokptr; - va_list arglist; - int tokcount = 0; - - va_start(arglist, string); - tokptr = va_arg(arglist, char **); - while (tokptr) { - while (*string && isspace((unsigned char)*string)) - string++; - if (!*string) - break; - *tokptr = string; - while (*string && !isspace((unsigned char)*string)) - string++; - tokptr = va_arg(arglist, char **); - tokcount++; - if (!*string) - break; - *string++ = 0; - } - va_end(arglist); - - return tokcount; -} - -static int comparef(const void *key1, const void *key2) -{ - return strcmp(key1, key2); -} - -static char *dupstring(char *str) -{ - int sz = strlen(str) + 1; - char *new = XCALLOC(MTYPE_ISIS_TMP, sz); - - memcpy(new, str, sz); - return new; -} - -static dnode_t *new_node(void *c) -{ - static dnode_t few[5]; - static int count; - - if (count < 5) - return few + count++; - - return NULL; -} - -static void del_node(dnode_t *n, void *c) -{ -} - -static int prompt = 0; - -static void construct(dict_t *d) -{ - input_t in; - int done = 0; - dict_load_t dl; - dnode_t *dn; - char *tok1, *tok2, *val; - const char *key; - char *help = - "p turn prompt on\n" - "q finish construction\n" - "a add new entry\n"; - - if (!dict_isempty(d)) - puts("warning: dictionary not empty!"); - - dict_load_begin(&dl, d); - - while (!done) { - if (prompt) - putchar('>'); - fflush(stdout); - - if (!fgets(in, sizeof(input_t), stdin)) - break; - - switch (in[0]) { - case '?': - puts(help); - break; - case 'p': - prompt = 1; - break; - case 'q': - done = 1; - break; - case 'a': - if (tokenize(in + 1, &tok1, &tok2, (char **)0) != 2) { - puts("what?"); - break; - } - key = dupstring(tok1); - val = dupstring(tok2); - dn = dnode_create(val); - - if (!key || !val || !dn) { - puts("out of memory"); - free((void *)key); - free(val); - if (dn) - dnode_destroy(dn); - } else - dict_load_next(&dl, dn, key); - break; - default: - putchar('?'); - putchar('\n'); - break; - } - } - - dict_load_end(&dl); -} - -int main(void) -{ - input_t in; - dict_t darray[10]; - dict_t *d = &darray[0]; - dnode_t *dn; - int i; - char *tok1, *tok2, *val; - const char *key; - - char *help = - "a add value to dictionary\n" - "d delete value from dictionary\n" - "l lookup value in dictionary\n" - "( lookup lower bound\n" - ") lookup upper bound\n" - "# switch to alternate dictionary (0-9)\n" - "j merge two dictionaries\n" - "f free the whole dictionary\n" - "k allow duplicate keys\n" - "c show number of entries\n" - "t dump whole dictionary in sort order\n" - "m make dictionary out of sorted items\n" - "p turn prompt on\n" - "s switch to non-functioning allocator\n" - "q quit"; - - for (i = 0; i < 10; i++) - dict_init(&darray[i], DICTCOUNT_T_MAX, comparef); - - for (;;) { - if (prompt) - putchar('>'); - fflush(stdout); - - if (!fgets(in, sizeof(input_t), stdin)) - break; - - switch (in[0]) { - case '?': - puts(help); - break; - case 'a': - if (tokenize(in + 1, &tok1, &tok2, (char **)0) != 2) { - puts("what?"); - break; - } - key = dupstring(tok1); - val = dupstring(tok2); - - if (!key || !val) { - puts("out of memory"); - free((void *)key); - free(val); - } - - if (!dict_alloc_insert(d, key, val)) { - puts("dict_alloc_insert failed"); - free((void *)key); - free(val); - break; - } - break; - case 'd': - if (tokenize(in + 1, &tok1, (char **)0) != 1) { - puts("what?"); - break; - } - dn = dict_lookup(d, tok1); - if (!dn) { - puts("dict_lookup failed"); - break; - } - val = dnode_get(dn); - key = dnode_getkey(dn); - dict_delete_free(d, dn); - - free(val); - free((void *)key); - break; - case 'f': - dict_free(d); - break; - case 'l': - case '(': - case ')': - if (tokenize(in + 1, &tok1, (char **)0) != 1) { - puts("what?"); - break; - } - dn = 0; - switch (in[0]) { - case 'l': - dn = dict_lookup(d, tok1); - break; - case '(': - dn = dict_lower_bound(d, tok1); - break; - case ')': - dn = dict_upper_bound(d, tok1); - break; - } - if (!dn) { - puts("lookup failed"); - break; - } - val = dnode_get(dn); - puts(val); - break; - case 'm': - construct(d); - break; - case 'k': - dict_allow_dupes(d); - break; - case 'c': - printf("%lu\n", (unsigned long)dict_count(d)); - break; - case 't': - for (dn = dict_first(d); dn; dn = dict_next(d, dn)) { - printf("%s\t%s\n", (char *)dnode_getkey(dn), - (char *)dnode_get(dn)); - } - break; - case 'q': - exit(0); - break; - case '\0': - break; - case 'p': - prompt = 1; - break; - case 's': - dict_set_allocator(d, new_node, del_node, NULL); - break; - case '#': - if (tokenize(in + 1, &tok1, (char **)0) != 1) { - puts("what?"); - break; - } else { - int dictnum = atoi(tok1); - if (dictnum < 0 || dictnum > 9) { - puts("invalid number"); - break; - } - d = &darray[dictnum]; - } - break; - case 'j': - if (tokenize(in + 1, &tok1, &tok2, (char **)0) != 2) { - puts("what?"); - break; - } else { - int dict1 = atoi(tok1), dict2 = atoi(tok2); - if (dict1 < 0 || dict1 > 9 || dict2 < 0 - || dict2 > 9) { - puts("invalid number"); - break; - } - dict_merge(&darray[dict1], &darray[dict2]); - } - break; - default: - putchar('?'); - putchar('\n'); - break; - } - } - - return 0; -} - -#endif diff --git a/isisd/dict.h b/isisd/dict.h deleted file mode 100644 index 32683c57d58a..000000000000 --- a/isisd/dict.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Dictionary Abstract Data Type - * Copyright (C) 1997 Kaz Kylheku - * - * Free Software License: - * - * All rights are reserved by the author, with the following exceptions: - * Permission is granted to freely reproduce and distribute this software, - * possibly in exchange for a fee, provided that this copyright notice appears - * intact. Permission is also granted to adapt this software to produce - * derivative works, as long as the modified versions carry this copyright - * notice and additional notices stating that the work has been modified. - * This source code may be translated into executable form and incorporated - * into proprietary software; there is no requirement for such software to - * contain a copyright notice related to this source. - * - */ - -#ifndef DICT_H -#define DICT_H - -#include - -/* - * Blurb for inclusion into C++ translation units - */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef unsigned long dictcount_t; -#define DICTCOUNT_T_MAX ULONG_MAX - -/* - * The dictionary is implemented as a red-black tree - */ - -typedef enum { dnode_red, dnode_black } dnode_color_t; - -typedef struct dnode_t { - struct dnode_t *dict_left; - struct dnode_t *dict_right; - struct dnode_t *dict_parent; - dnode_color_t dict_color; - const void *dict_key; - void *dict_data; -} dnode_t; - -typedef int (*dict_comp_t)(const void *, const void *); -typedef dnode_t *(*dnode_alloc_t)(void *); -typedef void (*dnode_free_t)(dnode_t *, void *); - -typedef struct dict_t { - dnode_t dict_nilnode; - dictcount_t dict_nodecount; - dictcount_t dict_maxcount; - dict_comp_t dict_compare; - dnode_alloc_t dict_allocnode; - dnode_free_t dict_freenode; - void *dict_context; - int dict_dupes; -} dict_t; - -typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *); - -typedef struct dict_load_t { - dict_t *dict_dictptr; - dnode_t dict_nilnode; -} dict_load_t; - -extern dict_t *dict_create(dictcount_t, dict_comp_t); -extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *); -extern void dict_destroy(dict_t *); -extern void dict_free_nodes(dict_t *); -extern void dict_free(dict_t *); -extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t); -extern void dict_init_like(dict_t *, const dict_t *); -extern int dict_verify(dict_t *); -extern int dict_similar(const dict_t *, const dict_t *); -extern dnode_t *dict_lookup(dict_t *, const void *); -extern dnode_t *dict_lower_bound(dict_t *, const void *); -extern dnode_t *dict_upper_bound(dict_t *, const void *); -extern void dict_insert(dict_t *, dnode_t *, const void *); -extern dnode_t *dict_delete(dict_t *, dnode_t *); -extern int dict_alloc_insert(dict_t *, const void *, void *); -extern void dict_delete_free(dict_t *, dnode_t *); -extern dnode_t *dict_first(dict_t *); -extern dnode_t *dict_last(dict_t *); -extern dnode_t *dict_next(dict_t *, dnode_t *); -extern dnode_t *dict_prev(dict_t *, dnode_t *); -extern dictcount_t dict_count(dict_t *); -extern int dict_isempty(dict_t *); -extern int dict_isfull(dict_t *); -extern int dict_contains(dict_t *, dnode_t *); -extern void dict_allow_dupes(dict_t *); -extern int dnode_is_in_a_dict(dnode_t *); -extern dnode_t *dnode_create(void *); -extern dnode_t *dnode_init(dnode_t *, void *); -extern void dnode_destroy(dnode_t *); -extern void *dnode_get(dnode_t *); -extern const void *dnode_getkey(dnode_t *); -extern void dnode_put(dnode_t *, void *); -extern void dict_process(dict_t *, void *, dnode_process_t); -extern void dict_load_begin(dict_load_t *, dict_t *); -extern void dict_load_next(dict_load_t *, dnode_t *, const void *); -extern void dict_load_end(dict_load_t *); -extern void dict_merge(dict_t *, dict_t *); - -#define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount) -#define dict_count(D) ((D)->dict_nodecount) -#define dict_isempty(D) ((D)->dict_nodecount == 0) -#define dnode_get(N) ((N)->dict_data) -#define dnode_getkey(N) ((N)->dict_key) -#define dnode_put(N, X) ((N)->dict_data = (X)) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/isisd/fabricd.c b/isisd/fabricd.c index 96af28f0a1cc..b9c27d51bd89 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -108,9 +108,9 @@ static void neighbor_lists_clear(struct fabricd *f) hash_clean(f->neighbors_neighbors, neighbor_entry_del_void); } -static unsigned neighbor_entry_hash_key(void *np) +static unsigned neighbor_entry_hash_key(const void *np) { - struct neighbor_entry *n = np; + const struct neighbor_entry *n = np; return jhash(n->id, sizeof(n->id), 0x55aa5a5a); } diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 62814329eada..9b368cc40426 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -32,7 +32,6 @@ #include "if.h" #include "stream.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c index 81976f8dd293..fa89c80049bb 100644 --- a/isisd/isis_bfd.c +++ b/isisd/isis_bfd.c @@ -92,14 +92,14 @@ static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst, isis_adj_state_change(adj, ISIS_ADJ_DOWN, "bfd session went down"); } -static int isis_bfd_interface_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct prefix dst_ip; int status; - ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, NULL, &status, vrf_id); + ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, NULL, &status, + NULL, vrf_id); if (!ifp || dst_ip.family != AF_INET) return 0; @@ -138,10 +138,9 @@ static int isis_bfd_interface_dest_update(int command, struct zclient *zclient, return 0; } -static int isis_bfd_nbr_replay(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) { - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); struct listnode *anode; struct isis_area *area; @@ -169,7 +168,7 @@ static void isis_bfd_zebra_connected(struct zclient *zclient) if (orig_zebra_connected) orig_zebra_connected(zclient); - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); } static void bfd_debug(struct in_addr *dst, struct in_addr *src, @@ -219,6 +218,7 @@ static void bfd_handle_adj_down(struct isis_adjacency *adj) adj->circuit->interface->name, 0, /* ttl */ 0, /* multihop */ + 1, /* control plane independent bit is on */ ZEBRA_BFD_DEST_DEREGISTER, 0, /* set_flag */ VRF_DEFAULT); @@ -260,6 +260,7 @@ static void bfd_handle_adj_up(struct isis_adjacency *adj, int command) circuit->interface->name, 0, /* ttl */ 0, /* multihop */ + 1, /* control plane independent bit is on */ command, 0, /* set flag */ VRF_DEFAULT); diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c index 28750278b017..4e9aef47adf0 100644 --- a/isisd/isis_bpf.c +++ b/isisd/isis_bpf.c @@ -34,7 +34,6 @@ #include "if.h" #include "lib_errors.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_circuit.h" diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 8377638b9216..8d008d78bd48 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -40,7 +40,6 @@ #include "qobj.h" #include "lib/northbound_cli.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" @@ -540,7 +539,6 @@ static void isis_circuit_update_all_srmflags(struct isis_circuit *circuit, { struct isis_area *area; struct isis_lsp *lsp; - dnode_t *dnode; int level; assert(circuit); @@ -550,14 +548,10 @@ static void isis_circuit_update_all_srmflags(struct isis_circuit *circuit, if (!(level & circuit->is_type)) continue; - if (!area->lspdb[level - 1] - || !dict_count(area->lspdb[level - 1])) + if (!lspdb_count(&area->lspdb[level - 1])) continue; - for (dnode = dict_first(area->lspdb[level - 1]); - dnode != NULL; - dnode = dict_next(area->lspdb[level - 1], dnode)) { - lsp = dnode_get(dnode); + frr_each (lspdb, &area->lspdb[level - 1], lsp) { if (is_set) { isis_tx_queue_add(circuit->tx_queue, lsp, TX_LSP_NORMAL); diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index e0ea4f78b44f..2371c0b73a15 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -121,8 +121,7 @@ struct isis_circuit { uint16_t psnp_interval[2]; /* psnp-interval in seconds */ uint8_t metric[2]; uint32_t te_metric[2]; - struct mpls_te_circuit - *mtc; /* Support for MPLS-TE parameters - see isis_te.[c,h] */ + struct mpls_te_circuit *mtc; /* MPLS-TE parameters */ int ip_router; /* Route IP ? */ int is_passive; /* Is Passive ? */ struct list *mt_settings; /* IS-IS MT Settings */ diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index 5f7be160341a..0334b98a122f 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -188,10 +188,14 @@ DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag", } /* check if the interface is a loopback and if so set it as passive */ - ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); - if (ifp && if_is_loopback(ifp)) - nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive", - NB_OP_MODIFY, "true"); + pthread_rwlock_rdlock(&running_config->lock); + { + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + if (ifp && if_is_loopback(ifp)) + nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive", + NB_OP_MODIFY, "true"); + } + pthread_rwlock_unlock(&running_config->lock); return nb_cli_apply_changes(vty, NULL); } @@ -258,10 +262,14 @@ DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag", } /* check if the interface is a loopback and if so set it as passive */ - ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); - if (ifp && if_is_loopback(ifp)) - nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive", - NB_OP_MODIFY, "true"); + pthread_rwlock_rdlock(&running_config->lock); + { + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + if (ifp && if_is_loopback(ifp)) + nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive", + NB_OP_MODIFY, "true"); + } + pthread_rwlock_unlock(&running_config->lock); return nb_cli_apply_changes(vty, NULL); } @@ -368,20 +376,26 @@ DEFPY(no_is_type, no_is_type_cmd, "Act as both a station router and an area router\n" "Act as an area router only\n") { - const char *value = NULL; - struct isis_area *area; + const char *value; - area = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + pthread_rwlock_rdlock(&running_config->lock); + { + struct isis_area *area; + + area = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + + /* + * Put the is-type back to defaults: + * - level-1-2 on first area + * - level-1 for the rest + */ + if (area && listgetdata(listhead(isis->area_list)) == area) + value = "level-1-2"; + else + value = NULL; + } + pthread_rwlock_unlock(&running_config->lock); - /* - * Put the is-type back to defaults: - * - level-1-2 on first area - * - level-1 for the rest - */ - if (area && listgetdata(listhead(isis->area_list)) == area) - value = "level-1-2"; - else - value = NULL; nb_cli_enqueue_change(vty, "./is-type", NB_OP_MODIFY, value); return nb_cli_apply_changes(vty, NULL); @@ -928,12 +942,12 @@ void cli_show_isis_purge_origin(struct vty *vty, struct lyd_node *dnode, } /* - * XPath: /frr-isisd:isis/mpls-te + * XPath: /frr-isisd:isis/instance/mpls-te */ DEFPY(isis_mpls_te_on, isis_mpls_te_on_cmd, "mpls-te on", MPLS_TE_STR "Enable the MPLS-TE functionality\n") { - nb_cli_enqueue_change(vty, "/frr-isisd:isis/mpls-te", NB_OP_CREATE, + nb_cli_enqueue_change(vty, "./mpls-te", NB_OP_CREATE, NULL); return nb_cli_apply_changes(vty, NULL); @@ -942,9 +956,9 @@ DEFPY(isis_mpls_te_on, isis_mpls_te_on_cmd, "mpls-te on", DEFPY(no_isis_mpls_te_on, no_isis_mpls_te_on_cmd, "no mpls-te [on]", NO_STR "Disable the MPLS-TE functionality\n" - "Enable the MPLS-TE functionality\n") + "Disable the MPLS-TE functionality\n") { - nb_cli_enqueue_change(vty, "/frr-isisd:isis/mpls-te", NB_OP_DESTROY, + nb_cli_enqueue_change(vty, "./mpls-te", NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); @@ -957,7 +971,7 @@ void cli_show_isis_mpls_te(struct vty *vty, struct lyd_node *dnode, } /* - * XPath: /frr-isisd:isis/mpls-te/router-address + * XPath: /frr-isisd:isis/instance/mpls-te/router-address */ DEFPY(isis_mpls_te_router_addr, isis_mpls_te_router_addr_cmd, "mpls-te router-address A.B.C.D", @@ -965,12 +979,24 @@ DEFPY(isis_mpls_te_router_addr, isis_mpls_te_router_addr_cmd, "Stable IP address of the advertising router\n" "MPLS-TE router address in IPv4 address format\n") { - nb_cli_enqueue_change(vty, "/frr-isisd:isis/mpls-te/router-address", + nb_cli_enqueue_change(vty, "./mpls-te/router-address", NB_OP_MODIFY, router_address_str); return nb_cli_apply_changes(vty, NULL); } +DEFPY(no_isis_mpls_te_router_addr, no_isis_mpls_te_router_addr_cmd, + "no mpls-te router-address [A.B.C.D]", + NO_STR MPLS_TE_STR + "Delete IP address of the advertising router\n" + "MPLS-TE router address in IPv4 address format\n") +{ + nb_cli_enqueue_change(vty, "./mpls-te/router-address", + NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + void cli_show_isis_mpls_te_router_addr(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { @@ -986,7 +1012,7 @@ DEFPY(isis_mpls_te_inter_as, isis_mpls_te_inter_as_cmd, "AREA native mode self originate INTER-AS LSP with L1 and L2 flooding scope\n" "AS native mode self originate INTER-AS LSP with L2 only flooding scope\n") { - vty_out(vty, "MPLS-TE Inter-AS is not yet supported."); + vty_out(vty, "MPLS-TE Inter-AS is not yet supported\n"); return CMD_SUCCESS; } @@ -1757,50 +1783,43 @@ DEFPY(no_isis_circuit_type, no_isis_circuit_type_cmd, "Level-1-2 adjacencies are formed\n" "Level-2 only adjacencies are formed\n") { - struct interface *ifp; - struct isis_circuit *circuit; - int is_type; - const char *circ_type; + const char *circ_type = NULL; /* * Default value depends on whether the circuit is part of an area, * and the is-type of the area if there is one. So we need to do this * here. */ - ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); - if (!ifp) - goto def_val; + pthread_rwlock_rdlock(&running_config->lock); + { + struct interface *ifp; + struct isis_circuit *circuit; - circuit = circuit_scan_by_ifp(ifp); - if (!circuit) - goto def_val; + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + if (!ifp) + goto unlock; - if (circuit->state == C_STATE_UP) - is_type = circuit->area->is_type; - else - goto def_val; + circuit = circuit_scan_by_ifp(ifp); + if (!circuit || circuit->state != C_STATE_UP) + goto unlock; - switch (is_type) { - case IS_LEVEL_1: - circ_type = "level-1"; - break; - case IS_LEVEL_2: - circ_type = "level-2"; - break; - case IS_LEVEL_1_AND_2: - circ_type = "level-1-2"; - break; - default: - return CMD_ERR_NO_MATCH; + switch (circuit->area->is_type) { + case IS_LEVEL_1: + circ_type = "level-1"; + break; + case IS_LEVEL_2: + circ_type = "level-2"; + break; + case IS_LEVEL_1_AND_2: + circ_type = "level-1-2"; + break; + } } - nb_cli_enqueue_change(vty, "./frr-isisd:isis/circuit-type", - NB_OP_MODIFY, circ_type); +unlock: + pthread_rwlock_unlock(&running_config->lock); - return nb_cli_apply_changes(vty, NULL); - -def_val: nb_cli_enqueue_change(vty, "./frr-isisd:isis/circuit-type", - NB_OP_MODIFY, NULL); + NB_OP_MODIFY, circ_type); return nb_cli_apply_changes(vty, NULL); } @@ -1967,6 +1986,7 @@ void isis_cli_init(void) install_element(ISIS_NODE, &isis_mpls_te_on_cmd); install_element(ISIS_NODE, &no_isis_mpls_te_on_cmd); install_element(ISIS_NODE, &isis_mpls_te_router_addr_cmd); + install_element(ISIS_NODE, &no_isis_mpls_te_router_addr_cmd); install_element(ISIS_NODE, &isis_mpls_te_inter_as_cmd); install_element(ISIS_NODE, &isis_default_originate_cmd); diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c index e50f57ae1d2f..88676dd99069 100644 --- a/isisd/isis_csm.c +++ b/isisd/isis_csm.c @@ -32,7 +32,6 @@ #include "prefix.h" #include "stream.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c index 148b4386616e..a96dd93804c0 100644 --- a/isisd/isis_dlpi.c +++ b/isisd/isis_dlpi.c @@ -38,7 +38,6 @@ #include "if.h" #include "lib_errors.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_circuit.h" diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c index 449648656a6b..7be53075000f 100644 --- a/isisd/isis_dr.c +++ b/isisd/isis_dr.c @@ -32,7 +32,6 @@ #include "stream.h" #include "if.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_misc.h" diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c index 1d29d1086d8f..921e23d33a31 100644 --- a/isisd/isis_dynhn.c +++ b/isisd/isis_dynhn.c @@ -31,7 +31,6 @@ #include "if.h" #include "thread.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_events.c b/isisd/isis_events.c index 4da23c591261..6a5dcfb0756c 100644 --- a/isisd/isis_events.c +++ b/isisd/isis_events.c @@ -32,7 +32,6 @@ #include "stream.h" #include "table.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index b56a56fa3ffd..4b29e6dc7e0d 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -40,7 +40,6 @@ #include "srcdest_table.h" #include "lib_errors.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" @@ -63,41 +62,38 @@ static int lsp_refresh(struct thread *thread); static int lsp_l1_refresh_pseudo(struct thread *thread); static int lsp_l2_refresh_pseudo(struct thread *thread); +static void lsp_destroy(struct isis_lsp *lsp); + int lsp_id_cmp(uint8_t *id1, uint8_t *id2) { return memcmp(id1, id2, ISIS_SYS_ID_LEN + 2); } -dict_t *lsp_db_init(void) +int lspdb_compare(const struct isis_lsp *a, const struct isis_lsp *b) { - dict_t *dict; - - dict = dict_create(DICTCOUNT_T_MAX, (dict_comp_t)lsp_id_cmp); - - return dict; + return memcmp(a->hdr.lsp_id, b->hdr.lsp_id, sizeof(a->hdr.lsp_id)); } -struct isis_lsp *lsp_search(uint8_t *id, dict_t *lspdb) +void lsp_db_init(struct lspdb_head *head) { - dnode_t *node; - -#ifdef EXTREME_DEBUG - dnode_t *dn; + lspdb_init(head); +} - zlog_debug("searching db"); - for (dn = dict_first(lspdb); dn; dn = dict_next(lspdb, dn)) { - zlog_debug("%s\t%pX", - rawlspid_print((uint8_t *)dnode_getkey(dn)), - dnode_get(dn)); - } -#endif /* EXTREME DEBUG */ +void lsp_db_fini(struct lspdb_head *head) +{ + struct isis_lsp *lsp; - node = dict_lookup(lspdb, id); + while ((lsp = lspdb_pop(head))) + lsp_destroy(lsp); + lspdb_fini(head); +} - if (node) - return (struct isis_lsp *)dnode_get(node); +struct isis_lsp *lsp_search(struct lspdb_head *head, const uint8_t *id) +{ + struct isis_lsp searchfor; + memcpy(searchfor.hdr.lsp_id, id, sizeof(searchfor.hdr.lsp_id)); - return NULL; + return lspdb_find(head, &searchfor); } static void lsp_clear_data(struct isis_lsp *lsp) @@ -109,7 +105,7 @@ static void lsp_clear_data(struct isis_lsp *lsp) lsp->tlvs = NULL; } -static void lsp_remove_frags(struct list *frags, dict_t *lspdb); +static void lsp_remove_frags(struct lspdb_head *head, struct list *frags); static void lsp_destroy(struct isis_lsp *lsp) { @@ -128,8 +124,8 @@ static void lsp_destroy(struct isis_lsp *lsp) if (!LSP_FRAGMENT(lsp->hdr.lsp_id)) { if (lsp->lspu.frags) { - lsp_remove_frags(lsp->lspu.frags, - lsp->area->lspdb[lsp->level - 1]); + lsp_remove_frags(&lsp->area->lspdb[lsp->level - 1], + lsp->lspu.frags); list_delete(&lsp->lspu.frags); } } else { @@ -148,56 +144,34 @@ static void lsp_destroy(struct isis_lsp *lsp) XFREE(MTYPE_ISIS_LSP, lsp); } -void lsp_db_destroy(dict_t *lspdb) -{ - dnode_t *dnode, *next; - struct isis_lsp *lsp; - - dnode = dict_first(lspdb); - while (dnode) { - next = dict_next(lspdb, dnode); - lsp = dnode_get(dnode); - lsp_destroy(lsp); - dict_delete_free(lspdb, dnode); - dnode = next; - } - - dict_free(lspdb); - - return; -} - /* * Remove all the frags belonging to the given lsp */ -static void lsp_remove_frags(struct list *frags, dict_t *lspdb) +static void lsp_remove_frags(struct lspdb_head *head, struct list *frags) { - dnode_t *dnode; struct listnode *lnode, *lnnode; struct isis_lsp *lsp; for (ALL_LIST_ELEMENTS(frags, lnode, lnnode, lsp)) { - dnode = dict_lookup(lspdb, lsp->hdr.lsp_id); + lsp = lsp_search(head, lsp->hdr.lsp_id); + lspdb_del(head, lsp); lsp_destroy(lsp); - dnode_destroy(dict_delete(lspdb, dnode)); } } -void lsp_search_and_destroy(uint8_t *id, dict_t *lspdb) +void lsp_search_and_destroy(struct lspdb_head *head, const uint8_t *id) { - dnode_t *node; struct isis_lsp *lsp; - node = dict_lookup(lspdb, id); - if (node) { - node = dict_delete(lspdb, node); - lsp = dnode_get(node); + lsp = lsp_search(head, id); + if (lsp) { + lspdb_del(head, lsp); /* * If this is a zero lsp, remove all the frags now */ if (LSP_FRAGMENT(lsp->hdr.lsp_id) == 0) { if (lsp->lspu.frags) - lsp_remove_frags(lsp->lspu.frags, lspdb); + lsp_remove_frags(head, lsp->lspu.frags); } else { /* * else just remove this frag, from the zero lsps' frag @@ -209,7 +183,6 @@ void lsp_search_and_destroy(uint8_t *id, dict_t *lspdb) lsp); } lsp_destroy(lsp); - dnode_destroy(node); } } @@ -514,7 +487,7 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, memcpy(lspid, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(lspid) = 0; - lsp0 = lsp_search(lspid, area->lspdb[level - 1]); + lsp0 = lsp_search(&area->lspdb[level - 1], lspid); if (lsp0) lsp_link_fragment(lsp, lsp0); } @@ -582,9 +555,9 @@ struct isis_lsp *lsp_new(struct isis_area *area, uint8_t *lsp_id, return lsp; } -void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb) +void lsp_insert(struct lspdb_head *head, struct isis_lsp *lsp) { - dict_alloc_insert(lspdb, lsp->hdr.lsp_id, lsp); + lspdb_add(head, lsp); if (lsp->hdr.seqno) isis_spf_schedule(lsp->area, lsp->level); } @@ -592,13 +565,16 @@ void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb) /* * Build a list of LSPs with non-zero ht bounded by start and stop ids */ -void lsp_build_list_nonzero_ht(uint8_t *start_id, uint8_t *stop_id, - struct list *list, dict_t *lspdb) +void lsp_build_list_nonzero_ht(struct lspdb_head *head, const uint8_t *start_id, + const uint8_t *stop_id, struct list *list) { - for (dnode_t *curr = dict_lower_bound(lspdb, start_id); - curr; curr = dict_next(lspdb, curr)) { - struct isis_lsp *lsp = curr->dict_data; + struct isis_lsp searchfor; + struct isis_lsp *lsp, *start; + + memcpy(&searchfor.hdr.lsp_id, start_id, sizeof(searchfor.hdr.lsp_id)); + start = lspdb_find_gteq(head, &searchfor); + frr_each_from (lspdb, head, lsp, start) { if (memcmp(lsp->hdr.lsp_id, stop_id, ISIS_SYS_ID_LEN + 2) > 0) break; @@ -699,26 +675,20 @@ void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost) } /* print all the lsps info in the local lspdb */ -int lsp_print_all(struct vty *vty, dict_t *lspdb, char detail, char dynhost) +int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail, + char dynhost) { - - dnode_t *node = dict_first(lspdb), *next; + struct isis_lsp *lsp; int lsp_count = 0; if (detail == ISIS_UI_LEVEL_BRIEF) { - while (node != NULL) { - /* I think it is unnecessary, so I comment it out */ - /* dict_contains (lspdb, node); */ - next = dict_next(lspdb, node); - lsp_print(dnode_get(node), vty, dynhost); - node = next; + frr_each (lspdb, head, lsp) { + lsp_print(lsp, vty, dynhost); lsp_count++; } } else if (detail == ISIS_UI_LEVEL_DETAIL) { - while (node != NULL) { - next = dict_next(lspdb, node); - lsp_print_detail(dnode_get(node), vty, dynhost); - node = next; + frr_each (lspdb, head, lsp) { + lsp_print_detail(lsp, vty, dynhost); lsp_count++; } } @@ -847,7 +817,7 @@ static struct isis_lsp *lsp_next_frag(uint8_t frag_num, struct isis_lsp *lsp0, memcpy(frag_id, lsp0->hdr.lsp_id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(frag_id) = frag_num; - lsp = lsp_search(frag_id, area->lspdb[level - 1]); + lsp = lsp_search(&area->lspdb[level - 1], frag_id); if (lsp) { lsp_clear_data(lsp); if (!lsp->lspu.zero_lsp) @@ -860,7 +830,7 @@ static struct isis_lsp *lsp_next_frag(uint8_t frag_num, struct isis_lsp *lsp0, area->attached_bit), 0, lsp0, level); lsp->own_lsp = 1; - lsp_insert(lsp, area->lspdb[level - 1]); + lsp_insert(&area->lspdb[level - 1], lsp); return lsp; } @@ -1070,7 +1040,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) uint8_t subtlvs[256]; uint8_t subtlv_len; - if (IS_MPLS_TE(isisMplsTE) + if (IS_MPLS_TE(area->mta) && circuit->interface && HAS_LINK_PARAMS( circuit->interface)) @@ -1112,7 +1082,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) uint8_t subtlvs[256]; uint8_t subtlv_len; - if (IS_MPLS_TE(isisMplsTE) + if (IS_MPLS_TE(area->mta) && circuit->interface != NULL && HAS_LINK_PARAMS( circuit->interface)) @@ -1228,12 +1198,12 @@ int lsp_generate(struct isis_area *area, int level) memcpy(&lspid, isis->sysid, ISIS_SYS_ID_LEN); /* only builds the lsp if the area shares the level */ - oldlsp = lsp_search(lspid, area->lspdb[level - 1]); + oldlsp = lsp_search(&area->lspdb[level - 1], lspid); if (oldlsp) { /* FIXME: we should actually initiate a purge */ seq_num = oldlsp->hdr.seqno; - lsp_search_and_destroy(oldlsp->hdr.lsp_id, - area->lspdb[level - 1]); + lsp_search_and_destroy(&area->lspdb[level - 1], + oldlsp->hdr.lsp_id); } rem_lifetime = lsp_rem_lifetime(area, level); newlsp = @@ -1243,7 +1213,7 @@ int lsp_generate(struct isis_area *area, int level) newlsp->area = area; newlsp->own_lsp = 1; - lsp_insert(newlsp, area->lspdb[level - 1]); + lsp_insert(&area->lspdb[level - 1], newlsp); /* build_lsp_data (newlsp, area); */ lsp_build(newlsp, area); /* time to calculate our checksum */ @@ -1288,7 +1258,7 @@ int lsp_generate(struct isis_area *area, int level) */ static int lsp_regenerate(struct isis_area *area, int level) { - dict_t *lspdb; + struct lspdb_head *head; struct isis_lsp *lsp, *frag; struct listnode *node; uint8_t lspid[ISIS_SYS_ID_LEN + 2]; @@ -1297,12 +1267,12 @@ static int lsp_regenerate(struct isis_area *area, int level) if ((area == NULL) || (area->is_type & level) != level) return ISIS_ERROR; - lspdb = area->lspdb[level - 1]; + head = &area->lspdb[level - 1]; memset(lspid, 0, ISIS_SYS_ID_LEN + 2); memcpy(lspid, isis->sysid, ISIS_SYS_ID_LEN); - lsp = lsp_search(lspid, lspdb); + lsp = lsp_search(head, lspid); if (!lsp) { flog_err(EC_LIB_DEVELOPMENT, @@ -1445,7 +1415,7 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, continue; } - lsp = lsp_search(id, area->lspdb[lvl - 1]); + lsp = lsp_search(&area->lspdb[lvl - 1], id); if (!lsp) { sched_debug( "ISIS (%s): We do not have any LSPs to regenerate, nothing todo.", @@ -1597,7 +1567,7 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, int lsp_generate_pseudo(struct isis_circuit *circuit, int level) { - dict_t *lspdb = circuit->area->lspdb[level - 1]; + struct lspdb_head *head = &circuit->area->lspdb[level - 1]; struct isis_lsp *lsp; uint8_t lsp_id[ISIS_SYS_ID_LEN + 2]; uint16_t rem_lifetime, refresh_time; @@ -1615,7 +1585,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) /* * If for some reason have a pseudo LSP in the db already -> regenerate */ - if (lsp_search(lsp_id, lspdb)) + if (lsp_search(head, lsp_id)) return lsp_regenerate_schedule_pseudo(circuit, level); rem_lifetime = lsp_rem_lifetime(circuit->area, level); @@ -1628,7 +1598,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) lsp_build_pseudo(lsp, circuit, level); lsp_pack_pdu(lsp); lsp->own_lsp = 1; - lsp_insert(lsp, lspdb); + lsp_insert(head, lsp); lsp_flood(lsp, NULL); refresh_time = lsp_refresh_time(lsp, rem_lifetime); @@ -1659,7 +1629,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) { - dict_t *lspdb = circuit->area->lspdb[level - 1]; + struct lspdb_head *head = &circuit->area->lspdb[level - 1]; struct isis_lsp *lsp; uint8_t lsp_id[ISIS_SYS_ID_LEN + 2]; uint16_t rem_lifetime, refresh_time; @@ -1674,7 +1644,7 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id; LSP_FRAGMENT(lsp_id) = 0; - lsp = lsp_search(lsp_id, lspdb); + lsp = lsp_search(head, lsp_id); if (!lsp) { flog_err(EC_LIB_DEVELOPMENT, @@ -1813,7 +1783,7 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level) continue; } - lsp = lsp_search(lsp_id, circuit->area->lspdb[lvl - 1]); + lsp = lsp_search(&circuit->area->lspdb[lvl - 1], lsp_id); if (!lsp) { sched_debug( "ISIS (%s): Pseudonode LSP does not exist yet, nothing to regenerate.", @@ -1869,7 +1839,6 @@ int lsp_tick(struct thread *thread) { struct isis_area *area; struct isis_lsp *lsp; - dnode_t *dnode, *dnode_next; int level; uint16_t rem_lifetime; bool fabricd_sync_incomplete = false; @@ -1885,83 +1854,69 @@ int lsp_tick(struct thread *thread) * Remove LSPs which have aged out */ for (level = 0; level < ISIS_LEVELS; level++) { - if (area->lspdb[level] && dict_count(area->lspdb[level]) > 0) { - for (dnode = dict_first(area->lspdb[level]); - dnode != NULL; dnode = dnode_next) { - dnode_next = - dict_next(area->lspdb[level], dnode); - lsp = dnode_get(dnode); - - /* - * The lsp rem_lifetime is kept at 0 for MaxAge - * or - * ZeroAgeLifetime depending on explicit purge - * or - * natural age out. So schedule spf only once - * when - * the first time rem_lifetime becomes 0. - */ - rem_lifetime = lsp->hdr.rem_lifetime; - lsp_set_time(lsp); - - /* - * Schedule may run spf which should be done - * only after - * the lsp rem_lifetime becomes 0 for the first - * time. - * ISO 10589 - 7.3.16.4 first paragraph. - */ - if (rem_lifetime == 1 && lsp->hdr.seqno != 0) { - /* 7.3.16.4 a) set SRM flags on all */ - /* 7.3.16.4 b) retain only the header */ - if (lsp->area->purge_originator) - lsp_purge(lsp, lsp->level, NULL); - else - lsp_flood(lsp, NULL); - /* 7.3.16.4 c) record the time to purge - * FIXME */ - isis_spf_schedule(lsp->area, lsp->level); - } + struct isis_lsp *next = lspdb_first(&area->lspdb[level]); + frr_each_from (lspdb, &area->lspdb[level], lsp, next) { + /* + * The lsp rem_lifetime is kept at 0 for MaxAge + * or + * ZeroAgeLifetime depending on explicit purge + * or + * natural age out. So schedule spf only once + * when + * the first time rem_lifetime becomes 0. + */ + rem_lifetime = lsp->hdr.rem_lifetime; + lsp_set_time(lsp); - if (lsp->age_out == 0) { - zlog_debug( - "ISIS-Upd (%s): L%u LSP %s seq " - "0x%08" PRIx32 " aged out", - area->area_tag, lsp->level, - rawlspid_print(lsp->hdr.lsp_id), - lsp->hdr.seqno); - - /* if we're aging out fragment 0, - * lsp_destroy() below will delete all - * other fragments too, so we need to - * skip over those - */ - while (!LSP_FRAGMENT(lsp->hdr.lsp_id) - && dnode_next) { - struct isis_lsp *nextlsp; - - nextlsp = dnode_get(dnode_next); - if (memcmp(nextlsp->hdr.lsp_id, - lsp->hdr.lsp_id, - ISIS_SYS_ID_LEN + 1)) - break; - - dnode_next = dict_next( - area->lspdb[level], - dnode_next); - } + /* + * Schedule may run spf which should be done + * only after + * the lsp rem_lifetime becomes 0 for the first + * time. + * ISO 10589 - 7.3.16.4 first paragraph. + */ + if (rem_lifetime == 1 && lsp->hdr.seqno != 0) { + /* 7.3.16.4 a) set SRM flags on all */ + /* 7.3.16.4 b) retain only the header */ + if (lsp->area->purge_originator) + lsp_purge(lsp, lsp->level, NULL); + else + lsp_flood(lsp, NULL); + /* 7.3.16.4 c) record the time to purge + * FIXME */ + isis_spf_schedule(lsp->area, lsp->level); + } - lsp_destroy(lsp); - lsp = NULL; - dict_delete_free(area->lspdb[level], - dnode); - } + if (lsp->age_out == 0) { + zlog_debug( + "ISIS-Upd (%s): L%u LSP %s seq " + "0x%08" PRIx32 " aged out", + area->area_tag, lsp->level, + rawlspid_print(lsp->hdr.lsp_id), + lsp->hdr.seqno); + + /* if we're aging out fragment 0, lsp_destroy() + * below will delete all other fragments too, + * so we need to skip over those + */ + if (!LSP_FRAGMENT(lsp->hdr.lsp_id)) + while (next && + !memcmp(next->hdr.lsp_id, + lsp->hdr.lsp_id, + ISIS_SYS_ID_LEN + 1)) + next = lspdb_next( + &area->lspdb[level], + next); + + lspdb_del(&area->lspdb[level], lsp); + lsp_destroy(lsp); + lsp = NULL; + } - if (fabricd_init_c && lsp) { - fabricd_sync_incomplete |= - ISIS_CHECK_FLAG(lsp->SSNflags, - fabricd_init_c); - } + if (fabricd_init_c && lsp) { + fabricd_sync_incomplete |= + ISIS_CHECK_FLAG(lsp->SSNflags, + fabricd_init_c); } } } @@ -1979,7 +1934,7 @@ void lsp_purge_pseudo(uint8_t *id, struct isis_circuit *circuit, int level) { struct isis_lsp *lsp; - lsp = lsp_search(id, circuit->area->lspdb[level - 1]); + lsp = lsp_search(&circuit->area->lspdb[level - 1], id); if (!lsp) return; @@ -2012,7 +1967,7 @@ void lsp_purge_non_exist(int level, struct isis_lsp_hdr *hdr, lsp_pack_pdu(lsp); - lsp_insert(lsp, area->lspdb[lsp->level - 1]); + lsp_insert(&area->lspdb[lsp->level - 1], lsp); lsp_flood(lsp, NULL); return; diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index e6ea0b4eda7f..4cbca5d517c3 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -24,13 +24,18 @@ #ifndef _ZEBRA_ISIS_LSP_H #define _ZEBRA_ISIS_LSP_H +#include "lib/typesafe.h" #include "isisd/isis_pdu.h" +PREDECL_RBTREE_UNIQ(lspdb) + /* Structure for isis_lsp, this structure will only support the fixed * System ID (Currently 6) (atleast for now). In order to support more * We will have to split the header into two parts, and for readability * sake it should better be avoided */ struct isis_lsp { + struct lspdb_item dbe; + struct isis_lsp_hdr hdr; struct stream *pdu; /* full pdu lsp */ union { @@ -54,8 +59,11 @@ struct isis_lsp { bool flooding_circuit_scoped; }; -dict_t *lsp_db_init(void); -void lsp_db_destroy(dict_t *lspdb); +extern int lspdb_compare(const struct isis_lsp *a, const struct isis_lsp *b); +DECLARE_RBTREE_UNIQ(lspdb, struct isis_lsp, dbe, lspdb_compare) + +void lsp_db_init(struct lspdb_head *head); +void lsp_db_fini(struct lspdb_head *head); int lsp_tick(struct thread *thread); int lsp_generate(struct isis_area *area, int level); @@ -76,14 +84,16 @@ struct isis_lsp *lsp_new_from_recv(struct isis_lsp_hdr *hdr, struct isis_tlvs *tlvs, struct stream *stream, struct isis_lsp *lsp0, struct isis_area *area, int level); -void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb); -struct isis_lsp *lsp_search(uint8_t *id, dict_t *lspdb); +void lsp_insert(struct lspdb_head *head, struct isis_lsp *lsp); +struct isis_lsp *lsp_search(struct lspdb_head *head, const uint8_t *id); -void lsp_build_list(uint8_t *start_id, uint8_t *stop_id, uint8_t num_lsps, - struct list *list, dict_t *lspdb); -void lsp_build_list_nonzero_ht(uint8_t *start_id, uint8_t *stop_id, - struct list *list, dict_t *lspdb); -void lsp_search_and_destroy(uint8_t *id, dict_t *lspdb); +void lsp_build_list(struct lspdb_head *head, const uint8_t *start_id, + const uint8_t *stop_id, uint8_t num_lsps, + struct list *list); +void lsp_build_list_nonzero_ht(struct lspdb_head *head, + const uint8_t *start_id, + const uint8_t *stop_id, struct list *list); +void lsp_search_and_destroy(struct lspdb_head *head, const uint8_t *id); void lsp_purge_pseudo(uint8_t *id, struct isis_circuit *circuit, int level); void lsp_purge_non_exist(int level, struct isis_lsp_hdr *hdr, struct isis_area *area); @@ -108,7 +118,8 @@ void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno); void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag); void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost); void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost); -int lsp_print_all(struct vty *vty, dict_t *lspdb, char detail, char dynhost); +int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail, + char dynhost); /* sets SRMflags for all active circuits of an lsp */ void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set); diff --git a/isisd/isis_main.c b/isisd/isis_main.c index e74a9baadd2a..48ae76017351 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -41,7 +41,6 @@ #include "qobj.h" #include "libfrr.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c index 2ce68262ebd7..0a42adea37ba 100644 --- a/isisd/isis_misc.c +++ b/isisd/isis_misc.c @@ -29,7 +29,6 @@ #include "if.h" #include "command.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_northbound.c b/isisd/isis_northbound.c index 95595e37b9d9..d5cdec154b4d 100644 --- a/isisd/isis_northbound.c +++ b/isisd/isis_northbound.c @@ -25,7 +25,6 @@ #include "libfrr.h" #include "linklist.h" #include "log.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" @@ -1365,19 +1364,40 @@ isis_instance_log_adjacency_changes_modify(enum nb_event event, } /* - * XPath: /frr-isisd:isis/mpls-te + * XPath: /frr-isisd:isis/instance/mpls-te */ -static int isis_mpls_te_create(enum nb_event event, +static int isis_instance_mpls_te_create(enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource) { struct listnode *node; + struct isis_area *area; struct isis_circuit *circuit; if (event != NB_EV_APPLY) return NB_OK; - isisMplsTE.status = enable; + area = nb_running_get_entry(dnode, NULL, true); + if (area->mta == NULL) { + + struct mpls_te_area *new; + + zlog_debug("ISIS MPLS-TE: Initialize area %s", + area->area_tag); + + new = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof(struct mpls_te_area)); + + /* Initialize MPLS_TE structure */ + new->status = enable; + new->level = 0; + new->inter_as = off; + new->interas_areaid.s_addr = 0; + new->router_id.s_addr = 0; + + area->mta = new; + } else { + area->mta->status = enable; + } /* * Following code is intended to handle two cases; @@ -1387,11 +1407,11 @@ static int isis_mpls_te_create(enum nb_event event, * MPLS_TE flag * 2) MPLS-TE was once enabled then disabled, and now enabled again. */ - for (ALL_LIST_ELEMENTS_RO(isisMplsTE.cir_list, node, circuit)) { + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { if (circuit->mtc == NULL || IS_FLOOD_AS(circuit->mtc->type)) continue; - if ((circuit->mtc->status == disable) + if (!IS_MPLS_TE(circuit->mtc) && HAS_LINK_PARAMS(circuit->interface)) circuit->mtc->status = enable; else @@ -1406,19 +1426,24 @@ static int isis_mpls_te_create(enum nb_event event, return NB_OK; } -static int isis_mpls_te_destroy(enum nb_event event, +static int isis_instance_mpls_te_destroy(enum nb_event event, const struct lyd_node *dnode) { struct listnode *node; + struct isis_area *area; struct isis_circuit *circuit; if (event != NB_EV_APPLY) return NB_OK; - isisMplsTE.status = disable; + area = nb_running_get_entry(dnode, NULL, true); + if (IS_MPLS_TE(area->mta)) + area->mta->status = disable; + else + return NB_OK; /* Flush LSP if circuit engage */ - for (ALL_LIST_ELEMENTS_RO(isisMplsTE.cir_list, node, circuit)) { + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { if (circuit->mtc == NULL || (circuit->mtc->status == disable)) continue; @@ -1435,55 +1460,53 @@ static int isis_mpls_te_destroy(enum nb_event event, } /* - * XPath: /frr-isisd:isis/mpls-te/router-address + * XPath: /frr-isisd:isis/instance/mpls-te/router-address */ -static int isis_mpls_te_router_address_modify(enum nb_event event, +static int isis_instance_mpls_te_router_address_modify(enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource) { struct in_addr value; - struct listnode *node; struct isis_area *area; if (event != NB_EV_APPLY) return NB_OK; - yang_dnode_get_ipv4(&value, dnode, NULL); - isisMplsTE.router_id.s_addr = value.s_addr; + area = nb_running_get_entry(dnode, NULL, true); /* only proceed if MPLS-TE is enabled */ - if (isisMplsTE.status == disable) + if (!IS_MPLS_TE(area->mta)) return NB_OK; - /* Update main Router ID in isis global structure */ - isis->router_id = value.s_addr; + /* Update Area Router ID */ + yang_dnode_get_ipv4(&value, dnode, NULL); + area->mta->router_id.s_addr = value.s_addr; + /* And re-schedule LSP update */ - for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) - if (listcount(area->area_addrs) > 0) - lsp_regenerate_schedule(area, area->is_type, 0); + if (listcount(area->area_addrs) > 0) + lsp_regenerate_schedule(area, area->is_type, 0); return NB_OK; } -static int isis_mpls_te_router_address_destroy(enum nb_event event, +static int isis_instance_mpls_te_router_address_destroy(enum nb_event event, const struct lyd_node *dnode) { - struct listnode *node; struct isis_area *area; if (event != NB_EV_APPLY) return NB_OK; - isisMplsTE.router_id.s_addr = INADDR_ANY; + area = nb_running_get_entry(dnode, NULL, true); /* only proceed if MPLS-TE is enabled */ - if (isisMplsTE.status == disable) + if (!IS_MPLS_TE(area->mta)) return NB_OK; - /* Update main Router ID in isis global structure */ - isis->router_id = 0; + /* Reset Area Router ID */ + area->mta->router_id.s_addr = INADDR_ANY; + /* And re-schedule LSP update */ - for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) - if (listcount(area->area_addrs) > 0) - lsp_regenerate_schedule(area, area->is_type, 0); + if (listcount(area->area_addrs) > 0) + lsp_regenerate_schedule(area, area->is_type, 0); return NB_OK; } @@ -3015,15 +3038,15 @@ const struct frr_yang_module_info frr_isisd_info = { .cbs.cli_show = cli_show_isis_log_adjacency, }, { - .xpath = "/frr-isisd:isis/mpls-te", - .cbs.create = isis_mpls_te_create, - .cbs.destroy = isis_mpls_te_destroy, + .xpath = "/frr-isisd:isis/instance/mpls-te", + .cbs.create = isis_instance_mpls_te_create, + .cbs.destroy = isis_instance_mpls_te_destroy, .cbs.cli_show = cli_show_isis_mpls_te, }, { - .xpath = "/frr-isisd:isis/mpls-te/router-address", - .cbs.modify = isis_mpls_te_router_address_modify, - .cbs.destroy = isis_mpls_te_router_address_destroy, + .xpath = "/frr-isisd:isis/instance/mpls-te/router-address", + .cbs.modify = isis_instance_mpls_te_router_address_modify, + .cbs.destroy = isis_instance_mpls_te_router_address_destroy, .cbs.cli_show = cli_show_isis_mpls_te_router_addr, }, { diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 8e9302963dab..3d16d5601656 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -36,7 +36,6 @@ #include "md5.h" #include "lib_errors.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" @@ -201,8 +200,9 @@ static int process_p2p_hello(struct iih_info *iih) adj); /* Update MPLS TE Remote IP address parameter if possible */ - if (IS_MPLS_TE(isisMplsTE) && iih->circuit->mtc - && IS_CIRCUIT_TE(iih->circuit->mtc) && adj->ipv4_address_count) + if (IS_MPLS_TE(iih->circuit->area->mta) + && IS_MPLS_TE(iih->circuit->mtc) + && adj->ipv4_address_count) set_circuitparams_rmt_ipaddr(iih->circuit->mtc, adj->ipv4_addresses[0]); @@ -959,7 +959,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, /* Find the LSP in our database and compare it to this Link State header */ struct isis_lsp *lsp = - lsp_search(hdr.lsp_id, circuit->area->lspdb[level - 1]); + lsp_search(&circuit->area->lspdb[level - 1], hdr.lsp_id); int comp = 0; if (lsp) comp = lsp_compare(circuit->area->area_tag, lsp, hdr.seqno, @@ -1186,7 +1186,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, memcpy(lspid, hdr.lsp_id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(lspid) = 0; lsp0 = lsp_search( - lspid, circuit->area->lspdb[level - 1]); + &circuit->area->lspdb[level - 1], lspid); if (!lsp0) { zlog_debug( "Got lsp frag, while zero lsp not in database"); @@ -1199,8 +1199,8 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, &hdr, tlvs, circuit->rcv_stream, lsp0, circuit->area, level); tlvs = NULL; - lsp_insert(lsp, - circuit->area->lspdb[level - 1]); + lsp_insert(&circuit->area->lspdb[level - 1], + lsp); } else /* exists, so we overwrite */ { lsp_update(lsp, &hdr, tlvs, circuit->rcv_stream, @@ -1416,7 +1416,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, for (struct isis_lsp_entry *entry = entry_head; entry; entry = entry->next) { struct isis_lsp *lsp = - lsp_search(entry->id, circuit->area->lspdb[level - 1]); + lsp_search(&circuit->area->lspdb[level - 1], entry->id); bool own_lsp = !memcmp(entry->id, isis->sysid, ISIS_SYS_ID_LEN); if (lsp) { /* 7.3.15.2 b) 1) is this LSP newer */ @@ -1467,8 +1467,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(lspid) = 0; lsp0 = lsp_search( - lspid, - circuit->area->lspdb[level - 1]); + &circuit->area->lspdb[level - 1], + lspid); if (!lsp0) { zlog_debug("Got lsp frag in snp, while zero not in database"); continue; @@ -1477,8 +1477,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, lsp = lsp_new(circuit->area, entry->id, entry->rem_lifetime, 0, 0, entry->checksum, lsp0, level); - lsp_insert(lsp, - circuit->area->lspdb[level - 1]); + lsp_insert(&circuit->area->lspdb[level - 1], + lsp); lsp_set_all_srmflags(lsp, false); ISIS_SET_FLAG(lsp->SSNflags, circuit); @@ -1495,8 +1495,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, * start_lsp_id and stop_lsp_id */ struct list *lsp_list = list_new(); - lsp_build_list_nonzero_ht(start_lsp_id, stop_lsp_id, lsp_list, - circuit->area->lspdb[level - 1]); + lsp_build_list_nonzero_ht(&circuit->area->lspdb[level - 1], + start_lsp_id, stop_lsp_id, lsp_list); /* Fixme: Find a better solution */ struct listnode *node, *nnode; @@ -2040,8 +2040,7 @@ static uint16_t get_max_lsp_count(uint16_t size) int send_csnp(struct isis_circuit *circuit, int level) { - if (circuit->area->lspdb[level - 1] == NULL - || dict_count(circuit->area->lspdb[level - 1]) == 0) + if (lspdb_count(&circuit->area->lspdb[level - 1]) == 0) return ISIS_OK; uint8_t pdu_type = (level == ISIS_LEVEL1) ? L1_COMPLETE_SEQ_NUM @@ -2094,7 +2093,7 @@ int send_csnp(struct isis_circuit *circuit, int level) struct isis_lsp *last_lsp; isis_tlvs_add_csnp_entries(tlvs, start, stop, num_lsps, - circuit->area->lspdb[level - 1], + &circuit->area->lspdb[level - 1], &last_lsp); /* * Update the stop lsp_id before encoding this CSNP. @@ -2215,8 +2214,7 @@ static int send_psnp(int level, struct isis_circuit *circuit) && circuit->u.bc.is_dr[level - 1]) return ISIS_OK; - if (circuit->area->lspdb[level - 1] == NULL - || dict_count(circuit->area->lspdb[level - 1]) == 0) + if (lspdb_count(&circuit->area->lspdb[level - 1]) == 0) return ISIS_OK; if (!circuit->snd_stream) @@ -2254,16 +2252,13 @@ static int send_psnp(int level, struct isis_circuit *circuit) get_max_lsp_count(STREAM_WRITEABLE(circuit->snd_stream)); while (1) { + struct isis_lsp *lsp; + tlvs = isis_alloc_tlvs(); if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) isis_tlvs_add_auth(tlvs, passwd); - for (dnode_t *dnode = - dict_first(circuit->area->lspdb[level - 1]); - dnode; dnode = dict_next(circuit->area->lspdb[level - 1], - dnode)) { - struct isis_lsp *lsp = dnode_get(dnode); - + frr_each (lspdb, &circuit->area->lspdb[level - 1], lsp) { if (ISIS_CHECK_FLAG(lsp->SSNflags, circuit)) isis_tlvs_add_lsp_entry(tlvs, lsp); diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c index 2f6526bc5efa..ea66e6950e68 100644 --- a/isisd/isis_pfpacket.c +++ b/isisd/isis_pfpacket.c @@ -33,7 +33,6 @@ #include "if.h" #include "lib_errors.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_circuit.h" @@ -74,7 +73,6 @@ uint8_t ALL_ISS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x05}; uint8_t ALL_ESS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x04}; static uint8_t discard_buff[8192]; -static uint8_t sock_buff[8192]; /* * if level is 0 we are joining p2p multicast @@ -278,19 +276,22 @@ int isis_recv_pdu_bcast(struct isis_circuit *circuit, uint8_t *ssnpa) return ISIS_WARNING; } - /* on lan we have to read to the static buff first */ - bytesread = recvfrom(circuit->fd, sock_buff, sizeof(sock_buff), - MSG_DONTWAIT, (struct sockaddr *)&s_addr, - (socklen_t *)&addr_len); + /* Ensure that we have enough space for a pdu padded to fill the mtu */ + unsigned int max_size = + circuit->interface->mtu > circuit->interface->mtu6 + ? circuit->interface->mtu + : circuit->interface->mtu6; + uint8_t temp_buff[max_size]; + bytesread = + recvfrom(circuit->fd, temp_buff, max_size, MSG_DONTWAIT, + (struct sockaddr *)&s_addr, (socklen_t *)&addr_len); if (bytesread < 0) { - zlog_warn("isis_recv_pdu_bcast(): recvfrom() failed"); + zlog_warn("%s: recvfrom() failed", __func__); return ISIS_WARNING; } - /* then we lose the LLC */ - stream_write(circuit->rcv_stream, sock_buff + LLC_LEN, + stream_write(circuit->rcv_stream, temp_buff + LLC_LEN, bytesread - LLC_LEN); - memcpy(ssnpa, &s_addr.sll_addr, s_addr.sll_halen); return ISIS_OK; @@ -338,6 +339,7 @@ int isis_send_pdu_bcast(struct isis_circuit *circuit, int level) { struct msghdr msg; struct iovec iov[2]; + char temp_buff[LLC_LEN]; /* we need to do the LLC in here because of P2P circuits, which will * not need it @@ -362,16 +364,16 @@ int isis_send_pdu_bcast(struct isis_circuit *circuit, int level) /* on a broadcast circuit */ /* first we put the LLC in */ - sock_buff[0] = 0xFE; - sock_buff[1] = 0xFE; - sock_buff[2] = 0x03; + temp_buff[0] = 0xFE; + temp_buff[1] = 0xFE; + temp_buff[2] = 0x03; memset(&msg, 0, sizeof(msg)); msg.msg_name = &sa; msg.msg_namelen = sizeof(struct sockaddr_ll); msg.msg_iov = iov; msg.msg_iovlen = 2; - iov[0].iov_base = sock_buff; + iov[0].iov_base = temp_buff; iov[0].iov_len = LLC_LEN; iov[1].iov_base = circuit->snd_stream->data; iov[1].iov_len = stream_get_endp(circuit->snd_stream); diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c index 9047707bf0df..dc23e8ea499d 100644 --- a/isisd/isis_redist.c +++ b/isisd/isis_redist.c @@ -32,7 +32,6 @@ #include "vty.h" #include "srcdest_table.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" diff --git a/isisd/isis_route.c b/isisd/isis_route.c index 1439a4229a1a..281eaf11bc96 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -38,7 +38,6 @@ #include "isis_constants.h" #include "isis_common.h" #include "isis_flags.h" -#include "dict.h" #include "isisd.h" #include "isis_misc.h" #include "isis_adjacency.h" @@ -544,7 +543,8 @@ void isis_route_verify_merge(struct isis_area *area, ISIS_ROUTE_FLAG_ZEBRA_SYNCED ); continue; - } else { + } else if (CHECK_FLAG(rinfo->flag, + ISIS_ROUTE_FLAG_ACTIVE)) { /* Clear the ZEBRA_SYNCED flag on the L1 * route when L2 wins, otherwise L1 * won't get reinstalled when it @@ -554,6 +554,11 @@ void isis_route_verify_merge(struct isis_area *area, mrinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED ); + } else if ( + CHECK_FLAG( + mrinfo->flag, + ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) { + continue; } } mrnode->info = rnode->info; diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c index 3c2cf7b3fcb5..d63676256b3b 100644 --- a/isisd/isis_routemap.c +++ b/isisd/isis_routemap.c @@ -37,7 +37,6 @@ #include "isis_constants.h" #include "isis_common.h" #include "isis_flags.h" -#include "dict.h" #include "isisd.h" #include "isis_misc.h" #include "isis_adjacency.h" diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 18eb857ec95e..a28220eb2603 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -39,7 +39,6 @@ #include "isis_constants.h" #include "isis_common.h" #include "isis_flags.h" -#include "dict.h" #include "isisd.h" #include "isis_misc.h" #include "isis_adjacency.h" @@ -313,7 +312,7 @@ static struct isis_lsp *isis_root_system_lsp(struct isis_area *area, int level, memcpy(lspid, sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(lspid) = 0; LSP_FRAGMENT(lspid) = 0; - lsp = lsp_search(lspid, area->lspdb[level - 1]); + lsp = lsp_search(&area->lspdb[level - 1], lspid); if (lsp && lsp->hdr.rem_lifetime != 0) return lsp; return NULL; @@ -870,10 +869,8 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, [spftree->level - 1], parent); lsp = lsp_search( - lsp_id, - spftree->area - ->lspdb[spftree->level - - 1]); + &spftree->area->lspdb[spftree->level- 1], + lsp_id); if (lsp == NULL || lsp->hdr.rem_lifetime == 0) zlog_warn( @@ -923,8 +920,8 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, continue; } lsp = lsp_search( - lsp_id, - spftree->area->lspdb[spftree->level - 1]); + &spftree->area->lspdb[spftree->level - 1], + lsp_id); if (lsp == NULL || lsp->hdr.rem_lifetime == 0) { zlog_warn( "ISIS-Spf: No lsp (%p) found from root " diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h index 3a05df3f1493..a8185a8be0fe 100644 --- a/isisd/isis_spf_private.h +++ b/isisd/isis_spf_private.h @@ -79,9 +79,9 @@ struct isis_vertex_queue { }; __attribute__((__unused__)) -static unsigned isis_vertex_queue_hash_key(void *vp) +static unsigned isis_vertex_queue_hash_key(const void *vp) { - struct isis_vertex *vertex = vp; + const struct isis_vertex *vertex = vp; if (VTYPE_IP(vertex->type)) { uint32_t key; @@ -347,8 +347,8 @@ static struct isis_lsp *lsp_for_vertex(struct isis_spftree *spftree, memcpy(lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(lsp_id) = 0; - dict_t *lspdb = spftree->area->lspdb[spftree->level - 1]; - struct isis_lsp *lsp = lsp_search(lsp_id, lspdb); + struct lspdb_head *lspdb = &spftree->area->lspdb[spftree->level - 1]; + struct isis_lsp *lsp = lsp_search(lspdb, lsp_id); if (lsp && lsp->hdr.rem_lifetime != 0) return lsp; diff --git a/isisd/isis_te.c b/isisd/isis_te.c index 23a1f10a18ad..4ea6c2c60d65 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -43,7 +43,6 @@ #include "network.h" #include "sbuf.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" @@ -58,9 +57,6 @@ #include "isisd/isis_spf.h" #include "isisd/isis_te.h" -/* Global varial for MPLS TE management */ -struct isis_mpls_te isisMplsTE; - const char *mode2text[] = {"Disable", "Area", "AS", "Emulate"}; /*------------------------------------------------------------------------* @@ -624,7 +620,7 @@ void isis_link_params_update(struct isis_circuit *circuit, /* Finally Update LSP */ #if 0 - if (IS_MPLS_TE(isisMplsTE) && circuit->area) + if (circuit->area && IS_MPLS_TE(circuit->area->mta)) lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); #endif return; @@ -646,7 +642,7 @@ void isis_mpls_te_update(struct interface *ifp) isis_link_params_update(circuit, ifp); /* ... and LSP */ - if (IS_MPLS_TE(isisMplsTE) && circuit->area) + if (circuit->area && IS_MPLS_TE(circuit->area->mta)) lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); return; @@ -1058,35 +1054,11 @@ void mpls_te_print_detail(struct sbuf *buf, int indent, return; } -/* Specific MPLS TE router parameters write function */ -void isis_mpls_te_config_write_router(struct vty *vty) -{ - if (IS_MPLS_TE(isisMplsTE)) { - vty_out(vty, " mpls-te on\n"); - vty_out(vty, " mpls-te router-address %s\n", - inet_ntoa(isisMplsTE.router_id)); - } - - return; -} - - /*------------------------------------------------------------------------* * Followings are vty command functions. *------------------------------------------------------------------------*/ #ifndef FABRICD -/* Search MPLS TE Circuit context from Interface */ -static struct mpls_te_circuit *lookup_mpls_params_by_ifp(struct interface *ifp) -{ - struct isis_circuit *circuit; - - if ((circuit = circuit_scan_by_ifp(ifp)) == NULL) - return NULL; - - return circuit->mtc; -} - DEFUN (show_isis_mpls_te_router, show_isis_mpls_te_router_cmd, "show " PROTO_NAME " mpls-te router", @@ -1095,84 +1067,73 @@ DEFUN (show_isis_mpls_te_router, MPLS_TE_STR "Router information\n") { - if (IS_MPLS_TE(isisMplsTE)) { - vty_out(vty, "--- MPLS-TE router parameters ---\n"); - if (ntohs(isisMplsTE.router_id.s_addr) != 0) - vty_out(vty, " Router-Address: %s\n", - inet_ntoa(isisMplsTE.router_id)); + struct listnode *anode; + struct isis_area *area; + + if (!isis) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { + + if (!IS_MPLS_TE(area->mta)) + continue; + + vty_out(vty, "Area %s:\n", area->area_tag); + if (ntohs(area->mta->router_id.s_addr) != 0) + vty_out(vty, " MPLS-TE Router-Address: %s\n", + inet_ntoa(area->mta->router_id)); else vty_out(vty, " N/A\n"); - } else - vty_out(vty, " MPLS-TE is disable on this router\n"); + } return CMD_SUCCESS; } -static void show_mpls_te_sub(struct vty *vty, struct interface *ifp) +static void show_mpls_te_sub(struct vty *vty, char *name, + struct mpls_te_circuit *mtc) { - struct mpls_te_circuit *mtc; struct sbuf buf; sbuf_init(&buf, NULL, 0); - if ((IS_MPLS_TE(isisMplsTE)) - && ((mtc = lookup_mpls_params_by_ifp(ifp)) != NULL)) { - /* Continue only if interface is not passive or support Inter-AS - * TEv2 */ - if (mtc->status != enable) { - if (IS_INTER_AS(mtc->type)) { - vty_out(vty, - "-- Inter-AS TEv2 link parameters for %s --\n", - ifp->name); - } else { - /* MPLS-TE is not activate on this interface */ - /* or this interface is passive and Inter-AS - * TEv2 is not activate */ - vty_out(vty, - " %s: MPLS-TE is disabled on this interface\n", - ifp->name); - return; - } - } else { - vty_out(vty, "-- MPLS-TE link parameters for %s --\n", - ifp->name); - } - - sbuf_reset(&buf); - print_subtlv_admin_grp(&buf, 4, &mtc->admin_grp); + if (mtc->status != enable) + return; - if (SUBTLV_TYPE(mtc->local_ipaddr) != 0) - print_subtlv_local_ipaddr(&buf, 4, &mtc->local_ipaddr); - if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0) - print_subtlv_rmt_ipaddr(&buf, 4, &mtc->rmt_ipaddr); + vty_out(vty, "-- MPLS-TE link parameters for %s --\n", name); - print_subtlv_max_bw(&buf, 4, &mtc->max_bw); - print_subtlv_max_rsv_bw(&buf, 4, &mtc->max_rsv_bw); - print_subtlv_unrsv_bw(&buf, 4, &mtc->unrsv_bw); - print_subtlv_te_metric(&buf, 4, &mtc->te_metric); + sbuf_reset(&buf); + print_subtlv_admin_grp(&buf, 4, &mtc->admin_grp); - if (IS_INTER_AS(mtc->type)) { - if (SUBTLV_TYPE(mtc->ras) != 0) - print_subtlv_ras(&buf, 4, &mtc->ras); - if (SUBTLV_TYPE(mtc->rip) != 0) - print_subtlv_rip(&buf, 4, &mtc->rip); - } + if (SUBTLV_TYPE(mtc->local_ipaddr) != 0) + print_subtlv_local_ipaddr(&buf, 4, &mtc->local_ipaddr); + if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0) + print_subtlv_rmt_ipaddr(&buf, 4, &mtc->rmt_ipaddr); + + print_subtlv_max_bw(&buf, 4, &mtc->max_bw); + print_subtlv_max_rsv_bw(&buf, 4, &mtc->max_rsv_bw); + print_subtlv_unrsv_bw(&buf, 4, &mtc->unrsv_bw); + print_subtlv_te_metric(&buf, 4, &mtc->te_metric); + + if (IS_INTER_AS(mtc->type)) { + if (SUBTLV_TYPE(mtc->ras) != 0) + print_subtlv_ras(&buf, 4, &mtc->ras); + if (SUBTLV_TYPE(mtc->rip) != 0) + print_subtlv_rip(&buf, 4, &mtc->rip); + } - print_subtlv_av_delay(&buf, 4, &mtc->av_delay); - print_subtlv_mm_delay(&buf, 4, &mtc->mm_delay); - print_subtlv_delay_var(&buf, 4, &mtc->delay_var); - print_subtlv_pkt_loss(&buf, 4, &mtc->pkt_loss); - print_subtlv_res_bw(&buf, 4, &mtc->res_bw); - print_subtlv_ava_bw(&buf, 4, &mtc->ava_bw); - print_subtlv_use_bw(&buf, 4, &mtc->use_bw); + print_subtlv_av_delay(&buf, 4, &mtc->av_delay); + print_subtlv_mm_delay(&buf, 4, &mtc->mm_delay); + print_subtlv_delay_var(&buf, 4, &mtc->delay_var); + print_subtlv_pkt_loss(&buf, 4, &mtc->pkt_loss); + print_subtlv_res_bw(&buf, 4, &mtc->res_bw); + print_subtlv_ava_bw(&buf, 4, &mtc->ava_bw); + print_subtlv_use_bw(&buf, 4, &mtc->use_bw); - vty_multiline(vty, "", "%s", sbuf_buf(&buf)); - vty_out(vty, "---------------\n\n"); - } else { - vty_out(vty, " %s: MPLS-TE is disabled on this interface\n", - ifp->name); - } + vty_multiline(vty, "", "%s", sbuf_buf(&buf)); + vty_out(vty, "---------------\n\n"); sbuf_free(&buf); return; @@ -1187,23 +1148,45 @@ DEFUN (show_isis_mpls_te_interface, "Interface information\n" "Interface name\n") { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); - int idx_interface = 4; + struct listnode *anode, *cnode; + struct isis_area *area; + struct isis_circuit *circuit; struct interface *ifp; + int idx_interface = 4; - /* Show All Interfaces. */ - if (argc == 4) { - FOR_ALL_INTERFACES (vrf, ifp) - show_mpls_te_sub(vty, ifp); + if (!isis) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; } - /* Interface name is specified. */ - else { - if ((ifp = if_lookup_by_name(argv[idx_interface]->arg, - VRF_DEFAULT)) - == NULL) + + if (argc == idx_interface) { + /* Show All Interfaces. */ + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { + + if (!IS_MPLS_TE(area->mta)) + continue; + + vty_out(vty, "Area %s:\n", area->area_tag); + + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, + circuit)) + show_mpls_te_sub(vty, circuit->interface->name, + circuit->mtc); + } + } else { + /* Interface name is specified. */ + ifp = if_lookup_by_name(argv[idx_interface]->arg, VRF_DEFAULT); + if (ifp == NULL) vty_out(vty, "No such interface name\n"); - else - show_mpls_te_sub(vty, ifp); + else { + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) + vty_out(vty, + "ISIS is not enabled on circuit %s\n", + ifp->name); + else + show_mpls_te_sub(vty, ifp->name, circuit->mtc); + } } return CMD_SUCCESS; @@ -1214,16 +1197,6 @@ DEFUN (show_isis_mpls_te_interface, void isis_mpls_te_init(void) { - zlog_debug("ISIS MPLS-TE: Initialize"); - - /* Initialize MPLS_TE structure */ - isisMplsTE.status = disable; - isisMplsTE.level = 0; - isisMplsTE.inter_as = off; - isisMplsTE.interas_areaid.s_addr = 0; - isisMplsTE.cir_list = list_new(); - isisMplsTE.router_id.s_addr = 0; - #ifndef FABRICD /* Register new VTY commands */ install_element(VIEW_NODE, &show_isis_mpls_te_router_cmd); diff --git a/isisd/isis_te.h b/isisd/isis_te.h index e9eff08cd138..beb0c1836ff9 100644 --- a/isisd/isis_te.h +++ b/isisd/isis_te.h @@ -244,11 +244,10 @@ typedef enum _status_t { disable, enable, learn } status_t; /* Mode for Inter-AS LSP */ /* TODO: Check how if LSP is flooded in RFC5316 */ typedef enum _interas_mode_t { off, region, as, emulate } interas_mode_t; -#define IS_MPLS_TE(m) (m.status == enable) -#define IS_CIRCUIT_TE(c) (c->status == enable) +#define IS_MPLS_TE(m) (m && m->status == enable) -/* Following structure are internal use only. */ -struct isis_mpls_te { +/* Per area MPLS-TE parameters */ +struct mpls_te_area { /* Status of MPLS-TE: enable or disable */ status_t status; @@ -259,15 +258,11 @@ struct isis_mpls_te { interas_mode_t inter_as; struct in_addr interas_areaid; - /* Circuit list on which TE are enable */ - struct list *cir_list; - /* MPLS_TE router ID */ struct in_addr router_id; }; -extern struct isis_mpls_te isisMplsTE; - +/* Per Circuit MPLS-TE parameters */ struct mpls_te_circuit { /* Status of MPLS-TE on this interface */ @@ -318,6 +313,5 @@ uint8_t add_te_subtlvs(uint8_t *, struct mpls_te_circuit *); uint8_t build_te_subtlvs(uint8_t *, struct isis_circuit *); void isis_link_params_update(struct isis_circuit *, struct interface *); void isis_mpls_te_update(struct interface *); -void isis_mpls_te_config_write_router(struct vty *); #endif /* _ZEBRA_ISIS_MPLS_TE_H */ diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index fbb1e5714cc9..6edfeb4c97cc 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -405,7 +405,7 @@ static int pack_subtlvs(struct isis_subtlvs *subtlvs, struct stream *s) static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len, struct stream *stream, struct sbuf *log, void *dest, - int indent); + int indent, bool *unpacked_known_tlvs); /* Functions related to TLVs 1 Area Addresses */ @@ -796,7 +796,7 @@ static int unpack_item_extended_reach(uint16_t mtid, uint8_t len, size_t subtlv_start = stream_get_getp(s); if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_NE_REACH, subtlv_len, s, - log, NULL, indent + 4)) { + log, NULL, indent + 4, NULL)) { goto out; } @@ -1272,6 +1272,11 @@ static void format_item_extended_ip_reach(uint16_t mtid, struct isis_item *i, if (mtid != ISIS_MT_IPV4_UNICAST) sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); sbuf_push(buf, 0, "\n"); + + if (r->subtlvs) { + sbuf_push(buf, indent, " Subtlvs:\n"); + format_subtlvs(r->subtlvs, buf, indent + 4); + } } static void free_item_extended_ip_reach(struct isis_item *i) @@ -1386,10 +1391,16 @@ static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len, } rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH); + bool unpacked_known_tlvs = false; + if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IP_REACH, subtlv_len, s, - log, rv->subtlvs, indent + 4)) { + log, rv->subtlvs, indent + 4, &unpacked_known_tlvs)) { goto out; } + if (!unpacked_known_tlvs) { + isis_free_subtlvs(rv->subtlvs); + rv->subtlvs = NULL; + } } append_item(items, (struct isis_item *)rv); @@ -1865,10 +1876,16 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s, } rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH); + bool unpacked_known_tlvs = false; + if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH, subtlv_len, s, - log, rv->subtlvs, indent + 4)) { + log, rv->subtlvs, indent + 4, &unpacked_known_tlvs)) { goto out; } + if (!unpacked_known_tlvs) { + isis_free_subtlvs(rv->subtlvs); + rv->subtlvs = NULL; + } } append_item(items, (struct isis_item *)rv); @@ -2966,7 +2983,7 @@ static int unpack_tlv_unknown(enum isis_tlv_context context, uint8_t tlv_type, static int unpack_tlv(enum isis_tlv_context context, size_t avail_len, struct stream *stream, struct sbuf *log, void *dest, - int indent) + int indent, bool *unpacked_known_tlvs) { uint8_t tlv_type, tlv_len; const struct tlv_ops *ops; @@ -2997,6 +3014,8 @@ static int unpack_tlv(enum isis_tlv_context context, size_t avail_len, ops = tlv_table[context][tlv_type]; if (ops && ops->unpack) { + if (unpacked_known_tlvs) + *unpacked_known_tlvs = true; return ops->unpack(context, tlv_type, tlv_len, stream, log, dest, indent + 2); } @@ -3007,7 +3026,7 @@ static int unpack_tlv(enum isis_tlv_context context, size_t avail_len, static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len, struct stream *stream, struct sbuf *log, void *dest, - int indent) + int indent, bool *unpacked_known_tlvs) { int rv; size_t tlv_start, tlv_pos; @@ -3020,7 +3039,7 @@ static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len, while (tlv_pos < avail_len) { rv = unpack_tlv(context, avail_len - tlv_pos, stream, log, dest, - indent + 2); + indent + 2, unpacked_known_tlvs); if (rv) return rv; @@ -3052,7 +3071,7 @@ int isis_unpack_tlvs(size_t avail_len, struct stream *stream, result = isis_alloc_tlvs(); rv = unpack_tlvs(ISIS_CONTEXT_LSP, avail_len, stream, &logbuf, result, - indent); + indent, NULL); *log = sbuf_buf(&logbuf); *dest = result; @@ -3252,13 +3271,18 @@ void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs, { struct listnode *node; struct prefix_ipv6 *ip_addr; + unsigned int addr_count = 0; for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) { + if (addr_count >= 15) + break; + struct isis_ipv6_address *a = XCALLOC(MTYPE_ISIS_TLV, sizeof(*a)); a->addr = ip_addr->prefix; append_item(&tlvs->ipv6_address, (struct isis_item *)a); + addr_count++; } } @@ -3522,26 +3546,24 @@ void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp) void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id, uint8_t *stop_id, uint16_t num_lsps, - dict_t *lspdb, struct isis_lsp **last_lsp) + struct lspdb_head *head, + struct isis_lsp **last_lsp) { - dnode_t *first = dict_lower_bound(lspdb, start_id); + struct isis_lsp searchfor; + struct isis_lsp *first, *lsp; + + memcpy(&searchfor.hdr.lsp_id, start_id, sizeof(searchfor.hdr.lsp_id)); + first = lspdb_find_gteq(head, &searchfor); if (!first) return; - dnode_t *last = dict_upper_bound(lspdb, stop_id); - dnode_t *curr = first; - - isis_tlvs_add_lsp_entry(tlvs, first->dict_data); - *last_lsp = first->dict_data; - - while (curr) { - curr = dict_next(lspdb, curr); - if (curr) { - isis_tlvs_add_lsp_entry(tlvs, curr->dict_data); - *last_lsp = curr->dict_data; - } - if (curr == last || tlvs->lsp_entries.count == num_lsps) + frr_each_from (lspdb, head, lsp, first) { + if (memcmp(lsp->hdr.lsp_id, stop_id, sizeof(lsp->hdr.lsp_id)) + > 0 || tlvs->lsp_entries.count == num_lsps) break; + + isis_tlvs_add_lsp_entry(tlvs, lsp); + *last_lsp = lsp; } } diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index fce30d4ee775..4954d791d86e 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -25,8 +25,8 @@ #include "openbsd-tree.h" #include "prefix.h" -#include "isisd/dict.h" +struct lspdb_head; struct isis_subtlvs; struct isis_area_address; @@ -355,7 +355,8 @@ bool isis_tlvs_own_snpa_found(struct isis_tlvs *tlvs, uint8_t *snpa); void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp); void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id, uint8_t *stop_id, uint16_t num_lsps, - dict_t *lspdb, struct isis_lsp **last_lsp); + struct lspdb_head *lspdb, + struct isis_lsp **last_lsp); void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs, const char *hostname); void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs, diff --git a/isisd/isis_tx_queue.c b/isisd/isis_tx_queue.c index 270dcae7d0a0..507fd489bc62 100644 --- a/isisd/isis_tx_queue.c +++ b/isisd/isis_tx_queue.c @@ -27,7 +27,6 @@ #include "isisd/isisd.h" #include "isisd/isis_memory.h" #include "isisd/isis_flags.h" -#include "dict.h" #include "isisd/isis_circuit.h" #include "isisd/isis_lsp.h" #include "isisd/isis_misc.h" @@ -51,9 +50,9 @@ struct isis_tx_queue_entry { struct isis_tx_queue *queue; }; -static unsigned tx_queue_hash_key(void *p) +static unsigned tx_queue_hash_key(const void *p) { - struct isis_tx_queue_entry *e = p; + const struct isis_tx_queue_entry *e = p; uint32_t id_key = jhash(e->lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2, 0x55aa5a5a); diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c index b2c0440de6ce..431ad9712aea 100644 --- a/isisd/isis_vty_fabricd.c +++ b/isisd/isis_vty_fabricd.c @@ -161,13 +161,14 @@ DEFUN (show_lsp_flooding, struct isis_area *area; for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { - dict_t *lspdb = area->lspdb[ISIS_LEVEL2 - 1]; + struct lspdb_head *head = &area->lspdb[ISIS_LEVEL2 - 1]; + struct isis_lsp *lsp; vty_out(vty, "Area %s:\n", area->area_tag ? area->area_tag : "null"); if (lspid) { - struct isis_lsp *lsp = lsp_for_arg(lspid, lspdb); + lsp = lsp_for_arg(head, lspid); if (lsp) lsp_print_flooding(vty, lsp); @@ -175,9 +176,8 @@ DEFUN (show_lsp_flooding, continue; } - for (dnode_t *dnode = dict_first(lspdb); dnode; - dnode = dict_next(lspdb, dnode)) { - lsp_print_flooding(vty, dnode_get(dnode)); + frr_each (lspdb, head, lsp) { + lsp_print_flooding(vty, lsp); vty_out(vty, "\n"); } } diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 79d79f891164..e2ef93469630 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -37,7 +37,6 @@ #include "vrf.h" #include "libfrr.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" @@ -54,20 +53,12 @@ struct zclient *zclient = NULL; /* Router-id update message from zebra. */ -static int isis_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct isis_area *area; struct listnode *node; struct prefix router_id; - /* - * If ISIS TE is enable, TE Router ID is set through specific command. - * See mpls_te_router_addr() command in isis_te.c - */ - if (IS_MPLS_TE(isisMplsTE)) - return 0; - zebra_router_id_update_read(zclient->ibuf, &router_id); if (isis->router_id == router_id.u.prefix4.s_addr) return 0; @@ -80,8 +71,7 @@ static int isis_router_id_update_zebra(int command, struct zclient *zclient, return 0; } -static int isis_zebra_if_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -94,8 +84,7 @@ static int isis_zebra_if_add(int command, struct zclient *zclient, return 0; } -static int isis_zebra_if_del(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_del(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -121,8 +110,7 @@ static int isis_zebra_if_del(int command, struct zclient *zclient, return 0; } -static int isis_zebra_if_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_state_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -136,8 +124,7 @@ static int isis_zebra_if_state_up(int command, struct zclient *zclient, return 0; } -static int isis_zebra_if_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_state_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct isis_circuit *circuit; @@ -155,8 +142,7 @@ static int isis_zebra_if_state_down(int command, struct zclient *zclient, return 0; } -static int isis_zebra_if_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; struct prefix *p; @@ -183,8 +169,7 @@ static int isis_zebra_if_address_add(int command, struct zclient *zclient, return 0; } -static int isis_zebra_if_address_del(int command, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_address_del(ZAPI_CALLBACK_ARGS) { struct connected *c; struct interface *ifp; @@ -218,8 +203,7 @@ static int isis_zebra_if_address_del(int command, struct zclient *client, return 0; } -static int isis_zebra_link_params(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_link_params(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -354,8 +338,7 @@ void isis_zebra_route_update(struct prefix *prefix, isis_zebra_route_del_route(prefix, src_p, route_info); } -static int isis_zebra_read(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_read(ZAPI_CALLBACK_ARGS) { struct zapi_route api; @@ -375,10 +358,10 @@ static int isis_zebra_read(int command, struct zclient *zclient, if (api.prefix.prefixlen == 0 && api.src_prefix.prefixlen == 0 && api.type == PROTO_TYPE) { - command = ZEBRA_REDISTRIBUTE_ROUTE_DEL; + cmd = ZEBRA_REDISTRIBUTE_ROUTE_DEL; } - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) isis_redist_add(api.type, &api.prefix, &api.src_prefix, api.distance, api.metric); else diff --git a/isisd/isisd.c b/isisd/isisd.c index ad02220438f8..bee3b6deb56e 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -38,7 +38,6 @@ #include "spf_backoff.h" #include "lib/northbound_cli.h" -#include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" @@ -95,7 +94,6 @@ void isis_new(unsigned long process_id) * uncomment the next line for full debugs */ /* isis->debugs = 0xFFFF; */ - isisMplsTE.status = disable; /* Only support TE metric */ QOBJ_REG(isis, isis); } @@ -122,12 +120,10 @@ struct isis_area *isis_area_create(const char *area_tag) /* * intialize the databases */ - if (area->is_type & IS_LEVEL_1) { - area->lspdb[0] = lsp_db_init(); - } - if (area->is_type & IS_LEVEL_2) { - area->lspdb[1] = lsp_db_init(); - } + if (area->is_type & IS_LEVEL_1) + lsp_db_init(&area->lspdb[0]); + if (area->is_type & IS_LEVEL_2) + lsp_db_init(&area->lspdb[1]); spftree_area_init(area); @@ -258,6 +254,10 @@ int isis_area_destroy(const char *area_tag) if (fabricd) fabricd_finish(area->fabricd); + /* Disable MPLS if necessary before flooding LSP */ + if (IS_MPLS_TE(area->mta)) + area->mta->status = disable; + if (area->circuit_list) { for (ALL_LIST_ELEMENTS(area->circuit_list, node, nnode, circuit)) { @@ -268,17 +268,11 @@ int isis_area_destroy(const char *area_tag) list_delete(&area->circuit_list); } - if (area->lspdb[0] != NULL) { - lsp_db_destroy(area->lspdb[0]); - area->lspdb[0] = NULL; - } - if (area->lspdb[1] != NULL) { - lsp_db_destroy(area->lspdb[1]); - area->lspdb[1] = NULL; - } + lsp_db_fini(&area->lspdb[0]); + lsp_db_fini(&area->lspdb[1]); /* invalidate and verify to delete all routes from zebra */ - isis_area_invalidate_routes(area, ISIS_LEVEL1 & ISIS_LEVEL2); + isis_area_invalidate_routes(area, area->is_type); isis_area_verify_routes(area); spftree_area_del(area); @@ -744,11 +738,7 @@ DEFUN (clear_isis_neighbor_arg, */ void print_debug(struct vty *vty, int flags, int onoff) { - char onoffs[4]; - if (onoff) - strcpy(onoffs, "on"); - else - strcpy(onoffs, "off"); + const char *onoffs = onoff ? "on" : "off"; if (flags & DEBUG_ADJ_PACKETS) vty_out(vty, @@ -1341,7 +1331,7 @@ DEFUN (show_isis_summary, return CMD_SUCCESS; } -struct isis_lsp *lsp_for_arg(const char *argv, dict_t *lspdb) +struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv) { char sysid[255] = {0}; uint8_t number[3]; @@ -1389,13 +1379,13 @@ struct isis_lsp *lsp_for_arg(const char *argv, dict_t *lspdb) * hostname.- */ if (sysid2buff(lspid, sysid)) { - lsp = lsp_search(lspid, lspdb); + lsp = lsp_search(head, lspid); } else if ((dynhn = dynhn_find_by_name(sysid))) { memcpy(lspid, dynhn->id, ISIS_SYS_ID_LEN); - lsp = lsp_search(lspid, lspdb); + lsp = lsp_search(head, lspid); } else if (strncmp(cmd_hostname_get(), sysid, 15) == 0) { memcpy(lspid, isis->sysid, ISIS_SYS_ID_LEN); - lsp = lsp_search(lspid, lspdb); + lsp = lsp_search(head, lspid); } return lsp; @@ -1432,9 +1422,8 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level) area->area_tag ? area->area_tag : "null"); for (level = 0; level < ISIS_LEVELS; level++) { - if (area->lspdb[level] - && dict_count(area->lspdb[level]) > 0) { - lsp = lsp_for_arg(argv, area->lspdb[level]); + if (lspdb_count(&area->lspdb[level]) > 0) { + lsp = lsp_for_arg(&area->lspdb[level], argv); if (lsp != NULL || argv == NULL) { vty_out(vty, @@ -1456,7 +1445,7 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level) area->dynhostname); } else if (argv == NULL) { lsp_count = lsp_print_all( - vty, area->lspdb[level], + vty, &area->lspdb[level], ui_level, area->dynhostname); vty_out(vty, " %u LSPs\n\n", @@ -1696,10 +1685,7 @@ static void area_resign_level(struct isis_area *area, int level) isis_area_invalidate_routes(area, level); isis_area_verify_routes(area); - if (area->lspdb[level - 1]) { - lsp_db_destroy(area->lspdb[level - 1]); - area->lspdb[level - 1] = NULL; - } + lsp_db_fini(&area->lspdb[level - 1]); for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) { if (area->spftree[tree][level - 1]) { @@ -1735,8 +1721,7 @@ void isis_area_is_type_set(struct isis_area *area, int is_type) if (is_type == IS_LEVEL_2) area_resign_level(area, IS_LEVEL_1); - if (area->lspdb[1] == NULL) - area->lspdb[1] = lsp_db_init(); + lsp_db_init(&area->lspdb[1]); break; case IS_LEVEL_1_AND_2: @@ -1750,8 +1735,7 @@ void isis_area_is_type_set(struct isis_area *area, int is_type) if (is_type == IS_LEVEL_1) area_resign_level(area, IS_LEVEL_2); - if (area->lspdb[0] == NULL) - area->lspdb[0] = lsp_db_init(); + lsp_db_init(&area->lspdb[0]); break; default: @@ -2137,7 +2121,6 @@ int isis_config_write(struct vty *vty) write += area_write_mt_settings(area, vty); write += fabricd_write_settings(area, vty); } - isis_mpls_te_config_write_router(vty); } return write; diff --git a/isisd/isisd.h b/isisd/isisd.h index fb879395c1b7..f8486ae0d6cd 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -31,7 +31,7 @@ #include "isisd/isis_pdu_counter.h" #include "isisd/isis_circuit.h" #include "isis_flags.h" -#include "dict.h" +#include "isis_lsp.h" #include "isis_memory.h" #include "qobj.h" @@ -107,7 +107,7 @@ enum isis_metric_style { struct isis_area { struct isis *isis; /* back pointer */ - dict_t *lspdb[ISIS_LEVELS]; /* link-state dbs */ + struct lspdb_head lspdb[ISIS_LEVELS]; /* link-state dbs */ struct isis_spftree *spftree[SPFTREE_COUNT][ISIS_LEVELS]; #define DEFAULT_LSP_MTU 1497 unsigned int lsp_mtu; /* Size of LSPs to generate */ @@ -165,6 +165,8 @@ struct isis_area { uint8_t log_adj_changes; /* multi topology settings */ struct list *mt_settings; + /* MPLS-TE settings */ + struct mpls_te_area *mta; int ipv6_circuits; bool purge_originator; /* Counters */ @@ -195,7 +197,7 @@ struct isis_area *isis_area_lookup(const char *); int isis_area_get(struct vty *vty, const char *area_tag); int isis_area_destroy(const char *area_tag); void print_debug(struct vty *, int, int); -struct isis_lsp *lsp_for_arg(const char *argv, dict_t *lspdb); +struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv); void isis_area_invalidate_routes(struct isis_area *area, int levels); void isis_area_verify_routes(struct isis_area *area); diff --git a/isisd/subdir.am b/isisd/subdir.am index 4371d5993af4..bae56309cf03 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -25,7 +25,6 @@ dist_examples_DATA += isisd/fabricd.conf.sample endif noinst_HEADERS += \ - isisd/dict.h \ isisd/isis_adjacency.h \ isisd/isis_bfd.h \ isisd/isis_circuit.h \ @@ -61,7 +60,6 @@ noinst_HEADERS += \ # end LIBISIS_SOURCES = \ - isisd/dict.c \ isisd/isis_adjacency.c \ isisd/isis_bfd.c \ isisd/isis_circuit.c \ diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index 9dc56773584a..35a7d944d30c 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -38,22 +38,14 @@ static void ifp2kif(struct interface *, struct kif *); static void ifc2kaddr(struct interface *, struct connected *, struct kaddr *); static int zebra_send_mpls_labels(int, struct kroute *); -static int ldp_router_id_update(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_interface_add(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_interface_delete(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_interface_status_change(int command, struct zclient *, - zebra_size_t, vrf_id_t); -static int ldp_interface_address_add(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_interface_address_delete(int, struct zclient *, - zebra_size_t, vrf_id_t); -static int ldp_zebra_read_route(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_zebra_read_pw_status_update(int, struct zclient *, - zebra_size_t, vrf_id_t); +static int ldp_router_id_update(ZAPI_CALLBACK_ARGS); +static int ldp_interface_add(ZAPI_CALLBACK_ARGS); +static int ldp_interface_delete(ZAPI_CALLBACK_ARGS); +static int ldp_interface_status_change(ZAPI_CALLBACK_ARGS); +static int ldp_interface_address_add(ZAPI_CALLBACK_ARGS); +static int ldp_interface_address_delete(ZAPI_CALLBACK_ARGS); +static int ldp_zebra_read_route(ZAPI_CALLBACK_ARGS); +static int ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS); static void ldp_zebra_connected(struct zclient *); static struct zclient *zclient; @@ -235,8 +227,7 @@ kif_redistribute(const char *ifname) } static int -ldp_router_id_update(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +ldp_router_id_update(ZAPI_CALLBACK_ARGS) { struct prefix router_id; @@ -255,8 +246,7 @@ ldp_router_id_update(int command, struct zclient *zclient, zebra_size_t length, } static int -ldp_interface_add(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +ldp_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct kif kif; @@ -272,8 +262,7 @@ ldp_interface_add(int command, struct zclient *zclient, zebra_size_t length, } static int -ldp_interface_delete(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +ldp_interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct kif kif; @@ -297,8 +286,7 @@ ldp_interface_delete(int command, struct zclient *zclient, zebra_size_t length, } static int -ldp_interface_status_change(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +ldp_interface_status_change(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct listnode *node; @@ -337,14 +325,13 @@ ldp_interface_status_change(int command, struct zclient *zclient, } static int -ldp_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +ldp_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct interface *ifp; struct kaddr ka; - ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return (0); @@ -365,14 +352,13 @@ ldp_interface_address_add(int command, struct zclient *zclient, } static int -ldp_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +ldp_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct interface *ifp; struct kaddr ka; - ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return (0); @@ -394,8 +380,7 @@ ldp_interface_address_delete(int command, struct zclient *zclient, } static int -ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +ldp_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; struct zapi_nexthop *api_nh; @@ -439,7 +424,7 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, (kr.af == AF_INET6 && IN6_IS_SCOPE_EMBED(&kr.prefix.v6))) return (0); - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) add = 1; if (api.nexthop_num == 0) @@ -502,12 +487,11 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, * Receive PW status update from Zebra and send it to LDE process. */ static int -ldp_zebra_read_pw_status_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS) { struct zapi_pw_status zpw; - zebra_read_pw_status_update(command, zclient, length, vrf_id, &zpw); + zebra_read_pw_status_update(cmd, zclient, length, vrf_id, &zpw); debug_zebra_in("pseudowire %s status %s", zpw.ifname, (zpw.status == PW_STATUS_UP) ? "up" : "down"); diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 771d3b7459a0..5aaa2ec3253d 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -116,7 +116,7 @@ struct zebra_privs_t ldpd_privs = }; /* CTL Socket path */ -char ctl_sock_path[MAXPATHLEN] = LDPD_SOCKET; +char ctl_sock_path[MAXPATHLEN]; /* LDPd options. */ #define OPTION_CTLSOCK 1001 @@ -219,6 +219,10 @@ main(int argc, char *argv[]) int pipe_parent2lde[2], pipe_parent2lde_sync[2]; char *ctl_sock_name; struct thread *thread = NULL; + bool ctl_sock_used = false; + + snprintf(ctl_sock_path, sizeof(ctl_sock_path), LDPD_SOCKET, + "", ""); ldpd_process = PROC_MAIN; log_procname = log_procnames[ldpd_process]; @@ -244,6 +248,7 @@ main(int argc, char *argv[]) case 0: break; case OPTION_CTLSOCK: + ctl_sock_used = true; ctl_sock_name = strrchr(LDPD_SOCKET, '/'); if (ctl_sock_name) /* skip '/' */ @@ -277,6 +282,10 @@ main(int argc, char *argv[]) } } + if (ldpd_di.pathspace && !ctl_sock_used) + snprintf(ctl_sock_path, sizeof(ctl_sock_path), LDPD_SOCKET, + "/", ldpd_di.pathspace); + strlcpy(init.user, ldpd_privs.user, sizeof(init.user)); strlcpy(init.group, ldpd_privs.group, sizeof(init.group)); strlcpy(init.ctl_sock_path, ctl_sock_path, sizeof(init.ctl_sock_path)); diff --git a/lib/atomlist.c b/lib/atomlist.c new file mode 100644 index 000000000000..8169ba9eb453 --- /dev/null +++ b/lib/atomlist.c @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "atomlist.h" + +void atomlist_add_head(struct atomlist_head *h, struct atomlist_item *item) +{ + atomptr_t prevval; + atomptr_t i = atomptr_i(item); + + atomic_fetch_add_explicit(&h->count, 1, memory_order_relaxed); + + /* updating ->last is possible here, but makes the code considerably + * more complicated... let's not. + */ + prevval = ATOMPTR_NULL; + item->next = ATOMPTR_NULL; + + /* head-insert atomically + * release barrier: item + item->next writes must be completed + */ + while (!atomic_compare_exchange_weak_explicit(&h->first, &prevval, i, + memory_order_release, memory_order_relaxed)) + atomic_store_explicit(&item->next, prevval, + memory_order_relaxed); +} + +void atomlist_add_tail(struct atomlist_head *h, struct atomlist_item *item) +{ + atomptr_t prevval = ATOMPTR_NULL; + atomptr_t i = atomptr_i(item); + atomptr_t hint; + struct atomlist_item *prevptr; + _Atomic atomptr_t *prev; + + item->next = ATOMPTR_NULL; + + atomic_fetch_add_explicit(&h->count, 1, memory_order_relaxed); + + /* place new item into ->last + * release: item writes completed; acquire: DD barrier on hint + */ + hint = atomic_exchange_explicit(&h->last, i, memory_order_acq_rel); + + while (1) { + if (atomptr_p(hint) == NULL) + prev = &h->first; + else + prev = &atomlist_itemp(hint)->next; + + do { + prevval = atomic_load_explicit(prev, + memory_order_consume); + prevptr = atomlist_itemp(prevval); + if (prevptr == NULL) + break; + + prev = &prevptr->next; + } while (prevptr); + + /* last item is being deleted - start over */ + if (atomptr_l(prevval)) { + hint = ATOMPTR_NULL; + continue; + } + + /* no barrier - item->next is NULL and was so in xchg above */ + if (!atomic_compare_exchange_strong_explicit(prev, &prevval, i, + memory_order_consume, + memory_order_consume)) { + hint = prevval; + continue; + } + break; + } +} + +static void atomlist_del_core(struct atomlist_head *h, + struct atomlist_item *item, + _Atomic atomptr_t *hint, + atomptr_t next) +{ + _Atomic atomptr_t *prev = hint ? hint : &h->first, *upd; + atomptr_t prevval, updval; + struct atomlist_item *prevptr; + + /* drop us off "last" if needed. no r/w to barrier. */ + prevval = atomptr_i(item); + atomic_compare_exchange_strong_explicit(&h->last, &prevval, + ATOMPTR_NULL, + memory_order_relaxed, memory_order_relaxed); + + atomic_fetch_sub_explicit(&h->count, 1, memory_order_relaxed); + + /* the following code should be identical (except sort<>list) to + * atomsort_del_hint() + */ + while (1) { + upd = NULL; + updval = ATOMPTR_LOCK; + + do { + prevval = atomic_load_explicit(prev, + memory_order_consume); + + /* track the beginning of a chain of deleted items + * this is neccessary to make this lock-free; we can + * complete deletions started by other threads. + */ + if (!atomptr_l(prevval)) { + updval = prevval; + upd = prev; + } + + prevptr = atomlist_itemp(prevval); + if (prevptr == item) + break; + + prev = &prevptr->next; + } while (prevptr); + + if (prevptr != item) + /* another thread completed our deletion */ + return; + + if (!upd || atomptr_l(updval)) { + /* failed to find non-deleted predecessor... + * have to try again + */ + prev = &h->first; + continue; + } + + if (!atomic_compare_exchange_strong_explicit(upd, &updval, + next, memory_order_consume, + memory_order_consume)) { + /* prev doesn't point to item anymore, something + * was inserted. continue at same position forward. + */ + continue; + } + break; + } +} + +void atomlist_del_hint(struct atomlist_head *h, struct atomlist_item *item, + _Atomic atomptr_t *hint) +{ + atomptr_t next; + + /* mark ourselves in-delete - full barrier */ + next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK, + memory_order_acquire); + assert(!atomptr_l(next)); /* delete race on same item */ + + atomlist_del_core(h, item, hint, next); +} + +struct atomlist_item *atomlist_pop(struct atomlist_head *h) +{ + struct atomlist_item *item; + atomptr_t next; + + /* grab head of the list - and remember it in replval for the + * actual delete below. No matter what, the head of the list is + * where we start deleting because either it's our item, or it's + * some delete-marked items and then our item. + */ + next = atomic_load_explicit(&h->first, memory_order_consume); + + do { + item = atomlist_itemp(next); + if (!item) + return NULL; + + /* try to mark deletion */ + next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK, + memory_order_acquire); + + } while (atomptr_l(next)); + /* if loop is taken: delete race on same item (another pop or del) + * => proceed to next item + * if loop exited here: we have our item selected and marked + */ + atomlist_del_core(h, item, &h->first, next); + return item; +} + +struct atomsort_item *atomsort_add(struct atomsort_head *h, + struct atomsort_item *item, int (*cmpfn)( + const struct atomsort_item *, + const struct atomsort_item *)) +{ + _Atomic atomptr_t *prev; + atomptr_t prevval; + atomptr_t i = atomptr_i(item); + struct atomsort_item *previtem; + int cmpval; + + do { + prev = &h->first; + + do { + prevval = atomic_load_explicit(prev, + memory_order_acquire); + previtem = atomptr_p(prevval); + + if (!previtem || (cmpval = cmpfn(previtem, item)) > 0) + break; + if (cmpval == 0) + return previtem; + + prev = &previtem->next; + } while (1); + + if (atomptr_l(prevval)) + continue; + + item->next = prevval; + if (atomic_compare_exchange_strong_explicit(prev, &prevval, i, + memory_order_release, memory_order_relaxed)) + break; + } while (1); + + atomic_fetch_add_explicit(&h->count, 1, memory_order_relaxed); + return NULL; +} + +static void atomsort_del_core(struct atomsort_head *h, + struct atomsort_item *item, _Atomic atomptr_t *hint, + atomptr_t next) +{ + _Atomic atomptr_t *prev = hint ? hint : &h->first, *upd; + atomptr_t prevval, updval; + struct atomsort_item *prevptr; + + atomic_fetch_sub_explicit(&h->count, 1, memory_order_relaxed); + + /* the following code should be identical (except sort<>list) to + * atomlist_del_core() + */ + while (1) { + upd = NULL; + updval = ATOMPTR_LOCK; + + do { + prevval = atomic_load_explicit(prev, + memory_order_consume); + + /* track the beginning of a chain of deleted items + * this is neccessary to make this lock-free; we can + * complete deletions started by other threads. + */ + if (!atomptr_l(prevval)) { + updval = prevval; + upd = prev; + } + + prevptr = atomsort_itemp(prevval); + if (prevptr == item) + break; + + prev = &prevptr->next; + } while (prevptr); + + if (prevptr != item) + /* another thread completed our deletion */ + return; + + if (!upd || atomptr_l(updval)) { + /* failed to find non-deleted predecessor... + * have to try again + */ + prev = &h->first; + continue; + } + + if (!atomic_compare_exchange_strong_explicit(upd, &updval, + next, memory_order_relaxed, + memory_order_relaxed)) { + /* prev doesn't point to item anymore, something + * was inserted. continue at same position forward. + */ + continue; + } + break; + } +} + +void atomsort_del_hint(struct atomsort_head *h, struct atomsort_item *item, + _Atomic atomptr_t *hint) +{ + atomptr_t next; + + /* mark ourselves in-delete - full barrier */ + next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK, + memory_order_seq_cst); + assert(!atomptr_l(next)); /* delete race on same item */ + + atomsort_del_core(h, item, hint, next); +} + +struct atomsort_item *atomsort_pop(struct atomsort_head *h) +{ + struct atomsort_item *item; + atomptr_t next; + + /* grab head of the list - and remember it in replval for the + * actual delete below. No matter what, the head of the list is + * where we start deleting because either it's our item, or it's + * some delete-marked items and then our item. + */ + next = atomic_load_explicit(&h->first, memory_order_consume); + + do { + item = atomsort_itemp(next); + if (!item) + return NULL; + + /* try to mark deletion */ + next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK, + memory_order_acquire); + + } while (atomptr_l(next)); + /* if loop is taken: delete race on same item (another pop or del) + * => proceed to next item + * if loop exited here: we have our item selected and marked + */ + atomsort_del_core(h, item, &h->first, next); + return item; +} diff --git a/lib/atomlist.h b/lib/atomlist.h new file mode 100644 index 000000000000..e4098ccb54d1 --- /dev/null +++ b/lib/atomlist.h @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2016-2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +#ifndef _FRR_ATOMLIST_H +#define _FRR_ATOMLIST_H + +#include "typesafe.h" +#include "frratomic.h" + +/* pointer with lock/deleted/invalid bit in lowest bit + * + * for atomlist/atomsort, "locked" means "this pointer can't be updated, the + * item is being deleted". it is permissible to assume the item will indeed + * be deleted (as there are no replace/etc. ops in this). + * + * in general, lowest 2/3 bits on 32/64bit architectures are available for + * uses like this; the only thing that will really break this is putting an + * atomlist_item in a struct with "packed" attribute. (it'll break + * immediately and consistently.) -- don't do that. + * + * ATOMPTR_USER is currently unused (and available for atomic hash or skiplist + * implementations.) + */ +typedef uintptr_t atomptr_t; +#define ATOMPTR_MASK (UINTPTR_MAX - 3) +#define ATOMPTR_LOCK (1) +#define ATOMPTR_USER (2) +#define ATOMPTR_NULL (0) + +static inline atomptr_t atomptr_i(void *val) +{ + atomptr_t atomval = (atomptr_t)val; + + assert(!(atomval & ATOMPTR_LOCK)); + return atomval; +} +static inline void *atomptr_p(atomptr_t val) +{ + return (void *)(val & ATOMPTR_MASK); +} +static inline bool atomptr_l(atomptr_t val) +{ + return (bool)(val & ATOMPTR_LOCK); +} +static inline bool atomptr_u(atomptr_t val) +{ + return (bool)(val & ATOMPTR_USER); +} + + +/* the problem with, find(), find_gteq() and find_lt() on atomic lists is that + * they're neither an "acquire" nor a "release" operation; the element that + * was found is still on the list and doesn't change ownership. Therefore, + * an atomic transition in ownership state can't be implemented. + * + * Contrast this with add() or pop(): both function calls atomically transfer + * ownership of an item to or from the list, which makes them "acquire" / + * "release" operations. + * + * What can be implemented atomically is a "find_pop()", i.e. try to locate an + * item and atomically try to remove it if found. It's not currently + * implemented but can be added when needed. + * + * Either way - for find(), generally speaking, if you need to use find() on + * a list then the whole thing probably isn't well-suited to atomic + * implementation and you'll need to have extra locks around to make it work + * correctly. + */ +#ifdef WNO_ATOMLIST_UNSAFE_FIND +# define atomic_find_warn +#else +# define atomic_find_warn __attribute__((_DEPRECATED( \ + "WARNING: find() on atomic lists cannot be atomic by principle; " \ + "check code to make sure usage pattern is OK and if it is, use " \ + "#define WNO_ATOMLIST_UNSAFE_FIND"))) +#endif + + +/* single-linked list, unsorted/arbitrary. + * can be used as queue with add_tail / pop + * + * all operations are lock-free, but not neccessarily wait-free. this means + * that there is no state where the system as a whole stops making process, + * but it *is* possible that a *particular* thread is delayed by some time. + * + * the only way for this to happen is for other threads to continuously make + * updates. an inactive / blocked / deadlocked other thread cannot cause such + * delays, and to cause such delays a thread must be heavily hitting the list - + * it's a rather theoretical concern. + */ + +/* don't use these structs directly */ +struct atomlist_item { + _Atomic atomptr_t next; +}; +#define atomlist_itemp(val) ((struct atomlist_item *)atomptr_p(val)) + +struct atomlist_head { + _Atomic atomptr_t first, last; + _Atomic size_t count; +}; + +/* use as: + * + * PREDECL_ATOMLIST(namelist) + * struct name { + * struct namelist_item nlitem; + * } + * DECLARE_ATOMLIST(namelist, struct name, nlitem) + */ +#define PREDECL_ATOMLIST(prefix) \ +struct prefix ## _head { struct atomlist_head ah; }; \ +struct prefix ## _item { struct atomlist_item ai; }; + +#define INIT_ATOMLIST(var) { } + +#define DECLARE_ATOMLIST(prefix, type, field) \ +macro_inline void prefix ## _add_head(struct prefix##_head *h, type *item) \ +{ atomlist_add_head(&h->ah, &item->field.ai); } \ +macro_inline void prefix ## _add_tail(struct prefix##_head *h, type *item) \ +{ atomlist_add_tail(&h->ah, &item->field.ai); } \ +macro_inline void prefix ## _del_hint(struct prefix##_head *h, type *item, \ + _Atomic atomptr_t *hint) \ +{ atomlist_del_hint(&h->ah, &item->field.ai, hint); } \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ atomlist_del_hint(&h->ah, &item->field.ai, NULL); } \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ char *p = (char *)atomlist_pop(&h->ah); \ + return p ? (type *)(p - offsetof(type, field)) : NULL; } \ +macro_inline type *prefix ## _first(struct prefix##_head *h) \ +{ char *p = atomptr_p(atomic_load_explicit(&h->ah.first, \ + memory_order_acquire)); \ + return p ? (type *)(p - offsetof(type, field)) : NULL; } \ +macro_inline type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ char *p = atomptr_p(atomic_load_explicit(&item->field.ai.next, \ + memory_order_acquire)); \ + return p ? (type *)(p - offsetof(type, field)) : NULL; } \ +macro_inline type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ return item ? prefix##_next(h, item) : NULL; } \ +macro_inline size_t prefix ## _count(struct prefix##_head *h) \ +{ return atomic_load_explicit(&h->ah.count, memory_order_relaxed); } \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + assert(prefix ## _count(h) == 0); \ + memset(h, 0, sizeof(*h)); \ +} \ +/* ... */ + +/* add_head: + * - contention on ->first pointer + * - return implies completion + */ +void atomlist_add_head(struct atomlist_head *h, struct atomlist_item *item); + +/* add_tail: + * - concurrent add_tail can cause wait but has progress guarantee + * - return does NOT imply completion. completion is only guaranteed after + * all other add_tail operations that started before this add_tail have + * completed as well. + */ +void atomlist_add_tail(struct atomlist_head *h, struct atomlist_item *item); + +/* del/del_hint: + * + * OWNER MUST HOLD REFERENCE ON ITEM TO BE DELETED, ENSURING NO OTHER THREAD + * WILL TRY TO DELETE THE SAME ITEM. DELETING INCLUDES pop(). + * + * as with all deletions, threads that started reading earlier may still hold + * pointers to the deleted item. completion is however guaranteed for all + * reads starting later. + */ +void atomlist_del_hint(struct atomlist_head *h, struct atomlist_item *item, + _Atomic atomptr_t *hint); + +/* pop: + * + * as with all deletions, threads that started reading earlier may still hold + * pointers to the deleted item. completion is however guaranteed for all + * reads starting later. + */ +struct atomlist_item *atomlist_pop(struct atomlist_head *h); + + + +struct atomsort_item { + _Atomic atomptr_t next; +}; +#define atomsort_itemp(val) ((struct atomsort_item *)atomptr_p(val)) + +struct atomsort_head { + _Atomic atomptr_t first; + _Atomic size_t count; +}; + +#define _PREDECL_ATOMSORT(prefix) \ +struct prefix ## _head { struct atomsort_head ah; }; \ +struct prefix ## _item { struct atomsort_item ai; }; + +#define INIT_ATOMSORT_UNIQ(var) { } +#define INIT_ATOMSORT_NONUNIQ(var) { } + +#define _DECLARE_ATOMSORT(prefix, type, field, cmpfn_nuq, cmpfn_uq) \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + assert(h->ah.count == 0); \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ +{ \ + struct atomsort_item *p; \ + p = atomsort_add(&h->ah, &item->field.ai, cmpfn_uq); \ + return container_of_null(p, type, field.ai); \ +} \ +macro_inline type *prefix ## _first(struct prefix##_head *h) \ +{ \ + struct atomsort_item *p; \ + p = atomptr_p(atomic_load_explicit(&h->ah.first, \ + memory_order_acquire)); \ + return container_of_null(p, type, field.ai); \ +} \ +macro_inline type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ \ + struct atomsort_item *p; \ + p = atomptr_p(atomic_load_explicit(&item->field.ai.next, \ + memory_order_acquire)); \ + return container_of_null(p, type, field.ai); \ +} \ +macro_inline type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + return item ? prefix##_next(h, item) : NULL; \ +} \ +atomic_find_warn \ +macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ + const type *item) \ +{ \ + type *p = prefix ## _first(h); \ + while (p && cmpfn_nuq(&p->field.ai, &item->field.ai) < 0) \ + p = prefix ## _next(h, p); \ + return p; \ +} \ +atomic_find_warn \ +macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ + const type *item) \ +{ \ + type *p = prefix ## _first(h), *prev = NULL; \ + while (p && cmpfn_nuq(&p->field.ai, &item->field.ai) < 0) \ + p = prefix ## _next(h, (prev = p)); \ + return prev; \ +} \ +macro_inline void prefix ## _del_hint(struct prefix##_head *h, type *item, \ + _Atomic atomptr_t *hint) \ +{ \ + atomsort_del_hint(&h->ah, &item->field.ai, hint); \ +} \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + atomsort_del_hint(&h->ah, &item->field.ai, NULL); \ +} \ +macro_inline size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return atomic_load_explicit(&h->ah.count, memory_order_relaxed); \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct atomsort_item *p = atomsort_pop(&h->ah); \ + return p ? container_of(p, type, field.ai) : NULL; \ +} \ +/* ... */ + +#define PREDECL_ATOMSORT_UNIQ(prefix) \ + _PREDECL_ATOMSORT(prefix) +#define DECLARE_ATOMSORT_UNIQ(prefix, type, field, cmpfn) \ + \ +macro_inline int prefix ## __cmp(const struct atomsort_item *a, \ + const struct atomsort_item *b) \ +{ \ + return cmpfn(container_of(a, type, field.ai), \ + container_of(b, type, field.ai)); \ +} \ + \ +_DECLARE_ATOMSORT(prefix, type, field, \ + prefix ## __cmp, prefix ## __cmp) \ + \ +atomic_find_warn \ +macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +{ \ + type *p = prefix ## _first(h); \ + int cmpval = 0; \ + while (p && (cmpval = cmpfn(p, item)) < 0) \ + p = prefix ## _next(h, p); \ + if (!p || cmpval > 0) \ + return NULL; \ + return p; \ +} \ +/* ... */ + +#define PREDECL_ATOMSORT_NONUNIQ(prefix) \ + _PREDECL_ATOMSORT(prefix) +#define DECLARE_ATOMSORT_NONUNIQ(prefix, type, field, cmpfn) \ + \ +macro_inline int prefix ## __cmp(const struct atomsort_item *a, \ + const struct atomsort_item *b) \ +{ \ + return cmpfn(container_of(a, type, field.ai), \ + container_of(b, type, field.ai)); \ +} \ +macro_inline int prefix ## __cmp_uq(const struct atomsort_item *a, \ + const struct atomsort_item *b) \ +{ \ + int cmpval = cmpfn(container_of(a, type, field.ai), \ + container_of(b, type, field.ai)); \ + if (cmpval) \ + return cmpval; \ + if (a < b) \ + return -1; \ + if (a > b) \ + return 1; \ + return 0; \ +} \ + \ +_DECLARE_ATOMSORT(prefix, type, field, \ + prefix ## __cmp, prefix ## __cmp_uq) \ +/* ... */ + +struct atomsort_item *atomsort_add(struct atomsort_head *h, + struct atomsort_item *item, int (*cmpfn)( + const struct atomsort_item *, + const struct atomsort_item *)); + +void atomsort_del_hint(struct atomsort_head *h, + struct atomsort_item *item, _Atomic atomptr_t *hint); + +struct atomsort_item *atomsort_pop(struct atomsort_head *h); + +#endif /* _FRR_ATOMLIST_H */ diff --git a/lib/bfd.c b/lib/bfd.c index 7e27a64de7bc..00dbd1b3d1ef 100644 --- a/lib/bfd.c +++ b/lib/bfd.c @@ -127,8 +127,8 @@ void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx, */ void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info, int family, void *dst_ip, void *src_ip, char *if_name, - int ttl, int multihop, int command, int set_flag, - vrf_id_t vrf_id) + int ttl, int multihop, int cbit, int command, + int set_flag, vrf_id_t vrf_id) { struct stream *s; int ret; @@ -208,6 +208,11 @@ void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info, stream_putc(s, 0); } } + /* cbit */ + if (cbit) + stream_putc(s, 1); + else + stream_putc(s, 0); stream_putw_at(s, 0, stream_get_endp(s)); @@ -253,11 +258,13 @@ const char *bfd_get_command_dbg_str(int command) */ 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) { unsigned int ifindex; struct interface *ifp = NULL; int plen; + int local_remote_cbit; /* Get interface index. */ ifindex = stream_getl(s); @@ -292,6 +299,9 @@ struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, stream_get(&sp->u.prefix, s, plen); sp->prefixlen = stream_getc(s); } + local_remote_cbit = stream_getc(s); + if (remote_cbit) + *remote_cbit = local_remote_cbit; return ifp; } @@ -433,7 +443,8 @@ void bfd_show_info(struct vty *vty, struct bfd_info *bfd_info, int multihop, * bfd_client_sendmsg - Format and send a client register * command to Zebra to be forwarded to BFD */ -void bfd_client_sendmsg(struct zclient *zclient, int command) +void bfd_client_sendmsg(struct zclient *zclient, int command, + vrf_id_t vrf_id) { struct stream *s; int ret; @@ -450,7 +461,7 @@ void bfd_client_sendmsg(struct zclient *zclient, int command) s = zclient->obuf; stream_reset(s); - zclient_create_header(s, command, VRF_DEFAULT); + zclient_create_header(s, command, vrf_id); stream_putl(s, getpid()); diff --git a/lib/bfd.h b/lib/bfd.h index a93875c4cf80..e4781f4eaf9e 100644 --- a/lib/bfd.h +++ b/lib/bfd.h @@ -48,6 +48,8 @@ struct bfd_gbl { #define BFD_FLAG_PARAM_CFG (1 << 0) /* parameters have been configured */ #define BFD_FLAG_BFD_REG (1 << 1) /* Peer registered with BFD */ #define BFD_FLAG_BFD_TYPE_MULTIHOP (1 << 2) /* Peer registered with BFD as multihop */ +#define BFD_FLAG_BFD_CBIT_ON (1 << 3) /* Peer registered with CBIT set to on */ +#define BFD_FLAG_BFD_CHECK_CONTROLPLANE (1 << 4) /* BFD and controlplane daemon are linked */ #define BFD_STATUS_UNKNOWN (1 << 0) /* BFD session status never received */ #define BFD_STATUS_DOWN (1 << 1) /* BFD session status is down */ @@ -83,13 +85,14 @@ extern void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, int *command); extern void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info, int family, void *dst_ip, void *src_ip, - char *if_name, int ttl, int multihop, int command, - int set_flag, vrf_id_t vrf_id); + char *if_name, int ttl, int multihop, int cbit, + int command, int set_flag, vrf_id_t vrf_id); extern const char *bfd_get_command_dbg_str(int command); extern 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); const char *bfd_get_status_str(int status); @@ -102,7 +105,8 @@ extern void bfd_show_info(struct vty *vty, struct bfd_info *bfd_info, int multihop, int extra_space, bool use_json, json_object *json_obj); -extern void bfd_client_sendmsg(struct zclient *zclient, int command); +extern void bfd_client_sendmsg(struct zclient *zclient, int command, + vrf_id_t vrf_id); extern void bfd_gbl_init(void); diff --git a/lib/checksum.c b/lib/checksum.c index 18e385047443..34733700417f 100644 --- a/lib/checksum.c +++ b/lib/checksum.c @@ -46,6 +46,24 @@ int /* return checksum in low-order 16 bits */ return (answer); } +int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes) +{ + uint8_t dat[sizeof(struct ipv4_ph) + nbytes]; + + memcpy(dat, ph, sizeof(struct ipv4_ph)); + memcpy(dat + sizeof(struct ipv4_ph), data, nbytes); + return in_cksum(dat, sizeof(dat)); +} + +int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes) +{ + uint8_t dat[sizeof(struct ipv6_ph) + nbytes]; + + memcpy(dat, ph, sizeof(struct ipv6_ph)); + memcpy(dat + sizeof(struct ipv6_ph), data, nbytes); + return in_cksum(dat, sizeof(dat)); +} + /* Fletcher Checksum -- Refer to RFC1008. */ #define MODX 4102U /* 5802 should be fine */ diff --git a/lib/checksum.h b/lib/checksum.h index 7d5037143975..56771d4f24ea 100644 --- a/lib/checksum.h +++ b/lib/checksum.h @@ -1,8 +1,33 @@ +#include +#include + #ifdef __cplusplus extern "C" { #endif -extern int in_cksum(void *, int); + +/* IPv4 pseudoheader */ +struct ipv4_ph { + struct in_addr src; + struct in_addr dst; + uint8_t rsvd; + uint8_t proto; + uint16_t len; +} __attribute__((packed)); + +/* IPv6 pseudoheader */ +struct ipv6_ph { + struct in6_addr src; + struct in6_addr dst; + uint32_t ulpl; + uint8_t zero[3]; + uint8_t next_hdr; +} __attribute__((packed)); + +extern int in_cksum(void *data, int nbytes); +extern int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes); +extern int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes); + #define FLETCHER_CHECKSUM_VALIDATE 0xffff extern uint16_t fletcher_checksum(uint8_t *, const size_t len, const uint16_t offset); diff --git a/lib/command.c b/lib/command.c index 559457c11998..e5e06231631c 100644 --- a/lib/command.c +++ b/lib/command.c @@ -149,6 +149,7 @@ const char *node_names[] = { "bfd", /* BFD_NODE */ "bfd peer", /* BFD_PEER_NODE */ "openfabric", // OPENFABRIC_NODE + "vrrp", /* VRRP_NODE */ }; /* clang-format on */ @@ -332,7 +333,7 @@ int argv_find(struct cmd_token **argv, int argc, const char *text, int *index) return found; } -static unsigned int cmd_hash_key(void *p) +static unsigned int cmd_hash_key(const void *p) { int size = sizeof(p); @@ -1386,7 +1387,7 @@ int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num) /* Configuration from terminal */ DEFUN (config_terminal, config_terminal_cmd, - "configure terminal", + "configure [terminal]", "Configuration from vty interface\n" "Configuration terminal\n") { @@ -1705,12 +1706,16 @@ static int vty_write_config(struct vty *vty) vty_out(vty, "frr defaults %s\n", DFLT_NAME); vty_out(vty, "!\n"); - for (i = 0; i < vector_active(cmdvec); i++) - if ((node = vector_slot(cmdvec, i)) && node->func - && (node->vtysh || vty->type != VTY_SHELL)) { - if ((*node->func)(vty)) - vty_out(vty, "!\n"); - } + pthread_rwlock_rdlock(&running_config->lock); + { + for (i = 0; i < vector_active(cmdvec); i++) + if ((node = vector_slot(cmdvec, i)) && node->func + && (node->vtysh || vty->type != VTY_SHELL)) { + if ((*node->func)(vty)) + vty_out(vty, "!\n"); + } + } + pthread_rwlock_unlock(&running_config->lock); if (vty->type == VTY_TERM) { vty_out(vty, "end\n"); @@ -1755,10 +1760,10 @@ static int file_write_config(struct vty *vty) dirfd = open(".", O_DIRECTORY | O_RDONLY); /* if dirfd is invalid, directory sync fails, but we're still OK */ - config_file_sav = XMALLOC( - MTYPE_TMP, strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1); - strcpy(config_file_sav, config_file); - strcat(config_file_sav, CONF_BACKUP_EXT); + size_t config_file_sav_sz = strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1; + config_file_sav = XMALLOC(MTYPE_TMP, config_file_sav_sz); + strlcpy(config_file_sav, config_file, config_file_sav_sz); + strlcat(config_file_sav, CONF_BACKUP_EXT, config_file_sav_sz); config_file_tmp = XMALLOC(MTYPE_TMP, strlen(config_file) + 8); @@ -1957,7 +1962,15 @@ DEFUN (config_hostname, struct cmd_token *word = argv[1]; if (!isalnum((int)word->arg[0])) { - vty_out(vty, "Please specify string starting with alphabet\n"); + vty_out(vty, + "Please specify string starting with alphabet or number\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* With reference to RFC 1123 Section 2.1 */ + if (strlen(word->arg) > HOSTNAME_LEN) { + vty_out(vty, "Hostname length should be less than %d chars\n", + HOSTNAME_LEN); return CMD_WARNING_CONFIG_FAILED; } @@ -2798,9 +2811,10 @@ void cmd_init(int terminal) /* Each node's basic commands. */ install_element(VIEW_NODE, &show_version_cmd); install_element(ENABLE_NODE, &show_startup_config_cmd); - install_element(ENABLE_NODE, &debug_memstats_cmd); if (terminal) { + install_element(ENABLE_NODE, &debug_memstats_cmd); + install_element(VIEW_NODE, &config_list_cmd); install_element(VIEW_NODE, &config_exit_cmd); install_element(VIEW_NODE, &config_quit_cmd); @@ -2834,9 +2848,10 @@ void cmd_init(int terminal) install_element(CONFIG_NODE, &domainname_cmd); install_element(CONFIG_NODE, &no_domainname_cmd); install_element(CONFIG_NODE, &frr_version_defaults_cmd); - install_element(CONFIG_NODE, &debug_memstats_cmd); if (terminal > 0) { + install_element(CONFIG_NODE, &debug_memstats_cmd); + install_element(CONFIG_NODE, &password_cmd); install_element(CONFIG_NODE, &no_password_cmd); install_element(CONFIG_NODE, &enable_password_cmd); diff --git a/lib/command.h b/lib/command.h index a5f9616dbf3c..d6c41e08244c 100644 --- a/lib/command.h +++ b/lib/command.h @@ -37,6 +37,17 @@ extern "C" { DECLARE_MTYPE(HOST) DECLARE_MTYPE(COMPLETION) +/* + * From RFC 1123 (Requirements for Internet Hosts), Section 2.1 on hostnames: + * One aspect of host name syntax is hereby changed: the restriction on + * the first character is relaxed to allow either a letter or a digit. + * Host software MUST support this more liberal syntax. + * + * Host software MUST handle host names of up to 63 characters and + * SHOULD handle host names of up to 255 characters. + */ +#define HOSTNAME_LEN 255 + /* Host configuration variable */ struct host { /* Host name of this router. */ @@ -147,6 +158,7 @@ enum node_type { BFD_NODE, /* BFD protocol mode. */ BFD_PEER_NODE, /* BFD peer configuration mode. */ OPENFABRIC_NODE, /* OpenFabric router configuration node */ + VRRP_NODE, /* VRRP node */ NODE_TYPE_MAX, /* maximum */ }; diff --git a/lib/command_match.c b/lib/command_match.c index 8b34d1e3eb90..9456e1585a9e 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -723,7 +723,7 @@ static enum match_type match_ipv4(const char *str) if (str - sp > 3) return no_match; - strncpy(buf, sp, str - sp); + memcpy(buf, sp, str - sp); if (atoi(buf) > 255) return no_match; @@ -774,7 +774,7 @@ static enum match_type match_ipv4_prefix(const char *str) if (str - sp > 3) return no_match; - strncpy(buf, sp, str - sp); + memcpy(buf, sp, str - sp); if (atoi(buf) > 255) return no_match; diff --git a/lib/compiler.h b/lib/compiler.h index cb4f7531ecde..9ce91e3361aa 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -32,6 +32,10 @@ extern "C" { # define _FALLTHROUGH __attribute__((fallthrough)); #endif # define _CONSTRUCTOR(x) constructor(x) +# define _DEPRECATED(x) deprecated(x) +# if __has_builtin(assume) +# define assume(x) __builtin_assume(x) +# endif #elif defined(__GNUC__) #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9) # define _RET_NONNULL , returns_nonnull @@ -41,11 +45,40 @@ extern "C" { # define _DESTRUCTOR(x) destructor(x) # define _ALLOC_SIZE(x) alloc_size(x) #endif +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +# define _DEPRECATED(x) deprecated(x) +# define assume(x) do { if (!(x)) __builtin_unreachable(); } while (0) +#endif +#if __GNUC__ < 5 +# define __has_attribute(x) 0 +#endif #if __GNUC__ >= 7 # define _FALLTHROUGH __attribute__((fallthrough)); #endif #endif +#if __has_attribute(hot) +# define _OPTIMIZE_HOT __attribute__((hot)) +#else +# define _OPTIMIZE_HOT +#endif +#if __has_attribute(optimize) +# define _OPTIMIZE_O3 __attribute__((optimize("3"))) +#else +# define _OPTIMIZE_O3 +#endif +#define OPTIMIZE _OPTIMIZE_O3 _OPTIMIZE_HOT + +#if !defined(__GNUC__) +#error module code needs GCC visibility extensions +#elif __GNUC__ < 4 +#error module code needs GCC visibility extensions +#else +# define DSO_PUBLIC __attribute__ ((visibility ("default"))) +# define DSO_SELF __attribute__ ((visibility ("protected"))) +# define DSO_LOCAL __attribute__ ((visibility ("hidden"))) +#endif + #ifdef __sun /* Solaris doesn't do constructor priorities due to linker restrictions */ #undef _CONSTRUCTOR @@ -68,6 +101,25 @@ extern "C" { #ifndef _FALLTHROUGH #define _FALLTHROUGH #endif +#ifndef _DEPRECATED +#define _DEPRECATED(x) deprecated +#endif +#ifndef assume +#define assume(x) +#endif + +/* pure = function does not modify memory & return value is the same if + * memory hasn't changed (=> allows compiler to optimize) + * + * Mostly autodetected by the compiler if function body is available (i.e. + * static inline functions in headers). Since that implies it should only be + * used in headers for non-inline functions, the "extern" is included here. + */ +#define ext_pure extern __attribute__((pure)) + +/* for helper functions defined inside macros */ +#define macro_inline static inline __attribute__((unused)) +#define macro_pure static inline __attribute__((unused, pure)) /* * for warnings on macros, put in the macro content like this: @@ -92,6 +144,80 @@ extern "C" { #define CPP_NOTICE(text) #endif +/* MAX / MIN are not commonly defined, but useful */ +/* note: glibc sys/param.h has #define MIN(a,b) (((a)<(b))?(a):(b)) */ +#ifdef MAX +#undef MAX +#endif +#define MAX(a, b) \ + ({ \ + typeof(a) _max_a = (a); \ + typeof(b) _max_b = (b); \ + _max_a > _max_b ? _max_a : _max_b; \ + }) +#ifdef MIN +#undef MIN +#endif +#define MIN(a, b) \ + ({ \ + typeof(a) _min_a = (a); \ + typeof(b) _min_b = (b); \ + _min_a < _min_b ? _min_a : _min_b; \ + }) + +#ifndef offsetof +#ifdef __compiler_offsetof +#define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE,MEMBER) +#else +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif +#endif + +/* this variant of container_of() retains 'const' on pointers without needing + * to be told to do so. The following will all work without warning: + * + * struct member *p; + * const struct member *cp; + * + * const struct cont *x = container_of(cp, struct cont, member); + * const struct cont *x = container_of(cp, const struct cont, member); + * const struct cont *x = container_of(p, struct cont, member); + * const struct cont *x = container_of(p, const struct cont, member); + * struct cont *x = container_of(p, struct cont, member); + * + * but the following will generate warnings about stripping const: + * + * struct cont *x = container_of(cp, struct cont, member); + * struct cont *x = container_of(cp, const struct cont, member); + * struct cont *x = container_of(p, const struct cont, member); + */ +#ifdef container_of +#undef container_of +#endif +#define container_of(ptr, type, member) \ + (__builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(&((type *)0)->member), \ + typeof(ptr)) \ + || __builtin_types_compatible_p(void *, typeof(ptr)), \ + ({ \ + typeof(((type *)0)->member) *__mptr = (void *)(ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); \ + }), \ + ({ \ + typeof(((const type *)0)->member) *__mptr = (ptr); \ + (const type *)((const char *)__mptr - \ + offsetof(type, member)); \ + }) \ + )) + +#define container_of_null(ptr, type, member) \ + ({ \ + typeof(ptr) _tmp = (ptr); \ + _tmp ? container_of(_tmp, type, member) : NULL; \ + }) + +#define array_size(ar) (sizeof(ar) / sizeof(ar[0])) + #ifdef __cplusplus } #endif diff --git a/lib/debug.c b/lib/debug.c index 72fd4648ee85..3248ceb13b53 100644 --- a/lib/debug.c +++ b/lib/debug.c @@ -18,29 +18,46 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include +#include "typesafe.h" #include "debug.h" #include "command.h" -static const struct debug_callbacks *callbacks; +static struct debug_cb_list_head cb_head; + +DECLARE_LIST(debug_cb_list, struct debug_callbacks, item) /* All code in this section should be reentrant and MT-safe */ DEFUN_NOSH(debug_all, debug_all_cmd, "[no] debug all", NO_STR DEBUG_STR "Toggle all debugging output\n") { + struct debug_callbacks *cb; + bool set = !strmatch(argv[0]->text, "no"); uint32_t mode = DEBUG_NODE2MODE(vty->node); - if (callbacks->debug_set_all) - callbacks->debug_set_all(mode, set); + frr_each (debug_cb_list, &cb_head, cb) + cb->debug_set_all(mode, set); + return CMD_SUCCESS; } /* ------------------------------------------------------------------------- */ -void debug_init(const struct debug_callbacks *cb) +void debug_init(struct debug_callbacks *cb) +{ + static bool inited = false; + + if (!inited) { + inited = true; + debug_cb_list_init(&cb_head); + } + + debug_cb_list_add_head(&cb_head, cb); +} + +void debug_init_cli(void) { - callbacks = cb; install_element(ENABLE_NODE, &debug_all_cmd); install_element(CONFIG_NODE, &debug_all_cmd); } diff --git a/lib/debug.h b/lib/debug.h index ace060d05789..f25cd426913f 100644 --- a/lib/debug.h +++ b/lib/debug.h @@ -84,6 +84,7 @@ struct debug { const char *desc; }; +PREDECL_LIST(debug_cb_list) /* * Callback set for debugging code. * @@ -92,6 +93,11 @@ struct debug { * mode set. */ struct debug_callbacks { + /* + * Linked list of Callbacks to call + */ + struct debug_cb_list_item item; + /* * flags * flags to set on debug flag fields @@ -233,7 +239,13 @@ struct debug_callbacks { * * MT-Safe */ -void debug_init(const struct debug_callbacks *cb); +void debug_init(struct debug_callbacks *cb); + +/* + * Turn on the cli to turn on/off debugs. + * Should only be called by libfrr + */ +void debug_init_cli(void); #ifdef __cplusplus } diff --git a/lib/distribute.c b/lib/distribute.c index be40bd2603d5..2aa6b927fb01 100644 --- a/lib/distribute.c +++ b/lib/distribute.c @@ -131,7 +131,7 @@ static struct distribute *distribute_get(struct distribute_ctx *ctx, return ret; } -static unsigned int distribute_hash_make(void *arg) +static unsigned int distribute_hash_make(const void *arg) { const struct distribute *dist = arg; diff --git a/lib/ferr.c b/lib/ferr.c index d7fa1a84f895..65c0cf886d3f 100644 --- a/lib/ferr.c +++ b/lib/ferr.c @@ -72,9 +72,9 @@ static bool ferr_hash_cmp(const void *a, const void *b) return f_a->code == f_b->code; } -static inline unsigned int ferr_hash_key(void *a) +static inline unsigned int ferr_hash_key(const void *a) { - struct log_ref *f = a; + const struct log_ref *f = a; return f->code; } diff --git a/lib/fifo.h b/lib/fifo.h deleted file mode 100644 index 6f9c59b5c116..000000000000 --- a/lib/fifo.h +++ /dev/null @@ -1,66 +0,0 @@ -/* FIFO common header. - * Copyright (C) 2015 Kunihiro Ishiguro - * - * This file is part of Quagga. - * - * Quagga is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * Quagga is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __LIB_FIFO_H__ -#define __LIB_FIFO_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -/* FIFO -- first in first out structure and macros. */ -struct fifo { - struct fifo *next; - struct fifo *prev; -}; - -#define FIFO_INIT(F) \ - do { \ - struct fifo *Xfifo = (struct fifo *)(F); \ - Xfifo->next = Xfifo->prev = Xfifo; \ - } while (0) - -#define FIFO_ADD(F, N) \ - do { \ - struct fifo *Xfifo = (struct fifo *)(F); \ - struct fifo *Xnode = (struct fifo *)(N); \ - Xnode->next = Xfifo; \ - Xnode->prev = Xfifo->prev; \ - Xfifo->prev = Xfifo->prev->next = Xnode; \ - } while (0) - -#define FIFO_DEL(N) \ - do { \ - struct fifo *Xnode = (struct fifo *)(N); \ - Xnode->prev->next = Xnode->next; \ - Xnode->next->prev = Xnode->prev; \ - } while (0) - -#define FIFO_HEAD(F) \ - ((((struct fifo *)(F))->next == (struct fifo *)(F)) ? NULL : (F)->next) - -#define FIFO_EMPTY(F) (((struct fifo *)(F))->next == (struct fifo *)(F)) - -#define FIFO_TOP(F) (FIFO_EMPTY(F) ? NULL : ((struct fifo *)(F))->next) - -#ifdef __cplusplus -} -#endif - -#endif /* __LIB_FIFO_H__ */ diff --git a/lib/frratomic.h b/lib/frratomic.h index e86030f83c7c..1e28253f2b2e 100644 --- a/lib/frratomic.h +++ b/lib/frratomic.h @@ -80,6 +80,9 @@ typedef std::atomic atomic_uint_fast32_t; #define atomic_compare_exchange_weak_explicit(atom, expect, desire, mem1, \ mem2) \ __atomic_compare_exchange_n(atom, expect, desire, 1, mem1, mem2) +#define atomic_compare_exchange_strong_explicit(atom, expect, desire, mem1, \ + mem2) \ + __atomic_compare_exchange_n(atom, expect, desire, 0, mem1, mem2) /* gcc 4.1 and newer, * clang 3.3 (possibly older) @@ -152,7 +155,7 @@ typedef std::atomic atomic_uint_fast32_t; rval; \ }) -#define atomic_compare_exchange_weak_explicit(atom, expect, desire, mem1, \ +#define atomic_compare_exchange_strong_explicit(atom, expect, desire, mem1, \ mem2) \ ({ \ typeof(atom) _atom = (atom); \ @@ -166,6 +169,8 @@ typedef std::atomic atomic_uint_fast32_t; *_expect = rval; \ ret; \ }) +#define atomic_compare_exchange_weak_explicit \ + atomic_compare_exchange_strong_explicit #define atomic_fetch_and_explicit(ptr, val, mem) \ ({ \ diff --git a/lib/lua.c b/lib/frrlua.c similarity index 99% rename from lib/lua.c rename to lib/frrlua.c index 3d701a936442..b7d8eea6e8d3 100644 --- a/lib/lua.c +++ b/lib/frrlua.c @@ -26,7 +26,7 @@ #if defined(HAVE_LUA) #include "prefix.h" -#include "lua.h" +#include "frrlua.h" #include "log.h" static int lua_zlog_debug(lua_State *L) diff --git a/lib/lua.h b/lib/frrlua.h similarity index 97% rename from lib/lua.h rename to lib/frrlua.h index a864ab30e04d..374eb70311cf 100644 --- a/lib/lua.h +++ b/lib/frrlua.h @@ -25,9 +25,9 @@ #if defined(HAVE_LUA) -#include -#include -#include +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" #ifdef __cplusplus extern "C" { diff --git a/lib/frrstr.c b/lib/frrstr.c index fd337073f812..c575c0b568ef 100644 --- a/lib/frrstr.c +++ b/lib/frrstr.c @@ -152,7 +152,33 @@ void frrstr_strvec_free(vector v) vector_free(v); } -bool begins_with(const char *str, const char *prefix) +char *frrstr_replace(const char *str, const char *find, const char *replace) +{ + char *ch; + char *nustr = XSTRDUP(MTYPE_TMP, str); + + size_t findlen = strlen(find); + size_t repllen = strlen(replace); + + while ((ch = strstr(nustr, find))) { + if (repllen > findlen) { + size_t nusz = strlen(nustr) + repllen - findlen + 1; + nustr = XREALLOC(MTYPE_TMP, nustr, nusz); + ch = strstr(nustr, find); + } + + size_t nustrlen = strlen(nustr); + size_t taillen = (nustr + nustrlen) - (ch + findlen); + + memmove(ch + findlen + (repllen - findlen), ch + findlen, + taillen + 1); + memcpy(ch, replace, repllen); + } + + return nustr; +} + +bool frrstr_startswith(const char *str, const char *prefix) { if (!str || !prefix) return false; @@ -166,6 +192,20 @@ bool begins_with(const char *str, const char *prefix) return strncmp(str, prefix, lenprefix) == 0; } +bool frrstr_endswith(const char *str, const char *suffix) +{ + if (!str || !suffix) + return false; + + size_t lenstr = strlen(str); + size_t lensuffix = strlen(suffix); + + if (lensuffix > lenstr) + return false; + + return strncmp(&str[lenstr - lensuffix], suffix, lensuffix) == 0; +} + int all_digit(const char *str) { for (; *str != '\0'; str++) diff --git a/lib/frrstr.h b/lib/frrstr.h index 8b591849a3e6..3a935c90cb26 100644 --- a/lib/frrstr.h +++ b/lib/frrstr.h @@ -87,6 +87,29 @@ void frrstr_filter_vec(vector v, regex_t *filter); */ void frrstr_strvec_free(vector v); +/* + * Given a string, replaces all occurrences of a substring with a different + * string. The result is a new string. The original string is not modified. + * + * If 'replace' is longer than 'find', this function performs N+1 allocations, + * where N is the number of times 'find' occurs in 'str'. If 'replace' is equal + * in length or shorter than 'find', only 1 allocation is performed. + * + * str + * String to perform replacement on. + * + * find + * Substring to replace. + * + * replace + * String to replace 'find' with. + * + * Returns: + * A new string, allocated with MTYPE_TMP, that is the result of performing + * the replacement on 'str'. This must be freed by the caller. + */ +char *frrstr_replace(const char *str, const char *find, const char *replace); + /* * Prefix match for string. * @@ -97,9 +120,23 @@ void frrstr_strvec_free(vector v); * prefix to look for * * Returns: - * true str starts with prefix, false otherwise + * true if str starts with prefix, false otherwise + */ +bool frrstr_startswith(const char *str, const char *prefix); + +/* + * Suffix match for string. + * + * str + * string to check for suffix match + * + * suffix + * suffix to look for + * + * Returns: + * true if str ends with suffix, false otherwise */ -bool begins_with(const char *str, const char *prefix); +bool frrstr_endswith(const char *str, const char *suffix); /* * Check the string only contains digit characters. diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index c9c942f9bf89..6d28a667b387 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -56,8 +56,10 @@ int main(int argc, char **argv) host.name = strdup("test"); host.domainname = strdup("testdomainname"); - vty_init(master); + vty_init(master, true); memory_init(); + yang_init(); + nb_init(master, NULL, 0); vty_stdio(vty_do_exit); diff --git a/lib/hash.c b/lib/hash.c index 884c8f22af72..fad7de51385d 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -37,7 +37,7 @@ static pthread_mutex_t _hashes_mtx = PTHREAD_MUTEX_INITIALIZER; static struct list *_hashes; struct hash *hash_create_size(unsigned int size, - unsigned int (*hash_key)(void *), + unsigned int (*hash_key)(const void *), bool (*hash_cmp)(const void *, const void *), const char *name) { @@ -66,7 +66,7 @@ struct hash *hash_create_size(unsigned int size, return hash; } -struct hash *hash_create(unsigned int (*hash_key)(void *), +struct hash *hash_create(unsigned int (*hash_key)(const void *), bool (*hash_cmp)(const void *, const void *), const char *name) { diff --git a/lib/hash.h b/lib/hash.h index 60c412b8e071..c56a98d50c64 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -79,7 +79,7 @@ struct hash { unsigned int max_size; /* Key make function. */ - unsigned int (*hash_key)(void *); + unsigned int (*hash_key)(const void *); /* Data compare function. */ bool (*hash_cmp)(const void *, const void *); @@ -123,7 +123,7 @@ struct hash { * Returns: * a new hash table */ -extern struct hash *hash_create(unsigned int (*hash_key)(void *), +extern struct hash *hash_create(unsigned int (*hash_key)(const void *), bool (*hash_cmp)(const void *, const void *), const char *name); @@ -158,7 +158,7 @@ extern struct hash *hash_create(unsigned int (*hash_key)(void *), * a new hash table */ extern struct hash * -hash_create_size(unsigned int size, unsigned int (*hash_key)(void *), +hash_create_size(unsigned int size, unsigned int (*hash_key)(const void *), bool (*hash_cmp)(const void *, const void *), const char *name); diff --git a/lib/if.c b/lib/if.c index 86b850c059f7..58b486c571a8 100644 --- a/lib/if.c +++ b/lib/if.c @@ -187,18 +187,21 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id) if (yang_module_find("frr-interface")) { struct lyd_node *if_dnode; - if_dnode = yang_dnode_get( - running_config->dnode, - "/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf", - ifp->name, old_vrf->name); - if (if_dnode) { - yang_dnode_change_leaf(if_dnode, vrf->name); - running_config->version++; + pthread_rwlock_wrlock(&running_config->lock); + { + if_dnode = yang_dnode_get( + running_config->dnode, + "/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf", + ifp->name, old_vrf->name); + if (if_dnode) { + yang_dnode_change_leaf(if_dnode, vrf->name); + running_config->version++; + } } + pthread_rwlock_unlock(&running_config->lock); } } - /* Delete interface structure. */ void if_delete_retain(struct interface *ifp) { @@ -386,6 +389,34 @@ struct interface *if_lookup_prefix(struct prefix *prefix, vrf_id_t vrf_id) return NULL; } +size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz, + struct interface ***result, vrf_id_t vrf_id) +{ + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + + struct list *rs = list_new(); + struct interface *ifp; + + FOR_ALL_INTERFACES (vrf, ifp) { + if (ifp->hw_addr_len == (int)addrsz + && !memcmp(hw_addr, ifp->hw_addr, addrsz)) + listnode_add(rs, ifp); + } + + if (rs->count) { + *result = XCALLOC(MTYPE_TMP, + sizeof(struct interface *) * rs->count); + list_to_array(rs, (void **)*result, rs->count); + } + + int count = rs->count; + + list_delete(&rs); + + return count; +} + + /* Get interface by name if given name interface doesn't exist create one. */ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id) @@ -873,6 +904,19 @@ struct connected *connected_add_by_prefix(struct interface *ifp, return ifc; } +struct connected *connected_get_linklocal(struct interface *ifp) +{ + struct listnode *n; + struct connected *c = NULL; + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) { + if (c->address->family == AF_INET6 + && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + break; + } + return c; +} + #if 0 /* this route_table of struct connected's is unused \ * however, it would be good to use a route_table rather than \ * a list.. \ @@ -1097,7 +1141,11 @@ DEFPY_NOSH (interface, * VRF. */ VRF_GET_ID(vrf_id, vrfname, false); - ifp = if_lookup_by_name_all_vrf(ifname); + if (vrf_get_backend() == VRF_BACKEND_UNKNOWN || + vrf_get_backend() == VRF_BACKEND_NETNS) + ifp = if_lookup_by_name(ifname, vrf_id); + else + ifp = if_lookup_by_name_all_vrf(ifname); if (ifp && ifp->vrf_id != vrf_id) { struct vrf *vrf; diff --git a/lib/if.h b/lib/if.h index d26d4dd68b1d..2dc1a7b2de23 100644 --- a/lib/if.h +++ b/lib/if.h @@ -225,6 +225,10 @@ struct interface { not work as expected. */ ifindex_t ifindex; + /* + * ifindex of parent interface, if any + */ + ifindex_t link_ifindex; #define IFINDEX_INTERNAL 0 /* Zebra internal interface status */ @@ -482,6 +486,8 @@ extern struct connected *if_lookup_address(void *matchaddr, int family, vrf_id_t vrf_id); extern struct interface *if_lookup_prefix(struct prefix *prefix, vrf_id_t vrf_id); +size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz, + struct interface ***result, vrf_id_t vrf_id); /* These 3 functions are to be used when the ifname argument is terminated by a '\0' character: */ @@ -540,6 +546,7 @@ extern struct connected *connected_lookup_prefix_exact(struct interface *, extern struct nbr_connected *nbr_connected_new(void); extern void nbr_connected_free(struct nbr_connected *); struct nbr_connected *nbr_connected_check(struct interface *, struct prefix *); +struct connected *connected_get_linklocal(struct interface *ifp); /* link parameters */ struct if_link_params *if_link_params_get(struct interface *); diff --git a/lib/if_rmap.c b/lib/if_rmap.c index b0802da96147..ca6f512ec6d6 100644 --- a/lib/if_rmap.c +++ b/lib/if_rmap.c @@ -107,7 +107,7 @@ static struct if_rmap *if_rmap_get(struct if_rmap_ctx *ctx, const char *ifname) return ret; } -static unsigned int if_rmap_hash_make(void *data) +static unsigned int if_rmap_hash_make(const void *data) { const struct if_rmap *if_rmap = data; diff --git a/lib/ipaddr.h b/lib/ipaddr.h index f4ddadc66e18..1c2399fdd3ea 100644 --- a/lib/ipaddr.h +++ b/lib/ipaddr.h @@ -56,6 +56,9 @@ struct ipaddr { #define SET_IPADDR_V4(p) (p)->ipa_type = IPADDR_V4 #define SET_IPADDR_V6(p) (p)->ipa_type = IPADDR_V6 +#define IPADDRSZ(p) \ + (IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr)) + static inline int str2ipaddr(const char *str, struct ipaddr *ip) { int ret; diff --git a/lib/json.c b/lib/json.c index 4ea20ba178ad..efc3794040aa 100644 --- a/lib/json.c +++ b/lib/json.c @@ -64,6 +64,11 @@ void json_object_boolean_true_add(struct json_object *obj, const char *key) json_object_object_add(obj, key, json_object_new_boolean(1)); } +void json_object_boolean_add(struct json_object *obj, const char *key, bool val) +{ + json_object_object_add(obj, key, json_object_new_boolean(val)); +} + struct json_object *json_object_lock(struct json_object *obj) { return json_object_get(obj); diff --git a/lib/json.h b/lib/json.h index a5251662be1a..c4d566b3189c 100644 --- a/lib/json.h +++ b/lib/json.h @@ -61,6 +61,8 @@ extern void json_object_string_add(struct json_object *obj, const char *key, const char *s); extern void json_object_int_add(struct json_object *obj, const char *key, int64_t i); +void json_object_boolean_add(struct json_object *obj, const char *key, + bool val); extern void json_object_boolean_false_add(struct json_object *obj, const char *key); extern void json_object_boolean_true_add(struct json_object *obj, diff --git a/lib/lib_errors.c b/lib/lib_errors.c index 5f6c25b770e0..b6c764d8739b 100644 --- a/lib/lib_errors.c +++ b/lib/lib_errors.c @@ -332,6 +332,12 @@ static struct log_ref ferr_lib_err[] = { .description = "The northbound subsystem has detected that the libsysrepo library returned an error", .suggestion = "Open an Issue with all relevant log files and restart FRR" }, + { + .code = EC_LIB_GRPC_INIT, + .title = "gRPC initialization error", + .description = "Upon startup FRR failed to properly initialize and startup the gRPC northbound plugin", + .suggestion = "Check if the gRPC libraries are installed correctly in the system.", + }, { .code = EC_LIB_NB_CB_CONFIG_ABORT, .title = "A northbound configuration callback has failed in the ABORT phase", diff --git a/lib/lib_errors.h b/lib/lib_errors.h index fc405c20983a..39b39fb065b9 100644 --- a/lib/lib_errors.h +++ b/lib/lib_errors.h @@ -80,6 +80,7 @@ enum lib_log_refs { EC_LIB_SYSREPO_INIT, EC_LIB_SYSREPO_DATA_CONVERT, EC_LIB_LIBSYSREPO, + EC_LIB_GRPC_INIT, EC_LIB_ID_CONSISTENCY, EC_LIB_ID_EXHAUST, }; diff --git a/lib/libfrr.c b/lib/libfrr.c index 0d4c8d6c0f1b..ed784fc73a18 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -39,13 +39,14 @@ #include "db.h" #include "northbound_cli.h" #include "northbound_db.h" +#include "debug.h" DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) DEFINE_KOOH(frr_early_fini, (), ()) DEFINE_KOOH(frr_fini, (), ()) const char frr_sysconfdir[] = SYSCONFDIR; -const char frr_vtydir[] = DAEMON_VTY_DIR; +char frr_vtydir[256]; #ifdef HAVE_SQLITE3 const char frr_dbdir[] = DAEMON_DB_DIR; #endif @@ -56,11 +57,11 @@ char frr_protonameinst[256] = "NONE"; char config_default[512]; char frr_zclientpath[256]; -static char pidfile_default[512]; +static char pidfile_default[1024]; #ifdef HAVE_SQLITE3 static char dbfile_default[512]; #endif -static char vtypath_default[256]; +static char vtypath_default[512]; bool debug_memstats_at_exit = false; static bool nodetach_term, nodetach_daemon; @@ -80,8 +81,8 @@ static void opt_extend(const struct optspec *os) { const struct option *lo; - strcat(comb_optstr, os->optstr); - strcat(comb_helpstr, os->helpstr); + strlcat(comb_optstr, os->optstr, sizeof(comb_optstr)); + strlcat(comb_helpstr, os->helpstr, sizeof(comb_helpstr)); for (lo = os->longopts; lo->name; lo++) memcpy(comb_next_lo++, lo, sizeof(*lo)); } @@ -93,6 +94,7 @@ static void opt_extend(const struct optspec *os) #define OPTION_LOGLEVEL 1004 #define OPTION_TCLI 1005 #define OPTION_DB_FILE 1006 +#define OPTION_LOGGING 1007 static const struct option lo_always[] = { {"help", no_argument, NULL, 'h'}, @@ -104,6 +106,7 @@ static const struct option lo_always[] = { {"log", required_argument, NULL, OPTION_LOG}, {"log-level", required_argument, NULL, OPTION_LOGLEVEL}, {"tcli", no_argument, NULL, OPTION_TCLI}, + {"command-log-always", no_argument, NULL, OPTION_LOGGING}, {NULL}}; static const struct optspec os_always = { "hvdM:", @@ -176,7 +179,7 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, memset(sa, 0, sizeof(*sa)); if (!path) - path = ZEBRA_SERV_PATH; + path = frr_zclientpath; if (!strncmp(path, ZAPI_TCP_PATHNAME, strlen(ZAPI_TCP_PATHNAME))) { /* note: this functionality is disabled at bottom */ @@ -282,6 +285,11 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, static struct frr_daemon_info *di = NULL; +void frr_init_vtydir(void) +{ + snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "", ""); +} + void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) { di = daemon; @@ -304,10 +312,13 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) if (di->flags & FRR_DETACH_LATER) nodetach_daemon = true; + frr_init_vtydir(); snprintf(config_default, sizeof(config_default), "%s/%s.conf", frr_sysconfdir, di->name); snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid", frr_vtydir, di->name); + snprintf(frr_zclientpath, sizeof(frr_zclientpath), + ZEBRA_SERV_PATH, "", ""); #ifdef HAVE_SQLITE3 snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s.db", frr_dbdir, di->name); @@ -316,8 +327,6 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) strlcpy(frr_protoname, di->logname, sizeof(frr_protoname)); strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst)); - strlcpy(frr_zclientpath, ZEBRA_SERV_PATH, sizeof(frr_zclientpath)); - di->cli_mode = FRR_CLI_CLASSIC; } @@ -397,6 +406,10 @@ static int frr_opt(int opt) errors++; break; } + if (di->zpathspace) + fprintf(stderr, + "-N option overriden by -z for zebra named socket path\n"); + if (strchr(optarg, '/') || strchr(optarg, '.')) { fprintf(stderr, "slashes or dots are not permitted in the --pathspace option.\n"); @@ -404,6 +417,14 @@ static int frr_opt(int opt) break; } di->pathspace = optarg; + + if (!di->zpathspace) + snprintf(frr_zclientpath, sizeof(frr_zclientpath), + ZEBRA_SERV_PATH, "/", di->pathspace); + snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "/", + di->pathspace); + snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid", + frr_vtydir, di->name); break; #ifdef HAVE_SQLITE3 case OPTION_DB_FILE: @@ -423,6 +444,10 @@ static int frr_opt(int opt) di->terminal = 1; break; case 'z': + di->zpathspace = true; + if (di->pathspace) + fprintf(stderr, + "-z option overrides -N option for zebra named socket path\n"); if (di->flags & FRR_NO_ZCLIENT) return 1; strlcpy(frr_zclientpath, optarg, sizeof(frr_zclientpath)); @@ -495,6 +520,9 @@ static int frr_opt(int opt) case OPTION_LOGLEVEL: di->early_loglevel = optarg; break; + case OPTION_LOGGING: + di->log_always = true; + break; default: return 1; } @@ -589,8 +617,8 @@ struct thread_master *frr_init(void) snprintf(config_default, sizeof(config_default), "%s%s%s%s.conf", frr_sysconfdir, p_pathspace, di->name, p_instance); - snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s%s.pid", - frr_vtydir, p_pathspace, di->name, p_instance); + snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s.pid", + frr_vtydir, di->name, p_instance); #ifdef HAVE_SQLITE3 snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s%s%s.db", frr_dbdir, p_pathspace, di->name, p_instance); @@ -647,13 +675,16 @@ struct thread_master *frr_init(void) else cmd_init(1); - vty_init(master); + vty_init(master, di->log_always); memory_init(); log_ref_init(); lib_error_init(); yang_init(); + + debug_init_cli(); + nb_init(master, di->yang_modules, di->n_yang_modules); if (nb_db_init() != NB_OK) flog_warn(EC_LIB_NB_DATABASE, @@ -830,7 +861,12 @@ static int frr_config_read_in(struct thread *t) /* * Update the shared candidate after reading the startup configuration. */ - nb_config_replace(vty_shared_candidate_config, running_config, true); + pthread_rwlock_rdlock(&running_config->lock); + { + nb_config_replace(vty_shared_candidate_config, running_config, + true); + } + pthread_rwlock_unlock(&running_config->lock); return 0; } @@ -866,9 +902,7 @@ static void frr_vty_serv(void) const char *dir; char defvtydir[256]; - snprintf(defvtydir, sizeof(defvtydir), "%s%s%s", frr_vtydir, - di->pathspace ? "/" : "", - di->pathspace ? di->pathspace : ""); + snprintf(defvtydir, sizeof(defvtydir), "%s", frr_vtydir); dir = di->vty_sock_path ? di->vty_sock_path : defvtydir; diff --git a/lib/libfrr.h b/lib/libfrr.h index 891e2c12825c..e2b3db74a3f7 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -81,7 +81,10 @@ struct frr_daemon_info { #endif const char *vty_path; const char *module_path; + const char *pathspace; + bool zpathspace; + const char *early_logging; const char *early_loglevel; @@ -97,6 +100,8 @@ struct frr_daemon_info { const struct frr_yang_module_info **yang_modules; size_t n_yang_modules; + + bool log_always; }; /* execname is the daemon's executable (and pidfile and configfile) name, @@ -118,6 +123,7 @@ struct frr_daemon_info { .version = FRR_VERSION, ) \ /* end */ +extern void frr_init_vtydir(void); extern void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv); extern void frr_opt_add(const char *optstr, const struct option *longopts, const char *helpstr); @@ -148,7 +154,7 @@ extern void frr_fini(void); extern char config_default[512]; extern char frr_zclientpath[256]; extern const char frr_sysconfdir[]; -extern const char frr_vtydir[]; +extern char frr_vtydir[256]; extern const char frr_moduledir[]; extern char frr_protoname[]; diff --git a/lib/linklist.c b/lib/linklist.c index 40c4b271698c..0d1efdf3aa1f 100644 --- a/lib/linklist.c +++ b/lib/linklist.c @@ -92,6 +92,46 @@ void listnode_add_head(struct list *list, void *val) list->count++; } +bool listnode_add_sort_nodup(struct list *list, void *val) +{ + struct listnode *n; + struct listnode *new; + int ret; + + assert(val != NULL); + + if (list->cmp) { + for (n = list->head; n; n = n->next) { + ret = (*list->cmp)(val, n->data); + if (ret < 0) { + new = listnode_new(); + new->data = val; + + new->next = n; + new->prev = n->prev; + + if (n->prev) + n->prev->next = new; + else + list->head = new; + n->prev = new; + list->count++; + return true; + } + /* found duplicate return false */ + if (ret == 0) + return false; + } + } + + new = listnode_new(); + new->data = val; + + LISTNODE_ATTACH(list, new); + + return true; +} + void listnode_add_sort(struct list *list, void *val) { struct listnode *n; @@ -206,7 +246,7 @@ void listnode_move_to_tail(struct list *l, struct listnode *n) LISTNODE_ATTACH(l, n); } -void listnode_delete(struct list *list, void *val) +void listnode_delete(struct list *list, const void *val) { struct listnode *node = listnode_lookup(list, val); @@ -242,6 +282,23 @@ void list_delete_all_node(struct list *list) list->count = 0; } +void list_filter_out_nodes(struct list *list, bool (*cond)(void *data)) +{ + struct listnode *node; + struct listnode *next; + void *data; + + assert(list); + + for (ALL_LIST_ELEMENTS(list, node, next, data)) { + if ((cond && cond(data)) || (!cond)) { + if (*list->del) + (*list->del)(data); + list_delete_node(list, node); + } + } +} + void list_delete(struct list **list) { assert(*list); @@ -250,7 +307,7 @@ void list_delete(struct list **list) *list = NULL; } -struct listnode *listnode_lookup(struct list *list, void *data) +struct listnode *listnode_lookup(struct list *list, const void *data) { struct listnode *node; @@ -334,3 +391,18 @@ struct listnode *listnode_add_force(struct list **list, void *val) *list = list_new(); return listnode_add(*list, val); } + +void **list_to_array(struct list *list, void **arr, size_t arrlen) +{ + struct listnode *ln; + void *vp; + size_t idx = 0; + + for (ALL_LIST_ELEMENTS_RO(list, ln, vp)) { + arr[idx++] = vp; + if (idx == arrlen) + break; + } + + return arr; +} diff --git a/lib/linklist.h b/lib/linklist.h index c30d8d314a3b..d23d425d627f 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -180,7 +180,7 @@ extern void listnode_move_to_tail(struct list *list, struct listnode *node); * data * data to insert into list */ -extern void listnode_delete(struct list *list, void *data); +extern void listnode_delete(struct list *list, const void *data); /* * Find the listnode corresponding to an element in a list. @@ -194,7 +194,7 @@ extern void listnode_delete(struct list *list, void *data); * Returns: * pointer to listnode storing the given data if found, NULL otherwise */ -extern struct listnode *listnode_lookup(struct list *list, void *data); +extern struct listnode *listnode_lookup(struct list *list, const void *data); /* * Retrieve the element at the head of a list. @@ -239,6 +239,26 @@ extern struct list *list_dup(struct list *l); extern void list_sort(struct list *list, int (*cmp)(const void **, const void **)); +/* + * Convert a list to an array of void pointers. + * + * Starts from the list head and ends either on the last node of the list or + * when the provided array cannot store any more elements. + * + * list + * list to convert + * + * arr + * Pre-allocated array of void * + * + * arrlen + * Number of elements in arr + * + * Returns: + * arr + */ +void **list_to_array(struct list *list, void **arr, size_t arrlen); + /* * Delete a list and NULL its pointer. * @@ -288,6 +308,39 @@ extern void list_delete_node(struct list *list, struct listnode *node); */ extern void list_add_list(struct list *list, struct list *add); +/* + * Delete all nodes which satisfy a condition from a list. + * Deletes the node if cond function returns true for the node. + * If function ptr passed is NULL, it deletes all nodes + * + * list + * list to operate on + * cond + * function pointer which takes node data as input and return TRUE or FALSE + */ + +extern void list_filter_out_nodes(struct list *list, bool (*cond)(void *data)); + +/* + * Insert a new element into a list with insertion sort if there is no + * duplicate element present in the list. This assumes the input list is + * sorted. If unsorted, it will check for duplicate until it finds out + * the position to do insertion sort with the unsorted list. + * + * If list->cmp is set, this function is used to determine the position to + * insert the new element. If it is not set, this function is equivalent to + * listnode_add. duplicate element is determined by cmp function returning 0. + * + * Runtime is O(N). + * + * list + * list to operate on + * + * val + * element to add + */ + +extern bool listnode_add_sort_nodup(struct list *list, void *val); /* List iteration macro. * Usage: for (ALL_LIST_ELEMENTS (...) { ... } * It is safe to delete the listnode using this macro. diff --git a/lib/log.c b/lib/log.c index e64c00186be2..5ce3bd7020b4 100644 --- a/lib/log.c +++ b/lib/log.c @@ -30,6 +30,7 @@ #include "command.h" #include "lib_errors.h" #include "lib/hook.h" +#include "printfrr.h" #ifndef SUNOS_5 #include @@ -191,19 +192,13 @@ static void time_print(FILE *fp, struct timestamp_control *ctl) static void vzlog_file(struct zlog *zl, struct timestamp_control *tsctl, const char *proto_str, int record_priority, int priority, - FILE *fp, const char *format, va_list args) + FILE *fp, const char *msg) { - va_list ac; - time_print(fp, tsctl); if (record_priority) fprintf(fp, "%s: ", zlog_priority[priority]); - fprintf(fp, "%s", proto_str); - va_copy(ac, args); - vfprintf(fp, format, ac); - va_end(ac); - fprintf(fp, "\n"); + fprintf(fp, "%s%s\n", proto_str, msg); fflush(fp); } @@ -217,33 +212,26 @@ void vzlog(int priority, const char *format, va_list args) struct timestamp_control tsctl; tsctl.already_rendered = 0; struct zlog *zl = zlog_default; + char buf[256], *msg; /* call external hook */ hook_call(zebra_ext_log, priority, format, args); + msg = vasnprintfrr(MTYPE_TMP, buf, sizeof(buf), format, args); + /* When zlog_default is also NULL, use stderr for logging. */ if (zl == NULL) { tsctl.precision = 0; time_print(stderr, &tsctl); - fprintf(stderr, "%s: ", "unknown"); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); + fprintf(stderr, "%s: %s\n", "unknown", msg); fflush(stderr); - - /* In this case we return at here. */ - errno = original_errno; - pthread_mutex_unlock(&loglock); - return; + goto out; } tsctl.precision = zl->timestamp_precision; /* Syslog output */ - if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) { - va_list ac; - va_copy(ac, args); - vsyslog(priority | zlog_default->facility, format, ac); - va_end(ac); - } + if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) + syslog(priority | zlog_default->facility, "%s", msg); if (zl->instance) sprintf(proto_str, "%s[%d]: ", zl->protoname, zl->instance); @@ -253,7 +241,7 @@ void vzlog(int priority, const char *format, va_list args) /* File output. */ if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority, - zl->fp, format, args); + zl->fp, msg); /* fixed-config logging to stderr while we're stating up & haven't * daemonized / reached mainloop yet @@ -261,17 +249,19 @@ void vzlog(int priority, const char *format, va_list args) * note the "else" on stdout output -- we don't want to print the same * message to both stderr and stdout. */ if (zlog_startup_stderr && priority <= LOG_WARNING) - vzlog_file(zl, &tsctl, proto_str, 1, priority, stderr, format, - args); + vzlog_file(zl, &tsctl, proto_str, 1, priority, stderr, msg); else if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority, - stdout, format, args); + stdout, msg); /* Terminal monitor. */ if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR]) vty_log((zl->record_priority ? zlog_priority[priority] : NULL), - proto_str, format, &tsctl, args); + proto_str, msg, &tsctl); +out: + if (msg != buf) + XFREE(MTYPE_TMP, msg); errno = original_errno; pthread_mutex_unlock(&loglock); } @@ -305,47 +295,11 @@ int vzlog_test(int priority) return ret; } -static char *str_append(char *dst, int len, const char *src) -{ - while ((len-- > 0) && *src) - *dst++ = *src++; - return dst; -} - -static char *num_append(char *s, int len, unsigned long x) -{ - char buf[30]; - char *t; - - if (!x) - return str_append(s, len, "0"); - *(t = &buf[sizeof(buf) - 1]) = '\0'; - while (x && (t > buf)) { - *--t = '0' + (x % 10); - x /= 10; - } - return str_append(s, len, t); -} - -#if defined(SA_SIGINFO) \ - || defined(HAVE_PRINTSTACK) \ - || defined(HAVE_GLIBC_BACKTRACE) -static char *hex_append(char *s, int len, unsigned long x) -{ - char buf[30]; - char *t; - - if (!x) - return str_append(s, len, "0"); - *(t = &buf[sizeof(buf) - 1]) = '\0'; - while (x && (t > buf)) { - unsigned int cc = (x % 16); - *--t = ((cc < 10) ? ('0' + cc) : ('a' + cc - 10)); - x /= 16; - } - return str_append(s, len, t); -} -#endif +/* + * crash handling + * + * NB: only AS-Safe (async-signal) functions can be used here! + */ /* Needs to be enhanced to support Solaris. */ static int syslog_connect(void) @@ -354,7 +308,6 @@ static int syslog_connect(void) return -1; #else int fd; - char *s; struct sockaddr_un addr; if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) @@ -365,10 +318,8 @@ static int syslog_connect(void) #else #define SYSLOG_SOCKET_PATH "/dev/log" #endif - s = str_append(addr.sun_path, sizeof(addr.sun_path), - SYSLOG_SOCKET_PATH); + strlcpy(addr.sun_path, SYSLOG_SOCKET_PATH, sizeof(addr.sun_path)); #undef SYSLOG_SOCKET_PATH - *s = '\0'; if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { close(fd); return -1; @@ -381,168 +332,109 @@ static void syslog_sigsafe(int priority, const char *msg, size_t msglen) { static int syslog_fd = -1; char buf[sizeof("<1234567890>ripngd[1234567890]: ") + msglen + 50]; - char *s; + struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) }; if ((syslog_fd < 0) && ((syslog_fd = syslog_connect()) < 0)) return; -#define LOC s,buf+sizeof(buf)-s - s = buf; - s = str_append(LOC, "<"); - s = num_append(LOC, priority); - s = str_append(LOC, ">"); /* forget about the timestamp, too difficult in a signal handler */ - s = str_append(LOC, zlog_default->ident); - if (zlog_default->syslog_options & LOG_PID) { - s = str_append(LOC, "["); - s = num_append(LOC, getpid()); - s = str_append(LOC, "]"); - } - s = str_append(LOC, ": "); - s = str_append(LOC, msg); - write_wrapper(syslog_fd, buf, s - buf); -#undef LOC + bprintfrr(&fb, "<%d>%s", priority, zlog_default->ident); + if (zlog_default->syslog_options & LOG_PID) + bprintfrr(&fb, "[%ld]", (long)getpid()); + bprintfrr(&fb, ": %s", msg); + write_wrapper(syslog_fd, fb.buf, fb.pos - fb.buf); } static int open_crashlog(void) { -#define CRASHLOG_PREFIX "/var/tmp/quagga." -#define CRASHLOG_SUFFIX "crashlog" - if (zlog_default && zlog_default->ident) { - /* Avoid strlen since it is not async-signal-safe. */ - const char *p; - size_t ilen; - - for (p = zlog_default->ident, ilen = 0; *p; p++) - ilen++; - { - char buf[sizeof(CRASHLOG_PREFIX) + ilen - + sizeof(CRASHLOG_SUFFIX) + 3]; - char *s = buf; -#define LOC s,buf+sizeof(buf)-s - s = str_append(LOC, CRASHLOG_PREFIX); - s = str_append(LOC, zlog_default->ident); - s = str_append(LOC, "."); - s = str_append(LOC, CRASHLOG_SUFFIX); -#undef LOC - *s = '\0'; - return open(buf, O_WRONLY | O_CREAT | O_EXCL, - LOGFILE_MASK); - } - } - return open(CRASHLOG_PREFIX CRASHLOG_SUFFIX, - O_WRONLY | O_CREAT | O_EXCL, LOGFILE_MASK); -#undef CRASHLOG_SUFFIX -#undef CRASHLOG_PREFIX -} - -/* Note: the goal here is to use only async-signal-safe functions. */ -void zlog_signal(int signo, const char *action -#ifdef SA_SIGINFO - , - siginfo_t *siginfo, void *program_counter -#endif - ) -{ - time_t now; - char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...") - + 100]; - char *s = buf; - char *msgstart = buf; -#define LOC s,buf+sizeof(buf)-s + char crashlog_buf[PATH_MAX]; + const char *crashlog_default = "/var/tmp/frr.crashlog", *crashlog; - time(&now); - if (zlog_default) { - s = str_append(LOC, zlog_default->protoname); - *s++ = ':'; - *s++ = ' '; - msgstart = s; - } - s = str_append(LOC, "Received signal "); - s = num_append(LOC, signo); - s = str_append(LOC, " at "); - s = num_append(LOC, now); -#ifdef SA_SIGINFO - s = str_append(LOC, " (si_addr 0x"); - s = hex_append(LOC, (unsigned long)(siginfo->si_addr)); - if (program_counter) { - s = str_append(LOC, ", PC 0x"); - s = hex_append(LOC, (unsigned long)program_counter); + if (!zlog_default || !zlog_default->ident) + crashlog = crashlog_default; + else { + snprintfrr(crashlog_buf, sizeof(crashlog_buf), + "/var/tmp/frr.%s.crashlog", zlog_default->ident); + crashlog = crashlog_buf; } - s = str_append(LOC, "); "); -#else /* SA_SIGINFO */ - s = str_append(LOC, "; "); -#endif /* SA_SIGINFO */ - s = str_append(LOC, action); - if (s < buf + sizeof(buf)) - *s++ = '\n'; + return open(crashlog, O_WRONLY | O_CREAT | O_EXCL, LOGFILE_MASK); +} /* N.B. implicit priority is most severe */ #define PRI LOG_CRIT -#define DUMP(FD) write_wrapper(FD, buf, s-buf); +static void crash_write(struct fbuf *fb, char *msgstart) +{ + if (fb->pos == fb->buf) + return; + if (!msgstart) + msgstart = fb->buf; + /* If no file logging configured, try to write to fallback log file. */ if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0)) - DUMP(logfile_fd) + write(logfile_fd, fb->buf, fb->pos - fb->buf); if (!zlog_default) - DUMP(STDERR_FILENO) + write(STDERR_FILENO, fb->buf, fb->pos - fb->buf); else { if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) - DUMP(STDOUT_FILENO) + write(STDOUT_FILENO, fb->buf, fb->pos - fb->buf); /* Remove trailing '\n' for monitor and syslog */ - *--s = '\0'; + fb->pos--; if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); + vty_log_fixed(fb->buf, fb->pos - fb->buf); if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) syslog_sigsafe(PRI | zlog_default->facility, msgstart, - s - msgstart); + fb->pos - msgstart); } -#undef DUMP +} - zlog_backtrace_sigsafe(PRI, +/* Note: the goal here is to use only async-signal-safe functions. */ +void zlog_signal(int signo, const char *action, void *siginfo_v, + void *program_counter) +{ #ifdef SA_SIGINFO - program_counter -#else - NULL + siginfo_t *siginfo = siginfo_v; #endif - ); + time_t now; + char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...") + + 100]; + char *msgstart; + struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) }; + + time(&now); + if (zlog_default) + bprintfrr(&fb, "%s: ", zlog_default->protoname); + + msgstart = fb.pos; + + bprintfrr(&fb, "Received signal %d at %lld", signo, (long long)now); +#ifdef SA_SIGINFO + if (program_counter) + bprintfrr(&fb, " (si_addr 0x%tx, PC 0x%tx)", + (ptrdiff_t)siginfo->si_addr, + (ptrdiff_t)program_counter); + else + bprintfrr(&fb, " (si_addr 0x%tx)", + (ptrdiff_t)siginfo->si_addr); +#endif /* SA_SIGINFO */ + bprintfrr(&fb, "; %s\n", action); + + crash_write(&fb, msgstart); + + zlog_backtrace_sigsafe(PRI, program_counter); + + fb.pos = buf; - s = buf; struct thread *tc; tc = pthread_getspecific(thread_current); - if (!tc) - s = str_append(LOC, "no thread information available\n"); - else { - s = str_append(LOC, "in thread "); - s = str_append(LOC, tc->funcname); - s = str_append(LOC, " scheduled from "); - s = str_append(LOC, tc->schedfrom); - s = str_append(LOC, ":"); - s = num_append(LOC, tc->schedfrom_line); - s = str_append(LOC, "\n"); - } -#define DUMP(FD) write_wrapper(FD, buf, s-buf); - /* If no file logging configured, try to write to fallback log file. */ - if (logfile_fd >= 0) - DUMP(logfile_fd) - if (!zlog_default) - DUMP(STDERR_FILENO) - else { - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) - DUMP(STDOUT_FILENO) - /* Remove trailing '\n' for monitor and syslog */ - *--s = '\0'; - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe(PRI | zlog_default->facility, msgstart, - s - msgstart); - } -#undef DUMP + if (!tc) + bprintfrr(&fb, "no thread information available\n"); + else + bprintfrr(&fb, "in thread %s scheduled from %s:%d\n", + tc->funcname, tc->schedfrom, tc->schedfrom_line); -#undef PRI -#undef LOC + crash_write(&fb, NULL); } /* Log a backtrace using only async-signal-safe functions. @@ -550,7 +442,8 @@ void zlog_signal(int signo, const char *action void zlog_backtrace_sigsafe(int priority, void *program_counter) { #ifdef HAVE_LIBUNWIND - char buf[100]; + char buf[256]; + struct fbuf fb = { .buf = buf, .len = sizeof(buf) }; unw_cursor_t cursor; unw_context_t uc; unw_word_t ip, off, sp; @@ -564,28 +457,27 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_reg(&cursor, UNW_REG_SP, &sp); - if (unw_is_signal_frame(&cursor)) - dprintf(2, " ---- signal ----\n"); - - if (!unw_get_proc_name(&cursor, buf, sizeof(buf), &off)) { - snprintf(name, sizeof(name), "%s+%#lx", - buf, (long)off); - } - dprintf(2, "%-30s %16lx %16lx", name, (long)ip, (long)sp); - if (dladdr((void *)ip, &dlinfo)) { - dprintf(2, " %s (mapped at %p)", - dlinfo.dli_fname, dlinfo.dli_fbase); - } - dprintf(2, "\n"); + if (!unw_get_proc_name(&cursor, buf, sizeof(buf), &off)) + snprintfrr(name, sizeof(name), "%s+%#lx", + buf, (long)off); + fb.pos = buf; + if (unw_is_signal_frame(&cursor)) + bprintfrr(&fb, " ---- signal ----\n"); + bprintfrr(&fb, "%-30s %16lx %16lx", name, (long)ip, (long)sp); + if (dladdr((void *)ip, &dlinfo)) + bprintfrr(&fb, " %s (mapped at %p)", + dlinfo.dli_fname, dlinfo.dli_fbase); + bprintfrr(&fb, "\n"); + crash_write(&fb, NULL); } #elif defined(HAVE_GLIBC_BACKTRACE) || defined(HAVE_PRINTSTACK) static const char pclabel[] = "Program counter: "; void *array[64]; int size; - char buf[100]; - char *s, **bt = NULL; -#define LOC s,buf+sizeof(buf)-s + char buf[128]; + struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) }; + char **bt = NULL; #ifdef HAVE_GLIBC_BACKTRACE size = backtrace(array, array_size(array)); @@ -598,23 +490,22 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) write_wrapper(FD, pclabel, sizeof(pclabel) - 1); \ backtrace_symbols_fd(&program_counter, 1, FD); \ } \ - write_wrapper(FD, buf, s - buf); \ + write_wrapper(FD, fb.buf, fb.pos - fb.buf); \ backtrace_symbols_fd(array, size, FD); \ } #elif defined(HAVE_PRINTSTACK) + size = 0; + #define DUMP(FD) \ { \ if (program_counter) \ write_wrapper((FD), pclabel, sizeof(pclabel) - 1); \ - write_wrapper((FD), buf, s - buf); \ + write_wrapper((FD), fb.buf, fb.pos - fb.buf); \ printstack((FD)); \ } #endif /* HAVE_GLIBC_BACKTRACE, HAVE_PRINTSTACK */ - s = buf; - s = str_append(LOC, "Backtrace for "); - s = num_append(LOC, size); - s = str_append(LOC, " stack frames:\n"); + bprintfrr(&fb, "Backtrace for %d stack frames:\n", size); if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0)) DUMP(logfile_fd) @@ -624,12 +515,12 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) if (priority <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) DUMP(STDOUT_FILENO) /* Remove trailing '\n' for monitor and syslog */ - *--s = '\0'; + fb.pos--; if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); + vty_log_fixed(fb.buf, fb.pos - fb.buf); if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe(priority | zlog_default->facility, buf, - s - buf); + syslog_sigsafe(priority | zlog_default->facility, + fb.buf, fb.pos - fb.buf); { int i; #ifdef HAVE_GLIBC_BACKTRACE @@ -637,34 +528,26 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) #endif /* Just print the function addresses. */ for (i = 0; i < size; i++) { - s = buf; + fb.pos = buf; if (bt) - s = str_append(LOC, bt[i]); - else { - s = str_append(LOC, "[bt "); - s = num_append(LOC, i); - s = str_append(LOC, "] 0x"); - s = hex_append( - LOC, (unsigned long)(array[i])); - } - *s = '\0'; + bprintfrr(&fb, "%s", bt[i]); + else + bprintfrr(&fb, "[bt %d] 0x%tx", i, + (ptrdiff_t)(array[i])); if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); + vty_log_fixed(fb.buf, fb.pos - fb.buf); if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe( - priority - | zlog_default - ->facility, - buf, s - buf); + syslog_sigsafe(priority + | zlog_default->facility, + fb.buf, fb.pos - fb.buf); } if (bt) free(bt); } } #undef DUMP -#undef LOC #endif /* HAVE_STRACK_TRACE */ } diff --git a/lib/log.h b/lib/log.h index 9368bf9e8299..3ef4d2f379fa 100644 --- a/lib/log.h +++ b/lib/log.h @@ -128,12 +128,8 @@ const char *lookup_msg(const struct message *mz, int kz, const char *nf); extern const char *safe_strerror(int errnum); /* To be called when a fatal signal is caught. */ -extern void zlog_signal(int signo, const char *action -#ifdef SA_SIGINFO - , - siginfo_t *siginfo, void *program_counter -#endif - ); +extern void zlog_signal(int signo, const char *action, void *siginfo, + void *program_counter); /* Log a backtrace. */ extern void zlog_backtrace(int priority); @@ -158,6 +154,25 @@ extern void zlog_hexdump(const void *mem, unsigned int len); extern const char *zlog_sanitize(char *buf, size_t bufsz, const void *in, size_t inlen); +/* Note: whenever a new route-type or zserv-command is added the + * corresponding {command,route}_types[] table in lib/log.c MUST be + * updated! */ + +/* Map a route type to a string. For example, ZEBRA_ROUTE_RIPNG -> "ripng". */ +extern const char *zebra_route_string(unsigned int route_type); +/* Map a route type to a char. For example, ZEBRA_ROUTE_RIPNG -> 'R'. */ +extern char zebra_route_char(unsigned int route_type); +/* Map a zserv command type to the same string, + * e.g. ZEBRA_INTERFACE_ADD -> "ZEBRA_INTERFACE_ADD" */ +/* Map a protocol name to its number. e.g. ZEBRA_ROUTE_BGP->9*/ +extern int proto_name2num(const char *s); +/* Map redistribute X argument to protocol number. + * unlike proto_name2num, this accepts shorthands and takes + * an AFI value to restrict input */ +extern int proto_redistnum(int afi, const char *s); + +extern const char *zserv_command_string(unsigned int command); + extern int vzlog_test(int priority); diff --git a/lib/memory.h b/lib/memory.h index 91a02b7966c2..0002ea3349ad 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -26,8 +26,6 @@ extern "C" { #endif -#define array_size(ar) (sizeof(ar) / sizeof(ar[0])) - #if defined(HAVE_MALLOC_SIZE) && !defined(HAVE_MALLOC_USABLE_SIZE) #define malloc_usable_size(x) malloc_size(x) #define HAVE_MALLOC_USABLE_SIZE diff --git a/lib/module.h b/lib/module.h index c5f96db85b05..79cf52d75aa6 100644 --- a/lib/module.h +++ b/lib/module.h @@ -24,16 +24,6 @@ extern "C" { #endif -#if !defined(__GNUC__) -#error module code needs GCC visibility extensions -#elif __GNUC__ < 4 -#error module code needs GCC visibility extensions -#else -# define DSO_PUBLIC __attribute__ ((visibility ("default"))) -# define DSO_SELF __attribute__ ((visibility ("protected"))) -# define DSO_LOCAL __attribute__ ((visibility ("hidden"))) -#endif - struct frrmod_runtime; struct frrmod_info { diff --git a/lib/nexthop.c b/lib/nexthop.c index 8e16e705901b..4cea14955abd 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -32,44 +32,137 @@ #include "nexthop.h" #include "mpls.h" #include "jhash.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop") DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label") -/* check if nexthops are same, non-recursive */ -int nexthop_same_no_recurse(const struct nexthop *next1, - const struct nexthop *next2) +static int _nexthop_labels_cmp(const struct nexthop *nh1, + const struct nexthop *nh2) { - if (next1->type != next2->type) + const struct mpls_label_stack *nhl1 = NULL; + const struct mpls_label_stack *nhl2 = NULL; + + nhl1 = nh1->nh_label; + nhl2 = nh2->nh_label; + + /* No labels is a match */ + if (!nhl1 && !nhl2) return 0; - switch (next1->type) { + if (nhl1 && !nhl2) + return 1; + + if (nhl2 && !nhl1) + return -1; + + if (nhl1->num_labels > nhl2->num_labels) + return 1; + + if (nhl1->num_labels < nhl2->num_labels) + return -1; + + return memcmp(nhl1->label, nhl2->label, nhl1->num_labels); +} + +static int _nexthop_g_addr_cmp(enum nexthop_types_t type, + const union g_addr *addr1, + const union g_addr *addr2) +{ + int ret = 0; + + switch (type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: - if (!IPV4_ADDR_SAME(&next1->gate.ipv4, &next2->gate.ipv4)) - return 0; - if (next1->ifindex && (next1->ifindex != next2->ifindex)) - return 0; + ret = IPV4_ADDR_CMP(&addr1->ipv4, &addr2->ipv4); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + ret = IPV6_ADDR_CMP(&addr1->ipv6, &addr2->ipv6); break; case NEXTHOP_TYPE_IFINDEX: - if (next1->ifindex != next2->ifindex) - return 0; + case NEXTHOP_TYPE_BLACKHOLE: + /* No addr here */ break; + } + + return ret; +} + +static int _nexthop_gateway_cmp(const struct nexthop *nh1, + const struct nexthop *nh2) +{ + return _nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate); +} + +static int _nexthop_source_cmp(const struct nexthop *nh1, + const struct nexthop *nh2) +{ + return _nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src); +} + +static int _nexthop_cmp_no_labels(const struct nexthop *next1, + const struct nexthop *next2) +{ + int ret = 0; + + if (next1->vrf_id < next2->vrf_id) + return -1; + + if (next1->vrf_id > next2->vrf_id) + return 1; + + if (next1->type < next2->type) + return -1; + + if (next1->type > next2->type) + return 1; + + switch (next1->type) { + case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV6: - if (!IPV6_ADDR_SAME(&next1->gate.ipv6, &next2->gate.ipv6)) - return 0; + ret = _nexthop_gateway_cmp(next1, next2); + if (ret != 0) + return ret; break; + case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV6_IFINDEX: - if (!IPV6_ADDR_SAME(&next1->gate.ipv6, &next2->gate.ipv6)) - return 0; - if (next1->ifindex != next2->ifindex) - return 0; + ret = _nexthop_gateway_cmp(next1, next2); + if (ret != 0) + return ret; + /* Intentional Fall-Through */ + case NEXTHOP_TYPE_IFINDEX: + if (next1->ifindex < next2->ifindex) + return -1; + + if (next1->ifindex > next2->ifindex) + return 1; break; - default: - /* do nothing */ + case NEXTHOP_TYPE_BLACKHOLE: + if (next1->bh_type < next2->bh_type) + return -1; + + if (next1->bh_type > next2->bh_type) + return 1; break; } - return 1; + + ret = _nexthop_source_cmp(next1, next2); + + return ret; +} + +int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2) +{ + int ret = 0; + + ret = _nexthop_cmp_no_labels(next1, next2); + if (ret != 0) + return ret; + + ret = _nexthop_labels_cmp(next1, next2); + + return ret; } int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2) @@ -121,27 +214,12 @@ const char *nexthop_type_to_str(enum nexthop_types_t nh_type) /* * Check if the labels match for the 2 nexthops specified. */ -int nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2) +bool nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2) { - const struct mpls_label_stack *nhl1, *nhl2; - - nhl1 = nh1->nh_label; - nhl2 = nh2->nh_label; - - /* No labels is a match */ - if (!nhl1 && !nhl2) - return 1; - - if (!nhl1 || !nhl2) - return 0; - - if (nhl1->num_labels != nhl2->num_labels) - return 0; - - if (memcmp(nhl1->label, nhl2->label, nhl1->num_labels)) - return 0; + if (_nexthop_labels_cmp(nh1, nh2) != 0) + return false; - return 1; + return true; } struct nexthop *nexthop_new(void) @@ -180,45 +258,28 @@ bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2) if (nh1 == nh2) return true; - if (nh1->vrf_id != nh2->vrf_id) + if (nexthop_cmp(nh1, nh2) != 0) + return false; + + return true; +} + +bool nexthop_same_no_labels(const struct nexthop *nh1, + const struct nexthop *nh2) +{ + if (nh1 && !nh2) return false; - if (nh1->type != nh2->type) + if (!nh1 && nh2) return false; - switch (nh1->type) { - case NEXTHOP_TYPE_IFINDEX: - if (nh1->ifindex != nh2->ifindex) - return false; - break; - case NEXTHOP_TYPE_IPV4: - if (nh1->gate.ipv4.s_addr != nh2->gate.ipv4.s_addr) - return false; - break; - case NEXTHOP_TYPE_IPV4_IFINDEX: - if (nh1->gate.ipv4.s_addr != nh2->gate.ipv4.s_addr) - return false; - if (nh1->ifindex != nh2->ifindex) - return false; - break; - case NEXTHOP_TYPE_IPV6: - if (memcmp(&nh1->gate.ipv6, &nh2->gate.ipv6, 16)) - return false; - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - if (memcmp(&nh1->gate.ipv6, &nh2->gate.ipv6, 16)) - return false; - if (nh1->ifindex != nh2->ifindex) - return false; - break; - case NEXTHOP_TYPE_BLACKHOLE: - if (nh1->bh_type != nh2->bh_type) - return false; - break; - } + if (nh1 == nh2) + return true; - /* Compare labels too (if present) */ - return (!!nexthop_labels_match(nh1, nh2)); + if (_nexthop_cmp_no_labels(nh1, nh2) != 0) + return false; + + return true; } /* Update nexthop with label information. */ @@ -363,3 +424,87 @@ uint32_t nexthop_hash(const struct nexthop *nexthop) } return key; } + +/* + * nexthop printing variants: + * %pNHvv + * via 1.2.3.4 + * via 1.2.3.4, eth0 + * is directly connected, eth0 + * unreachable (blackhole) + * %pNHv + * 1.2.3.4 + * 1.2.3.4, via eth0 + * directly connected, eth0 + * unreachable (blackhole) + * %pNHs + * nexthop2str() + */ +printfrr_ext_autoreg_p("NH", printfrr_nh) +static ssize_t printfrr_nh(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct nexthop *nexthop = ptr; + struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 }; + bool do_ifi = false; + const char *s, *v_is = "", *v_via = "", *v_viaif = "via "; + ssize_t ret = 3; + + switch (fmt[2]) { + case 'v': + if (fmt[3] == 'v') { + v_is = "is "; + v_via = "via "; + v_viaif = ""; + ret++; + } + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + bprintfrr(&fb, "%s%pI4", v_via, &nexthop->gate.ipv4); + do_ifi = true; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + bprintfrr(&fb, "%s%pI6", v_via, &nexthop->gate.ipv6); + do_ifi = true; + break; + case NEXTHOP_TYPE_IFINDEX: + bprintfrr(&fb, "%sdirectly connected, %s", v_is, + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + s = " (ICMP unreachable)"; + break; + case BLACKHOLE_ADMINPROHIB: + s = " (ICMP admin-prohibited)"; + break; + case BLACKHOLE_NULL: + s = " (blackhole)"; + break; + default: + s = ""; + break; + } + bprintfrr(&fb, "unreachable%s", s); + break; + default: + break; + } + if (do_ifi && nexthop->ifindex) + bprintfrr(&fb, ", %s%s", v_viaif, ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + + *fb.pos = '\0'; + return ret; + case 's': + nexthop2str(nexthop, buf, bsz); + return 3; + } + return 0; +} diff --git a/lib/nexthop.h b/lib/nexthop.h index 663acaeb6951..5b6c12d4ef50 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -139,12 +139,13 @@ void nexthop_del_labels(struct nexthop *); uint32_t nexthop_hash(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, + const struct nexthop *nh2); +extern int nexthop_cmp(const struct nexthop *nh1, const struct nexthop *nh2); extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type); -extern int nexthop_same_no_recurse(const struct nexthop *next1, - const struct nexthop *next2); -extern int nexthop_labels_match(const struct nexthop *nh1, - const struct nexthop *nh2); +extern bool nexthop_labels_match(const struct nexthop *nh1, + const struct nexthop *nh2); extern int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2); extern const char *nexthop2str(const struct nexthop *nexthop, diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index ed22f64494b4..10f610db377d 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -308,7 +308,7 @@ static void nhgc_delete(struct nexthop_group_cmd *nhgc) DEFINE_QOBJ_TYPE(nexthop_group_cmd) -DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NAME", +DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NHGNAME", "Enter into the nexthop-group submode\n" "Specify the NAME of the nexthop-group\n") { @@ -321,7 +321,7 @@ DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NAME", return CMD_SUCCESS; } -DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NAME", +DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME", NO_STR "Delete the nexthop-group\n" "Specify the NAME of the nexthop-group\n") @@ -714,6 +714,19 @@ void nexthop_group_interface_state_change(struct interface *ifp, } } +static void nhg_name_autocomplete(vector comps, struct cmd_token *token) +{ + struct nexthop_group_cmd *nhgc; + + RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name)); + } +} + +static const struct cmd_variable_handler nhg_name_handlers[] = { + {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete}, + {.completions = NULL}}; + void nexthop_group_init(void (*new)(const char *name), void (*add_nexthop)(const struct nexthop_group_cmd *nhg, const struct nexthop *nhop), @@ -723,6 +736,8 @@ void nexthop_group_init(void (*new)(const char *name), { RB_INIT(nhgc_entry_head, &nhgc_entries); + cmd_variable_handler_register(nhg_name_handlers); + install_node(&nexthop_group_node, nexthop_group_write); install_element(CONFIG_NODE, &nexthop_group_cmd); install_element(CONFIG_NODE, &no_nexthop_group_cmd); diff --git a/lib/northbound.c b/lib/northbound.c index 5e031ac2cea8..8a5cd0ef14db 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -40,6 +40,21 @@ struct nb_config *running_config; /* Hash table of user pointers associated with configuration entries. */ static struct hash *running_config_entries; +/* Management lock for the running configuration. */ +static struct { + /* Mutex protecting this structure. */ + pthread_mutex_t mtx; + + /* Actual lock. */ + bool locked; + + /* Northbound client who owns this lock. */ + enum nb_client owner_client; + + /* Northbound user who owns this lock. */ + const void *owner_user; +} running_config_mgmt_lock; + /* * Global lock used to prevent multiple configuration transactions from * happening concurrently. @@ -51,6 +66,7 @@ static int nb_callback_configuration(const enum nb_event event, static struct nb_transaction *nb_transaction_new(struct nb_config *config, struct nb_config_cbs *changes, enum nb_client client, + const void *user, const char *comment); static void nb_transaction_free(struct nb_transaction *transaction); static int nb_transaction_process(enum nb_event event, @@ -248,6 +264,7 @@ struct nb_config *nb_config_new(struct lyd_node *dnode) else config->dnode = yang_dnode_new(ly_native_ctx, true); config->version = 0; + pthread_rwlock_init(&config->lock, NULL); return config; } @@ -256,6 +273,7 @@ void nb_config_free(struct nb_config *config) { if (config->dnode) yang_dnode_free(config->dnode); + pthread_rwlock_destroy(&config->lock); XFREE(MTYPE_NB_CONFIG, config); } @@ -266,6 +284,7 @@ struct nb_config *nb_config_dup(const struct nb_config *config) dup = XCALLOC(MTYPE_NB_CONFIG, sizeof(*dup)); dup->dnode = yang_dnode_dup(config->dnode); dup->version = config->version; + pthread_rwlock_init(&dup->lock, NULL); return dup; } @@ -513,17 +532,28 @@ int nb_candidate_edit(struct nb_config *candidate, bool nb_candidate_needs_update(const struct nb_config *candidate) { - if (candidate->version < running_config->version) - return true; + bool ret = false; - return false; + pthread_rwlock_rdlock(&running_config->lock); + { + if (candidate->version < running_config->version) + ret = true; + } + pthread_rwlock_unlock(&running_config->lock); + + return ret; } int nb_candidate_update(struct nb_config *candidate) { struct nb_config *updated_config; - updated_config = nb_config_dup(running_config); + pthread_rwlock_rdlock(&running_config->lock); + { + updated_config = nb_config_dup(running_config); + } + pthread_rwlock_unlock(&running_config->lock); + if (nb_config_merge(updated_config, candidate, true) != NB_OK) return NB_ERR; @@ -575,15 +605,20 @@ int nb_candidate_validate(struct nb_config *candidate) return NB_ERR_VALIDATION; RB_INIT(nb_config_cbs, &changes); - nb_config_diff(running_config, candidate, &changes); - ret = nb_candidate_validate_changes(candidate, &changes); - nb_config_diff_del_changes(&changes); + pthread_rwlock_rdlock(&running_config->lock); + { + nb_config_diff(running_config, candidate, &changes); + ret = nb_candidate_validate_changes(candidate, &changes); + nb_config_diff_del_changes(&changes); + } + pthread_rwlock_unlock(&running_config->lock); return ret; } int nb_candidate_commit_prepare(struct nb_config *candidate, - enum nb_client client, const char *comment, + enum nb_client client, const void *user, + const char *comment, struct nb_transaction **transaction) { struct nb_config_cbs changes; @@ -596,25 +631,36 @@ int nb_candidate_commit_prepare(struct nb_config *candidate, } RB_INIT(nb_config_cbs, &changes); - nb_config_diff(running_config, candidate, &changes); - if (RB_EMPTY(nb_config_cbs, &changes)) - return NB_ERR_NO_CHANGES; + pthread_rwlock_rdlock(&running_config->lock); + { + nb_config_diff(running_config, candidate, &changes); + if (RB_EMPTY(nb_config_cbs, &changes)) { + pthread_rwlock_unlock(&running_config->lock); + return NB_ERR_NO_CHANGES; + } - if (nb_candidate_validate_changes(candidate, &changes) != NB_OK) { - flog_warn(EC_LIB_NB_CANDIDATE_INVALID, - "%s: failed to validate candidate configuration", - __func__); - nb_config_diff_del_changes(&changes); - return NB_ERR_VALIDATION; - } + if (nb_candidate_validate_changes(candidate, &changes) + != NB_OK) { + flog_warn( + EC_LIB_NB_CANDIDATE_INVALID, + "%s: failed to validate candidate configuration", + __func__); + nb_config_diff_del_changes(&changes); + pthread_rwlock_unlock(&running_config->lock); + return NB_ERR_VALIDATION; + } - *transaction = nb_transaction_new(candidate, &changes, client, comment); - if (*transaction == NULL) { - flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED, - "%s: failed to create transaction", __func__); - nb_config_diff_del_changes(&changes); - return NB_ERR_LOCKED; + *transaction = nb_transaction_new(candidate, &changes, client, + user, comment); + if (*transaction == NULL) { + flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED, + "%s: failed to create transaction", __func__); + nb_config_diff_del_changes(&changes); + pthread_rwlock_unlock(&running_config->lock); + return NB_ERR_LOCKED; + } } + pthread_rwlock_unlock(&running_config->lock); return nb_transaction_process(NB_EV_PREPARE, *transaction); } @@ -633,7 +679,11 @@ void nb_candidate_commit_apply(struct nb_transaction *transaction, /* Replace running by candidate. */ transaction->config->version++; - nb_config_replace(running_config, transaction->config, true); + pthread_rwlock_wrlock(&running_config->lock); + { + nb_config_replace(running_config, transaction->config, true); + } + pthread_rwlock_unlock(&running_config->lock); /* Record transaction. */ if (save_transaction @@ -645,13 +695,13 @@ void nb_candidate_commit_apply(struct nb_transaction *transaction, } int nb_candidate_commit(struct nb_config *candidate, enum nb_client client, - bool save_transaction, const char *comment, - uint32_t *transaction_id) + const void *user, bool save_transaction, + const char *comment, uint32_t *transaction_id) { struct nb_transaction *transaction = NULL; int ret; - ret = nb_candidate_commit_prepare(candidate, client, comment, + ret = nb_candidate_commit_prepare(candidate, client, user, comment, &transaction); /* * Apply the changes if the preparation phase succeeded. Otherwise abort @@ -666,6 +716,60 @@ int nb_candidate_commit(struct nb_config *candidate, enum nb_client client, return ret; } +int nb_running_lock(enum nb_client client, const void *user) +{ + int ret = -1; + + pthread_mutex_lock(&running_config_mgmt_lock.mtx); + { + if (!running_config_mgmt_lock.locked) { + running_config_mgmt_lock.locked = true; + running_config_mgmt_lock.owner_client = client; + running_config_mgmt_lock.owner_user = user; + ret = 0; + } + } + pthread_mutex_unlock(&running_config_mgmt_lock.mtx); + + return ret; +} + +int nb_running_unlock(enum nb_client client, const void *user) +{ + int ret = -1; + + pthread_mutex_lock(&running_config_mgmt_lock.mtx); + { + if (running_config_mgmt_lock.locked + && running_config_mgmt_lock.owner_client == client + && running_config_mgmt_lock.owner_user == user) { + running_config_mgmt_lock.locked = false; + running_config_mgmt_lock.owner_client = NB_CLIENT_NONE; + running_config_mgmt_lock.owner_user = NULL; + ret = 0; + } + } + pthread_mutex_unlock(&running_config_mgmt_lock.mtx); + + return ret; +} + +int nb_running_lock_check(enum nb_client client, const void *user) +{ + int ret = -1; + + pthread_mutex_lock(&running_config_mgmt_lock.mtx); + { + if (!running_config_mgmt_lock.locked + || (running_config_mgmt_lock.owner_client == client + && running_config_mgmt_lock.owner_user == user)) + ret = 0; + } + pthread_mutex_unlock(&running_config_mgmt_lock.mtx); + + return ret; +} + static void nb_log_callback(const enum nb_event event, enum nb_operation operation, const char *xpath, const char *value) @@ -673,7 +777,7 @@ static void nb_log_callback(const enum nb_event event, zlog_debug( "northbound callback: event [%s] op [%s] xpath [%s] value [%s]", nb_event_name(event), nb_operation_name(operation), xpath, - value); + value ? value : "(NULL)"); } /* @@ -812,13 +916,20 @@ int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, return nb_node->cbs.rpc(xpath, input, output); } -static struct nb_transaction *nb_transaction_new(struct nb_config *config, - struct nb_config_cbs *changes, - enum nb_client client, - const char *comment) +static struct nb_transaction * +nb_transaction_new(struct nb_config *config, struct nb_config_cbs *changes, + enum nb_client client, const void *user, const char *comment) { struct nb_transaction *transaction; + if (nb_running_lock_check(client, user)) { + flog_warn( + EC_LIB_NB_TRANSACTION_CREATION_FAILED, + "%s: running configuration is locked by another client", + __func__); + return NULL; + } + if (transaction_in_progress) { flog_warn( EC_LIB_NB_TRANSACTION_CREATION_FAILED, @@ -852,40 +963,52 @@ static int nb_transaction_process(enum nb_event event, { struct nb_config_cb *cb; - RB_FOREACH (cb, nb_config_cbs, &transaction->changes) { - struct nb_config_change *change = (struct nb_config_change *)cb; - int ret; - - /* - * Only try to release resources that were allocated - * successfully. - */ - if (event == NB_EV_ABORT && change->prepare_ok == false) - break; + /* + * Need to lock the running configuration since transaction->changes + * can contain pointers to data nodes from the running configuration. + */ + pthread_rwlock_rdlock(&running_config->lock); + { + RB_FOREACH (cb, nb_config_cbs, &transaction->changes) { + struct nb_config_change *change = + (struct nb_config_change *)cb; + int ret; - /* Call the appropriate callback. */ - ret = nb_callback_configuration(event, change); - switch (event) { - case NB_EV_PREPARE: - if (ret != NB_OK) - return ret; - change->prepare_ok = true; - break; - case NB_EV_ABORT: - case NB_EV_APPLY: /* - * At this point it's not possible to reject the - * transaction anymore, so any failure here can lead to - * inconsistencies and should be treated as a bug. - * Operations prone to errors, like validations and - * resource allocations, should be performed during the - * 'prepare' phase. + * Only try to release resources that were allocated + * successfully. */ - break; - default: - break; + if (event == NB_EV_ABORT && change->prepare_ok == false) + break; + + /* Call the appropriate callback. */ + ret = nb_callback_configuration(event, change); + switch (event) { + case NB_EV_PREPARE: + if (ret != NB_OK) { + pthread_rwlock_unlock( + &running_config->lock); + return ret; + } + change->prepare_ok = true; + break; + case NB_EV_ABORT: + case NB_EV_APPLY: + /* + * At this point it's not possible to reject the + * transaction anymore, so any failure here can + * lead to inconsistencies and should be treated + * as a bug. Operations prone to errors, like + * validations and resource allocations, should + * be performed during the 'prepare' phase. + */ + break; + default: + break; + } } } + pthread_rwlock_unlock(&running_config->lock); return NB_OK; } @@ -1531,7 +1654,7 @@ static bool running_config_entry_cmp(const void *value1, const void *value2) return strmatch(c1->xpath, c2->xpath); } -static unsigned int running_config_entry_key_make(void *value) +static unsigned int running_config_entry_key_make(const void *value) { return string_hash_make(value); } @@ -1704,6 +1827,8 @@ const char *nb_client_name(enum nb_client client) return "ConfD"; case NB_CLIENT_SYSREPO: return "Sysrepo"; + case NB_CLIENT_GRPC: + return "gRPC"; default: return "unknown"; } @@ -1761,6 +1886,7 @@ void nb_init(struct thread_master *tm, running_config_entries = hash_create(running_config_entry_key_make, running_config_entry_cmp, "Running Configuration Entries"); + pthread_mutex_init(&running_config_mgmt_lock.mtx, NULL); /* Initialize the northbound CLI. */ nb_cli_init(tm); @@ -1778,4 +1904,5 @@ void nb_terminate(void) hash_clean(running_config_entries, running_config_entry_free); hash_free(running_config_entries); nb_config_free(running_config); + pthread_mutex_destroy(&running_config_mgmt_lock.mtx); } diff --git a/lib/northbound.h b/lib/northbound.h index 14f27c1d4103..8f6753506b9d 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -414,15 +414,28 @@ enum nb_error { /* Northbound clients. */ enum nb_client { - NB_CLIENT_CLI = 0, + NB_CLIENT_NONE = 0, + NB_CLIENT_CLI, NB_CLIENT_CONFD, NB_CLIENT_SYSREPO, + NB_CLIENT_GRPC, }; /* Northbound configuration. */ struct nb_config { + /* Configuration data. */ struct lyd_node *dnode; + + /* Configuration version. */ uint32_t version; + + /* + * Lock protecting this structure. The use of this lock is always + * necessary when reading or modifying the global running configuration. + * For candidate configurations, use of this lock is optional depending + * on the threading scheme of the northbound plugin. + */ + pthread_rwlock_t lock; }; /* Northbound configuration callback. */ @@ -662,6 +675,9 @@ extern int nb_candidate_validate(struct nb_config *candidate); * client * Northbound client performing the commit. * + * user + * Northbound user performing the commit (can be NULL). + * * comment * Optional comment describing the commit. * @@ -682,7 +698,7 @@ extern int nb_candidate_validate(struct nb_config *candidate); * - NB_ERR for other errors. */ extern int nb_candidate_commit_prepare(struct nb_config *candidate, - enum nb_client client, + enum nb_client client, const void *user, const char *comment, struct nb_transaction **transaction); @@ -727,6 +743,9 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction, * client * Northbound client performing the commit. * + * user + * Northbound user performing the commit (can be NULL). + * * save_transaction * Specify whether the transaction should be recorded in the transactions log * or not. @@ -748,11 +767,57 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction, * - NB_ERR for other errors. */ extern int nb_candidate_commit(struct nb_config *candidate, - enum nb_client client, bool save_transaction, - const char *comment, uint32_t *transaction_id); + enum nb_client client, const void *user, + bool save_transaction, const char *comment, + uint32_t *transaction_id); + +/* + * Lock the running configuration. + * + * client + * Northbound client. + * + * user + * Northbound user (can be NULL). + * + * Returns: + * 0 on success, -1 when the running configuration is already locked. + */ +extern int nb_running_lock(enum nb_client client, const void *user); + +/* + * Unlock the running configuration. + * + * client + * Northbound client. + * + * user + * Northbound user (can be NULL). + * + * Returns: + * 0 on success, -1 when the running configuration is already unlocked or + * locked by another client/user. + */ +extern int nb_running_unlock(enum nb_client client, const void *user); + +/* + * Check if the running configuration is locked or not for the given + * client/user. + * + * client + * Northbound client. + * + * user + * Northbound user (can be NULL). + * + * Returns: + * 0 if the running configuration is unlocked or if the client/user owns the + * lock, -1 otherwise. + */ +extern int nb_running_lock_check(enum nb_client client, const void *user); /* - * Iterate over operetional data. + * Iterate over operational data. * * xpath * Data path of the YANG data we want to iterate over. diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 48faa7595aa0..7b7b526af02a 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -185,7 +185,7 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) /* Do an implicit "commit" when using the classic CLI mode. */ if (frr_get_cli_mode() == FRR_CLI_CLASSIC) { ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, - false, NULL, NULL); + vty, false, NULL, NULL); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { vty_out(vty, "%% Configuration failed: %s.\n\n", nb_err_name(ret)); @@ -193,8 +193,13 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) "Please check the logs for more details.\n"); /* Regenerate candidate for consistency. */ - nb_config_replace(vty->candidate_config, running_config, - true); + pthread_rwlock_rdlock(&running_config->lock); + { + nb_config_replace(vty->candidate_config, + running_config, true); + } + pthread_rwlock_unlock(&running_config->lock); + return CMD_WARNING_CONFIG_FAILED; } } @@ -237,7 +242,7 @@ int nb_cli_confirmed_commit_rollback(struct vty *vty) /* Perform the rollback. */ ret = nb_candidate_commit( - vty->confirmed_commit_rollback, NB_CLIENT_CLI, true, + vty->confirmed_commit_rollback, NB_CLIENT_CLI, vty, true, "Rollback to previous configuration - confirmed commit has timed out", &transaction_id); if (ret == NB_OK) @@ -291,11 +296,6 @@ static int nb_cli_commit(struct vty *vty, bool force, return CMD_SUCCESS; } - if (vty_exclusive_lock != NULL && vty_exclusive_lock != vty) { - vty_out(vty, "%% Configuration is locked by another VTY.\n\n"); - return CMD_WARNING; - } - /* "force" parameter. */ if (!force && nb_candidate_needs_update(vty->candidate_config)) { vty_out(vty, @@ -307,7 +307,12 @@ static int nb_cli_commit(struct vty *vty, bool force, /* "confirm" parameter. */ if (confirmed_timeout) { - vty->confirmed_commit_rollback = nb_config_dup(running_config); + pthread_rwlock_rdlock(&running_config->lock); + { + vty->confirmed_commit_rollback = + nb_config_dup(running_config); + } + pthread_rwlock_unlock(&running_config->lock); vty->t_confirmed_commit_timeout = NULL; thread_add_timer(master, nb_cli_confirmed_commit_timeout, vty, @@ -315,14 +320,19 @@ static int nb_cli_commit(struct vty *vty, bool force, &vty->t_confirmed_commit_timeout); } - ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, true, - comment, &transaction_id); + ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, vty, + true, comment, &transaction_id); /* Map northbound return code to CLI return code. */ switch (ret) { case NB_OK: - nb_config_replace(vty->candidate_config_base, running_config, - true); + pthread_rwlock_rdlock(&running_config->lock); + { + nb_config_replace(vty->candidate_config_base, + running_config, true); + } + pthread_rwlock_unlock(&running_config->lock); + vty_out(vty, "%% Configuration committed successfully (Transaction ID #%u).\n\n", transaction_id); @@ -687,7 +697,12 @@ DEFPY (config_update, return CMD_WARNING; } - nb_config_replace(vty->candidate_config_base, running_config, true); + pthread_rwlock_rdlock(&running_config->lock); + { + nb_config_replace(vty->candidate_config_base, running_config, + true); + } + pthread_rwlock_unlock(&running_config->lock); vty_out(vty, "%% Candidate configuration updated successfully.\n\n"); @@ -787,8 +802,12 @@ DEFPY (show_config_running, } } - nb_cli_show_config(vty, running_config, format, translator, - !!with_defaults); + pthread_rwlock_rdlock(&running_config->lock); + { + nb_cli_show_config(vty, running_config, format, translator, + !!with_defaults); + } + pthread_rwlock_unlock(&running_config->lock); return CMD_SUCCESS; } @@ -902,57 +921,68 @@ DEFPY (show_config_compare, struct nb_config *config2, *config_transaction2 = NULL; int ret = CMD_WARNING; - if (c1_candidate) - config1 = vty->candidate_config; - else if (c1_running) - config1 = running_config; - else { - config_transaction1 = nb_db_transaction_load(c1_tid); - if (!config_transaction1) { - vty_out(vty, "%% Transaction %u does not exist\n\n", - (unsigned int)c1_tid); - goto exit; + /* + * For simplicity, lock the running configuration regardless if it's + * going to be used or not. + */ + pthread_rwlock_rdlock(&running_config->lock); + { + if (c1_candidate) + config1 = vty->candidate_config; + else if (c1_running) + config1 = running_config; + else { + config_transaction1 = nb_db_transaction_load(c1_tid); + if (!config_transaction1) { + vty_out(vty, + "%% Transaction %u does not exist\n\n", + (unsigned int)c1_tid); + goto exit; + } + config1 = config_transaction1; } - config1 = config_transaction1; - } - if (c2_candidate) - config2 = vty->candidate_config; - else if (c2_running) - config2 = running_config; - else { - config_transaction2 = nb_db_transaction_load(c2_tid); - if (!config_transaction2) { - vty_out(vty, "%% Transaction %u does not exist\n\n", - (unsigned int)c2_tid); - goto exit; + if (c2_candidate) + config2 = vty->candidate_config; + else if (c2_running) + config2 = running_config; + else { + config_transaction2 = nb_db_transaction_load(c2_tid); + if (!config_transaction2) { + vty_out(vty, + "%% Transaction %u does not exist\n\n", + (unsigned int)c2_tid); + goto exit; + } + config2 = config_transaction2; } - config2 = config_transaction2; - } - if (json) - format = NB_CFG_FMT_JSON; - else if (xml) - format = NB_CFG_FMT_XML; - else - format = NB_CFG_FMT_CMDS; + if (json) + format = NB_CFG_FMT_JSON; + else if (xml) + format = NB_CFG_FMT_XML; + else + format = NB_CFG_FMT_CMDS; - if (translator_family) { - translator = yang_translator_find(translator_family); - if (!translator) { - vty_out(vty, "%% Module translator \"%s\" not found\n", - translator_family); - goto exit; + if (translator_family) { + translator = yang_translator_find(translator_family); + if (!translator) { + vty_out(vty, + "%% Module translator \"%s\" not found\n", + translator_family); + goto exit; + } } - } - ret = nb_cli_show_config_compare(vty, config1, config2, format, - translator); -exit: - if (config_transaction1) - nb_config_free(config_transaction1); - if (config_transaction2) - nb_config_free(config_transaction2); + ret = nb_cli_show_config_compare(vty, config1, config2, format, + translator); + exit: + if (config_transaction1) + nb_config_free(config_transaction1); + if (config_transaction2) + nb_config_free(config_transaction2); + } + pthread_rwlock_unlock(&running_config->lock); return ret; } @@ -1509,7 +1539,7 @@ static int nb_cli_rollback_configuration(struct vty *vty, snprintf(comment, sizeof(comment), "Rollback to transaction %u", transaction_id); - ret = nb_candidate_commit(candidate, NB_CLIENT_CLI, true, comment, + ret = nb_candidate_commit(candidate, NB_CLIENT_CLI, vty, true, comment, NULL); nb_config_free(candidate); switch (ret) { @@ -1692,8 +1722,8 @@ void nb_cli_init(struct thread_master *tm) /* Initialize the shared candidate configuration. */ vty_shared_candidate_config = nb_config_new(NULL); - /* Install debug commands */ debug_init(&nb_dbg_cbs); + install_node(&nb_debug_node, nb_debug_config_write); install_element(ENABLE_NODE, &debug_nb_cmd); install_element(CONFIG_NODE, &debug_nb_cmd); diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c index 0df286511ece..e9669fc7e16c 100644 --- a/lib/northbound_confd.c +++ b/lib/northbound_confd.c @@ -289,7 +289,11 @@ static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) struct cdb_iter_args iter_args; int ret; - candidate = nb_config_dup(running_config); + pthread_rwlock_rdlock(&running_config->lock); + { + candidate = nb_config_dup(running_config); + } + pthread_rwlock_unlock(&running_config->lock); /* Iterate over all configuration changes. */ iter_args.candidate = candidate; @@ -322,7 +326,7 @@ static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) */ transaction = NULL; ret = nb_candidate_commit_prepare(candidate, NB_CLIENT_CONFD, NULL, - &transaction); + NULL, &transaction); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { enum confd_errcode errcode; const char *errmsg; diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp new file mode 100644 index 000000000000..a55da23dd12d --- /dev/null +++ b/lib/northbound_grpc.cpp @@ -0,0 +1,936 @@ +// +// Copyright (C) 2019 NetDEF, Inc. +// Renato Westphal +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; see the file COPYING; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// + +#include + +#include "log.h" +#include "libfrr.h" +#include "version.h" +#include "command.h" +#include "lib_errors.h" +#include "northbound.h" +#include "northbound_db.h" + +#include +#include +#include +#include + +#include +#include "grpc/frr-northbound.grpc.pb.h" + +#define GRPC_DEFAULT_PORT 50051 + +/* + * NOTE: we can't use the FRR debugging infrastructure here since it uses + * atomics and C++ has a different atomics API. Enable gRPC debugging + * unconditionally until we figure out a way to solve this problem. + */ +static bool nb_dbg_client_grpc = 1; + +static pthread_t grpc_pthread; + +class NorthboundImpl final : public frr::Northbound::Service +{ + public: + NorthboundImpl(void) + { + _nextCandidateId = 0; + } + + ~NorthboundImpl(void) + { + // Delete candidates. + for (auto it = _candidates.begin(); it != _candidates.end(); + it++) + delete_candidate(&it->second); + } + + grpc::Status + GetCapabilities(grpc::ServerContext *context, + frr::GetCapabilitiesRequest const *request, + frr::GetCapabilitiesResponse *response) override + { + if (nb_dbg_client_grpc) + zlog_debug("received RPC GetCapabilities()"); + + // Response: string frr_version = 1; + response->set_frr_version(FRR_VERSION); + + // Response: bool rollback_support = 2; +#ifdef HAVE_CONFIG_ROLLBACKS + response->set_rollback_support(true); +#else + response->set_rollback_support(false); +#endif + + // Response: repeated ModuleData supported_modules = 3; + struct yang_module *module; + RB_FOREACH (module, yang_modules, &yang_modules) { + auto m = response->add_supported_modules(); + + m->set_name(module->name); + if (module->info->rev_size) + m->set_revision(module->info->rev[0].date); + m->set_organization(module->info->org); + } + + // Response: repeated Encoding supported_encodings = 4; + response->add_supported_encodings(frr::JSON); + response->add_supported_encodings(frr::XML); + + return grpc::Status::OK; + } + + grpc::Status Get(grpc::ServerContext *context, + frr::GetRequest const *request, + grpc::ServerWriter *writer) override + { + // Request: DataType type = 1; + int type = request->type(); + // Request: Encoding encoding = 2; + frr::Encoding encoding = request->encoding(); + // Request: bool with_defaults = 3; + bool with_defaults = request->with_defaults(); + + if (nb_dbg_client_grpc) + zlog_debug( + "received RPC Get(type: %u, encoding: %u, with_defaults: %u)", + type, encoding, with_defaults); + + // Request: repeated string path = 4; + auto paths = request->path(); + for (const std::string &path : paths) { + frr::GetResponse response; + grpc::Status status; + + // Response: int64 timestamp = 1; + response.set_timestamp(time(NULL)); + + // Response: DataTree data = 2; + auto *data = response.mutable_data(); + data->set_encoding(request->encoding()); + status = get_path(data, path, type, + encoding2lyd_format(encoding), + with_defaults); + + // Something went wrong... + if (!status.ok()) + return status; + + writer->Write(response); + } + + if (nb_dbg_client_grpc) + zlog_debug("received RPC Get() end"); + + return grpc::Status::OK; + } + + grpc::Status + CreateCandidate(grpc::ServerContext *context, + frr::CreateCandidateRequest const *request, + frr::CreateCandidateResponse *response) override + { + if (nb_dbg_client_grpc) + zlog_debug("received RPC CreateCandidate()"); + + struct candidate *candidate = create_candidate(); + if (!candidate) + return grpc::Status( + grpc::StatusCode::RESOURCE_EXHAUSTED, + "Can't create candidate configuration"); + + // Response: uint32 candidate_id = 1; + response->set_candidate_id(candidate->id); + + return grpc::Status::OK; + } + + grpc::Status + DeleteCandidate(grpc::ServerContext *context, + frr::DeleteCandidateRequest const *request, + frr::DeleteCandidateResponse *response) override + { + // Request: uint32 candidate_id = 1; + uint32_t candidate_id = request->candidate_id(); + + if (nb_dbg_client_grpc) + zlog_debug( + "received RPC DeleteCandidate(candidate_id: %u)", + candidate_id); + + struct candidate *candidate = get_candidate(candidate_id); + if (!candidate) + return grpc::Status( + grpc::StatusCode::NOT_FOUND, + "candidate configuration not found"); + + delete_candidate(candidate); + + return grpc::Status::OK; + } + + grpc::Status + UpdateCandidate(grpc::ServerContext *context, + frr::UpdateCandidateRequest const *request, + frr::UpdateCandidateResponse *response) override + { + // Request: uint32 candidate_id = 1; + uint32_t candidate_id = request->candidate_id(); + + if (nb_dbg_client_grpc) + zlog_debug( + "received RPC UpdateCandidate(candidate_id: %u)", + candidate_id); + + struct candidate *candidate = get_candidate(candidate_id); + if (!candidate) + return grpc::Status( + grpc::StatusCode::NOT_FOUND, + "candidate configuration not found"); + + if (candidate->transaction) + return grpc::Status( + grpc::StatusCode::FAILED_PRECONDITION, + "candidate is in the middle of a transaction"); + + if (nb_candidate_update(candidate->config) != NB_OK) + return grpc::Status( + grpc::StatusCode::INTERNAL, + "failed to update candidate configuration"); + + return grpc::Status::OK; + } + + grpc::Status + EditCandidate(grpc::ServerContext *context, + frr::EditCandidateRequest const *request, + frr::EditCandidateResponse *response) override + { + // Request: uint32 candidate_id = 1; + uint32_t candidate_id = request->candidate_id(); + + if (nb_dbg_client_grpc) + zlog_debug( + "received RPC EditCandidate(candidate_id: %u)", + candidate_id); + + struct candidate *candidate = get_candidate(candidate_id); + if (!candidate) + return grpc::Status( + grpc::StatusCode::NOT_FOUND, + "candidate configuration not found"); + + // Create a copy of the candidate. For consistency, we need to + // ensure that either all changes are accepted or none are (in + // the event of an error). + struct nb_config *candidate_tmp = + nb_config_dup(candidate->config); + + auto pvs = request->update(); + for (const frr::PathValue &pv : pvs) { + if (yang_dnode_edit(candidate_tmp->dnode, pv.path(), + pv.value()) + != 0) { + nb_config_free(candidate_tmp); + return grpc::Status( + grpc::StatusCode::INVALID_ARGUMENT, + "Failed to update \"" + pv.path() + + "\""); + } + } + + pvs = request->delete_(); + for (const frr::PathValue &pv : pvs) { + if (yang_dnode_delete(candidate_tmp->dnode, pv.path()) + != 0) { + nb_config_free(candidate_tmp); + return grpc::Status( + grpc::StatusCode::INVALID_ARGUMENT, + "Failed to remove \"" + pv.path() + + "\""); + } + } + + // No errors, accept all changes. + nb_config_replace(candidate->config, candidate_tmp, false); + + return grpc::Status::OK; + } + + grpc::Status + LoadToCandidate(grpc::ServerContext *context, + frr::LoadToCandidateRequest const *request, + frr::LoadToCandidateResponse *response) override + { + // Request: uint32 candidate_id = 1; + uint32_t candidate_id = request->candidate_id(); + // Request: LoadType type = 2; + int load_type = request->type(); + // Request: DataTree config = 3; + auto config = request->config(); + + if (nb_dbg_client_grpc) + zlog_debug( + "received RPC LoadToCandidate(candidate_id: %u)", + candidate_id); + + struct candidate *candidate = get_candidate(candidate_id); + if (!candidate) + return grpc::Status( + grpc::StatusCode::NOT_FOUND, + "candidate configuration not found"); + + struct lyd_node *dnode = dnode_from_data_tree(&config, true); + if (!dnode) + return grpc::Status( + grpc::StatusCode::INTERNAL, + "Failed to parse the configuration"); + + struct nb_config *loaded_config = nb_config_new(dnode); + + if (load_type == frr::LoadToCandidateRequest::REPLACE) + nb_config_replace(candidate->config, loaded_config, + false); + else if (nb_config_merge(candidate->config, loaded_config, + false) + != NB_OK) + return grpc::Status( + grpc::StatusCode::INTERNAL, + "Failed to merge the loaded configuration"); + + return grpc::Status::OK; + } + + grpc::Status Commit(grpc::ServerContext *context, + frr::CommitRequest const *request, + frr::CommitResponse *response) override + { + // Request: uint32 candidate_id = 1; + uint32_t candidate_id = request->candidate_id(); + // Request: Phase phase = 2; + int phase = request->phase(); + // Request: string comment = 3; + const std::string comment = request->comment(); + + if (nb_dbg_client_grpc) + zlog_debug("received RPC Commit(candidate_id: %u)", + candidate_id); + + // Find candidate configuration. + struct candidate *candidate = get_candidate(candidate_id); + if (!candidate) + return grpc::Status( + grpc::StatusCode::NOT_FOUND, + "candidate configuration not found"); + + int ret = NB_OK; + uint32_t transaction_id = 0; + + // Check for misuse of the two-phase commit protocol. + switch (phase) { + case frr::CommitRequest::PREPARE: + case frr::CommitRequest::ALL: + if (candidate->transaction) + return grpc::Status( + grpc::StatusCode::FAILED_PRECONDITION, + "pending transaction in progress"); + break; + case frr::CommitRequest::ABORT: + case frr::CommitRequest::APPLY: + if (!candidate->transaction) + return grpc::Status( + grpc::StatusCode::FAILED_PRECONDITION, + "no transaction in progress"); + break; + default: + break; + } + + // Execute the user request. + switch (phase) { + case frr::CommitRequest::VALIDATE: + ret = nb_candidate_validate(candidate->config); + break; + case frr::CommitRequest::PREPARE: + ret = nb_candidate_commit_prepare( + candidate->config, NB_CLIENT_GRPC, NULL, + comment.c_str(), &candidate->transaction); + break; + case frr::CommitRequest::ABORT: + nb_candidate_commit_abort(candidate->transaction); + break; + case frr::CommitRequest::APPLY: + nb_candidate_commit_apply(candidate->transaction, true, + &transaction_id); + break; + case frr::CommitRequest::ALL: + ret = nb_candidate_commit( + candidate->config, NB_CLIENT_GRPC, NULL, true, + comment.c_str(), &transaction_id); + break; + } + + // Map northbound error codes to gRPC error codes. + switch (ret) { + case NB_ERR_NO_CHANGES: + return grpc::Status( + grpc::StatusCode::ABORTED, + "No configuration changes detected"); + case NB_ERR_LOCKED: + return grpc::Status( + grpc::StatusCode::UNAVAILABLE, + "There's already a transaction in progress"); + case NB_ERR_VALIDATION: + return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, + "Validation error"); + case NB_ERR_RESOURCE: + return grpc::Status( + grpc::StatusCode::RESOURCE_EXHAUSTED, + "Failed do allocate resources"); + case NB_ERR: + return grpc::Status(grpc::StatusCode::INTERNAL, + "Internal error"); + default: + break; + } + + // Response: uint32 transaction_id = 1; + if (transaction_id) + response->set_transaction_id(transaction_id); + + return grpc::Status::OK; + } + + grpc::Status + ListTransactions(grpc::ServerContext *context, + frr::ListTransactionsRequest const *request, + grpc::ServerWriter + *writer) override + { + if (nb_dbg_client_grpc) + zlog_debug("received RPC ListTransactions()"); + + nb_db_transactions_iterate(list_transactions_cb, writer); + + return grpc::Status::OK; + } + + grpc::Status + GetTransaction(grpc::ServerContext *context, + frr::GetTransactionRequest const *request, + frr::GetTransactionResponse *response) override + { + struct nb_config *nb_config; + + // Request: uint32 transaction_id = 1; + uint32_t transaction_id = request->transaction_id(); + // Request: Encoding encoding = 2; + frr::Encoding encoding = request->encoding(); + // Request: bool with_defaults = 3; + bool with_defaults = request->with_defaults(); + + if (nb_dbg_client_grpc) + zlog_debug( + "received RPC GetTransaction(transaction_id: %u, encoding: %u)", + transaction_id, encoding); + + // Load configuration from the transactions database. + nb_config = nb_db_transaction_load(transaction_id); + if (!nb_config) + return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, + "Transaction not found"); + + // Response: DataTree config = 1; + auto config = response->mutable_config(); + config->set_encoding(encoding); + + // Dump data using the requested format. + if (data_tree_from_dnode(config, nb_config->dnode, + encoding2lyd_format(encoding), + with_defaults) + != 0) { + nb_config_free(nb_config); + return grpc::Status(grpc::StatusCode::INTERNAL, + "Failed to dump data"); + } + + nb_config_free(nb_config); + + return grpc::Status::OK; + } + + grpc::Status LockConfig(grpc::ServerContext *context, + frr::LockConfigRequest const *request, + frr::LockConfigResponse *response) override + { + if (nb_dbg_client_grpc) + zlog_debug("received RPC LockConfig()"); + + if (nb_running_lock(NB_CLIENT_GRPC, NULL)) + return grpc::Status( + grpc::StatusCode::FAILED_PRECONDITION, + "running configuration is locked already"); + + return grpc::Status::OK; + } + + grpc::Status UnlockConfig(grpc::ServerContext *context, + frr::UnlockConfigRequest const *request, + frr::UnlockConfigResponse *response) override + { + if (nb_dbg_client_grpc) + zlog_debug("received RPC UnlockConfig()"); + + if (nb_running_unlock(NB_CLIENT_GRPC, NULL)) + return grpc::Status( + grpc::StatusCode::FAILED_PRECONDITION, + "failed to unlock the running configuration"); + + return grpc::Status::OK; + } + + grpc::Status Execute(grpc::ServerContext *context, + frr::ExecuteRequest const *request, + frr::ExecuteResponse *response) override + { + struct nb_node *nb_node; + struct list *input_list; + struct list *output_list; + struct listnode *node; + struct yang_data *data; + const char *xpath; + + // Request: string path = 1; + xpath = request->path().c_str(); + + if (nb_dbg_client_grpc) + zlog_debug("received RPC Execute(path: \"%s\")", xpath); + + if (request->path().empty()) + return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, + "Data path is empty"); + + nb_node = nb_node_find(xpath); + if (!nb_node) + return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, + "Unknown data path"); + + input_list = yang_data_list_new(); + output_list = yang_data_list_new(); + + // Read input parameters. + auto input = request->input(); + for (const frr::PathValue &pv : input) { + // Request: repeated PathValue input = 2; + data = yang_data_new(pv.path().c_str(), + pv.value().c_str()); + listnode_add(input_list, data); + } + + // Execute callback registered for this XPath. + if (nb_node->cbs.rpc(xpath, input_list, output_list) != NB_OK) { + flog_warn(EC_LIB_NB_CB_RPC, + "%s: rpc callback failed: %s", __func__, + xpath); + list_delete(&input_list); + list_delete(&output_list); + return grpc::Status(grpc::StatusCode::INTERNAL, + "RPC failed"); + } + + // Process output parameters. + for (ALL_LIST_ELEMENTS_RO(output_list, node, data)) { + // Response: repeated PathValue output = 1; + frr::PathValue *pv = response->add_output(); + pv->set_path(data->xpath); + pv->set_value(data->value); + } + + // Release memory. + list_delete(&input_list); + list_delete(&output_list); + + return grpc::Status::OK; + } + + private: + struct candidate { + uint32_t id; + struct nb_config *config; + struct nb_transaction *transaction; + }; + std::map _candidates; + uint32_t _nextCandidateId; + + static int yang_dnode_edit(struct lyd_node *dnode, + const std::string &path, + const std::string &value) + { + ly_errno = LY_SUCCESS; + dnode = lyd_new_path(dnode, ly_native_ctx, path.c_str(), + (void *)value.c_str(), + (LYD_ANYDATA_VALUETYPE)0, + LYD_PATH_OPT_UPDATE); + if (!dnode && ly_errno != LY_SUCCESS) { + flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", + __func__); + return -1; + } + + return 0; + } + + static int yang_dnode_delete(struct lyd_node *dnode, + const std::string &path) + { + dnode = yang_dnode_get(dnode, path.c_str()); + if (!dnode) + return -1; + + lyd_free(dnode); + + return 0; + } + + static LYD_FORMAT encoding2lyd_format(enum frr::Encoding encoding) + { + switch (encoding) { + case frr::JSON: + return LYD_JSON; + case frr::XML: + return LYD_XML; + } + } + + static int get_oper_data_cb(const struct lys_node *snode, + struct yang_translator *translator, + struct yang_data *data, void *arg) + { + struct lyd_node *dnode = static_cast(arg); + int ret = yang_dnode_edit(dnode, data->xpath, data->value); + yang_data_free(data); + + return (ret == 0) ? NB_OK : NB_ERR; + } + + static void list_transactions_cb(void *arg, int transaction_id, + const char *client_name, + const char *date, const char *comment) + { + grpc::ServerWriter *writer = + static_cast *>(arg); + frr::ListTransactionsResponse response; + + // Response: uint32 id = 1; + response.set_id(transaction_id); + + // Response: string client = 2; + response.set_client(client_name); + + // Response: string date = 3; + response.set_date(date); + + // Response: string comment = 4; + response.set_comment(comment); + + writer->Write(response); + } + + static int data_tree_from_dnode(frr::DataTree *dt, + const struct lyd_node *dnode, + LYD_FORMAT lyd_format, + bool with_defaults) + { + char *strp; + int options = 0; + + SET_FLAG(options, LYP_FORMAT | LYP_WITHSIBLINGS); + if (with_defaults) + SET_FLAG(options, LYP_WD_ALL); + else + SET_FLAG(options, LYP_WD_TRIM); + + if (lyd_print_mem(&strp, dnode, lyd_format, options) == 0) { + if (strp) { + dt->set_data(strp); + free(strp); + } + return 0; + } + + return -1; + } + + static struct lyd_node *dnode_from_data_tree(const frr::DataTree *dt, + bool config_only) + { + struct lyd_node *dnode; + int options; + + if (config_only) + options = LYD_OPT_CONFIG; + else + options = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB; + + dnode = lyd_parse_mem(ly_native_ctx, dt->data().c_str(), + encoding2lyd_format(dt->encoding()), + options); + + return dnode; + } + + static struct lyd_node *get_dnode_config(const std::string &path) + { + struct lyd_node *dnode; + + pthread_rwlock_rdlock(&running_config->lock); + { + dnode = yang_dnode_get(running_config->dnode, + path.empty() ? NULL + : path.c_str()); + if (dnode) + dnode = yang_dnode_dup(dnode); + } + pthread_rwlock_unlock(&running_config->lock); + + return dnode; + } + + static struct lyd_node *get_dnode_state(const std::string &path) + { + struct lyd_node *dnode; + + dnode = yang_dnode_new(ly_native_ctx, false); + if (nb_oper_data_iterate(path.c_str(), NULL, 0, + get_oper_data_cb, dnode) + != NB_OK) { + yang_dnode_free(dnode); + return NULL; + } + + return dnode; + } + + static grpc::Status get_path(frr::DataTree *dt, const std::string &path, + int type, LYD_FORMAT lyd_format, + bool with_defaults) + { + struct lyd_node *dnode_config = NULL; + struct lyd_node *dnode_state = NULL; + struct lyd_node *dnode_final; + + // Configuration data. + if (type == frr::GetRequest_DataType_ALL + || type == frr::GetRequest_DataType_CONFIG) { + dnode_config = get_dnode_config(path); + if (!dnode_config) + return grpc::Status( + grpc::StatusCode::INVALID_ARGUMENT, + "Data path not found"); + } + + // Operational data. + if (type == frr::GetRequest_DataType_ALL + || type == frr::GetRequest_DataType_STATE) { + dnode_state = get_dnode_state(path); + if (!dnode_state) { + if (dnode_config) + yang_dnode_free(dnode_config); + return grpc::Status( + grpc::StatusCode::INVALID_ARGUMENT, + "Failed to fetch operational data"); + } + } + + switch (type) { + case frr::GetRequest_DataType_ALL: + // + // Combine configuration and state data into a single + // dnode. + // + if (lyd_merge(dnode_state, dnode_config, + LYD_OPT_EXPLICIT) + != 0) { + yang_dnode_free(dnode_state); + yang_dnode_free(dnode_config); + return grpc::Status( + grpc::StatusCode::INTERNAL, + "Failed to merge configuration and state data"); + } + + dnode_final = dnode_state; + break; + case frr::GetRequest_DataType_CONFIG: + dnode_final = dnode_config; + break; + case frr::GetRequest_DataType_STATE: + dnode_final = dnode_state; + break; + } + + // Validate data to create implicit default nodes if necessary. + int validate_opts = 0; + if (type == frr::GetRequest_DataType_CONFIG) + validate_opts = LYD_OPT_CONFIG; + else + validate_opts = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB; + lyd_validate(&dnode_final, validate_opts, ly_native_ctx); + + // Dump data using the requested format. + int ret = data_tree_from_dnode(dt, dnode_final, lyd_format, + with_defaults); + yang_dnode_free(dnode_final); + if (ret != 0) + return grpc::Status(grpc::StatusCode::INTERNAL, + "Failed to dump data"); + + return grpc::Status::OK; + } + + struct candidate *create_candidate(void) + { + uint32_t candidate_id = ++_nextCandidateId; + + // Check for overflow. + // TODO: implement an algorithm for unique reusable IDs. + if (candidate_id == 0) + return NULL; + + struct candidate *candidate = &_candidates[candidate_id]; + candidate->id = candidate_id; + pthread_rwlock_rdlock(&running_config->lock); + { + candidate->config = nb_config_dup(running_config); + } + pthread_rwlock_unlock(&running_config->lock); + candidate->transaction = NULL; + + return candidate; + } + + void delete_candidate(struct candidate *candidate) + { + _candidates.erase(candidate->id); + nb_config_free(candidate->config); + if (candidate->transaction) + nb_candidate_commit_abort(candidate->transaction); + } + + struct candidate *get_candidate(uint32_t candidate_id) + { + struct candidate *candidate; + + if (_candidates.count(candidate_id) == 0) + return NULL; + + return &_candidates[candidate_id]; + } +}; + +static void *grpc_pthread_start(void *arg) +{ + unsigned long *port = static_cast(arg); + NorthboundImpl service; + std::stringstream server_address; + + server_address << "0.0.0.0:" << *port; + + grpc::ServerBuilder builder; + builder.AddListeningPort(server_address.str(), + grpc::InsecureServerCredentials()); + builder.RegisterService(&service); + + std::unique_ptr server(builder.BuildAndStart()); + + zlog_notice("gRPC server listening on %s", + server_address.str().c_str()); + + server->Wait(); + + return NULL; +} + +static int frr_grpc_init(unsigned long *port) +{ + /* Create a pthread for gRPC since it runs its own event loop. */ + if (pthread_create(&grpc_pthread, NULL, grpc_pthread_start, port)) { + flog_err(EC_LIB_SYSTEM_CALL, "%s: error creating pthread: %s", + __func__, safe_strerror(errno)); + return -1; + } + pthread_detach(grpc_pthread); + + return 0; +} + +static int frr_grpc_finish(void) +{ + // TODO: cancel the gRPC pthreads gracefully. + + return 0; +} + +static int frr_grpc_module_late_init(struct thread_master *tm) +{ + static unsigned long port = GRPC_DEFAULT_PORT; + const char *args = THIS_MODULE->load_args; + + // Parse port number. + if (args) { + try { + port = std::stoul(args); + if (port < 1024) + throw std::invalid_argument( + "can't use privileged port"); + if (port > UINT16_MAX) + throw std::invalid_argument( + "port number is too big"); + } catch (std::exception &e) { + flog_err(EC_LIB_GRPC_INIT, + "%s: failed to parse port number: %s", + __func__, e.what()); + goto error; + } + } + + if (frr_grpc_init(&port) < 0) + goto error; + + hook_register(frr_fini, frr_grpc_finish); + + return 0; + +error: + flog_err(EC_LIB_GRPC_INIT, "failed to initialize the gRPC module"); + return -1; +} + +static int frr_grpc_module_init(void) +{ + hook_register(frr_late_init, frr_grpc_module_late_init); + + return 0; +} + +FRR_MODULE_SETUP(.name = "frr_grpc", .version = FRR_VERSION, + .description = "FRR gRPC northbound module", + .init = frr_grpc_module_init, ) diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index 33b6c247825e..44a55137f82e 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -256,7 +256,11 @@ static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session, return ret; } - candidate = nb_config_dup(running_config); + pthread_rwlock_rdlock(&running_config->lock); + { + candidate = nb_config_dup(running_config); + } + pthread_rwlock_unlock(&running_config->lock); while ((ret = sr_get_change_next(session, it, &sr_op, &sr_old_val, &sr_new_val)) @@ -282,15 +286,15 @@ static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session, * single event (SR_EV_ENABLED). This means we need to perform * the full two-phase commit protocol in one go here. */ - ret = nb_candidate_commit(candidate, NB_CLIENT_SYSREPO, true, - NULL, NULL); + ret = nb_candidate_commit(candidate, NB_CLIENT_SYSREPO, NULL, + true, NULL, NULL); } else { /* * Validate the configuration changes and allocate all resources * required to apply them. */ ret = nb_candidate_commit_prepare(candidate, NB_CLIENT_SYSREPO, - NULL, &transaction); + NULL, NULL, &transaction); } /* Map northbound return code to sysrepo return code. */ diff --git a/lib/ntop.c b/lib/ntop.c new file mode 100644 index 000000000000..d47a0b697aa6 --- /dev/null +++ b/lib/ntop.c @@ -0,0 +1,174 @@ +/* + * optimized ntop, about 10x faster than libc versions [as of 2019] + * + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "compiler.h" + +#define pos (*posx) + +static inline void putbyte(uint8_t bytex, char **posx) + __attribute__((always_inline)) OPTIMIZE; + +static inline void putbyte(uint8_t bytex, char **posx) +{ + bool zero = false; + int byte = bytex, tmp, a, b; + + if ((tmp = byte - 200) >= 0) { + *pos++ = '2'; + zero = true; + byte = tmp; + } else if ((tmp = byte - 100) >= 0) { + *pos++ = '1'; + zero = true; + byte = tmp; + } + + /* make sure the compiler knows the value range of "byte" */ + assume(byte < 100 && byte >= 0); + + b = byte % 10; + a = byte / 10; + if (a || zero) { + *pos++ = '0' + a; + *pos++ = '0' + b; + } else + *pos++ = '0' + b; +} + +static inline void puthex(uint16_t word, char **posx) + __attribute__((always_inline)) OPTIMIZE; + +static inline void puthex(uint16_t word, char **posx) +{ + const char *digits = "0123456789abcdef"; + if (word >= 0x1000) + *pos++ = digits[(word >> 12) & 0xf]; + if (word >= 0x100) + *pos++ = digits[(word >> 8) & 0xf]; + if (word >= 0x10) + *pos++ = digits[(word >> 4) & 0xf]; + *pos++ = digits[word & 0xf]; +} + +#undef pos + +const char *frr_inet_ntop(int af, const void * restrict src, + char * restrict dst, socklen_t size) + __attribute__((flatten)) DSO_SELF OPTIMIZE; + +const char *frr_inet_ntop(int af, const void * restrict src, + char * restrict dst, socklen_t size) +{ + const uint8_t *b = src; + /* 8 * "abcd:" for IPv6 + * note: the IPv4-embedded IPv6 syntax is only used for ::A.B.C.D, + * which isn't longer than 40 chars either. even with ::ffff:A.B.C.D + * it's shorter. + */ + char buf[8 * 5], *o = buf; + size_t best = 0, bestlen = 0, curlen = 0, i; + + switch (af) { + case AF_INET: +inet4: + putbyte(b[0], &o); + *o++ = '.'; + putbyte(b[1], &o); + *o++ = '.'; + putbyte(b[2], &o); + *o++ = '.'; + putbyte(b[3], &o); + *o++ = '\0'; + break; + case AF_INET6: + for (i = 0; i < 8; i++) { + if (b[i * 2] || b[i * 2 + 1]) { + if (curlen && curlen > bestlen) { + best = i - curlen; + bestlen = curlen; + } + curlen = 0; + continue; + } + curlen++; + } + if (curlen && curlen > bestlen) { + best = i - curlen; + bestlen = curlen; + } + /* do we want ::ffff:A.B.C.D? */ + if (best == 0 && bestlen == 6) { + *o++ = ':'; + *o++ = ':'; + b += 12; + goto inet4; + } + if (bestlen == 1) + bestlen = 0; + + for (i = 0; i < 8; i++) { + if (bestlen && i == best) { + if (i == 0) + *o++ = ':'; + *o++ = ':'; + continue; + } + if (i > best && i < best + bestlen) { + continue; + } + puthex((b[i * 2] << 8) | b[i * 2 + 1], &o); + + if (i < 7) + *o++ = ':'; + } + *o++ = '\0'; + break; + default: + return NULL; + } + + i = o - buf; + if (i > size) + return NULL; + /* compiler might inline memcpy if it knows the length is short, + * although neither gcc nor clang actually do this currently [2019] + */ + assume(i <= 8 * 5); + memcpy(dst, buf, i); + return dst; +} + +#ifndef INET_NTOP_NO_OVERRIDE +/* we want to override libc inet_ntop, but make sure it shows up in backtraces + * as frr_inet_ntop (to avoid confusion while debugging) + */ +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) + __attribute__((alias ("frr_inet_ntop"))) DSO_SELF; +#endif diff --git a/lib/openbsd-tree.c b/lib/openbsd-tree.c index eadef9902ba5..ddcc59fa8f59 100644 --- a/lib/openbsd-tree.c +++ b/lib/openbsd-tree.c @@ -435,7 +435,8 @@ void *_rb_insert(const struct rb_type *t, struct rbt_tree *rbt, void *elm) } /* Finds the node with the same key as elm */ -void *_rb_find(const struct rb_type *t, struct rbt_tree *rbt, const void *key) +void *_rb_find(const struct rb_type *t, const struct rbt_tree *rbt, + const void *key) { struct rb_entry *tmp = RBH_ROOT(rbt); void *node; @@ -456,7 +457,8 @@ void *_rb_find(const struct rb_type *t, struct rbt_tree *rbt, const void *key) } /* Finds the first node greater than or equal to the search key */ -void *_rb_nfind(const struct rb_type *t, struct rbt_tree *rbt, const void *key) +void *_rb_nfind(const struct rb_type *t, const struct rbt_tree *rbt, + const void *key) { struct rb_entry *tmp = RBH_ROOT(rbt); void *node; @@ -522,14 +524,14 @@ void *_rb_prev(const struct rb_type *t, void *elm) return (rbe == NULL ? NULL : rb_e2n(t, rbe)); } -void *_rb_root(const struct rb_type *t, struct rbt_tree *rbt) +void *_rb_root(const struct rb_type *t, const struct rbt_tree *rbt) { struct rb_entry *rbe = RBH_ROOT(rbt); return (rbe == NULL ? rbe : rb_e2n(t, rbe)); } -void *_rb_min(const struct rb_type *t, struct rbt_tree *rbt) +void *_rb_min(const struct rb_type *t, const struct rbt_tree *rbt) { struct rb_entry *rbe = RBH_ROOT(rbt); struct rb_entry *parent = NULL; @@ -542,7 +544,7 @@ void *_rb_min(const struct rb_type *t, struct rbt_tree *rbt) return (parent == NULL ? NULL : rb_e2n(t, parent)); } -void *_rb_max(const struct rb_type *t, struct rbt_tree *rbt) +void *_rb_max(const struct rb_type *t, const struct rbt_tree *rbt) { struct rb_entry *rbe = RBH_ROOT(rbt); struct rb_entry *parent = NULL; diff --git a/lib/openbsd-tree.h b/lib/openbsd-tree.h index d2f078133340..832a10141eb5 100644 --- a/lib/openbsd-tree.h +++ b/lib/openbsd-tree.h @@ -364,18 +364,18 @@ static inline void _rb_init(struct rbt_tree *rbt) rbt->rbt_root = NULL; } -static inline int _rb_empty(struct rbt_tree *rbt) +static inline int _rb_empty(const struct rbt_tree *rbt) { return (rbt->rbt_root == NULL); } void *_rb_insert(const struct rb_type *, struct rbt_tree *, void *); void *_rb_remove(const struct rb_type *, struct rbt_tree *, void *); -void *_rb_find(const struct rb_type *, struct rbt_tree *, const void *); -void *_rb_nfind(const struct rb_type *, struct rbt_tree *, const void *); -void *_rb_root(const struct rb_type *, struct rbt_tree *); -void *_rb_min(const struct rb_type *, struct rbt_tree *); -void *_rb_max(const struct rb_type *, struct rbt_tree *); +void *_rb_find(const struct rb_type *, const struct rbt_tree *, const void *); +void *_rb_nfind(const struct rb_type *, const struct rbt_tree *, const void *); +void *_rb_root(const struct rb_type *, const struct rbt_tree *); +void *_rb_min(const struct rb_type *, const struct rbt_tree *); +void *_rb_max(const struct rb_type *, const struct rbt_tree *); void *_rb_next(const struct rb_type *, void *); void *_rb_prev(const struct rb_type *, void *); void *_rb_left(const struct rb_type *, void *); @@ -401,56 +401,58 @@ int _rb_check(const struct rb_type *, void *, unsigned long); __attribute__((__unused__)) static inline struct _type \ *_name##_RB_INSERT(struct _name *head, struct _type *elm) \ { \ - return (struct _type *)_rb_insert( \ - _name##_RB_TYPE, &head->rbh_root, elm); \ + return (struct _type *)_rb_insert(_name##_RB_TYPE, \ + &head->rbh_root, elm); \ } \ \ __attribute__((__unused__)) static inline struct _type \ *_name##_RB_REMOVE(struct _name *head, struct _type *elm) \ { \ - return (struct _type *)_rb_remove( \ - _name##_RB_TYPE, &head->rbh_root, elm); \ + return (struct _type *)_rb_remove(_name##_RB_TYPE, \ + &head->rbh_root, elm); \ } \ \ __attribute__((__unused__)) static inline struct _type \ - *_name##_RB_FIND(struct _name *head, const struct _type *key) \ + *_name##_RB_FIND(const struct _name *head, \ + const struct _type *key) \ { \ - return (struct _type *)_rb_find( \ - _name##_RB_TYPE, &head->rbh_root, key); \ + return (struct _type *)_rb_find(_name##_RB_TYPE, \ + &head->rbh_root, key); \ } \ \ __attribute__((__unused__)) static inline struct _type \ - *_name##_RB_NFIND(struct _name *head, const struct _type *key) \ + *_name##_RB_NFIND(const struct _name *head, \ + const struct _type *key) \ { \ - return (struct _type *)_rb_nfind( \ - _name##_RB_TYPE, &head->rbh_root, key); \ + return (struct _type *)_rb_nfind(_name##_RB_TYPE, \ + &head->rbh_root, key); \ } \ \ __attribute__((__unused__)) static inline struct _type \ - *_name##_RB_ROOT(struct _name *head) \ + *_name##_RB_ROOT(const struct _name *head) \ { \ - return (struct _type *)_rb_root( \ - _name##_RB_TYPE, &head->rbh_root); \ + return (struct _type *)_rb_root(_name##_RB_TYPE, \ + &head->rbh_root); \ } \ \ __attribute__((__unused__)) static inline int _name##_RB_EMPTY( \ - struct _name *head) \ + const struct _name *head) \ { \ return _rb_empty(&head->rbh_root); \ } \ \ __attribute__((__unused__)) static inline struct _type \ - *_name##_RB_MIN(struct _name *head) \ + *_name##_RB_MIN(const struct _name *head) \ { \ - return (struct _type *)_rb_min( \ - _name##_RB_TYPE, &head->rbh_root); \ + return (struct _type *)_rb_min(_name##_RB_TYPE, \ + &head->rbh_root); \ } \ \ __attribute__((__unused__)) static inline struct _type \ - *_name##_RB_MAX(struct _name *head) \ + *_name##_RB_MAX(const struct _name *head) \ { \ - return (struct _type *)_rb_max( \ - _name##_RB_TYPE, &head->rbh_root); \ + return (struct _type *)_rb_max(_name##_RB_TYPE, \ + &head->rbh_root); \ } \ \ __attribute__((__unused__)) static inline struct _type \ diff --git a/lib/plist.c b/lib/plist.c index 2a97e1e5b2bf..54ea742c66c8 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -998,22 +998,36 @@ static int vty_prefix_list_uninstall(struct vty *vty, afi_t afi, return CMD_SUCCESS; } - /* We must have, at a minimum, both the type and prefix here */ - if ((typestr == NULL) || (prefix == NULL)) { - vty_out(vty, "%% Both prefix and type required\n"); - return CMD_WARNING_CONFIG_FAILED; - } - /* Check sequence number. */ if (seq) seqnum = (int64_t)atol(seq); + /* Sequence number specified, but nothing else. */ + if (seq && typestr == NULL && prefix == NULL && ge == NULL + && le == NULL) { + pentry = prefix_seq_check(plist, seqnum); + + if (pentry == NULL) { + vty_out(vty, + "%% Can't find prefix-list %s with sequence number %" PRIu64 "\n", + name, seqnum); + return CMD_WARNING_CONFIG_FAILED; + } + + prefix_list_entry_delete(plist, pentry, 1); + return CMD_SUCCESS; + } + /* ge and le number */ if (ge) genum = atoi(ge); if (le) lenum = atoi(le); + /* We must have, at a minimum, both the type and prefix here */ + if ((typestr == NULL) || (prefix == NULL)) + return CMD_WARNING_CONFIG_FAILED; + /* Check of filter type. */ if (strncmp("permit", typestr, 1) == 0) type = PREFIX_PERMIT; @@ -1375,6 +1389,17 @@ DEFPY (no_ip_prefix_list, action, dest, ge_str, le_str); } +DEFPY(no_ip_prefix_list_seq, no_ip_prefix_list_seq_cmd, + "no ip prefix-list WORD seq (1-4294967295)", + NO_STR IP_STR PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n") +{ + return vty_prefix_list_uninstall(vty, AFI_IP, prefix_list, seq_str, + NULL, NULL, NULL, NULL); +} + DEFPY (no_ip_prefix_list_all, no_ip_prefix_list_all_cmd, "no ip prefix-list WORD", @@ -2059,6 +2084,7 @@ static void prefix_list_init_ipv4(void) install_element(CONFIG_NODE, &ip_prefix_list_cmd); install_element(CONFIG_NODE, &no_ip_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_seq_cmd); install_element(CONFIG_NODE, &no_ip_prefix_list_all_cmd); install_element(CONFIG_NODE, &ip_prefix_list_description_cmd); diff --git a/lib/prefix.c b/lib/prefix.c index 6b91969218fd..134d9cf908df 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -28,6 +28,7 @@ #include "log.h" #include "jhash.h" #include "lib_errors.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, PREFIX, "Prefix") DEFINE_MTYPE_STATIC(LIB, PREFIX_FLOWSPEC, "Prefix Flowspec") @@ -1365,7 +1366,7 @@ void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr, int save_errno = errno; if (addr.s_addr == INADDR_ANY) - strcpy(buf, "*"); + strlcpy(buf, "*", buf_size); else { if (!inet_ntop(AF_INET, &addr, buf, buf_size)) { if (onfail) @@ -1543,7 +1544,7 @@ char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size) return ptr; } -unsigned prefix_hash_key(void *pp) +unsigned prefix_hash_key(const void *pp) { struct prefix copy; @@ -1626,3 +1627,48 @@ char *esi_to_str(const esi_t *esi, char *buf, int size) esi->val[9]); return ptr; } + +printfrr_ext_autoreg_p("I4", printfrr_i4) +static ssize_t printfrr_i4(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + inet_ntop(AF_INET, ptr, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("I6", printfrr_i6) +static ssize_t printfrr_i6(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + inet_ntop(AF_INET6, ptr, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("FX", printfrr_pfx) +static ssize_t printfrr_pfx(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + prefix2str(ptr, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("SG4", printfrr_psg) +static ssize_t printfrr_psg(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct prefix_sg *sg = ptr; + struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 }; + + if (sg->src.s_addr == INADDR_ANY) + bprintfrr(&fb, "(*,"); + else + bprintfrr(&fb, "(%pI4,", &sg->src); + + if (sg->grp.s_addr == INADDR_ANY) + bprintfrr(&fb, "*)"); + else + bprintfrr(&fb, "%pI4)", &sg->grp); + + fb.pos[0] = '\0'; + return 3; +} diff --git a/lib/prefix.h b/lib/prefix.h index d3c387e10294..d57b43dac689 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -466,7 +466,7 @@ extern int is_zero_mac(struct ethaddr *mac); extern int prefix_str2mac(const char *str, struct ethaddr *mac); extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size); -extern unsigned prefix_hash_key(void *pp); +extern unsigned prefix_hash_key(const void *pp); extern int str_to_esi(const char *str, esi_t *esi); extern char *esi_to_str(const esi_t *esi, char *buf, int size); diff --git a/lib/printf/README b/lib/printf/README new file mode 100644 index 000000000000..ac283642d798 --- /dev/null +++ b/lib/printf/README @@ -0,0 +1,9 @@ +This is the printf implementation from FreeBSD. It was imported on 2019-05-12, +from SVN revision 347514 (but the code hadn't been touched for 2 years before +that.) + +Please don't reindent or otherwise mangle the files to make importing fixes +easy (not that there are significant changes likely to happen...) + +The changes to this code are published under FreeBSD's license as listed in the +file headers. If you change license, please make that as obvious as possible. diff --git a/lib/printf/glue.c b/lib/printf/glue.c new file mode 100644 index 000000000000..1b760dc2d3dc --- /dev/null +++ b/lib/printf/glue.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "printfrr.h" +#include "printflocal.h" + +ssize_t bprintfrr(struct fbuf *out, const char *fmt, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vbprintfrr(out, fmt, ap); + va_end(ap); + return ret; +} + +ssize_t vsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap) +{ + struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; + struct fbuf *fb = (out && outsz) ? &fbb : NULL; + ssize_t ret; + + ret = vbprintfrr(fb, fmt, ap); + if (fb) + fb->pos[0] = '\0'; + return ret; +} + +ssize_t snprintfrr(char *out, size_t outsz, const char *fmt, ...) +{ + struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; + struct fbuf *fb = (out && outsz) ? &fbb : NULL; + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vbprintfrr(fb, fmt, ap); + va_end(ap); + if (fb) + fb->pos[0] = '\0'; + return ret; +} + +ssize_t vcsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap) +{ + if (!out || !outsz) + return vbprintfrr(NULL, fmt, ap); + + struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; + ssize_t ret; + size_t pos; + + pos = strnlen(out, outsz); + fbb.pos += pos; + + ret = vbprintfrr(&fbb, fmt, ap); + fbb.pos[0] = '\0'; + return ret >= 0 ? ret + (ssize_t)pos : ret; +} + +ssize_t csnprintfrr(char *out, size_t outsz, const char *fmt, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vcsnprintfrr(out, outsz, fmt, ap); + va_end(ap); + return ret; +} + +char *vasnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt, + va_list ap) +{ + struct fbuf fb = { .buf = out, .pos = out, .len = outsz - 1, }; + ssize_t len; + va_list ap2; + char *ret = out; + + va_copy(ap2, ap); + len = vbprintfrr(&fb, fmt, ap); + if (len < 0) + /* error = malformed format string => try something useful */ + return qstrdup(mt, fmt); + + if ((size_t)len >= outsz - 1) { + ret = qmalloc(mt, len + 1); + fb.buf = fb.pos = ret; + fb.len = len; + + vbprintfrr(&fb, fmt, ap2); + } + ret[len] = '\0'; + return ret; +} + +char *asnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt, + ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = vasnprintfrr(mt, out, outsz, fmt, ap); + va_end(ap); + return ret; +} + +char *vasprintfrr(struct memtype *mt, const char *fmt, va_list ap) +{ + char buf[256]; + char *ret; + + ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap); + + if (ret == buf) + ret = qstrdup(mt, ret); + return ret; +} + +char *asprintfrr(struct memtype *mt, const char *fmt, ...) +{ + char buf[256]; + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (ret == buf) + ret = qstrdup(mt, ret); + return ret; +} + +/* Q: WTF? + * A: since printf should be reasonably fast (think debugging logs), the idea + * here is to keep things close by each other in a cacheline. That's why + * ext_quick just has the first 2 characters of an extension, and we do a + * nice linear continuous sweep. Only if we find something, we go do more + * expensive things. + * + * Q: doesn't this need a mutex/lock? + * A: theoretically, yes, but that's quite expensive and I rather elide that + * necessity by putting down some usage rules. Just call this at startup + * while singlethreaded and all is fine. Ideally, just use constructors + * (and make sure dlopen() doesn't mess things up...) + */ +#define MAXEXT 64 + +struct ext_quick { + char fmt[2]; +}; + +static uint8_t ext_offsets[26] __attribute__((aligned(32))); +static struct ext_quick entries[MAXEXT] __attribute__((aligned(64))); +static const struct printfrr_ext *exts[MAXEXT] __attribute__((aligned(64))); + +void printfrr_ext_reg(const struct printfrr_ext *ext) +{ + uint8_t o; + ptrdiff_t i; + + if (!printfrr_ext_char(ext->match[0])) + return; + + o = ext->match[0] - 'A'; + for (i = ext_offsets[o]; + i < MAXEXT && entries[i].fmt[0] && + memcmp(entries[i].fmt, ext->match, 2) < 0; + i++) + ; + if (i == MAXEXT) + return; + for (o++; o <= 'Z' - 'A'; o++) + ext_offsets[o]++; + + memmove(entries + i + 1, entries + i, + (MAXEXT - i - 1) * sizeof(entries[0])); + memmove(exts + i + 1, exts + i, + (MAXEXT - i - 1) * sizeof(exts[0])); + + memcpy(entries[i].fmt, ext->match, 2); + exts[i] = ext; +} + +ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec, + const void *ptr) +{ + const struct printfrr_ext *ext; + size_t i; + + for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) { + if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0]) + return 0; + if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1]) + continue; + ext = exts[i]; + if (!ext->print_ptr) + continue; + if (strncmp(ext->match, fmt, strlen(ext->match))) + continue; + return ext->print_ptr(buf, sz, fmt, prec, ptr); + } + return 0; +} + +ssize_t printfrr_exti(char *buf, size_t sz, const char *fmt, int prec, + uintmax_t num) +{ + const struct printfrr_ext *ext; + size_t i; + + for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) { + if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0]) + return 0; + if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1]) + continue; + ext = exts[i]; + if (!ext->print_int) + continue; + if (strncmp(ext->match, fmt, strlen(ext->match))) + continue; + return ext->print_int(buf, sz, fmt, prec, num); + } + return 0; +} diff --git a/lib/printf/printf-pos.c b/lib/printf/printf-pos.c new file mode 100644 index 000000000000..1f5f283e82f6 --- /dev/null +++ b/lib/printf/printf-pos.c @@ -0,0 +1,787 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_CDEFS_H +#include +#endif + +/* + * This is the code responsible for handling positional arguments + * (%m$ and %m$.n$) for vfprintf() and vfwprintf(). + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "printflocal.h" + +#ifdef NL_ARGMAX +#define MAX_POSARG NL_ARGMAX +#else +#define MAX_POSARG 65536 +#endif + +/* + * Type ids for argument type table. + */ +enum typeid { + T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT, + T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG, + T_PTRDIFFT, TP_PTRDIFFT, T_SSIZET, T_SIZET, TP_SSIZET, + T_INT64T, T_UINT64T, T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, + TP_CHAR, TP_SCHAR, T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR +}; + +/* An expandable array of types. */ +struct typetable { + enum typeid *table; /* table of types */ + enum typeid stattable[STATIC_ARG_TBL_SIZE]; + u_int tablesize; /* current size of type table */ + u_int tablemax; /* largest used index in table */ + u_int nextarg; /* 1-based argument index */ +}; + +static int __grow_type_table(struct typetable *); +static void build_arg_table (struct typetable *, va_list, union arg **); + +/* + * Initialize a struct typetable. + */ +static inline void +inittypes(struct typetable *types) +{ + u_int n; + + types->table = types->stattable; + types->tablesize = STATIC_ARG_TBL_SIZE; + types->tablemax = 0; + types->nextarg = 1; + for (n = 0; n < STATIC_ARG_TBL_SIZE; n++) + types->table[n] = T_UNUSED; +} + +/* + * struct typetable destructor. + */ +static inline void +freetypes(struct typetable *types) +{ + + if (types->table != types->stattable) + free (types->table); +} + +/* + * Ensure that there is space to add a new argument type to the type table. + * Expand the table if necessary. Returns 0 on success. + */ +static inline int +_ensurespace(struct typetable *types) +{ + + if (types->nextarg >= types->tablesize) { + if (__grow_type_table(types)) + return (-1); + } + if (types->nextarg > types->tablemax) + types->tablemax = types->nextarg; + return (0); +} + +/* + * Add an argument type to the table, expanding if necessary. + * Returns 0 on success. + */ +static inline int +addtype(struct typetable *types, enum typeid type) +{ + + if (_ensurespace(types)) + return (-1); + types->table[types->nextarg++] = type; + return (0); +} + +static inline int +addsarg(struct typetable *types, int flags) +{ + + if (_ensurespace(types)) + return (-1); + if (flags & LONGDBL) + types->table[types->nextarg++] = T_INT64T; + else if (flags & INTMAXT) + types->table[types->nextarg++] = T_INTMAXT; + else if (flags & SIZET) + types->table[types->nextarg++] = T_SSIZET; + else if (flags & PTRDIFFT) + types->table[types->nextarg++] = T_PTRDIFFT; + else if (flags & LLONGINT) + types->table[types->nextarg++] = T_LLONG; + else if (flags & LONGINT) + types->table[types->nextarg++] = T_LONG; + else + types->table[types->nextarg++] = T_INT; + return (0); +} + +static inline int +adduarg(struct typetable *types, int flags) +{ + + if (_ensurespace(types)) + return (-1); + if (flags & LONGDBL) + types->table[types->nextarg++] = T_UINT64T; + else if (flags & INTMAXT) + types->table[types->nextarg++] = T_UINTMAXT; + else if (flags & SIZET) + types->table[types->nextarg++] = T_SIZET; + else if (flags & PTRDIFFT) + types->table[types->nextarg++] = T_SIZET; + else if (flags & LLONGINT) + types->table[types->nextarg++] = T_U_LLONG; + else if (flags & LONGINT) + types->table[types->nextarg++] = T_U_LONG; + else + types->table[types->nextarg++] = T_U_INT; + return (0); +} + +/* + * Add * arguments to the type array. + */ +static inline int +addaster(struct typetable *types, char **fmtp) +{ + char *cp; + u_int n2; + + n2 = 0; + cp = *fmtp; + while (is_digit(*cp)) { + n2 = 10 * n2 + to_digit(*cp); + cp++; + } + if (*cp == '$') { + u_int hold = types->nextarg; + types->nextarg = n2; + if (addtype(types, T_INT)) + return (-1); + types->nextarg = hold; + *fmtp = ++cp; + } else { + if (addtype(types, T_INT)) + return (-1); + } + return (0); +} + +#ifdef WCHAR_SUPPORT +static inline int +addwaster(struct typetable *types, wchar_t **fmtp) +{ + wchar_t *cp; + u_int n2; + + n2 = 0; + cp = *fmtp; + while (is_digit(*cp)) { + n2 = 10 * n2 + to_digit(*cp); + cp++; + } + if (*cp == '$') { + u_int hold = types->nextarg; + types->nextarg = n2; + if (addtype(types, T_INT)) + return (-1); + types->nextarg = hold; + *fmtp = ++cp; + } else { + if (addtype(types, T_INT)) + return (-1); + } + return (0); +} +#endif /* WCHAR_SUPPORT */ + +/* + * Find all arguments when a positional parameter is encountered. Returns a + * table, indexed by argument number, of pointers to each arguments. The + * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. + * It will be replaces with a malloc-ed one if it overflows. + * Returns 0 on success. On failure, returns nonzero and sets errno. + */ +int +_frr_find_arguments (const char *fmt0, va_list ap, union arg **argtable) +{ + char *fmt; /* format string */ + int ch; /* character from fmt */ + u_int n; /* handy integer (short term usage) */ + int error; + int flags; /* flags as above */ + struct typetable types; /* table of types */ + + fmt = (char *)fmt0; + inittypes(&types); + error = 0; + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + while ((ch = *fmt) != '\0' && ch != '%') + fmt++; + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + +rflag: ch = *fmt++; +reswitch: switch (ch) { + case ' ': + case '#': + goto rflag; + case '*': + if ((error = addaster(&types, &fmt))) + goto error; + goto rflag; + case '-': + case '+': + case '\'': + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + if ((error = addaster(&types, &fmt))) + goto error; + goto rflag; + } + while (is_digit(ch)) { + ch = *fmt++; + } + goto reswitch; + case '0': + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + /* Detect overflow */ + if (n > MAX_POSARG) { + error = -1; + goto error; + } + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') { + types.nextarg = n; + goto rflag; + } + goto reswitch; + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; + goto rflag; + case 'l': + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else + flags |= LONGINT; + goto rflag; + case 'q': + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; + goto rflag; + case 'C': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'c': + error = addtype(&types, + (flags & LONGINT) ? T_WINT : T_INT); + if (error) + goto error; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if ((error = addsarg(&types, flags))) + goto error; + break; +#ifndef NO_FLOATING_POINT + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + error = addtype(&types, + (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE); + if (error) + goto error; + break; +#endif /* !NO_FLOATING_POINT */ + case 'n': + if (flags & INTMAXT) + error = addtype(&types, TP_INTMAXT); + else if (flags & PTRDIFFT) + error = addtype(&types, TP_PTRDIFFT); + else if (flags & SIZET) + error = addtype(&types, TP_SSIZET); + else if (flags & LLONGINT) + error = addtype(&types, TP_LLONG); + else if (flags & LONGINT) + error = addtype(&types, TP_LONG); + else if (flags & SHORTINT) + error = addtype(&types, TP_SHORT); + else if (flags & CHARINT) + error = addtype(&types, TP_SCHAR); + else + error = addtype(&types, TP_INT); + if (error) + goto error; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if ((error = adduarg(&types, flags))) + goto error; + break; + case 'p': + if ((error = addtype(&types, TP_VOID))) + goto error; + break; + case 'S': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 's': + error = addtype(&types, + (flags & LONGINT) ? TP_WCHAR : TP_CHAR); + if (error) + goto error; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + case 'X': + case 'x': + if ((error = adduarg(&types, flags))) + goto error; + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + break; + } + } +done: + build_arg_table(&types, ap, argtable); +error: + freetypes(&types); + return (error || *argtable == NULL); +} + +#ifdef WCHAR_SUPPORT +/* wchar version of __find_arguments. */ +int +_frr_find_warguments (const wchar_t *fmt0, va_list ap, union arg **argtable) +{ + wchar_t *fmt; /* format string */ + wchar_t ch; /* character from fmt */ + u_int n; /* handy integer (short term usage) */ + int error; + int flags; /* flags as above */ + struct typetable types; /* table of types */ + + fmt = (wchar_t *)fmt0; + inittypes(&types); + error = 0; + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + while ((ch = *fmt) != '\0' && ch != '%') + fmt++; + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + +rflag: ch = *fmt++; +reswitch: switch (ch) { + case ' ': + case '#': + goto rflag; + case '*': + if ((error = addwaster(&types, &fmt))) + goto error; + goto rflag; + case '-': + case '+': + case '\'': + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + if ((error = addwaster(&types, &fmt))) + goto error; + goto rflag; + } + while (is_digit(ch)) { + ch = *fmt++; + } + goto reswitch; + case '0': + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + /* Detect overflow */ + if (n > MAX_POSARG) { + error = -1; + goto error; + } + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') { + types.nextarg = n; + goto rflag; + } + goto reswitch; + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; + goto rflag; + case 'l': + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else + flags |= LONGINT; + goto rflag; + case 'q': + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; + goto rflag; + case 'C': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'c': + error = addtype(&types, + (flags & LONGINT) ? T_WINT : T_INT); + if (error) + goto error; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if ((error = addsarg(&types, flags))) + goto error; + break; +#ifndef NO_FLOATING_POINT + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + error = addtype(&types, + (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE); + if (error) + goto error; + break; +#endif /* !NO_FLOATING_POINT */ + case 'n': + if (flags & INTMAXT) + error = addtype(&types, TP_INTMAXT); + else if (flags & PTRDIFFT) + error = addtype(&types, TP_PTRDIFFT); + else if (flags & SIZET) + error = addtype(&types, TP_SSIZET); + else if (flags & LLONGINT) + error = addtype(&types, TP_LLONG); + else if (flags & LONGINT) + error = addtype(&types, TP_LONG); + else if (flags & SHORTINT) + error = addtype(&types, TP_SHORT); + else if (flags & CHARINT) + error = addtype(&types, TP_SCHAR); + else + error = addtype(&types, TP_INT); + if (error) + goto error; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if ((error = adduarg(&types, flags))) + goto error; + break; + case 'p': + if ((error = addtype(&types, TP_VOID))) + goto error; + break; + case 'S': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 's': + error = addtype(&types, + (flags & LONGINT) ? TP_WCHAR : TP_CHAR); + if (error) + goto error; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + case 'X': + case 'x': + if ((error = adduarg(&types, flags))) + goto error; + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + break; + } + } +done: + build_arg_table(&types, ap, argtable); +error: + freetypes(&types); + return (error || *argtable == NULL); +} +#endif /* WCHAR_SUPPORT */ + +/* + * Increase the size of the type table. Returns 0 on success. + */ +static int +__grow_type_table(struct typetable *types) +{ + enum typeid *const oldtable = types->table; + const int oldsize = types->tablesize; + enum typeid *newtable; + u_int n, newsize; + + /* Detect overflow */ + if (types->nextarg > MAX_POSARG) + return (-1); + + newsize = oldsize * 2; + if (newsize < types->nextarg + 1) + newsize = types->nextarg + 1; + if (oldsize == STATIC_ARG_TBL_SIZE) { + if ((newtable = malloc(newsize * sizeof(enum typeid))) == NULL) + return (-1); + bcopy(oldtable, newtable, oldsize * sizeof(enum typeid)); + } else { + newtable = realloc(oldtable, newsize * sizeof(enum typeid)); + if (newtable == NULL) + return (-1); + } + for (n = oldsize; n < newsize; n++) + newtable[n] = T_UNUSED; + + types->table = newtable; + types->tablesize = newsize; + + return (0); +} + +/* + * Build the argument table from the completed type table. + * On malloc failure, *argtable is set to NULL. + */ +static void +build_arg_table(struct typetable *types, va_list ap, union arg **argtable) +{ + u_int n; + + if (types->tablemax >= STATIC_ARG_TBL_SIZE) { + *argtable = (union arg *) + malloc (sizeof (union arg) * (types->tablemax + 1)); + if (*argtable == NULL) + return; + } + + (*argtable) [0].intarg = 0; + for (n = 1; n <= types->tablemax; n++) { + switch (types->table[n]) { + case T_UNUSED: /* whoops! */ + (*argtable) [n].intarg = va_arg (ap, int); + break; + case TP_SCHAR: + (*argtable) [n].pschararg = va_arg (ap, signed char *); + break; + case TP_SHORT: + (*argtable) [n].pshortarg = va_arg (ap, short *); + break; + case T_INT: + (*argtable) [n].intarg = va_arg (ap, int); + break; + case T_U_INT: + (*argtable) [n].uintarg = va_arg (ap, unsigned int); + break; + case TP_INT: + (*argtable) [n].pintarg = va_arg (ap, int *); + break; + case T_LONG: + (*argtable) [n].longarg = va_arg (ap, long); + break; + case T_U_LONG: + (*argtable) [n].ulongarg = va_arg (ap, unsigned long); + break; + case TP_LONG: + (*argtable) [n].plongarg = va_arg (ap, long *); + break; + case T_LLONG: + (*argtable) [n].longlongarg = va_arg (ap, long long); + break; + case T_U_LLONG: + (*argtable) [n].ulonglongarg = va_arg (ap, unsigned long long); + break; + case TP_LLONG: + (*argtable) [n].plonglongarg = va_arg (ap, long long *); + break; + case T_PTRDIFFT: + (*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t); + break; + case TP_PTRDIFFT: + (*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *); + break; + case T_SIZET: + (*argtable) [n].sizearg = va_arg (ap, size_t); + break; + case T_SSIZET: + (*argtable) [n].sizearg = va_arg (ap, ssize_t); + break; + case TP_SSIZET: + (*argtable) [n].pssizearg = va_arg (ap, ssize_t *); + break; + case T_INTMAXT: + (*argtable) [n].intmaxarg = va_arg (ap, intmax_t); + break; + case T_UINTMAXT: + (*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t); + break; + case TP_INTMAXT: + (*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *); + break; + case T_INT64T: + (*argtable) [n].intmaxarg = va_arg (ap, int64_t); + break; + case T_UINT64T: + (*argtable) [n].uintmaxarg = va_arg (ap, uint64_t); + break; + case T_DOUBLE: +#ifndef NO_FLOATING_POINT + (*argtable) [n].doublearg = va_arg (ap, double); +#endif + break; + case T_LONG_DOUBLE: +#ifndef NO_FLOATING_POINT + (*argtable) [n].longdoublearg = va_arg (ap, long double); +#endif + break; + case TP_CHAR: + (*argtable) [n].pchararg = va_arg (ap, char *); + break; + case TP_VOID: + (*argtable) [n].pvoidarg = va_arg (ap, void *); + break; + case T_WINT: + (*argtable) [n].wintarg = va_arg (ap, wint_t); + break; + case TP_WCHAR: + (*argtable) [n].pwchararg = va_arg (ap, wchar_t *); + break; + } + } +} diff --git a/lib/printf/printfcommon.h b/lib/printf/printfcommon.h new file mode 100644 index 000000000000..5c45520b4c3a --- /dev/null +++ b/lib/printf/printfcommon.h @@ -0,0 +1,244 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Copyright (c) 2011 The FreeBSD Foundation + * All rights reserved. + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * This file defines common routines used by both printf and wprintf. + * You must define CHAR to either char or wchar_t prior to including this. + */ + + +static CHAR *__ujtoa(uintmax_t, CHAR *, int, int, const char *); +static CHAR *__ultoa(u_long, CHAR *, int, int, const char *); + +#define NIOV 8 +struct io_state { + struct fbuf *cb; + size_t avail; +}; + +static inline void +io_init(struct io_state *iop, struct fbuf *cb) +{ + iop->cb = cb; + iop->avail = cb ? cb->len - (cb->pos - cb->buf) : 0; +} + +/* + * WARNING: The buffer passed to io_print() is not copied immediately; it must + * remain valid until io_flush() is called. + */ +static inline int +io_print(struct io_state *iop, const CHAR * __restrict ptr, size_t len) +{ + size_t copylen = len; + + if (!iop->cb) + return 0; + if (iop->avail < copylen) + copylen = iop->avail; + + memcpy(iop->cb->pos, ptr, copylen); + iop->avail -= copylen; + iop->cb->pos += copylen; + return 0; +} + +/* + * Choose PADSIZE to trade efficiency vs. size. If larger printf + * fields occur frequently, increase PADSIZE and make the initialisers + * below longer. + */ +#define PADSIZE 16 /* pad chunk size */ +static const CHAR blanks[PADSIZE] = +{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; +static const CHAR zeroes[PADSIZE] = +{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + +/* + * Pad with blanks or zeroes. 'with' should point to either the blanks array + * or the zeroes array. + */ +static inline int +io_pad(struct io_state *iop, int howmany, const CHAR * __restrict with) +{ + int n; + + while (howmany > 0) { + n = (howmany >= PADSIZE) ? PADSIZE : howmany; + if (io_print(iop, with, n)) + return (-1); + howmany -= n; + } + return (0); +} + +/* + * Print exactly len characters of the string spanning p to ep, truncating + * or padding with 'with' as necessary. + */ +static inline int +io_printandpad(struct io_state *iop, const CHAR *p, const CHAR *ep, + int len, const CHAR * __restrict with) +{ + int p_len; + + p_len = ep - p; + if (p_len > len) + p_len = len; + if (p_len > 0) { + if (io_print(iop, p, p_len)) + return (-1); + } else { + p_len = 0; + } + return (io_pad(iop, len - p_len, with)); +} + +/* + * Convert an unsigned long to ASCII for printf purposes, returning + * a pointer to the first character of the string representation. + * Octal numbers can be forced to have a leading zero; hex numbers + * use the given digits. + */ +static CHAR * +__ultoa(u_long val, CHAR *endp, int base, int octzero, const char *xdigs) +{ + CHAR *cp = endp; + long sval; + + /* + * Handle the three cases separately, in the hope of getting + * better/faster code. + */ + switch (base) { + case 10: + if (val < 10) { /* many numbers are 1 digit */ + *--cp = to_char(val); + return (cp); + } + /* + * On many machines, unsigned arithmetic is harder than + * signed arithmetic, so we do at most one unsigned mod and + * divide; this is sufficient to reduce the range of + * the incoming value to where signed arithmetic works. + */ + if (val > LONG_MAX) { + *--cp = to_char(val % 10); + sval = val / 10; + } else + sval = val; + do { + *--cp = to_char(sval % 10); + sval /= 10; + } while (sval != 0); + break; + + case 8: + do { + *--cp = to_char(val & 7); + val >>= 3; + } while (val); + if (octzero && *cp != '0') + *--cp = '0'; + break; + + case 16: + do { + *--cp = xdigs[val & 15]; + val >>= 4; + } while (val); + break; + + default: /* oops */ + abort(); + } + return (cp); +} + +/* Identical to __ultoa, but for intmax_t. */ +static CHAR * +__ujtoa(uintmax_t val, CHAR *endp, int base, int octzero, const char *xdigs) +{ + CHAR *cp = endp; + intmax_t sval; + + /* quick test for small values; __ultoa is typically much faster */ + /* (perhaps instead we should run until small, then call __ultoa?) */ + if (val <= ULONG_MAX) + return (__ultoa((u_long)val, endp, base, octzero, xdigs)); + switch (base) { + case 10: + if (val < 10) { + *--cp = to_char(val % 10); + return (cp); + } + if (val > INTMAX_MAX) { + *--cp = to_char(val % 10); + sval = val / 10; + } else + sval = val; + do { + *--cp = to_char(sval % 10); + sval /= 10; + } while (sval != 0); + break; + + case 8: + do { + *--cp = to_char(val & 7); + val >>= 3; + } while (val); + if (octzero && *cp != '0') + *--cp = '0'; + break; + + case 16: + do { + *--cp = xdigs[val & 15]; + val >>= 4; + } while (val); + break; + + default: + abort(); + } + return (cp); +} diff --git a/lib/printf/printflocal.h b/lib/printf/printflocal.h new file mode 100644 index 000000000000..335e09872e29 --- /dev/null +++ b/lib/printf/printflocal.h @@ -0,0 +1,105 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "compiler.h" +#include "printfrr.h" + +/* + * Flags used during conversion. + */ +#define ALT 0x001 /* alternate form */ +#define LADJUST 0x004 /* left adjustment */ +#define LONGDBL 0x008 /* long double */ +#define LONGINT 0x010 /* long integer */ +#define LLONGINT 0x020 /* long long integer */ +#define SHORTINT 0x040 /* short integer */ +#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ +#define FPT 0x100 /* Floating point number */ +#define GROUPING 0x200 /* use grouping ("'" flag) */ + /* C99 additional size modifiers: */ +#define SIZET 0x400 /* size_t */ +#define PTRDIFFT 0x800 /* ptrdiff_t */ +#define INTMAXT 0x1000 /* intmax_t */ +#define CHARINT 0x2000 /* print char using int format */ + +/* + * Macros for converting digits to letters and vice versa + */ +#define to_digit(c) ((c) - '0') +#define is_digit(c) ((unsigned)to_digit(c) <= 9) +#define to_char(n) ((n) + '0') + +/* Size of the static argument table. */ +#define STATIC_ARG_TBL_SIZE 8 + +union arg { + int intarg; + u_int uintarg; + long longarg; + u_long ulongarg; + long long longlongarg; + unsigned long long ulonglongarg; + ptrdiff_t ptrdiffarg; + size_t sizearg; + intmax_t intmaxarg; + uintmax_t uintmaxarg; + void *pvoidarg; + char *pchararg; + signed char *pschararg; + short *pshortarg; + int *pintarg; + long *plongarg; + long long *plonglongarg; + ptrdiff_t *pptrdiffarg; + ssize_t *pssizearg; + intmax_t *pintmaxarg; +#ifndef NO_FLOATING_POINT + double doublearg; + long double longdoublearg; +#endif + wint_t wintarg; + wchar_t *pwchararg; +}; + +/* Handle positional parameters. */ +int _frr_find_arguments(const char *, va_list, union arg **) DSO_LOCAL; +#ifdef WCHAR_SUPPORT +int _frr_find_warguments(const wchar_t *, va_list, union arg **) DSO_LOCAL; +#endif + +/* returns number of bytes consumed for extended specifier */ +ssize_t printfrr_extp(char *, size_t, const char *, int, const void *) DSO_LOCAL; +ssize_t printfrr_exti(char *, size_t, const char *, int, uintmax_t) DSO_LOCAL; diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c new file mode 100644 index 000000000000..07df6dd8fe17 --- /dev/null +++ b/lib/printf/vfprintf.c @@ -0,0 +1,730 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Copyright (c) 2011 The FreeBSD Foundation + * All rights reserved. + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_CDEFS_H +#include +#endif + +/* + * Actual printf innards. + * + * This code is large and complicated... + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "printflocal.h" + +#define CHAR char +#include "printfcommon.h" + +#ifdef WCHAR_SUPPORT +/* + * Convert a wide character string argument for the %ls format to a multibyte + * string representation. If not -1, prec specifies the maximum number of + * bytes to output, and also means that we can't assume that the wide char. + * string ends is null-terminated. + */ +static char * +__wcsconv(wchar_t *wcsarg, int prec) +{ + static const mbstate_t initial; + mbstate_t mbs; + char buf[MB_LEN_MAX]; + wchar_t *p; + char *convbuf; + size_t clen, nbytes; + + /* Allocate space for the maximum number of bytes we could output. */ + if (prec < 0) { + p = wcsarg; + mbs = initial; + nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs); + if (nbytes == (size_t)-1) + return (NULL); + } else { + /* + * Optimisation: if the output precision is small enough, + * just allocate enough memory for the maximum instead of + * scanning the string. + */ + if (prec < 128) + nbytes = prec; + else { + nbytes = 0; + p = wcsarg; + mbs = initial; + for (;;) { + clen = wcrtomb(buf, *p++, &mbs); + if (clen == 0 || clen == (size_t)-1 || + nbytes + clen > (size_t)prec) + break; + nbytes += clen; + } + } + } + if ((convbuf = malloc(nbytes + 1)) == NULL) + return (NULL); + + /* Fill the output buffer. */ + p = wcsarg; + mbs = initial; + if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p, + nbytes, &mbs)) == (size_t)-1) { + free(convbuf); + return (NULL); + } + convbuf[nbytes] = '\0'; + return (convbuf); +} +#endif /* WCHAR_SUPPORT */ + +/* + * The size of the buffer we use as scratch space for integer + * conversions, among other things. We need enough space to + * write a uintmax_t in octal (plus one byte). + */ +#if UINTMAX_MAX <= UINT64_MAX +#define BUF 64 +#else +#error "BUF must be large enough to format a uintmax_t" +#endif + +/* + * Non-MT-safe version + */ +ssize_t +vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap) +{ + const char *fmt; /* format string */ + int ch; /* character from fmt */ + int n, n2; /* handy integer (short term usage) */ + const char *cp; /* handy char pointer (short term usage) */ + int flags; /* flags as above */ + int ret; /* return value accumulator */ + int width; /* width from format (%8d), or 0 */ + int prec; /* precision from format; <0 for N/A */ + int saved_errno; + char sign; /* sign prefix (' ', '+', '-', or \0) */ + + u_long ulval = 0; /* integer arguments %[diouxX] */ + uintmax_t ujval = 0; /* %j, %ll, %q, %t, %z integers */ + void *ptrval; /* %p */ + int base; /* base for [diouxX] conversion */ + int dprec; /* a copy of prec if [diouxX], 0 otherwise */ + int realsz; /* field size expanded by dprec, sign, etc */ + int size; /* size of converted field or string */ + int prsize; /* max size of printed field */ + const char *xdigs; /* digits for %[xX] conversion */ + struct io_state io; /* I/O buffering state */ + char buf[BUF]; /* buffer with space for digits of uintmax_t */ + char ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */ + union arg *argtable; /* args, built due to positional arg */ + union arg statargtable [STATIC_ARG_TBL_SIZE]; + int nextarg; /* 1-based argument index */ + va_list orgap; /* original argument pointer */ + char *convbuf; /* wide to multibyte conversion result */ + + static const char xdigs_lower[16] = "0123456789abcdef"; + static const char xdigs_upper[16] = "0123456789ABCDEF"; + + /* BEWARE, these `goto error' on error. */ +#define PRINT(ptr, len) { \ + if (io_print(&io, (ptr), (len))) \ + goto error; \ +} +#define PAD(howmany, with) { \ + if (io_pad(&io, (howmany), (with))) \ + goto error; \ +} +#define PRINTANDPAD(p, ep, len, with) { \ + if (io_printandpad(&io, (p), (ep), (len), (with))) \ + goto error; \ +} +#define FLUSH() do { } while (0) + + /* + * Get the argument indexed by nextarg. If the argument table is + * built, use it to get the argument. If its not, get the next + * argument (and arguments must be gotten sequentially). + */ +#define GETARG(type) \ + ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ + (nextarg++, va_arg(ap, type))) + + /* + * To extend shorts properly, we need both signed and unsigned + * argument extraction methods. + */ +#define SARG() \ + (flags&LONGINT ? GETARG(long) : \ + flags&SHORTINT ? (long)(short)GETARG(int) : \ + flags&CHARINT ? (long)(signed char)GETARG(int) : \ + (long)GETARG(int)) +#define UARG() \ + (flags&LONGINT ? GETARG(u_long) : \ + flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ + flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ + (u_long)GETARG(u_int)) +#define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT|LONGDBL) +#define SJARG() \ + (flags&LONGDBL ? GETARG(int64_t) : \ + flags&INTMAXT ? GETARG(intmax_t) : \ + flags&SIZET ? (intmax_t)GETARG(ssize_t) : \ + flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ + (intmax_t)GETARG(long long)) +#define UJARG() \ + (flags&LONGDBL ? GETARG(uint64_t) : \ + flags&INTMAXT ? GETARG(uintmax_t) : \ + flags&SIZET ? (uintmax_t)GETARG(size_t) : \ + flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ + (uintmax_t)GETARG(unsigned long long)) + + /* + * Get * arguments, including the form *nn$. Preserve the nextarg + * that the argument can be gotten once the type is determined. + */ +#define GETASTER(val) \ + n2 = 0; \ + cp = fmt; \ + while (is_digit(*cp)) { \ + n2 = 10 * n2 + to_digit(*cp); \ + cp++; \ + } \ + if (*cp == '$') { \ + int hold = nextarg; \ + if (argtable == NULL) { \ + argtable = statargtable; \ + if (_frr_find_arguments (fmt0, orgap, &argtable)) { \ + ret = EOF; \ + goto error; \ + } \ + } \ + nextarg = n2; \ + val = GETARG (int); \ + nextarg = hold; \ + fmt = ++cp; \ + } else { \ + val = GETARG (int); \ + } + + xdigs = xdigs_lower; + saved_errno = errno; + convbuf = NULL; + fmt = (char *)fmt0; + argtable = NULL; + nextarg = 1; + va_copy(orgap, ap); + io_init(&io, cb); + ret = 0; + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) + /* void */; + if ((n = fmt - cp) != 0) { + if ((unsigned)ret + n > INT_MAX) { + ret = EOF; + errno = EOVERFLOW; + goto error; + } + PRINT(cp, n); + ret += n; + } + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + dprec = 0; + width = 0; + prec = -1; + sign = '\0'; + ox[1] = '\0'; + +rflag: ch = *fmt++; +reswitch: switch (ch) { + case ' ': + /*- + * ``If the space and + flags both appear, the space + * flag will be ignored.'' + * -- ANSI X3J11 + */ + if (!sign) + sign = ' '; + goto rflag; + case '#': + flags |= ALT; + goto rflag; + case '*': + /*- + * ``A negative field width argument is taken as a + * - flag followed by a positive field width.'' + * -- ANSI X3J11 + * They don't exclude field widths read from args. + */ + GETASTER (width); + if (width >= 0) + goto rflag; + width = -width; + /* FALLTHROUGH */ + case '-': + flags |= LADJUST; + goto rflag; + case '+': + sign = '+'; + goto rflag; + case '\'': + flags |= GROUPING; + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + GETASTER (prec); + goto rflag; + } + prec = 0; + while (is_digit(ch)) { + prec = 10 * prec + to_digit(ch); + ch = *fmt++; + } + goto reswitch; + case '0': + /*- + * ``Note that 0 is taken as a flag, not as the + * beginning of a field width.'' + * -- ANSI X3J11 + */ + flags |= ZEROPAD; + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') { + nextarg = n; + if (argtable == NULL) { + argtable = statargtable; + if (_frr_find_arguments (fmt0, orgap, + &argtable)) { + ret = EOF; + goto error; + } + } + goto rflag; + } + width = n; + goto reswitch; + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; + goto rflag; + case 'l': + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else + flags |= LONGINT; + goto rflag; + case 'q': + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; + goto rflag; + case 'C': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'c': +#ifdef WCHAR_SUPPORT + if (flags & LONGINT) { + static const mbstate_t initial; + mbstate_t mbs; + size_t mbseqlen; + + mbs = initial; + mbseqlen = wcrtomb(cp = buf, + (wchar_t)GETARG(wint_t), &mbs); + if (mbseqlen == (size_t)-1) { + goto error; + } + size = (int)mbseqlen; + } else +#endif /* WCHAR_SUPPORT */ + { + buf[0] = GETARG(int); + cp = buf; + size = 1; + } + sign = '\0'; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if (flags & INTMAX_SIZE) + ujval = SJARG(); + else + ulval = SARG(); + + if (printfrr_ext_char(fmt[0])) { + n2 = printfrr_exti(buf, sizeof(buf), fmt, prec, + (flags & INTMAX_SIZE) ? ujval + : (uintmax_t)ulval); + if (n2 > 0) { + fmt += n2; + cp = buf; + size = strlen(cp); + sign = '\0'; + break; + } + } + if (flags & INTMAX_SIZE) { + if ((intmax_t)ujval < 0) { + ujval = -ujval; + sign = '-'; + } + } else { + if ((long)ulval < 0) { + ulval = -ulval; + sign = '-'; + } + } + base = 10; + goto number; +#ifndef NO_FLOATING_POINT + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + if (flags & LONGDBL) { + long double arg = GETARG(long double); + char fmt[6] = "%.*L"; + fmt[4] = ch; + fmt[5] = '\0'; + + snprintf(buf, sizeof(buf), fmt, prec, arg); + } else { + double arg = GETARG(double); + char fmt[5] = "%.*"; + fmt[3] = ch; + fmt[4] = '\0'; + + snprintf(buf, sizeof(buf), fmt, prec, arg); + } + cp = buf; + /* for proper padding */ + if (*cp == '-') { + cp++; + sign = '-'; + } + /* "inf" */ + if (!is_digit(*cp) && *cp != '.') + flags &= ~ZEROPAD; + size = strlen(buf); + break; +#endif + case 'm': + cp = strerror(saved_errno); + size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp); + sign = '\0'; + break; + case 'n': + /* + * Assignment-like behavior is specified if the + * value overflows or is otherwise unrepresentable. + * C99 says to use `signed char' for %hhn conversions. + */ + if (flags & LLONGINT) + *GETARG(long long *) = ret; + else if (flags & SIZET) + *GETARG(ssize_t *) = (ssize_t)ret; + else if (flags & PTRDIFFT) + *GETARG(ptrdiff_t *) = ret; + else if (flags & INTMAXT) + *GETARG(intmax_t *) = ret; + else if (flags & LONGINT) + *GETARG(long *) = ret; + else if (flags & SHORTINT) + *GETARG(short *) = ret; + else if (flags & CHARINT) + *GETARG(signed char *) = ret; + else + *GETARG(int *) = ret; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if (flags & INTMAX_SIZE) + ujval = UJARG(); + else + ulval = UARG(); + base = 8; + goto nosign; + case 'p': + /*- + * ``The argument shall be a pointer to void. The + * value of the pointer is converted to a sequence + * of printable characters, in an implementation- + * defined manner.'' + * -- ANSI X3J11 + */ + ptrval = GETARG(void *); + if (printfrr_ext_char(fmt[0]) && + (n2 = printfrr_extp(buf, sizeof(buf), + fmt, prec, ptrval)) > 0) { + fmt += n2; + cp = buf; + size = strlen(cp); + sign = '\0'; + break; + } + ujval = (uintmax_t)(uintptr_t)ptrval; + base = 16; + xdigs = xdigs_lower; + flags = flags | INTMAXT; + ox[1] = 'x'; + goto nosign; + case 'S': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 's': +#ifdef WCHAR_SUPPORT + if (flags & LONGINT) { + wchar_t *wcp; + + if (convbuf != NULL) + free(convbuf); + if ((wcp = GETARG(wchar_t *)) == NULL) + cp = "(null)"; + else { + convbuf = __wcsconv(wcp, prec); + if (convbuf == NULL) { + goto error; + } + cp = convbuf; + } + } else +#endif + if ((cp = GETARG(char *)) == NULL) + cp = "(null)"; + size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp); + sign = '\0'; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + if (flags & INTMAX_SIZE) + ujval = UJARG(); + else + ulval = UARG(); + base = 10; + goto nosign; + case 'X': + xdigs = xdigs_upper; + goto hex; + case 'x': + xdigs = xdigs_lower; +hex: + if (flags & INTMAX_SIZE) + ujval = UJARG(); + else + ulval = UARG(); + base = 16; + /* leading 0x/X only if non-zero */ + if (flags & ALT && + (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) + ox[1] = ch; + + flags &= ~GROUPING; + /* unsigned conversions */ +nosign: sign = '\0'; + /*- + * ``... diouXx conversions ... if a precision is + * specified, the 0 flag will be ignored.'' + * -- ANSI X3J11 + */ +number: if ((dprec = prec) >= 0) + flags &= ~ZEROPAD; + + /*- + * ``The result of converting a zero value with an + * explicit precision of zero is no characters.'' + * -- ANSI X3J11 + * + * ``The C Standard is clear enough as is. The call + * printf("%#.0o", 0) should print 0.'' + * -- Defect Report #151 + */ + cp = buf + BUF; + if (flags & INTMAX_SIZE) { + if (ujval != 0 || prec != 0 || + (flags & ALT && base == 8)) + cp = __ujtoa(ujval, buf + BUF, base, + flags & ALT, xdigs); + } else { + if (ulval != 0 || prec != 0 || + (flags & ALT && base == 8)) + cp = __ultoa(ulval, buf + BUF, base, + flags & ALT, xdigs); + } + size = buf + BUF - cp; + if (size > BUF) /* should never happen */ + abort(); + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + /* pretend it was %c with argument ch */ + buf[0] = ch; + cp = buf; + size = 1; + sign = '\0'; + break; + } + + /* + * All reasonable formats wind up here. At this point, `cp' + * points to a string which (if not flags&LADJUST) should be + * padded out to `width' places. If flags&ZEROPAD, it should + * first be prefixed by any sign or other prefix; otherwise, + * it should be blank padded before the prefix is emitted. + * After any left-hand padding and prefixing, emit zeroes + * required by a decimal [diouxX] precision, then print the + * string proper, then emit zeroes required by any leftover + * floating precision; finally, if LADJUST, pad with blanks. + * + * Compute actual size, so we know how much to pad. + * size excludes decimal prec; realsz includes it. + */ + realsz = dprec > size ? dprec : size; + if (sign) + realsz++; + if (ox[1]) + realsz += 2; + + prsize = width > realsz ? width : realsz; + if ((unsigned)ret + prsize > INT_MAX) { + ret = EOF; + errno = EOVERFLOW; + goto error; + } + + /* right-adjusting blank padding */ + if ((flags & (LADJUST|ZEROPAD)) == 0) + PAD(width - realsz, blanks); + + /* prefix */ + if (sign) + PRINT(&sign, 1); + + if (ox[1]) { /* ox[1] is either x, X, or \0 */ + ox[0] = '0'; + PRINT(ox, 2); + } + + /* right-adjusting zero padding */ + if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) + PAD(width - realsz, zeroes); + + /* the string or number proper */ + /* leading zeroes from decimal precision */ + PAD(dprec - size, zeroes); + PRINT(cp, size); + /* left-adjusting padding (always blank) */ + if (flags & LADJUST) + PAD(width - realsz, blanks); + + /* finally, adjust ret */ + ret += prsize; + + FLUSH(); /* copy out the I/O vectors */ + } +done: + FLUSH(); +error: + va_end(orgap); + if (convbuf != NULL) + free(convbuf); + if ((argtable != NULL) && (argtable != statargtable)) + free (argtable); + return (ret); + /* NOTREACHED */ +} + diff --git a/lib/printfrr.h b/lib/printfrr.h new file mode 100644 index 000000000000..95dace702108 --- /dev/null +++ b/lib/printfrr.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +#ifndef _FRR_PRINTFRR_H +#define _FRR_PRINTFRR_H + +#include +#include +#include + +#include "compiler.h" +#include "memory.h" + +struct fbuf { + char *buf; + char *pos; + size_t len; +}; + +#define at(a, b) \ + __attribute__((format(printf, a, b))) +#define atn(a, b) \ + at(a, b) __attribute__((nonnull(1) _RET_NONNULL)) +#define atm(a, b) \ + atn(a, b) __attribute__((malloc)) + +/* return value is length needed for the full string (excluding \0) in all + * cases. The functions write as much as they can, but continue regardless, + * so the return value is independent of buffer length. Both bprintfrr and + * snprintf also accept NULL as output buffer. + */ + +/* bprintfrr does NOT null terminate! use sparingly (only provided since it's + * the most direct interface) - useful for incrementally building long text + * (call bprintfrr repeatedly with the same buffer) + */ +ssize_t vbprintfrr(struct fbuf *out, const char *fmt, va_list) at(2, 0); +ssize_t bprintfrr(struct fbuf *out, const char *fmt, ...) at(2, 3); + +/* these do null terminate like their snprintf cousins */ +ssize_t vsnprintfrr(char *out, size_t sz, const char *fmt, va_list) at(3, 0); +ssize_t snprintfrr(char *out, size_t sz, const char *fmt, ...) at(3, 4); + +/* c = continue / concatenate (append at the end of the string) + * return value is would-be string length (regardless of buffer length), + * i.e. includes already written chars */ +ssize_t vcsnprintfrr(char *out, size_t sz, const char *fmt, va_list) at(3, 0); +ssize_t csnprintfrr(char *out, size_t sz, const char *fmt, ...) at(3, 4); + +/* memory allocations don't fail in FRR, so you always get something here. + * (in case of error, returns a strdup of the format string) */ +char *vasprintfrr(struct memtype *mt, const char *fmt, va_list) atm(2, 0); +char *asprintfrr(struct memtype *mt, const char *fmt, ...) atm(2, 3); + +/* try to use provided buffer (presumably from stack), allocate if it's too + * short. Must call XFREE(mt, return value) if return value != out. + */ +char *vasnprintfrr(struct memtype *mt, char *out, size_t sz, + const char *fmt, va_list) atn(4, 0); +char *asnprintfrr(struct memtype *mt, char *out, size_t sz, + const char *fmt, ...) atn(4, 5); + +#undef at +#undef atm + +/* extension specs must start with a capital letter (this is a restriction + * for both performance's and human understanding's sake.) + * + * Note that the entire thing mostly works because a letter directly following + * a %p print specifier is extremely unlikely to occur (why would you want to + * print "0x12345678HELLO"?) Normally, you'd expect spacing or punctuation + * after a placeholder. That also means that neither of those works well for + * extension purposes, e.g. "%p{foo}" is reasonable to see actually used. + * + * TODO: would be nice to support a "%pF%dF" specifier that consumes 2 + * arguments, e.g. to pass an integer + a list of known values... can be + * done, but a bit tricky. + */ +#define printfrr_ext_char(ch) ((ch) >= 'A' && (ch) <= 'Z') + +struct printfrr_ext { + /* embedded string to minimize cache line pollution */ + char match[8]; + + /* both can be given, if not the code continues searching + * (you can do %pX and %dX in 2 different entries) + * + * return value: number of bytes consumed from the format string, so + * you can consume extra flags (e.g. register for "%pX", consume + * "%pXfoo" or "%pXbar" for flags.) Convention is to make those flags + * lowercase letters or numbers. + * + * bsz is a compile-time constant in printf; it's gonna be relatively + * small. This isn't designed to print Shakespeare from a pointer. + * + * prec is the precision specifier (the 999 in "%.999p") -1 means + * none given (value in the format string cannot be negative) + */ + ssize_t (*print_ptr)(char *buf, size_t bsz, const char *fmt, int prec, + const void *); + ssize_t (*print_int)(char *buf, size_t bsz, const char *fmt, int prec, + uintmax_t); +}; + +/* no locking - must be called when single threaded (e.g. at startup.) + * this restriction hopefully won't be a huge bother considering normal usage + * scenarios... + */ +void printfrr_ext_reg(const struct printfrr_ext *); + +#define printfrr_ext_autoreg_p(matchs, print_fn) \ + static ssize_t print_fn(char *, size_t, const char *, int, \ + const void *); \ + static struct printfrr_ext _printext_##print_fn = { \ + .match = matchs, \ + .print_ptr = print_fn, \ + }; \ + static void _printreg_##print_fn(void) __attribute__((constructor)); \ + static void _printreg_##print_fn(void) { \ + printfrr_ext_reg(&_printext_##print_fn); \ + } \ + /* end */ + +#define printfrr_ext_autoreg_i(matchs, print_fn) \ + static ssize_t print_fn(char *, size_t, const char *, int, uintmax_t); \ + static struct printfrr_ext _printext_##print_fn = { \ + .match = matchs, \ + .print_int = print_fn, \ + }; \ + static void _printreg_##print_fn(void) __attribute__((constructor)); \ + static void _printreg_##print_fn(void) { \ + printfrr_ext_reg(&_printext_##print_fn); \ + } \ + /* end */ + +#endif diff --git a/lib/privs.c b/lib/privs.c index a19707b1c928..a3314c6c3ca9 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -917,7 +917,7 @@ void zprivs_init(struct zebra_privs_t *zprivs) zprivs->user, zprivs->vty_group); exit(1); } - if (i >= ngroups && ngroups < (int)ZEBRA_NUM_OF(groups)) { + if (i >= ngroups && ngroups < (int)array_size(groups)) { groups[i] = zprivs_state.vtygrp; } } diff --git a/lib/qobj.c b/lib/qobj.c index 811645f3c33e..3e3860a96a9f 100644 --- a/lib/qobj.c +++ b/lib/qobj.c @@ -27,21 +27,27 @@ #include "qobj.h" #include "jhash.h" -static pthread_rwlock_t nodes_lock; -static struct hash *nodes = NULL; - -static unsigned int qobj_key(void *data) +static uint32_t qobj_hash(const struct qobj_node *node) { - struct qobj_node *node = data; - return (unsigned int)node->nid; + return (uint32_t)node->nid; } -static bool qobj_cmp(const void *a, const void *b) +static int qobj_cmp(const struct qobj_node *na, const struct qobj_node *nb) { - const struct qobj_node *na = a, *nb = b; - return na->nid == nb->nid; + if (na->nid < nb->nid) + return -1; + if (na->nid > nb->nid) + return 1; + return 0; } +DECLARE_HASH(qobj_nodes, struct qobj_node, nodehash, + qobj_cmp, qobj_hash) + +static pthread_rwlock_t nodes_lock; +static struct qobj_nodes_head nodes = { }; + + void qobj_reg(struct qobj_node *node, struct qobj_nodetype *type) { node->type = type; @@ -49,15 +55,15 @@ void qobj_reg(struct qobj_node *node, struct qobj_nodetype *type) do { node->nid = (uint64_t)random(); node->nid ^= (uint64_t)random() << 32; - } while (!node->nid - || hash_get(nodes, node, hash_alloc_intern) != node); + } while (!node->nid || qobj_nodes_find(&nodes, node)); + qobj_nodes_add(&nodes, node); pthread_rwlock_unlock(&nodes_lock); } void qobj_unreg(struct qobj_node *node) { pthread_rwlock_wrlock(&nodes_lock); - hash_release(nodes, node); + qobj_nodes_del(&nodes, node); pthread_rwlock_unlock(&nodes_lock); } @@ -65,7 +71,7 @@ struct qobj_node *qobj_get(uint64_t id) { struct qobj_node dummy = {.nid = id}, *rv; pthread_rwlock_rdlock(&nodes_lock); - rv = hash_lookup(nodes, &dummy); + rv = qobj_nodes_find(&nodes, &dummy); pthread_rwlock_unlock(&nodes_lock); return rv; } @@ -77,7 +83,7 @@ void *qobj_get_typed(uint64_t id, struct qobj_nodetype *type) void *rv; pthread_rwlock_rdlock(&nodes_lock); - node = hash_lookup(nodes, &dummy); + node = qobj_nodes_find(&nodes, &dummy); /* note: we explicitly hold the lock until after we have checked the * type. @@ -96,16 +102,14 @@ void *qobj_get_typed(uint64_t id, struct qobj_nodetype *type) void qobj_init(void) { - if (!nodes) { - pthread_rwlock_init(&nodes_lock, NULL); - nodes = hash_create_size(16, qobj_key, qobj_cmp, "QOBJ Hash"); - } + pthread_rwlock_init(&nodes_lock, NULL); + qobj_nodes_init(&nodes); } void qobj_finish(void) { - hash_clean(nodes, NULL); - hash_free(nodes); - nodes = NULL; + struct qobj_node *node; + while ((node = qobj_nodes_pop(&nodes))) + qobj_nodes_del(&nodes, node); pthread_rwlock_destroy(&nodes_lock); } diff --git a/lib/qobj.h b/lib/qobj.h index d63988cbab0b..415eae02efac 100644 --- a/lib/qobj.h +++ b/lib/qobj.h @@ -21,6 +21,8 @@ #include #include +#include "typesafe.h" + #ifdef __cplusplus extern "C" { #endif @@ -69,6 +71,8 @@ struct qobj_nodetype_capnp { }; #endif +#include "typesafe.h" + /* each different kind of object will have a global variable of this type, * which can be used by various other pieces to store type-related bits. * type equality can be tested as pointer equality. (cf. QOBJ_GET_TYPESAFE) @@ -79,9 +83,12 @@ struct qobj_nodetype { RESERVED_SPACE_STRUCT(qobj_nodetype_capnp, capnp, 256) }; +PREDECL_HASH(qobj_nodes) + /* anchor to be embedded somewhere in the object's struct */ struct qobj_node { uint64_t nid; + struct qobj_nodes_item nodehash; struct qobj_nodetype *type; }; diff --git a/lib/route_types.txt b/lib/route_types.txt index c5eff44ca7f0..59f3a91cf0f5 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -83,6 +83,7 @@ ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, 1, "SHARP" ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, 0, "PBR" ZEBRA_ROUTE_BFD, bfd, bfdd, '-', 0, 0, 0, "BFD" ZEBRA_ROUTE_OPENFABRIC, openfabric, fabricd, 'f', 1, 1, 1, "OpenFabric" +ZEBRA_ROUTE_VRRP, vrrp, vrrpd, '-', 0, 0, 0, "VRRP" ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-" @@ -110,4 +111,5 @@ ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)" ZEBRA_ROUTE_PBR, "Policy Based Routing (PBR)" ZEBRA_ROUTE_BFD, "Bidirectional Fowarding Detection (BFD)" +ZEBRA_ROUTE_VRRP, "Virtual Router Redundancy Protocol (VRRP)" ZEBRA_ROUTE_OPENFABRIC, "OpenFabric Routing Protocol" diff --git a/lib/routemap.c b/lib/routemap.c index 4898a8d0fa77..9336154b1af5 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -39,6 +39,7 @@ DEFINE_MTYPE(LIB, ROUTE_MAP_RULE, "Route map rule") DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_RULE_STR, "Route map rule str") DEFINE_MTYPE(LIB, ROUTE_MAP_COMPILED, "Route map compiled") DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP, "Route map dependency") +DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP_DATA, "Route map dependency data") DEFINE_QOBJ_TYPE(route_map_index) DEFINE_QOBJ_TYPE(route_map) @@ -475,7 +476,7 @@ int generic_match_add(struct vty *vty, struct route_map_index *index, { int ret; - ret = route_map_add_match(index, command, arg); + ret = route_map_add_match(index, command, arg, type); switch (ret) { case RMAP_COMPILE_SUCCESS: if (type != RMAP_EVENT_MATCH_ADDED) { @@ -616,14 +617,14 @@ struct route_map_list { void (*add_hook)(const char *); void (*delete_hook)(const char *); - void (*event_hook)(route_map_event_t, const char *); + void (*event_hook)(const char *); }; /* Master list of route map. */ static struct route_map_list route_map_master = {NULL, NULL, NULL, NULL, NULL}; struct hash *route_map_master_hash = NULL; -static unsigned int route_map_hash_key_make(void *p) +static unsigned int route_map_hash_key_make(const void *p) { const struct route_map *map = p; return string_hash_make(map->name); @@ -670,10 +671,20 @@ struct route_map_dep { struct hash *this_hash; /* ptr to the hash structure this is part of */ }; +struct route_map_dep_data { + /* Route-map name. + */ + char *rname; + /* Count of number of sequences of this + * route-map that depend on the same entity. + */ + uint16_t refcnt; +}; + /* Hashes maintaining dependency between various sublists used by route maps */ struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX]; -static unsigned int route_map_dep_hash_make_key(void *p); +static unsigned int route_map_dep_hash_make_key(const void *p); static void route_map_clear_all_references(char *rmap_name); static void route_map_rule_delete(struct route_map_rule_list *, struct route_map_rule *); @@ -902,10 +913,12 @@ static const char *route_map_type_str(enum route_map_type type) case RMAP_DENY: return "deny"; break; - default: + case RMAP_ANY: return ""; break; } + + return ""; } static int route_map_empty(struct route_map *map) @@ -1077,8 +1090,7 @@ static void route_map_index_delete(struct route_map_index *index, int notify) /* Execute event hook. */ if (route_map_master.event_hook && notify) { - (*route_map_master.event_hook)(RMAP_EVENT_INDEX_DELETED, - index->map->name); + (*route_map_master.event_hook)(index->map->name); route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); } @@ -1137,8 +1149,7 @@ route_map_index_add(struct route_map *map, enum route_map_type type, int pref) /* Execute event hook. */ if (route_map_master.event_hook) { - (*route_map_master.event_hook)(RMAP_EVENT_INDEX_ADDED, - map->name); + (*route_map_master.event_hook)(map->name); route_map_notify_dependencies(map->name, RMAP_EVENT_CALL_ADDED); } return index; @@ -1281,15 +1292,65 @@ const char *route_map_get_match_arg(struct route_map_index *index, return (NULL); } +static route_map_event_t get_route_map_delete_event(route_map_event_t type) +{ + switch (type) { + case RMAP_EVENT_CALL_ADDED: + return RMAP_EVENT_CALL_DELETED; + case RMAP_EVENT_PLIST_ADDED: + return RMAP_EVENT_PLIST_DELETED; + case RMAP_EVENT_CLIST_ADDED: + return RMAP_EVENT_CLIST_DELETED; + case RMAP_EVENT_ECLIST_ADDED: + return RMAP_EVENT_ECLIST_DELETED; + case RMAP_EVENT_LLIST_ADDED: + return RMAP_EVENT_LLIST_DELETED; + case RMAP_EVENT_ASLIST_ADDED: + return RMAP_EVENT_ASLIST_DELETED; + case RMAP_EVENT_FILTER_ADDED: + return RMAP_EVENT_FILTER_DELETED; + case RMAP_EVENT_SET_ADDED: + case RMAP_EVENT_SET_DELETED: + case RMAP_EVENT_SET_REPLACED: + case RMAP_EVENT_MATCH_ADDED: + case RMAP_EVENT_MATCH_DELETED: + case RMAP_EVENT_MATCH_REPLACED: + case RMAP_EVENT_INDEX_ADDED: + case RMAP_EVENT_INDEX_DELETED: + case RMAP_EVENT_CALL_DELETED: + case RMAP_EVENT_PLIST_DELETED: + case RMAP_EVENT_CLIST_DELETED: + case RMAP_EVENT_ECLIST_DELETED: + case RMAP_EVENT_LLIST_DELETED: + case RMAP_EVENT_ASLIST_DELETED: + case RMAP_EVENT_FILTER_DELETED: + /* This function returns the appropriate 'deleted' event type + * for every 'added' event type passed to this function. + * This is done only for named entities used in the + * route-map match commands. + * This function is not to be invoked for any of the other event + * types. + */ + assert(0); + } + + assert(0); + /* + * Return to make c happy but if we get here something has gone + * terribly terribly wrong, so yes this return makes no sense. + */ + return RMAP_EVENT_CALL_ADDED; +} + /* Add match statement to route map. */ int route_map_add_match(struct route_map_index *index, const char *match_name, - const char *match_arg) + const char *match_arg, route_map_event_t type) { struct route_map_rule *rule; struct route_map_rule *next; struct route_map_rule_cmd *cmd; void *compile; - int replaced = 0; + int8_t delete_rmap_event_type = 0; /* First lookup rule for add match statement. */ cmd = route_map_lookup_match(match_name); @@ -1308,8 +1369,30 @@ int route_map_add_match(struct route_map_index *index, const char *match_name, for (rule = index->match_list.head; rule; rule = next) { next = rule->next; if (rule->cmd == cmd) { + /* If the configured route-map match rule is exactly + * the same as the existing configuration then, + * ignore the duplicate configuration. + */ + if (strcmp(match_arg, rule->rule_str) == 0) { + if (cmd->func_free) + (*cmd->func_free)(compile); + + return RMAP_DUPLICATE_RULE; + } + + /* Remove the dependency of the route-map on the rule + * that is being replaced. + */ + if (type >= RMAP_EVENT_CALL_ADDED) { + delete_rmap_event_type = + get_route_map_delete_event(type); + route_map_upd8_dependency( + delete_rmap_event_type, + rule->rule_str, + index->map->name); + } + route_map_rule_delete(&index->match_list, rule); - replaced = 1; } } @@ -1327,10 +1410,7 @@ int route_map_add_match(struct route_map_index *index, const char *match_name, /* Execute event hook. */ if (route_map_master.event_hook) { - (*route_map_master.event_hook)( - replaced ? RMAP_EVENT_MATCH_REPLACED - : RMAP_EVENT_MATCH_ADDED, - index->map->name); + (*route_map_master.event_hook)(index->map->name); route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); } @@ -1355,9 +1435,7 @@ int route_map_delete_match(struct route_map_index *index, route_map_rule_delete(&index->match_list, rule); /* Execute event hook. */ if (route_map_master.event_hook) { - (*route_map_master.event_hook)( - RMAP_EVENT_MATCH_DELETED, - index->map->name); + (*route_map_master.event_hook)(index->map->name); route_map_notify_dependencies( index->map->name, RMAP_EVENT_CALL_ADDED); @@ -1376,7 +1454,6 @@ int route_map_add_set(struct route_map_index *index, const char *set_name, struct route_map_rule *next; struct route_map_rule_cmd *cmd; void *compile; - int replaced = 0; cmd = route_map_lookup_set(set_name); if (cmd == NULL) @@ -1395,10 +1472,8 @@ int route_map_add_set(struct route_map_index *index, const char *set_name, route_map_index. */ for (rule = index->set_list.head; rule; rule = next) { next = rule->next; - if (rule->cmd == cmd) { + if (rule->cmd == cmd) route_map_rule_delete(&index->set_list, rule); - replaced = 1; - } } /* Add new route map match rule. */ @@ -1415,10 +1490,7 @@ int route_map_add_set(struct route_map_index *index, const char *set_name, /* Execute event hook. */ if (route_map_master.event_hook) { - (*route_map_master.event_hook)(replaced - ? RMAP_EVENT_SET_REPLACED - : RMAP_EVENT_SET_ADDED, - index->map->name); + (*route_map_master.event_hook)(index->map->name); route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); } @@ -1442,9 +1514,7 @@ int route_map_delete_set(struct route_map_index *index, const char *set_name, route_map_rule_delete(&index->set_list, rule); /* Execute event hook. */ if (route_map_master.event_hook) { - (*route_map_master.event_hook)( - RMAP_EVENT_SET_DELETED, - index->map->name); + (*route_map_master.event_hook)(index->map->name); route_map_notify_dependencies( index->map->name, RMAP_EVENT_CALL_ADDED); @@ -1641,7 +1711,7 @@ void route_map_delete_hook(void (*func)(const char *)) route_map_master.delete_hook = func; } -void route_map_event_hook(void (*func)(route_map_event_t, const char *)) +void route_map_event_hook(void (*func)(const char *name)) { route_map_master.event_hook = func; } @@ -1649,7 +1719,9 @@ void route_map_event_hook(void (*func)(route_map_event_t, const char *)) /* Routines for route map dependency lists and dependency processing */ static bool route_map_rmap_hash_cmp(const void *p1, const void *p2) { - return (strcmp((const char *)p1, (const char *)p2) == 0); + return strcmp(((const struct route_map_dep_data *)p1)->rname, + ((const struct route_map_dep_data *)p2)->rname) + == 0; } static bool route_map_dep_hash_cmp(const void *p1, const void *p2) @@ -1662,14 +1734,17 @@ static bool route_map_dep_hash_cmp(const void *p1, const void *p2) static void route_map_clear_reference(struct hash_bucket *bucket, void *arg) { - struct route_map_dep *dep = (struct route_map_dep *)bucket->data; - char *rmap_name; + struct route_map_dep *dep = bucket->data; + struct route_map_dep_data *dep_data = NULL, tmp_dep_data; if (arg) { - rmap_name = - (char *)hash_release(dep->dep_rmap_hash, (void *)arg); - if (rmap_name) { - XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name); + memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data)); + tmp_dep_data.rname = arg; + dep_data = hash_release(dep->dep_rmap_hash, + &tmp_dep_data); + if (dep_data) { + XFREE(MTYPE_ROUTE_MAP_NAME, dep_data->rname); + XFREE(MTYPE_ROUTE_MAP_DEP_DATA, dep_data); } if (!dep->dep_rmap_hash->count) { dep = hash_release(dep->this_hash, @@ -1691,6 +1766,13 @@ static void route_map_clear_all_references(char *rmap_name) } } +static unsigned int route_map_dep_data_hash_make_key(const void *p) +{ + const struct route_map_dep_data *dep_data = p; + + return string_hash_make(dep_data->rname); +} + static void *route_map_dep_hash_alloc(void *p) { char *dep_name = (char *)p; @@ -1699,27 +1781,34 @@ static void *route_map_dep_hash_alloc(void *p) dep_entry = XCALLOC(MTYPE_ROUTE_MAP_DEP, sizeof(struct route_map_dep)); dep_entry->dep_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name); dep_entry->dep_rmap_hash = - hash_create_size(8, route_map_dep_hash_make_key, + hash_create_size(8, route_map_dep_data_hash_make_key, route_map_rmap_hash_cmp, "Route Map Dep Hash"); dep_entry->this_hash = NULL; - return ((void *)dep_entry); + return dep_entry; } static void *route_map_name_hash_alloc(void *p) { - return ((void *)XSTRDUP(MTYPE_ROUTE_MAP_NAME, (const char *)p)); + struct route_map_dep_data *dep_data = NULL, *tmp_dep_data = NULL; + + dep_data = XCALLOC(MTYPE_ROUTE_MAP_DEP_DATA, + sizeof(struct route_map_dep_data)); + tmp_dep_data = p; + dep_data->rname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, tmp_dep_data->rname); + return dep_data; } -static unsigned int route_map_dep_hash_make_key(void *p) +static unsigned int route_map_dep_hash_make_key(const void *p) { return (string_hash_make((char *)p)); } static void route_map_print_dependency(struct hash_bucket *bucket, void *data) { - char *rmap_name = (char *)bucket->data; - char *dep_name = (char *)data; + struct route_map_dep_data *dep_data = bucket->data; + char *rmap_name = dep_data->rname; + char *dep_name = data; zlog_debug("%s: Dependency for %s: %s", __FUNCTION__, dep_name, rmap_name); @@ -1729,9 +1818,10 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, const char *rmap_name, route_map_event_t type) { struct route_map_dep *dep = NULL; - char *ret_map_name; char *dname, *rname; int ret = 0; + struct route_map_dep_data *dep_data = NULL, *ret_dep_data = NULL; + struct route_map_dep_data tmp_dep_data; dname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name); rname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_name); @@ -1757,7 +1847,14 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, if (!dep->this_hash) dep->this_hash = dephash; - hash_get(dep->dep_rmap_hash, rname, route_map_name_hash_alloc); + memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data)); + tmp_dep_data.rname = rname; + dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data); + if (!dep_data) + dep_data = hash_get(dep->dep_rmap_hash, &tmp_dep_data, + route_map_name_hash_alloc); + + dep_data->refcnt++; break; case RMAP_EVENT_PLIST_DELETED: case RMAP_EVENT_CLIST_DELETED: @@ -1774,8 +1871,20 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, goto out; } - ret_map_name = (char *)hash_release(dep->dep_rmap_hash, rname); - XFREE(MTYPE_ROUTE_MAP_NAME, ret_map_name); + memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data)); + tmp_dep_data.rname = rname; + dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data); + dep_data->refcnt--; + + if (!dep_data->refcnt) { + ret_dep_data = hash_release(dep->dep_rmap_hash, + &tmp_dep_data); + if (ret_dep_data) { + XFREE(MTYPE_ROUTE_MAP_NAME, + ret_dep_data->rname); + XFREE(MTYPE_ROUTE_MAP_DEP_DATA, ret_dep_data); + } + } if (!dep->dep_rmap_hash->count) { dep = hash_release(dephash, dname); @@ -1785,7 +1894,14 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, dep = NULL; } break; - default: + case RMAP_EVENT_SET_ADDED: + case RMAP_EVENT_SET_DELETED: + case RMAP_EVENT_SET_REPLACED: + case RMAP_EVENT_MATCH_ADDED: + case RMAP_EVENT_MATCH_DELETED: + case RMAP_EVENT_MATCH_REPLACED: + case RMAP_EVENT_INDEX_ADDED: + case RMAP_EVENT_INDEX_DELETED: break; } @@ -1828,13 +1944,26 @@ static struct hash *route_map_get_dep_hash(route_map_event_t event) break; case RMAP_EVENT_CALL_ADDED: case RMAP_EVENT_CALL_DELETED: + case RMAP_EVENT_MATCH_ADDED: + case RMAP_EVENT_MATCH_DELETED: upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_RMAP]; break; case RMAP_EVENT_FILTER_ADDED: case RMAP_EVENT_FILTER_DELETED: upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_FILTER]; break; - default: + /* + * Should we actually be ignoring these? + * I am not sure but at this point in time, let + * us get them into this switch and we can peel + * them into the appropriate place in the future + */ + case RMAP_EVENT_SET_ADDED: + case RMAP_EVENT_SET_DELETED: + case RMAP_EVENT_SET_REPLACED: + case RMAP_EVENT_MATCH_REPLACED: + case RMAP_EVENT_INDEX_ADDED: + case RMAP_EVENT_INDEX_DELETED: upd8_hash = NULL; break; } @@ -1843,14 +1972,17 @@ static struct hash *route_map_get_dep_hash(route_map_event_t event) static void route_map_process_dependency(struct hash_bucket *bucket, void *data) { - char *rmap_name = (char *)bucket->data; - route_map_event_t type = (route_map_event_t)(ptrdiff_t)data; + struct route_map_dep_data *dep_data = NULL; + char *rmap_name = NULL; + + dep_data = bucket->data; + rmap_name = dep_data->rname; if (rmap_debug) zlog_debug("%s: Notifying %s of dependency", __FUNCTION__, rmap_name); if (route_map_master.event_hook) - (*route_map_master.event_hook)(type, rmap_name); + (*route_map_master.event_hook)(rmap_name); } void route_map_upd8_dependency(route_map_event_t type, const char *arg, @@ -2803,6 +2935,13 @@ DEFUN (rmap_call, assert(index); + /* If "call" is invoked with the same route-map name as + * the one previously configured then, ignore the duplicate + * configuration. + */ + if (index->nextrm && (strcmp(index->nextrm, rmap) == 0)) + return CMD_SUCCESS; + if (index->nextrm) { route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED, index->nextrm, index->map->name); diff --git a/lib/routemap.h b/lib/routemap.h index e43e74a633dd..3781d227df8a 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -104,13 +104,18 @@ struct route_map_rule_cmd { }; /* Route map apply error. */ -enum { RMAP_COMPILE_SUCCESS, +enum { + RMAP_COMPILE_SUCCESS, - /* Route map rule is missing. */ - RMAP_RULE_MISSING, + /* Route map rule is missing. */ + RMAP_RULE_MISSING, - /* Route map rule can't compile */ - RMAP_COMPILE_ERROR }; + /* Route map rule can't compile */ + RMAP_COMPILE_ERROR, + + /* Route map rule is duplicate */ + RMAP_DUPLICATE_RULE +}; /* Route map rule list. */ struct route_map_rule_list { @@ -192,7 +197,8 @@ extern void route_map_finish(void); /* Add match statement to route map. */ extern int route_map_add_match(struct route_map_index *index, - const char *match_name, const char *match_arg); + const char *match_name, const char *match_arg, + route_map_event_t type); /* Delete specified route match rule. */ extern int route_map_delete_match(struct route_map_index *index, @@ -238,7 +244,15 @@ extern route_map_result_t route_map_apply(struct route_map *map, extern void route_map_add_hook(void (*func)(const char *)); extern void route_map_delete_hook(void (*func)(const char *)); -extern void route_map_event_hook(void (*func)(route_map_event_t, const char *)); + +/* + * This is the callback for when something has changed about a + * route-map. The interested parties can register to receive + * this data. + * + * name - Is the name of the changed route-map + */ +extern void route_map_event_hook(void (*func)(const char *name)); extern int route_map_mark_updated(const char *name); extern void route_map_walk_update_list(void (*update_fn)(char *name)); extern void route_map_upd8_dependency(route_map_event_t type, const char *arg, diff --git a/lib/seqlock.c b/lib/seqlock.c new file mode 100644 index 000000000000..223d14952ceb --- /dev/null +++ b/lib/seqlock.c @@ -0,0 +1,167 @@ +/* + * "Sequence" lock primitive + * + * Copyright (C) 2015 David Lamparter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "seqlock.h" + +#ifdef HAVE_SYNC_LINUX_FUTEX +/* Linux-specific - sys_futex() */ +#include +#include + +static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout, + void *addr2, int val3) +{ + return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3); +} + +#define wait_once(sqlo, val) \ + sys_futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0) +#define wait_poke(sqlo) \ + sys_futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0) + +#elif defined(HAVE_SYNC_OPENBSD_FUTEX) +/* OpenBSD variant of the above. untested, not upstream in OpenBSD. */ +#include +#include + +#define wait_once(sqlo, val) \ + futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0) +#define wait_poke(sqlo) \ + futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0) + +#elif defined(HAVE_SYNC_UMTX_OP) +/* FreeBSD-specific: umtx_op() */ +#include + +#define wait_once(sqlo, val) \ + _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, NULL, NULL) +#define wait_poke(sqlo) \ + _umtx_op((void *)&sqlo->pos, UMTX_OP_WAKE, INT_MAX, NULL, NULL) + +#else +/* generic version. used on *BSD, Solaris and OSX. + */ + +#define wait_init(sqlo) do { \ + pthread_mutex_init(&sqlo->lock, NULL); \ + pthread_cond_init(&sqlo->wake, NULL); \ + } while (0) +#define wait_prep(sqlo) pthread_mutex_lock(&sqlo->lock) +#define wait_once(sqlo, val) pthread_cond_wait(&sqlo->wake, &sqlo->lock) +#define wait_done(sqlo) pthread_mutex_unlock(&sqlo->lock) +#define wait_poke(sqlo) do { \ + pthread_mutex_lock(&sqlo->lock); \ + pthread_cond_broadcast(&sqlo->wake); \ + pthread_mutex_unlock(&sqlo->lock); \ + } while (0) + +#endif + +#ifndef wait_init +#define wait_init(sqlo) /**/ +#define wait_prep(sqlo) /**/ +#define wait_done(sqlo) /**/ +#endif /* wait_init */ + + +void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val) +{ + seqlock_val_t cur, cal; + + seqlock_assert_valid(val); + + wait_prep(sqlo); + while (1) { + cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire); + if (!(cur & 1)) + break; + cal = cur - val - 1; + assert(cal < 0x40000000 || cal > 0xc0000000); + if (cal < 0x80000000) + break; + + wait_once(sqlo, cur); + } + wait_done(sqlo); +} + +bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val) +{ + seqlock_val_t cur; + + seqlock_assert_valid(val); + + cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire); + if (!(cur & 1)) + return 1; + cur -= val; + assert(cur < 0x40000000 || cur > 0xc0000000); + return cur < 0x80000000; +} + +void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val) +{ + seqlock_assert_valid(val); + + atomic_store_explicit(&sqlo->pos, val, memory_order_release); + wait_poke(sqlo); +} + +void seqlock_release(struct seqlock *sqlo) +{ + atomic_store_explicit(&sqlo->pos, 0, memory_order_release); + wait_poke(sqlo); +} + +void seqlock_init(struct seqlock *sqlo) +{ + sqlo->pos = 0; + wait_init(sqlo); +} + + +seqlock_val_t seqlock_cur(struct seqlock *sqlo) +{ + return atomic_load_explicit(&sqlo->pos, memory_order_acquire); +} + +seqlock_val_t seqlock_bump(struct seqlock *sqlo) +{ + seqlock_val_t val; + + val = atomic_fetch_add_explicit(&sqlo->pos, 2, memory_order_release); + wait_poke(sqlo); + return val; +} diff --git a/lib/seqlock.h b/lib/seqlock.h new file mode 100644 index 000000000000..eef05a4307ec --- /dev/null +++ b/lib/seqlock.h @@ -0,0 +1,106 @@ +/* + * "Sequence" lock primitive + * + * Copyright (C) 2015 David Lamparter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef _SEQLOCK_H +#define _SEQLOCK_H + +#include +#include +#include +#include "frratomic.h" + +/* + * this locking primitive is intended to use in a 1:N setup. + * + * - one "counter" seqlock issuing increasing numbers + * - multiple seqlock users hold references on these numbers + * + * this is intended for implementing RCU reference-holding. There is one + * global counter, with threads locking a seqlock whenever they take a + * reference. A seqlock can also be idle/unlocked. + * + * The "counter" seqlock will always stay locked; the RCU cleanup thread + * continuously counts it up, waiting for threads to release or progress to a + * sequence number further ahead. If all threads are > N, references dropped + * in N can be free'd. + * + * generally, the lock function is: + * + * Thread-A Thread-B + * + * seqlock_acquire(a) + * | running seqlock_wait(b) -- a <= b + * seqlock_release() | blocked + * OR: seqlock_acquire(a') | -- a' > b + * (resumes) + */ + +/* use sequentially increasing "ticket numbers". lowest bit will always + * be 1 to have a 'cleared' indication (i.e., counts 1,3,5,7,etc. ) + */ +typedef _Atomic uint32_t seqlock_ctr_t; +typedef uint32_t seqlock_val_t; +#define seqlock_assert_valid(val) assert(val & 1) + + +struct seqlock { +/* always used */ + seqlock_ctr_t pos; +/* used when futexes not available: (i.e. non-linux) */ + pthread_mutex_t lock; + pthread_cond_t wake; +}; + + +/* sqlo = 0 - init state: not held */ +extern void seqlock_init(struct seqlock *sqlo); + + +/* while (sqlo <= val) - wait until seqlock->pos > val, or seqlock unheld */ +extern void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val); +extern bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val); + +static inline bool seqlock_held(struct seqlock *sqlo) +{ + return !!atomic_load_explicit(&sqlo->pos, memory_order_relaxed); +} + +/* sqlo - get seqlock position -- for the "counter" seqlock */ +extern seqlock_val_t seqlock_cur(struct seqlock *sqlo); +/* sqlo++ - note: like x++, returns previous value, before bumping */ +extern seqlock_val_t seqlock_bump(struct seqlock *sqlo); + + +/* sqlo = val - can be used on held seqlock. */ +extern void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val); +/* sqlo = ref - standard pattern: acquire relative to other seqlock */ +static inline void seqlock_acquire(struct seqlock *sqlo, struct seqlock *ref) +{ + seqlock_acquire_val(sqlo, seqlock_cur(ref)); +} + +/* sqlo = 0 - set seqlock position to 0, marking as non-held */ +extern void seqlock_release(struct seqlock *sqlo); +/* release should normally be followed by a bump on the "counter", if + * anything other than reading RCU items was done + */ + +#endif /* _SEQLOCK_H */ diff --git a/lib/sigevent.c b/lib/sigevent.c index f00ff4921e59..d02b07422394 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -209,12 +209,14 @@ exit_handler(int signo #endif ) { - zlog_signal(signo, "exiting..." -#ifdef SA_SIGINFO - , - siginfo, program_counter(context) +#ifndef SA_SIGINFO + void *siginfo = NULL; + void *pc = NULL; +#else + void *pc = program_counter(context); #endif - ); + + zlog_signal(signo, "exiting...", siginfo, pc); _exit(128 + signo); } @@ -226,6 +228,13 @@ core_handler(int signo #endif ) { +#ifndef SA_SIGINFO + void *siginfo = NULL; + void *pc = NULL; +#else + void *pc = program_counter(context); +#endif + /* make sure we don't hang in here. default for SIGALRM is terminate. * - if we're in backtrace for more than a second, abort. */ struct sigaction sa_default = {.sa_handler = SIG_DFL}; @@ -238,12 +247,8 @@ core_handler(int signo alarm(1); - zlog_signal(signo, "aborting..." -#ifdef SA_SIGINFO - , - siginfo, program_counter(context) -#endif - ); + zlog_signal(signo, "aborting...", siginfo, pc); + /* dump memory stats on core */ log_memstats(stderr, "core_handler"); abort(); diff --git a/lib/sockopt.c b/lib/sockopt.c index 89f3d5b594ed..8e38a2927848 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -701,6 +701,12 @@ int sockopt_tcp_signature_ext(int sock, union sockunion *su, uint16_t prefixlen, } return ret; #endif /* HAVE_TCP_MD5SIG */ + + /* + * Making compiler happy. If we get to this point we probably + * have done something really really wrong. + */ + return -2; } int sockopt_tcp_signature(int sock, union sockunion *su, const char *password) diff --git a/lib/sockunion.c b/lib/sockunion.c index af4f41f37ca3..8fa9a3fad991 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -163,7 +163,7 @@ int sockunion_accept(int sock, union sockunion *su) } /* Return sizeof union sockunion. */ -static int sockunion_sizeof(const union sockunion *su) +int sockunion_sizeof(const union sockunion *su) { int ret; @@ -366,21 +366,6 @@ int sockopt_cork(int sock, int onoff) return 0; } -int sockopt_mark_default(int sock, int mark, struct zebra_privs_t *cap) -{ -#ifdef SO_MARK - int ret; - - frr_elevate_privs(cap) { - ret = setsockopt(sock, SOL_SOCKET, SO_MARK, &mark, - sizeof(mark)); - } - return ret; -#else - return 0; -#endif -} - int sockopt_minttl(int family, int sock, int minttl) { #ifdef IP_MINTTL @@ -472,7 +457,7 @@ unsigned int sockunion_hash(const union sockunion *su) return jhash_1word(su->sin.sin_addr.s_addr, 0); case AF_INET6: return jhash2(su->sin6.sin6_addr.s6_addr32, - ZEBRA_NUM_OF(su->sin6.sin6_addr.s6_addr32), 0); + array_size(su->sin6.sin6_addr.s6_addr32), 0); } return 0; } diff --git a/lib/sockunion.h b/lib/sockunion.h index d7d26ba85ad9..7091c1b5e78b 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -83,6 +83,7 @@ extern void sockunion_set(union sockunion *, int family, const uint8_t *addr, extern union sockunion *sockunion_str2su(const char *str); extern int sockunion_accept(int sock, union sockunion *); +extern int sockunion_sizeof(const union sockunion *su); extern int sockunion_stream_socket(union sockunion *); extern int sockopt_reuseaddr(int); extern int sockopt_reuseport(int); @@ -92,7 +93,6 @@ extern int sockunion_bind(int sock, union sockunion *, unsigned short, extern int sockopt_ttl(int family, int sock, int ttl); extern int sockopt_minttl(int family, int sock, int minttl); extern int sockopt_cork(int sock, int onoff); -extern int sockopt_mark_default(int sock, int mark, struct zebra_privs_t *); extern int sockunion_socket(const union sockunion *su); extern const char *inet_sutop(const union sockunion *su, char *str); extern enum connect_result sockunion_connect(int fd, const union sockunion *su, diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c index 80004b41acaf..ee87d730770e 100644 --- a/lib/srcdest_table.c +++ b/lib/srcdest_table.c @@ -28,6 +28,7 @@ #include "memory.h" #include "prefix.h" #include "table.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, ROUTE_SRC_NODE, "Route source node") @@ -264,7 +265,8 @@ struct route_node *srcdest_rnode_lookup(struct route_table *table, return srn; } -void srcdest_rnode_prefixes(struct route_node *rn, const struct prefix **p, +void srcdest_rnode_prefixes(const struct route_node *rn, + const struct prefix **p, const struct prefix **src_p) { if (rnode_is_srcnode(rn)) { @@ -296,10 +298,22 @@ const char *srcdest2str(const struct prefix *dst_p, return str; } -const char *srcdest_rnode2str(struct route_node *rn, char *str, int size) +const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size) { const struct prefix *dst_p, *src_p; srcdest_rnode_prefixes(rn, &dst_p, &src_p); return srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, str, size); } + +printfrr_ext_autoreg_p("RN", printfrr_rn) +static ssize_t printfrr_rn(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct route_node *rn = ptr; + const struct prefix *dst_p, *src_p; + + srcdest_rnode_prefixes(rn, &dst_p, &src_p); + srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, buf, bsz); + return 2; +} diff --git a/lib/srcdest_table.h b/lib/srcdest_table.h index 8845931de79c..90418944c77f 100644 --- a/lib/srcdest_table.h +++ b/lib/srcdest_table.h @@ -65,22 +65,22 @@ extern struct route_node *srcdest_rnode_get(struct route_table *table, extern struct route_node *srcdest_rnode_lookup(struct route_table *table, union prefixconstptr dst_pu, const struct prefix_ipv6 *src_p); -extern void srcdest_rnode_prefixes(struct route_node *rn, +extern void srcdest_rnode_prefixes(const struct route_node *rn, const struct prefix **p, const struct prefix **src_p); extern const char *srcdest2str(const struct prefix *dst_p, const struct prefix_ipv6 *src_p, char *str, int size); -extern const char *srcdest_rnode2str(struct route_node *rn, char *str, +extern const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size); extern struct route_node *srcdest_route_next(struct route_node *rn); -static inline int rnode_is_dstnode(struct route_node *rn) +static inline int rnode_is_dstnode(const struct route_node *rn) { return rn->table->delegate == &_srcdest_dstnode_delegate; } -static inline int rnode_is_srcnode(struct route_node *rn) +static inline int rnode_is_srcnode(const struct route_node *rn) { return rn->table->delegate == &_srcdest_srcnode_delegate; } diff --git a/lib/subdir.am b/lib/subdir.am index 3b14be46767b..4e6adced7449 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -3,10 +3,11 @@ # lib_LTLIBRARIES += lib/libfrr.la lib_libfrr_la_LDFLAGS = -version-info 0:0:0 -Xlinker -e_libfrr_version -lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) +lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) $(LUA_LIB) lib_libfrr_la_SOURCES = \ lib/agg_table.c \ + lib/atomlist.c \ lib/bfd.c \ lib/buffer.c \ lib/checksum.c \ @@ -20,6 +21,7 @@ lib_libfrr_la_SOURCES = \ lib/distribute.c \ lib/ferr.c \ lib/filter.c \ + lib/frrlua.c \ lib/frr_pthread.c \ lib/frrstr.c \ lib/getopt.c \ @@ -54,6 +56,7 @@ lib_libfrr_la_SOURCES = \ lib/northbound.c \ lib/northbound_cli.c \ lib/northbound_db.c \ + lib/ntop.c \ lib/openbsd-tree.c \ lib/pid_output.c \ lib/plist.c \ @@ -65,6 +68,7 @@ lib_libfrr_la_SOURCES = \ lib/ringbuf.c \ lib/routemap.c \ lib/sbuf.c \ + lib/seqlock.c \ lib/sha256.c \ lib/sigevent.c \ lib/skiplist.c \ @@ -79,6 +83,8 @@ lib_libfrr_la_SOURCES = \ lib/table.c \ lib/termtable.c \ lib/thread.c \ + lib/typerb.c \ + lib/typesafe.c \ lib/vector.c \ lib/vrf.c \ lib/vty.c \ @@ -89,7 +95,9 @@ lib_libfrr_la_SOURCES = \ lib/yang_wrappers.c \ lib/zclient.c \ lib/logicalrouter.c \ - lib/lua.c \ + lib/printf/printf-pos.c \ + lib/printf/vfprintf.c \ + lib/printf/glue.c \ # end nodist_lib_libfrr_la_SOURCES = \ @@ -127,9 +135,12 @@ lib/nexthop_group_clippy.c: $(CLIPPY_DEPS) lib/nexthop_group.lo: lib/nexthop_group_clippy.c lib/northbound_cli_clippy.c: $(CLIPPY_DEPS) lib/northbound_cli.lo: lib/northbound_cli_clippy.c +lib/vty_clippy.c: $(CLIPPY_DEPS) +lib/vty.lo: lib/vty_clippy.c pkginclude_HEADERS += \ lib/agg_table.h \ + lib/atomlist.h \ lib/bfd.h \ lib/bitfield.h \ lib/buffer.h \ @@ -144,9 +155,9 @@ pkginclude_HEADERS += \ lib/debug.h \ lib/distribute.h \ lib/ferr.h \ - lib/fifo.h \ lib/filter.h \ lib/freebsd-queue.h \ + lib/frrlua.h \ lib/frr_pthread.h \ lib/frratomic.h \ lib/frrstr.h \ @@ -185,6 +196,7 @@ pkginclude_HEADERS += \ lib/plist.h \ lib/pqueue.h \ lib/prefix.h \ + lib/printfrr.h \ lib/privs.h \ lib/ptm_lib.h \ lib/pw.h \ @@ -193,6 +205,7 @@ pkginclude_HEADERS += \ lib/ringbuf.h \ lib/routemap.h \ lib/sbuf.h \ + lib/seqlock.h \ lib/sha256.h \ lib/sigevent.h \ lib/skiplist.h \ @@ -206,6 +219,8 @@ pkginclude_HEADERS += \ lib/table.h \ lib/termtable.h \ lib/thread.h \ + lib/typerb.h \ + lib/typesafe.h \ lib/vector.h \ lib/vlan.h \ lib/vrf.h \ @@ -221,7 +236,6 @@ pkginclude_HEADERS += \ lib/zclient.h \ lib/zebra.h \ lib/logicalrouter.h \ - lib/lua.h \ lib/pbr.h \ # end @@ -235,6 +249,8 @@ noinst_HEADERS += \ lib/clippy.h \ lib/log_int.h \ lib/plist_int.h \ + lib/printf/printfcommon.h \ + lib/printf/printflocal.h \ #end # General note about module and module helper library (libfrrsnmp, libfrrzmq) @@ -302,6 +318,18 @@ lib_sysrepo_la_LDFLAGS = -avoid-version -module -shared -export-dynamic lib_sysrepo_la_LIBADD = lib/libfrr.la $(SYSREPO_LIBS) lib_sysrepo_la_SOURCES = lib/northbound_sysrepo.c +# +# gRPC northbound plugin +# +if GRPC +module_LTLIBRARIES += lib/grpc.la +endif + +lib_grpc_la_CXXFLAGS = $(WERROR) $(GRPC_CFLAGS) +lib_grpc_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +lib_grpc_la_LIBADD = lib/libfrr.la grpc/libfrrgrpc_pb.la $(GRPC_LIBS) +lib_grpc_la_SOURCES = lib/northbound_grpc.cpp + # # CLI utilities # @@ -346,7 +374,7 @@ am__v_CLIPPY_1 = CLIPPY_DEPS = $(HOSTTOOLS)lib/clippy $(top_srcdir)/python/clidef.py -SUFFIXES = _clippy.c .proto .pb-c.c .pb-c.h .pb.h +SUFFIXES = _clippy.c .proto .pb-c.c .pb-c.h .pb.h .pb.cc .grpc.pb.cc .c_clippy.c: @{ test -x $(top_builddir)/$(HOSTTOOLS)lib/clippy || \ $(MAKE) -C $(top_builddir)/$(HOSTTOOLS) lib/clippy; } diff --git a/lib/table.c b/lib/table.c index edba7f193245..728615c77601 100644 --- a/lib/table.c +++ b/lib/table.c @@ -33,12 +33,14 @@ DEFINE_MTYPE(LIB, ROUTE_NODE, "Route node") static void route_table_free(struct route_table *); -static bool route_table_hash_cmp(const void *a, const void *b) +static int route_table_hash_cmp(const struct route_node *a, + const struct route_node *b) { - const struct prefix *pa = a, *pb = b; - return prefix_cmp(pa, pb) == 0; + return prefix_cmp(&a->p, &b->p); } +DECLARE_HASH(rn_hash_node, struct route_node, nodehash, route_table_hash_cmp, + prefix_hash_key) /* * route_table_init_with_delegate */ @@ -49,8 +51,7 @@ route_table_init_with_delegate(route_table_delegate_t *delegate) rt = XCALLOC(MTYPE_ROUTE_TABLE, sizeof(struct route_table)); rt->delegate = delegate; - rt->hash = hash_create(prefix_hash_key, route_table_hash_cmp, - "route table hash"); + rn_hash_node_init(&rt->hash); return rt; } @@ -69,15 +70,14 @@ static struct route_node *route_node_new(struct route_table *table) static struct route_node *route_node_set(struct route_table *table, const struct prefix *prefix) { - struct route_node *node, *inserted; + struct route_node *node; node = route_node_new(table); prefix_copy(&node->p, prefix); node->table = table; - inserted = hash_get(node->table->hash, node, hash_alloc_intern); - assert(inserted == node); + rn_hash_node_add(&node->table->hash, node); return node; } @@ -99,9 +99,6 @@ static void route_table_free(struct route_table *rt) if (rt == NULL) return; - hash_clean(rt->hash, NULL); - hash_free(rt->hash); - node = rt->top; /* Bulk deletion of nodes remaining in this table. This function is not @@ -123,6 +120,7 @@ static void route_table_free(struct route_table *rt) tmp_node->table->count--; tmp_node->lock = 0; /* to cause assert if unlocked after this */ + rn_hash_node_del(&rt->hash, tmp_node); route_node_free(rt, tmp_node); if (node != NULL) { @@ -137,6 +135,7 @@ static void route_table_free(struct route_table *rt) assert(rt->count == 0); + rn_hash_node_fini(&rt->hash); XFREE(MTYPE_ROUTE_TABLE, rt); return; } @@ -192,7 +191,7 @@ static void set_link(struct route_node *node, struct route_node *new) } /* Find matched prefix. */ -struct route_node *route_node_match(const struct route_table *table, +struct route_node *route_node_match(struct route_table *table, union prefixconstptr pu) { const struct prefix *p = pu.p; @@ -222,7 +221,7 @@ struct route_node *route_node_match(const struct route_table *table, return NULL; } -struct route_node *route_node_match_ipv4(const struct route_table *table, +struct route_node *route_node_match_ipv4(struct route_table *table, const struct in_addr *addr) { struct prefix_ipv4 p; @@ -235,7 +234,7 @@ struct route_node *route_node_match_ipv4(const struct route_table *table, return route_node_match(table, (struct prefix *)&p); } -struct route_node *route_node_match_ipv6(const struct route_table *table, +struct route_node *route_node_match_ipv6(struct route_table *table, const struct in6_addr *addr) { struct prefix_ipv6 p; @@ -245,49 +244,50 @@ struct route_node *route_node_match_ipv6(const struct route_table *table, p.prefixlen = IPV6_MAX_PREFIXLEN; p.prefix = *addr; - return route_node_match(table, (struct prefix *)&p); + return route_node_match(table, &p); } /* Lookup same prefix node. Return NULL when we can't find route. */ -struct route_node *route_node_lookup(const struct route_table *table, +struct route_node *route_node_lookup(struct route_table *table, union prefixconstptr pu) { - struct prefix p; - struct route_node *node; - prefix_copy(&p, pu.p); - apply_mask(&p); + struct route_node rn, *node; + prefix_copy(&rn.p, pu.p); + apply_mask(&rn.p); - node = hash_get(table->hash, (void *)&p, NULL); + node = rn_hash_node_find(&table->hash, &rn); return (node && node->info) ? route_lock_node(node) : NULL; } /* Lookup same prefix node. Return NULL when we can't find route. */ -struct route_node *route_node_lookup_maynull(const struct route_table *table, +struct route_node *route_node_lookup_maynull(struct route_table *table, union prefixconstptr pu) { - struct prefix p; - struct route_node *node; - prefix_copy(&p, pu.p); - apply_mask(&p); + struct route_node rn, *node; + prefix_copy(&rn.p, pu.p); + apply_mask(&rn.p); - node = hash_get(table->hash, (void *)&p, NULL); + node = rn_hash_node_find(&table->hash, &rn); return node ? route_lock_node(node) : NULL; } /* Add node to routing table. */ -struct route_node *route_node_get(struct route_table *const table, +struct route_node *route_node_get(struct route_table *table, union prefixconstptr pu) { - const struct prefix *p = pu.p; + struct route_node search; + struct prefix *p = &search.p; + + prefix_copy(p, pu.p); + apply_mask(p); + struct route_node *new; struct route_node *node; struct route_node *match; - struct route_node *inserted; uint16_t prefixlen = p->prefixlen; const uint8_t *prefix = &p->u.prefix; - apply_mask((struct prefix *)p); - node = hash_get(table->hash, (void *)p, NULL); + node = rn_hash_node_find(&table->hash, &search); if (node && node->info) return route_lock_node(node); @@ -314,8 +314,7 @@ struct route_node *route_node_get(struct route_table *const table, new->p.family = p->family; new->table = table; set_link(new, node); - inserted = hash_get(node->table->hash, new, hash_alloc_intern); - assert(inserted == new); + rn_hash_node_add(&table->hash, new); if (match) set_link(match, new); @@ -367,7 +366,7 @@ void route_node_delete(struct route_node *node) node->table->count--; - hash_release(node->table->hash, node); + rn_hash_node_del(&node->table->hash, node); /* WARNING: FRAGILE CODE! * route_node_free may have the side effect of free'ing the entire @@ -472,7 +471,7 @@ struct route_node *route_next_until(struct route_node *node, return NULL; } -unsigned long route_table_count(const struct route_table *table) +unsigned long route_table_count(struct route_table *table) { return table->count; } @@ -607,7 +606,7 @@ static struct route_node *route_get_subtree_next(struct route_node *node) * @see route_table_get_next */ static struct route_node * -route_table_get_next_internal(const struct route_table *table, +route_table_get_next_internal(struct route_table *table, const struct prefix *p) { struct route_node *node, *tmp_node; @@ -708,7 +707,7 @@ route_table_get_next_internal(const struct route_table *table, * Find the node that occurs after the given prefix in order of * iteration. */ -struct route_node *route_table_get_next(const struct route_table *table, +struct route_node *route_table_get_next(struct route_table *table, union prefixconstptr pu) { const struct prefix *p = pu.p; diff --git a/lib/table.h b/lib/table.h index ce578e795c64..eefd992546a5 100644 --- a/lib/table.h +++ b/lib/table.h @@ -25,6 +25,7 @@ #include "memory.h" #include "hash.h" #include "prefix.h" +#include "typesafe.h" #ifdef __cplusplus extern "C" { @@ -59,10 +60,12 @@ struct route_table_delegate_t_ { route_table_destroy_node_func_t destroy_node; }; +PREDECL_HASH(rn_hash_node) + /* Routing table top structure. */ struct route_table { struct route_node *top; - struct hash *hash; + struct rn_hash_node_head hash; /* * Delegate that performs certain functions for this table. @@ -129,6 +132,7 @@ struct route_table { /* Lock of this radix */ \ unsigned int table_rdonly(lock); \ \ + struct rn_hash_node_item nodehash; \ /* Each node of route. */ \ void *info; \ @@ -194,26 +198,29 @@ static inline void route_table_set_info(struct route_table *table, void *d) table->info = d; } +/* ext_pure => extern __attribute__((pure)) + * does not modify memory (but depends on mem), allows compiler to optimize + */ + extern void route_table_finish(struct route_table *table); -extern struct route_node *route_top(struct route_table *table); -extern struct route_node *route_next(struct route_node *node); -extern struct route_node *route_next_until(struct route_node *node, - const struct route_node *limit); -extern struct route_node *route_node_get(struct route_table *const table, +ext_pure struct route_node *route_top(struct route_table *table); +ext_pure struct route_node *route_next(struct route_node *node); +ext_pure struct route_node *route_next_until(struct route_node *node, + const struct route_node *limit); +extern struct route_node *route_node_get(struct route_table *table, union prefixconstptr pu); -extern struct route_node *route_node_lookup(const struct route_table *table, - union prefixconstptr pu); -extern struct route_node * -route_node_lookup_maynull(const struct route_table *table, - union prefixconstptr pu); -extern struct route_node *route_node_match(const struct route_table *table, - union prefixconstptr pu); -extern struct route_node *route_node_match_ipv4(const struct route_table *table, - const struct in_addr *addr); -extern struct route_node *route_node_match_ipv6(const struct route_table *table, - const struct in6_addr *addr); - -extern unsigned long route_table_count(const struct route_table *table); +ext_pure struct route_node *route_node_lookup(struct route_table *table, + union prefixconstptr pu); +ext_pure struct route_node *route_node_lookup_maynull(struct route_table *table, + union prefixconstptr pu); +ext_pure struct route_node *route_node_match(struct route_table *table, + union prefixconstptr pu); +ext_pure struct route_node *route_node_match_ipv4(struct route_table *table, + const struct in_addr *addr); +ext_pure struct route_node *route_node_match_ipv6(struct route_table *table, + const struct in6_addr *addr); + +ext_pure unsigned long route_table_count(struct route_table *table); extern struct route_node *route_node_create(route_table_delegate_t *delegate, struct route_table *table); @@ -222,10 +229,10 @@ extern void route_node_destroy(route_table_delegate_t *delegate, struct route_table *table, struct route_node *node); -extern struct route_node *route_table_get_next(const struct route_table *table, - union prefixconstptr pu); -extern int route_table_prefix_iter_cmp(const struct prefix *p1, - const struct prefix *p2); +ext_pure struct route_node *route_table_get_next(struct route_table *table, + union prefixconstptr pu); +ext_pure int route_table_prefix_iter_cmp(const struct prefix *p1, + const struct prefix *p2); /* * Iterator functions. @@ -291,6 +298,8 @@ static inline struct route_node *route_table_iter_next(route_table_iter_t *iter) return NULL; default: + /* Suppress uninitialized variable warning */ + node = NULL; assert(0); } diff --git a/lib/thread.c b/lib/thread.c index 2760b83fb3c4..7489be5c2dad 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -40,6 +40,8 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_MASTER, "Thread master") DEFINE_MTYPE_STATIC(LIB, THREAD_POLL, "Thread Poll Info") DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats") +DECLARE_LIST(thread_list, struct thread, threaditem) + #if defined(__APPLE__) #include #include @@ -61,7 +63,7 @@ static struct list *masters; static void thread_free(struct thread_master *master, struct thread *thread); /* CLI start ---------------------------------------------------------------- */ -static unsigned int cpu_record_hash_key(struct cpu_thread_history *a) +static unsigned int cpu_record_hash_key(const struct cpu_thread_history *a) { int size = sizeof(a->func); @@ -93,9 +95,11 @@ static void cpu_record_hash_free(void *a) static void vty_out_cpu_thread_history(struct vty *vty, struct cpu_thread_history *a) { - vty_out(vty, "%5zu %10zu.%03lu %9zu %8zu %9zu %8lu %9lu", - a->total_active, a->cpu.total / 1000, a->cpu.total % 1000, - a->total_calls, a->cpu.total / a->total_calls, a->cpu.max, + vty_out(vty, "%5zu %10zu.%03zu %9zu %8zu %9zu %8zu %9zu", + (size_t)a->total_active, + a->cpu.total / 1000, a->cpu.total % 1000, + (size_t)a->total_calls, + a->cpu.total / a->total_calls, a->cpu.max, a->real.total / a->total_calls, a->real.max); vty_out(vty, " %c%c%c%c%c %s\n", a->types & (1 << THREAD_READ) ? 'R' : ' ', @@ -279,7 +283,7 @@ DEFUN (show_thread_cpu, SHOW_STR "Thread information\n" "Thread CPU usage\n" - "Display filter (rwtexb)\n") + "Display filter (rwtex)\n") { uint8_t filter = (uint8_t)-1U; int idx = 0; @@ -310,7 +314,8 @@ static void show_thread_poll_helper(struct vty *vty, struct thread_master *m) vty_out(vty, "\nShowing poll FD's for %s\n", name); vty_out(vty, "----------------------%s\n", underline); - vty_out(vty, "Count: %u\n", (uint32_t)m->handler.pfdcount); + vty_out(vty, "Count: %u/%d\n", (uint32_t)m->handler.pfdcount, + m->fd_limit); for (i = 0; i < m->handler.pfdcount; i++) vty_out(vty, "\t%6d fd:%6d events:%2d revents:%2d\n", i, m->handler.pfds[i].fd, @@ -431,10 +436,13 @@ struct thread_master *thread_master_create(const char *name) sizeof(struct thread *) * rv->fd_limit); rv->cpu_record = hash_create_size( - 8, (unsigned int (*)(void *))cpu_record_hash_key, + 8, (unsigned int (*)(const void *))cpu_record_hash_key, (bool (*)(const void *, const void *))cpu_record_hash_cmp, "Thread Hash"); + thread_list_init(&rv->event); + thread_list_init(&rv->ready); + thread_list_init(&rv->unuse); /* Initialize the timer queues */ rv->timer = pqueue_create(); @@ -487,50 +495,6 @@ void thread_master_set_name(struct thread_master *master, const char *name) pthread_mutex_unlock(&master->mtx); } -/* Add a new thread to the list. */ -static void thread_list_add(struct thread_list *list, struct thread *thread) -{ - thread->next = NULL; - thread->prev = list->tail; - if (list->tail) - list->tail->next = thread; - else - list->head = thread; - list->tail = thread; - list->count++; -} - -/* Delete a thread from the list. */ -static struct thread *thread_list_delete(struct thread_list *list, - struct thread *thread) -{ - if (thread->next) - thread->next->prev = thread->prev; - else - list->tail = thread->prev; - if (thread->prev) - thread->prev->next = thread->next; - else - list->head = thread->next; - thread->next = thread->prev = NULL; - list->count--; - return thread; -} - -/* Thread list is empty or not. */ -static int thread_empty(struct thread_list *list) -{ - return list->head ? 0 : 1; -} - -/* Delete top of the list and return it. */ -static struct thread *thread_trim_head(struct thread_list *list) -{ - if (!thread_empty(list)) - return thread_list_delete(list, list->head); - return NULL; -} - #define THREAD_UNUSED_DEPTH 10 /* Move thread to unuse list. */ @@ -539,8 +503,6 @@ static void thread_add_unuse(struct thread_master *m, struct thread *thread) pthread_mutex_t mtxc = thread->mtx; assert(m != NULL && thread != NULL); - assert(thread->next == NULL); - assert(thread->prev == NULL); thread->hist->total_active--; memset(thread, 0, sizeof(struct thread)); @@ -549,8 +511,8 @@ static void thread_add_unuse(struct thread_master *m, struct thread *thread) /* Restore the thread mutex context. */ thread->mtx = mtxc; - if (m->unuse.count < THREAD_UNUSED_DEPTH) { - thread_list_add(&m->unuse, thread); + if (thread_list_count(&m->unuse) < THREAD_UNUSED_DEPTH) { + thread_list_add_tail(&m->unuse, thread); return; } @@ -558,16 +520,13 @@ static void thread_add_unuse(struct thread_master *m, struct thread *thread) } /* Free all unused thread. */ -static void thread_list_free(struct thread_master *m, struct thread_list *list) +static void thread_list_free(struct thread_master *m, + struct thread_list_head *list) { struct thread *t; - struct thread *next; - for (t = list->head; t; t = next) { - next = t->next; + while ((t = thread_list_pop(list))) thread_free(m, t); - list->count--; - } } static void thread_array_free(struct thread_master *m, @@ -609,9 +568,8 @@ void thread_master_free_unused(struct thread_master *m) pthread_mutex_lock(&m->mtx); { struct thread *t; - while ((t = thread_trim_head(&m->unuse)) != NULL) { + while ((t = thread_list_pop(&m->unuse))) thread_free(m, t); - } } pthread_mutex_unlock(&m->mtx); } @@ -690,7 +648,7 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type, int (*func)(struct thread *), void *arg, debugargdef) { - struct thread *thread = thread_trim_head(&m->unuse); + struct thread *thread = thread_list_pop(&m->unuse); struct cpu_thread_history tmp; if (!thread) { @@ -971,7 +929,7 @@ struct thread *funcname_thread_add_event(struct thread_master *m, pthread_mutex_lock(&thread->mtx); { thread->u.val = val; - thread_list_add(&m->event, thread); + thread_list_add_tail(&m->event, thread); } pthread_mutex_unlock(&thread->mtx); @@ -1063,7 +1021,7 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state) */ static void do_thread_cancel(struct thread_master *master) { - struct thread_list *list = NULL; + struct thread_list_head *list = NULL; struct pqueue *queue = NULL; struct thread **thread_array = NULL; struct thread *thread; @@ -1078,31 +1036,23 @@ static void do_thread_cancel(struct thread_master *master) * need to check every thread in the ready queue. */ if (cr->eventobj) { struct thread *t; - thread = master->event.head; - - while (thread) { - t = thread; - thread = t->next; - - if (t->arg == cr->eventobj) { - thread_list_delete(&master->event, t); - if (t->ref) - *t->ref = NULL; - thread_add_unuse(master, t); - } + + frr_each_safe(thread_list, &master->event, t) { + if (t->arg != cr->eventobj) + continue; + thread_list_del(&master->event, t); + if (t->ref) + *t->ref = NULL; + thread_add_unuse(master, t); } - thread = master->ready.head; - while (thread) { - t = thread; - thread = t->next; - - if (t->arg == cr->eventobj) { - thread_list_delete(&master->ready, t); - if (t->ref) - *t->ref = NULL; - thread_add_unuse(master, t); - } + frr_each_safe(thread_list, &master->ready, t) { + if (t->arg != cr->eventobj) + continue; + thread_list_del(&master->ready, t); + if (t->ref) + *t->ref = NULL; + thread_add_unuse(master, t); } continue; } @@ -1146,7 +1096,7 @@ static void do_thread_cancel(struct thread_master *master) assert(thread == queue->array[thread->index]); pqueue_remove_at(thread->index, queue); } else if (list) { - thread_list_delete(list, thread); + thread_list_del(list, thread); } else if (thread_array) { thread_array[thread->u.fd] = NULL; } else { @@ -1301,7 +1251,7 @@ static int thread_process_io_helper(struct thread_master *m, thread_array = m->write; thread_array[thread->u.fd] = NULL; - thread_list_add(&m->ready, thread); + thread_list_add_tail(&m->ready, thread); thread->type = THREAD_READY; /* if another pthread scheduled this file descriptor for the event we're * responding to, no problem; we're getting to it now */ @@ -1380,24 +1330,21 @@ static unsigned int thread_process_timers(struct pqueue *queue, return ready; pqueue_dequeue(queue); thread->type = THREAD_READY; - thread_list_add(&thread->master->ready, thread); + thread_list_add_tail(&thread->master->ready, thread); ready++; } return ready; } /* process a list en masse, e.g. for event thread lists */ -static unsigned int thread_process(struct thread_list *list) +static unsigned int thread_process(struct thread_list_head *list) { struct thread *thread; - struct thread *next; unsigned int ready = 0; - for (thread = list->head; thread; thread = next) { - next = thread->next; - thread_list_delete(list, thread); + while ((thread = thread_list_pop(list))) { thread->type = THREAD_READY; - thread_list_add(&thread->master->ready, thread); + thread_list_add_tail(&thread->master->ready, thread); ready++; } return ready; @@ -1429,7 +1376,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) * Attempt to flush ready queue before going into poll(). * This is performance-critical. Think twice before modifying. */ - if ((thread = thread_trim_head(&m->ready))) { + if ((thread = thread_list_pop(&m->ready))) { fetch = thread_run(m, thread, fetch); if (fetch->ref) *fetch->ref = NULL; @@ -1462,10 +1409,11 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) * In every case except the last, we need to hit poll() at least * once per loop to avoid starvation by events */ - if (m->ready.count == 0) + if (!thread_list_count(&m->ready)) tw = thread_timer_wait(m->timer, &tv); - if (m->ready.count != 0 || (tw && !timercmp(tw, &zerotime, >))) + if (thread_list_count(&m->ready) || + (tw && !timercmp(tw, &zerotime, >))) tw = &zerotime; if (!tw && m->handler.pfdcount == 0) { /* die */ diff --git a/lib/thread.h b/lib/thread.h index ec774a654309..789726512049 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -26,6 +26,7 @@ #include #include "monotime.h" #include "frratomic.h" +#include "typesafe.h" #ifdef __cplusplus extern "C" { @@ -39,12 +40,7 @@ struct rusage_t { #define GETRUSAGE(X) thread_getrusage(X) -/* Linked list of thread. */ -struct thread_list { - struct thread *head; - struct thread *tail; - int count; -}; +PREDECL_LIST(thread_list) struct pqueue; @@ -78,9 +74,7 @@ struct thread_master { struct thread **read; struct thread **write; struct pqueue *timer; - struct thread_list event; - struct thread_list ready; - struct thread_list unuse; + struct thread_list_head event, ready, unuse; struct list *cancel_req; bool canceled; pthread_cond_t cancel_cond; @@ -100,8 +94,7 @@ struct thread_master { struct thread { uint8_t type; /* thread type */ uint8_t add_type; /* thread type */ - struct thread *next; /* next pointer of the thread */ - struct thread *prev; /* previous pointer of the thread */ + struct thread_list_item threaditem; struct thread **ref; /* external reference (if given) */ struct thread_master *master; /* pointer to the struct thread_master */ int (*func)(struct thread *); /* event function */ diff --git a/lib/typerb.c b/lib/typerb.c new file mode 100644 index 000000000000..d361e7651e60 --- /dev/null +++ b/lib/typerb.c @@ -0,0 +1,472 @@ +/* RB-tree */ + +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 2016 David Gwynne + * + * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +#include "typerb.h" + +#define RB_BLACK 0 +#define RB_RED 1 + +#define rb_entry typed_rb_entry +#define rbt_tree typed_rb_root + +#define RBE_LEFT(_rbe) (_rbe)->rbt_left +#define RBE_RIGHT(_rbe) (_rbe)->rbt_right +#define RBE_PARENT(_rbe) (_rbe)->rbt_parent +#define RBE_COLOR(_rbe) (_rbe)->rbt_color + +#define RBH_ROOT(_rbt) (_rbt)->rbt_root + +static inline void rbe_set(struct rb_entry *rbe, struct rb_entry *parent) +{ + RBE_PARENT(rbe) = parent; + RBE_LEFT(rbe) = RBE_RIGHT(rbe) = NULL; + RBE_COLOR(rbe) = RB_RED; +} + +static inline void rbe_set_blackred(struct rb_entry *black, + struct rb_entry *red) +{ + RBE_COLOR(black) = RB_BLACK; + RBE_COLOR(red) = RB_RED; +} + +static inline void rbe_rotate_left(struct rbt_tree *rbt, struct rb_entry *rbe) +{ + struct rb_entry *parent; + struct rb_entry *tmp; + + tmp = RBE_RIGHT(rbe); + RBE_RIGHT(rbe) = RBE_LEFT(tmp); + if (RBE_RIGHT(rbe) != NULL) + RBE_PARENT(RBE_LEFT(tmp)) = rbe; + + parent = RBE_PARENT(rbe); + RBE_PARENT(tmp) = parent; + if (parent != NULL) { + if (rbe == RBE_LEFT(parent)) + RBE_LEFT(parent) = tmp; + else + RBE_RIGHT(parent) = tmp; + } else + RBH_ROOT(rbt) = tmp; + + RBE_LEFT(tmp) = rbe; + RBE_PARENT(rbe) = tmp; +} + +static inline void rbe_rotate_right(struct rbt_tree *rbt, struct rb_entry *rbe) +{ + struct rb_entry *parent; + struct rb_entry *tmp; + + tmp = RBE_LEFT(rbe); + RBE_LEFT(rbe) = RBE_RIGHT(tmp); + if (RBE_LEFT(rbe) != NULL) + RBE_PARENT(RBE_RIGHT(tmp)) = rbe; + + parent = RBE_PARENT(rbe); + RBE_PARENT(tmp) = parent; + if (parent != NULL) { + if (rbe == RBE_LEFT(parent)) + RBE_LEFT(parent) = tmp; + else + RBE_RIGHT(parent) = tmp; + } else + RBH_ROOT(rbt) = tmp; + + RBE_RIGHT(tmp) = rbe; + RBE_PARENT(rbe) = tmp; +} + +static inline void rbe_insert_color(struct rbt_tree *rbt, struct rb_entry *rbe) +{ + struct rb_entry *parent, *gparent, *tmp; + + rbt->count++; + + while ((parent = RBE_PARENT(rbe)) != NULL + && RBE_COLOR(parent) == RB_RED) { + gparent = RBE_PARENT(parent); + + if (parent == RBE_LEFT(gparent)) { + tmp = RBE_RIGHT(gparent); + if (tmp != NULL && RBE_COLOR(tmp) == RB_RED) { + RBE_COLOR(tmp) = RB_BLACK; + rbe_set_blackred(parent, gparent); + rbe = gparent; + continue; + } + + if (RBE_RIGHT(parent) == rbe) { + rbe_rotate_left(rbt, parent); + tmp = parent; + parent = rbe; + rbe = tmp; + } + + rbe_set_blackred(parent, gparent); + rbe_rotate_right(rbt, gparent); + } else { + tmp = RBE_LEFT(gparent); + if (tmp != NULL && RBE_COLOR(tmp) == RB_RED) { + RBE_COLOR(tmp) = RB_BLACK; + rbe_set_blackred(parent, gparent); + rbe = gparent; + continue; + } + + if (RBE_LEFT(parent) == rbe) { + rbe_rotate_right(rbt, parent); + tmp = parent; + parent = rbe; + rbe = tmp; + } + + rbe_set_blackred(parent, gparent); + rbe_rotate_left(rbt, gparent); + } + } + + RBE_COLOR(RBH_ROOT(rbt)) = RB_BLACK; +} + +static inline void rbe_remove_color(struct rbt_tree *rbt, + struct rb_entry *parent, + struct rb_entry *rbe) +{ + struct rb_entry *tmp; + + while ((rbe == NULL || RBE_COLOR(rbe) == RB_BLACK) + && rbe != RBH_ROOT(rbt) && parent) { + if (RBE_LEFT(parent) == rbe) { + tmp = RBE_RIGHT(parent); + if (RBE_COLOR(tmp) == RB_RED) { + rbe_set_blackred(tmp, parent); + rbe_rotate_left(rbt, parent); + tmp = RBE_RIGHT(parent); + } + if ((RBE_LEFT(tmp) == NULL + || RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK) + && (RBE_RIGHT(tmp) == NULL + || RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK)) { + RBE_COLOR(tmp) = RB_RED; + rbe = parent; + parent = RBE_PARENT(rbe); + } else { + if (RBE_RIGHT(tmp) == NULL + || RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK) { + struct rb_entry *oleft; + + oleft = RBE_LEFT(tmp); + if (oleft != NULL) + RBE_COLOR(oleft) = RB_BLACK; + + RBE_COLOR(tmp) = RB_RED; + rbe_rotate_right(rbt, tmp); + tmp = RBE_RIGHT(parent); + } + + RBE_COLOR(tmp) = RBE_COLOR(parent); + RBE_COLOR(parent) = RB_BLACK; + if (RBE_RIGHT(tmp)) + RBE_COLOR(RBE_RIGHT(tmp)) = RB_BLACK; + + rbe_rotate_left(rbt, parent); + rbe = RBH_ROOT(rbt); + break; + } + } else { + tmp = RBE_LEFT(parent); + if (RBE_COLOR(tmp) == RB_RED) { + rbe_set_blackred(tmp, parent); + rbe_rotate_right(rbt, parent); + tmp = RBE_LEFT(parent); + } + + if ((RBE_LEFT(tmp) == NULL + || RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK) + && (RBE_RIGHT(tmp) == NULL + || RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK)) { + RBE_COLOR(tmp) = RB_RED; + rbe = parent; + parent = RBE_PARENT(rbe); + } else { + if (RBE_LEFT(tmp) == NULL + || RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK) { + struct rb_entry *oright; + + oright = RBE_RIGHT(tmp); + if (oright != NULL) + RBE_COLOR(oright) = RB_BLACK; + + RBE_COLOR(tmp) = RB_RED; + rbe_rotate_left(rbt, tmp); + tmp = RBE_LEFT(parent); + } + + RBE_COLOR(tmp) = RBE_COLOR(parent); + RBE_COLOR(parent) = RB_BLACK; + if (RBE_LEFT(tmp) != NULL) + RBE_COLOR(RBE_LEFT(tmp)) = RB_BLACK; + + rbe_rotate_right(rbt, parent); + rbe = RBH_ROOT(rbt); + break; + } + } + } + + if (rbe != NULL) + RBE_COLOR(rbe) = RB_BLACK; +} + +static inline struct rb_entry * +rbe_remove(struct rbt_tree *rbt, struct rb_entry *rbe) +{ + struct rb_entry *child, *parent, *old = rbe; + unsigned int color; + + if (RBE_LEFT(rbe) == NULL) + child = RBE_RIGHT(rbe); + else if (RBE_RIGHT(rbe) == NULL) + child = RBE_LEFT(rbe); + else { + struct rb_entry *tmp; + + rbe = RBE_RIGHT(rbe); + while ((tmp = RBE_LEFT(rbe)) != NULL) + rbe = tmp; + + child = RBE_RIGHT(rbe); + parent = RBE_PARENT(rbe); + color = RBE_COLOR(rbe); + if (child != NULL) + RBE_PARENT(child) = parent; + if (parent != NULL) { + if (RBE_LEFT(parent) == rbe) + RBE_LEFT(parent) = child; + else + RBE_RIGHT(parent) = child; + } else + RBH_ROOT(rbt) = child; + if (RBE_PARENT(rbe) == old) + parent = rbe; + *rbe = *old; + + tmp = RBE_PARENT(old); + if (tmp != NULL) { + if (RBE_LEFT(tmp) == old) + RBE_LEFT(tmp) = rbe; + else + RBE_RIGHT(tmp) = rbe; + } else + RBH_ROOT(rbt) = rbe; + + RBE_PARENT(RBE_LEFT(old)) = rbe; + if (RBE_RIGHT(old)) + RBE_PARENT(RBE_RIGHT(old)) = rbe; + + goto color; + } + + parent = RBE_PARENT(rbe); + color = RBE_COLOR(rbe); + + if (child != NULL) + RBE_PARENT(child) = parent; + if (parent != NULL) { + if (RBE_LEFT(parent) == rbe) + RBE_LEFT(parent) = child; + else + RBE_RIGHT(parent) = child; + } else + RBH_ROOT(rbt) = child; +color: + if (color == RB_BLACK) + rbe_remove_color(rbt, parent, child); + + rbt->count--; + return (old); +} + +void typed_rb_remove(struct rbt_tree *rbt, struct rb_entry *rbe) +{ + rbe_remove(rbt, rbe); +} + +struct typed_rb_entry *typed_rb_insert(struct rbt_tree *rbt, + struct rb_entry *rbe, int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)) +{ + struct rb_entry *tmp; + struct rb_entry *parent = NULL; + int comp = 0; + + tmp = RBH_ROOT(rbt); + while (tmp != NULL) { + parent = tmp; + + comp = cmpfn(rbe, tmp); + if (comp < 0) + tmp = RBE_LEFT(tmp); + else if (comp > 0) + tmp = RBE_RIGHT(tmp); + else + return tmp; + } + + rbe_set(rbe, parent); + + if (parent != NULL) { + if (comp < 0) + RBE_LEFT(parent) = rbe; + else + RBE_RIGHT(parent) = rbe; + } else + RBH_ROOT(rbt) = rbe; + + rbe_insert_color(rbt, rbe); + + return NULL; +} + +/* Finds the node with the same key as elm */ +struct rb_entry *typed_rb_find(struct rbt_tree *rbt, const struct rb_entry *key, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)) +{ + struct rb_entry *tmp = RBH_ROOT(rbt); + int comp; + + while (tmp != NULL) { + comp = cmpfn(key, tmp); + if (comp < 0) + tmp = RBE_LEFT(tmp); + else if (comp > 0) + tmp = RBE_RIGHT(tmp); + else + return tmp; + } + + return (NULL); +} + +struct rb_entry *typed_rb_find_gteq(struct rbt_tree *rbt, + const struct rb_entry *key, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)) +{ + struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; + int comp; + + while (tmp != NULL) { + comp = cmpfn(key, tmp); + if (comp < 0) { + best = tmp; + tmp = RBE_LEFT(tmp); + } else if (comp > 0) + tmp = RBE_RIGHT(tmp); + else + return tmp; + } + + return best; +} + +struct rb_entry *typed_rb_find_lt(struct rbt_tree *rbt, + const struct rb_entry *key, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)) +{ + struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; + int comp; + + while (tmp != NULL) { + comp = cmpfn(key, tmp); + if (comp <= 0) + tmp = RBE_LEFT(tmp); + else { + best = tmp; + tmp = RBE_RIGHT(tmp); + } + } + + return best; +} + +struct rb_entry *typed_rb_next(struct rb_entry *rbe) +{ + if (RBE_RIGHT(rbe) != NULL) { + rbe = RBE_RIGHT(rbe); + while (RBE_LEFT(rbe) != NULL) + rbe = RBE_LEFT(rbe); + } else { + if (RBE_PARENT(rbe) && (rbe == RBE_LEFT(RBE_PARENT(rbe)))) + rbe = RBE_PARENT(rbe); + else { + while (RBE_PARENT(rbe) + && (rbe == RBE_RIGHT(RBE_PARENT(rbe)))) + rbe = RBE_PARENT(rbe); + rbe = RBE_PARENT(rbe); + } + } + + return rbe; +} + +struct rb_entry *typed_rb_min(struct rbt_tree *rbt) +{ + struct rb_entry *rbe = RBH_ROOT(rbt); + struct rb_entry *parent = NULL; + + while (rbe != NULL) { + parent = rbe; + rbe = RBE_LEFT(rbe); + } + + return parent; +} diff --git a/lib/typerb.h b/lib/typerb.h new file mode 100644 index 000000000000..ce8446f853a9 --- /dev/null +++ b/lib/typerb.h @@ -0,0 +1,190 @@ +/* + * The following Red-Black tree implementation is based off code with + * original copyright: + * + * Copyright (c) 2016 David Gwynne + * + * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +#ifndef _FRR_TYPERB_H +#define _FRR_TYPERB_H + +#include "typesafe.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct typed_rb_entry { + struct typed_rb_entry *rbt_parent; + struct typed_rb_entry *rbt_left; + struct typed_rb_entry *rbt_right; + unsigned int rbt_color; +}; + +struct typed_rb_root { + struct typed_rb_entry *rbt_root; + size_t count; +}; + +struct typed_rb_entry *typed_rb_insert(struct typed_rb_root *, + struct typed_rb_entry *rbe, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)); +void typed_rb_remove(struct typed_rb_root *, struct typed_rb_entry *rbe); +struct typed_rb_entry *typed_rb_find(struct typed_rb_root *, + const struct typed_rb_entry *rbe, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)); +struct typed_rb_entry *typed_rb_find_gteq(struct typed_rb_root *, + const struct typed_rb_entry *rbe, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)); +struct typed_rb_entry *typed_rb_find_lt(struct typed_rb_root *, + const struct typed_rb_entry *rbe, + int (*cmpfn)( + const struct typed_rb_entry *a, + const struct typed_rb_entry *b)); +struct typed_rb_entry *typed_rb_min(struct typed_rb_root *); +struct typed_rb_entry *typed_rb_next(struct typed_rb_entry *); + +#define _PREDECL_RBTREE(prefix) \ +struct prefix ## _head { struct typed_rb_root rr; }; \ +struct prefix ## _item { struct typed_rb_entry re; }; + +#define INIT_RBTREE_UNIQ(var) { } +#define INIT_RBTREE_NONUNIQ(var) { } + +#define _DECLARE_RBTREE(prefix, type, field, cmpfn_nuq, cmpfn_uq) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_insert(&h->rr, &item->field.re, cmpfn_uq); \ + return container_of_null(re, type, field.re); \ +} \ +macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ + const type *item) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_find_gteq(&h->rr, &item->field.re, cmpfn_nuq); \ + return container_of_null(re, type, field.re); \ +} \ +macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ + const type *item) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_find_lt(&h->rr, &item->field.re, cmpfn_nuq); \ + return container_of_null(re, type, field.re); \ +} \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + typed_rb_remove(&h->rr, &item->field.re); \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_min(&h->rr); \ + if (!re) \ + return NULL; \ + typed_rb_remove(&h->rr, re); \ + return container_of(re, type, field.re); \ +} \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_min(&h->rr); \ + return container_of_null(re, type, field.re); \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_next(&item->field.re); \ + return container_of_null(re, type, field.re); \ +} \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + struct typed_rb_entry *re; \ + re = item ? typed_rb_next(&item->field.re) : NULL; \ + return container_of_null(re, type, field.re); \ +} \ +macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return h->rr.count; \ +} \ +/* ... */ + +#define PREDECL_RBTREE_UNIQ(prefix) \ + _PREDECL_RBTREE(prefix) +#define DECLARE_RBTREE_UNIQ(prefix, type, field, cmpfn) \ + \ +macro_inline int prefix ## __cmp(const struct typed_rb_entry *a, \ + const struct typed_rb_entry *b) \ +{ \ + return cmpfn(container_of(a, type, field.re), \ + container_of(b, type, field.re)); \ +} \ +macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +{ \ + struct typed_rb_entry *re; \ + re = typed_rb_find(&h->rr, &item->field.re, &prefix ## __cmp); \ + return container_of_null(re, type, field.re); \ +} \ + \ +_DECLARE_RBTREE(prefix, type, field, prefix ## __cmp, prefix ## __cmp) \ +/* ... */ + +#define PREDECL_RBTREE_NONUNIQ(prefix) \ + _PREDECL_RBTREE(prefix) +#define DECLARE_RBTREE_NONUNIQ(prefix, type, field, cmpfn) \ + \ +macro_inline int prefix ## __cmp(const struct typed_rb_entry *a, \ + const struct typed_rb_entry *b) \ +{ \ + return cmpfn(container_of(a, type, field.re), \ + container_of(b, type, field.re)); \ +} \ +macro_inline int prefix ## __cmp_uq(const struct typed_rb_entry *a, \ + const struct typed_rb_entry *b) \ +{ \ + int cmpval = cmpfn(container_of(a, type, field.re), \ + container_of(b, type, field.re)); \ + if (cmpval) \ + return cmpval; \ + if (a < b) \ + return -1; \ + if (a > b) \ + return 1; \ + return 0; \ +} \ + \ +_DECLARE_RBTREE(prefix, type, field, prefix ## __cmp, prefix ## __cmp_uq) \ +/* ... */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_TYPERB_H */ diff --git a/lib/typesafe.c b/lib/typesafe.c new file mode 100644 index 000000000000..47a6d0af487f --- /dev/null +++ b/lib/typesafe.c @@ -0,0 +1,555 @@ +/* + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +#include +#include + +#include "typesafe.h" +#include "memory.h" + +DEFINE_MTYPE_STATIC(LIB, TYPEDHASH_BUCKET, "Typed-hash bucket") +DEFINE_MTYPE_STATIC(LIB, SKIPLIST_OFLOW, "Skiplist overflow") +DEFINE_MTYPE_STATIC(LIB, HEAP_ARRAY, "Typed-heap array") + +#if 0 +static void hash_consistency_check(struct thash_head *head) +{ + uint32_t i; + struct thash_item *item, *prev; + + for (i = 0; i < HASH_SIZE(*head); i++) { + item = head->entries[i]; + prev = NULL; + while (item) { + assert(HASH_KEY(*head, item->hashval) == i); + assert(!prev || item->hashval >= prev->hashval); + prev = item; + item = item->next; + } + } +} +#else +#define hash_consistency_check(x) +#endif + +void typesafe_hash_grow(struct thash_head *head) +{ + uint32_t newsize = head->count, i, j; + uint8_t newshift, delta; + + hash_consistency_check(head); + + newsize |= newsize >> 1; + newsize |= newsize >> 2; + newsize |= newsize >> 4; + newsize |= newsize >> 8; + newsize |= newsize >> 16; + newsize++; + newshift = __builtin_ctz(newsize) + 1; + + if (head->maxshift && newshift > head->maxshift) + newshift = head->maxshift; + if (newshift == head->tabshift) + return; + newsize = _HASH_SIZE(newshift); + + head->entries = XREALLOC(MTYPE_TYPEDHASH_BUCKET, head->entries, + sizeof(head->entries[0]) * newsize); + memset(head->entries + HASH_SIZE(*head), 0, + sizeof(head->entries[0]) * + (newsize - HASH_SIZE(*head))); + + delta = newshift - head->tabshift; + + i = HASH_SIZE(*head); + if (i == 0) + goto out; + do { + struct thash_item **apos, *item; + + i--; + apos = &head->entries[i]; + + for (j = 0; j < (1U << delta); j++) { + item = *apos; + *apos = NULL; + + head->entries[(i << delta) + j] = item; + apos = &head->entries[(i << delta) + j]; + + while ((item = *apos)) { + uint32_t midbits; + midbits = _HASH_KEY(newshift, item->hashval); + midbits &= (1 << delta) - 1; + if (midbits > j) + break; + apos = &item->next; + } + } + } while (i > 0); + +out: + head->tabshift = newshift; + hash_consistency_check(head); +} + +void typesafe_hash_shrink(struct thash_head *head) +{ + uint32_t newsize = head->count, i, j; + uint8_t newshift, delta; + + hash_consistency_check(head); + + if (!head->count) { + XFREE(MTYPE_TYPEDHASH_BUCKET, head->entries); + head->tabshift = 0; + return; + } + + newsize |= newsize >> 1; + newsize |= newsize >> 2; + newsize |= newsize >> 4; + newsize |= newsize >> 8; + newsize |= newsize >> 16; + newsize++; + newshift = __builtin_ctz(newsize) + 1; + + if (head->minshift && newshift < head->minshift) + newshift = head->minshift; + if (newshift == head->tabshift) + return; + newsize = _HASH_SIZE(newshift); + + delta = head->tabshift - newshift; + + for (i = 0; i < newsize; i++) { + struct thash_item **apos = &head->entries[i]; + + for (j = 0; j < (1U << delta); j++) { + *apos = head->entries[(i << delta) + j]; + while (*apos) + apos = &(*apos)->next; + } + } + head->entries = XREALLOC(MTYPE_TYPEDHASH_BUCKET, head->entries, + sizeof(head->entries[0]) * newsize); + head->tabshift = newshift; + + hash_consistency_check(head); +} + +/* skiplist */ + +static inline struct sskip_item *sl_level_get(struct sskip_item *item, + size_t level) +{ + if (level < SKIPLIST_OVERFLOW) + return item->next[level]; + if (level == SKIPLIST_OVERFLOW && !((uintptr_t)item->next[level] & 1)) + return item->next[level]; + + uintptr_t ptrval = (uintptr_t)item->next[SKIPLIST_OVERFLOW]; + ptrval &= UINTPTR_MAX - 3; + struct sskip_overflow *oflow = (struct sskip_overflow *)ptrval; + return oflow->next[level - SKIPLIST_OVERFLOW]; +} + +static inline void sl_level_set(struct sskip_item *item, size_t level, + struct sskip_item *value) +{ + if (level < SKIPLIST_OVERFLOW) + item->next[level] = value; + else if (level == SKIPLIST_OVERFLOW && !((uintptr_t)item->next[level] & 1)) + item->next[level] = value; + else { + uintptr_t ptrval = (uintptr_t)item->next[SKIPLIST_OVERFLOW]; + ptrval &= UINTPTR_MAX - 3; + struct sskip_overflow *oflow = (struct sskip_overflow *)ptrval; + oflow->next[level - SKIPLIST_OVERFLOW] = value; + } +} + +struct sskip_item *typesafe_skiplist_add(struct sskip_head *head, + struct sskip_item *item, + int (*cmpfn)(const struct sskip_item *a, + const struct sskip_item *b)) +{ + size_t level = SKIPLIST_MAXDEPTH, newlevel, auxlevel; + struct sskip_item *prev = &head->hitem, *next, *auxprev, *auxnext; + int cmpval; + + /* level / newlevel are 1-counted here */ + newlevel = __builtin_ctz(random()) + 1; + if (newlevel > SKIPLIST_MAXDEPTH) + newlevel = SKIPLIST_MAXDEPTH; + + next = NULL; + while (level >= newlevel) { + next = sl_level_get(prev, level - 1); + if (!next) { + level--; + continue; + } + cmpval = cmpfn(next, item); + if (cmpval < 0) { + prev = next; + continue; + } else if (cmpval == 0) { + return next; + } + level--; + } + + /* check for duplicate item - could be removed if code doesn't rely + * on it, but not really work the complication. */ + auxlevel = level; + auxprev = prev; + while (auxlevel) { + auxlevel--; + auxnext = sl_level_get(auxprev, auxlevel); + cmpval = 1; + while (auxnext && (cmpval = cmpfn(auxnext, item)) < 0) { + auxprev = auxnext; + auxnext = sl_level_get(auxprev, auxlevel); + } + if (cmpval == 0) + return auxnext; + }; + + head->count++; + memset(item, 0, sizeof(*item)); + if (newlevel > SKIPLIST_EMBED) { + struct sskip_overflow *oflow; + oflow = XMALLOC(MTYPE_SKIPLIST_OFLOW, sizeof(void *) + * (newlevel - SKIPLIST_OVERFLOW)); + item->next[SKIPLIST_OVERFLOW] = (struct sskip_item *) + ((uintptr_t)oflow | 1); + } + + sl_level_set(item, level, next); + sl_level_set(prev, level, item); + /* level is now 0-counted and < newlevel*/ + while (level) { + level--; + next = sl_level_get(prev, level); + while (next && cmpfn(next, item) < 0) { + prev = next; + next = sl_level_get(prev, level); + } + + sl_level_set(item, level, next); + sl_level_set(prev, level, item); + }; + return NULL; +} + +/* NOTE: level counting below is 1-based since that makes the code simpler! */ + +struct sskip_item *typesafe_skiplist_find(struct sskip_head *head, + const struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)) +{ + size_t level = SKIPLIST_MAXDEPTH; + struct sskip_item *prev = &head->hitem, *next; + int cmpval; + + while (level) { + next = sl_level_get(prev, level - 1); + if (!next) { + level--; + continue; + } + cmpval = cmpfn(next, item); + if (cmpval < 0) { + prev = next; + continue; + } + if (cmpval == 0) + return next; + level--; + } + return NULL; +} + +struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head, + const struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)) +{ + size_t level = SKIPLIST_MAXDEPTH; + struct sskip_item *prev = &head->hitem, *next; + int cmpval; + + while (level) { + next = sl_level_get(prev, level - 1); + if (!next) { + level--; + continue; + } + cmpval = cmpfn(next, item); + if (cmpval < 0) { + prev = next; + continue; + } + if (cmpval == 0) + return next; + level--; + } + return next; +} + +struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head, + const struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)) +{ + size_t level = SKIPLIST_MAXDEPTH; + struct sskip_item *prev = &head->hitem, *next, *best = NULL; + int cmpval; + + while (level) { + next = sl_level_get(prev, level - 1); + if (!next) { + level--; + continue; + } + cmpval = cmpfn(next, item); + if (cmpval < 0) { + best = prev = next; + continue; + } + level--; + } + return best; +} + +void typesafe_skiplist_del(struct sskip_head *head, struct sskip_item *item, + int (*cmpfn)(const struct sskip_item *a, + const struct sskip_item *b)) +{ + size_t level = SKIPLIST_MAXDEPTH; + struct sskip_item *prev = &head->hitem, *next; + int cmpval; + + while (level) { + next = sl_level_get(prev, level - 1); + if (!next) { + level--; + continue; + } + if (next == item) { + sl_level_set(prev, level - 1, + sl_level_get(item, level - 1)); + level--; + continue; + } + cmpval = cmpfn(next, item); + if (cmpval < 0) { + prev = next; + continue; + } + level--; + } + + /* TBD: assert when trying to remove non-existing item? */ + head->count--; + + if ((uintptr_t)item->next[SKIPLIST_OVERFLOW] & 1) { + uintptr_t ptrval = (uintptr_t)item->next[SKIPLIST_OVERFLOW]; + ptrval &= UINTPTR_MAX - 3; + struct sskip_overflow *oflow = (struct sskip_overflow *)ptrval; + XFREE(MTYPE_SKIPLIST_OFLOW, oflow); + } + memset(item, 0, sizeof(*item)); +} + +struct sskip_item *typesafe_skiplist_pop(struct sskip_head *head) +{ + size_t level = SKIPLIST_MAXDEPTH; + struct sskip_item *prev = &head->hitem, *next, *item; + + item = sl_level_get(prev, 0); + if (!item) + return NULL; + + do { + level--; + + next = sl_level_get(prev, level); + if (next != item) + continue; + + sl_level_set(prev, level, sl_level_get(item, level)); + } while (level); + + head->count--; + + if ((uintptr_t)item->next[SKIPLIST_OVERFLOW] & 1) { + uintptr_t ptrval = (uintptr_t)item->next[SKIPLIST_OVERFLOW]; + ptrval &= UINTPTR_MAX - 3; + struct sskip_overflow *oflow = (struct sskip_overflow *)ptrval; + XFREE(MTYPE_SKIPLIST_OFLOW, oflow); + } + memset(item, 0, sizeof(*item)); + + return item; +} + +/* heap */ + +#if 0 +static void heap_consistency_check(struct heap_head *head, + int (*cmpfn)(const struct heap_item *a, + const struct heap_item *b), + uint32_t pos) +{ + uint32_t rghtpos = pos + 1; + uint32_t downpos = HEAP_NARY * (pos + 1); + + if (pos + 1 > ~0U / HEAP_NARY) + downpos = ~0U; + + if ((pos & (HEAP_NARY - 1)) != HEAP_NARY - 1 && rghtpos < head->count) { + assert(cmpfn(head->array[rghtpos], head->array[pos]) >= 0); + heap_consistency_check(head, cmpfn, rghtpos); + } + if (downpos < head->count) { + assert(cmpfn(head->array[downpos], head->array[pos]) >= 0); + heap_consistency_check(head, cmpfn, downpos); + } +} +#else +#define heap_consistency_check(head, cmpfn, pos) +#endif + +void typesafe_heap_resize(struct heap_head *head, bool grow) +{ + uint32_t newsize; + + if (grow) { + newsize = head->arraysz; + if (newsize <= 36) + newsize = 72; + else if (newsize < 262144) + newsize += newsize / 2; + else if (newsize < 0xaaaa0000) + newsize += newsize / 3; + else + assert(!newsize); + } else if (head->count > 0) { + newsize = head->count; + } else { + XFREE(MTYPE_HEAP_ARRAY, head->array); + head->arraysz = 0; + return; + } + + newsize += HEAP_NARY - 1; + newsize &= ~(HEAP_NARY - 1); + if (newsize == head->arraysz) + return; + + head->array = XREALLOC(MTYPE_HEAP_ARRAY, head->array, + newsize * sizeof(struct heap_item *)); + head->arraysz = newsize; +} + +void typesafe_heap_pushdown(struct heap_head *head, uint32_t pos, + struct heap_item *item, + int (*cmpfn)(const struct heap_item *a, + const struct heap_item *b)) +{ + uint32_t rghtpos, downpos, moveto; + + while (1) { + /* rghtpos: neighbor to the "right", inside block of NARY. + * may be invalid if last in block, check nary_last() + * downpos: first neighbor in the "downwards" block further + * away from the root + */ + rghtpos = pos + 1; + + /* make sure we can use the full 4G items */ + downpos = HEAP_NARY * (pos + 1); + if (pos + 1 > ~0U / HEAP_NARY) + /* multiplication overflowed. ~0U is guaranteed + * to be an invalid index; size limit is enforced in + * resize() + */ + downpos = ~0U; + + /* only used on break */ + moveto = pos; + +#define nary_last(x) (((x) & (HEAP_NARY - 1)) == HEAP_NARY - 1) + if (downpos >= head->count + || cmpfn(head->array[downpos], item) >= 0) { + /* not moving down; either at end or down is >= item */ + if (nary_last(pos) || rghtpos >= head->count + || cmpfn(head->array[rghtpos], item) >= 0) + /* not moving right either - got our spot */ + break; + + moveto = rghtpos; + + /* else: downpos is valid and < item. choose between down + * or right (if the latter is an option) */ + } else if (nary_last(pos) || cmpfn(head->array[rghtpos], + head->array[downpos]) >= 0) + moveto = downpos; + else + moveto = rghtpos; +#undef nary_last + + head->array[pos] = head->array[moveto]; + head->array[pos]->index = pos; + pos = moveto; + } + + head->array[moveto] = item; + item->index = moveto; + + heap_consistency_check(head, cmpfn, 0); +} + +void typesafe_heap_pullup(struct heap_head *head, uint32_t pos, + struct heap_item *item, + int (*cmpfn)(const struct heap_item *a, + const struct heap_item *b)) +{ + uint32_t moveto; + + while (pos != 0) { + if ((pos & (HEAP_NARY - 1)) == 0) + moveto = pos / HEAP_NARY - 1; + else + moveto = pos - 1; + + if (cmpfn(head->array[moveto], item) <= 0) + break; + + head->array[pos] = head->array[moveto]; + head->array[pos]->index = pos; + + pos = moveto; + } + + head->array[pos] = item; + item->index = pos; + + heap_consistency_check(head, cmpfn, 0); +} diff --git a/lib/typesafe.h b/lib/typesafe.h new file mode 100644 index 000000000000..0a4ed69e4eb6 --- /dev/null +++ b/lib/typesafe.h @@ -0,0 +1,866 @@ +/* + * Copyright (c) 2016-2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +#ifndef _FRR_TYPESAFE_H +#define _FRR_TYPESAFE_H + +#include +#include +#include +#include +#include "compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* generic macros for all list-like types */ + +#define frr_each(prefix, head, item) \ + for (item = prefix##_first(head); item; \ + item = prefix##_next(head, item)) +#define frr_each_safe(prefix, head, item) \ + for (typeof(prefix##_next_safe(head, NULL)) prefix##_safe = \ + prefix##_next_safe(head, \ + (item = prefix##_first(head))); \ + item; \ + item = prefix##_safe, \ + prefix##_safe = prefix##_next_safe(head, prefix##_safe)) +#define frr_each_from(prefix, head, item, from) \ + for (item = from, from = prefix##_next_safe(head, item); \ + item; \ + item = from, from = prefix##_next_safe(head, from)) + +/* single-linked list, unsorted/arbitrary. + * can be used as queue with add_tail / pop + */ + +/* don't use these structs directly */ +struct slist_item { + struct slist_item *next; +}; + +struct slist_head { + struct slist_item *first, **last_next; + size_t count; +}; + +static inline void typesafe_list_add(struct slist_head *head, + struct slist_item **pos, struct slist_item *item) +{ + item->next = *pos; + *pos = item; + if (pos == head->last_next) + head->last_next = &item->next; + head->count++; +} + +/* use as: + * + * PREDECL_LIST(namelist) + * struct name { + * struct namelist_item nlitem; + * } + * DECLARE_LIST(namelist, struct name, nlitem) + */ +#define PREDECL_LIST(prefix) \ +struct prefix ## _head { struct slist_head sh; }; \ +struct prefix ## _item { struct slist_item si; }; + +#define INIT_LIST(var) { .sh = { .last_next = &var.sh.first, }, } + +#define DECLARE_LIST(prefix, type, field) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ + h->sh.last_next = &h->sh.first; \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _add_head(struct prefix##_head *h, type *item) \ +{ \ + typesafe_list_add(&h->sh, &h->sh.first, &item->field.si); \ +} \ +macro_inline void prefix ## _add_tail(struct prefix##_head *h, type *item) \ +{ \ + typesafe_list_add(&h->sh, h->sh.last_next, &item->field.si); \ +} \ +macro_inline void prefix ## _add_after(struct prefix##_head *h, \ + type *after, type *item) \ +{ \ + struct slist_item **nextp; \ + nextp = after ? &after->field.si.next : &h->sh.first; \ + typesafe_list_add(&h->sh, nextp, &item->field.si); \ +} \ +/* TODO: del_hint */ \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + struct slist_item **iter = &h->sh.first; \ + while (*iter && *iter != &item->field.si) \ + iter = &(*iter)->next; \ + if (!*iter) \ + return; \ + h->sh.count--; \ + *iter = item->field.si.next; \ + if (!item->field.si.next) \ + h->sh.last_next = iter; \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct slist_item *sitem = h->sh.first; \ + if (!sitem) \ + return NULL; \ + h->sh.count--; \ + h->sh.first = sitem->next; \ + if (h->sh.first == NULL) \ + h->sh.last_next = &h->sh.first; \ + return container_of(sitem, type, field.si); \ +} \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + return container_of_null(h->sh.first, type, field.si); \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head * h, type *item) \ +{ \ + struct slist_item *sitem = &item->field.si; \ + return container_of_null(sitem->next, type, field.si); \ +} \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + struct slist_item *sitem; \ + if (!item) \ + return NULL; \ + sitem = &item->field.si; \ + return container_of_null(sitem->next, type, field.si); \ +} \ +macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return h->sh.count; \ +} \ +/* ... */ + +/* don't use these structs directly */ +struct dlist_item { + struct dlist_item *next; + struct dlist_item *prev; +}; + +struct dlist_head { + struct dlist_item hitem; + size_t count; +}; + +static inline void typesafe_dlist_add(struct dlist_head *head, + struct dlist_item *prev, struct dlist_item *item) +{ + item->next = prev->next; + item->next->prev = item; + item->prev = prev; + prev->next = item; + head->count++; +} + +/* double-linked list, for fast item deletion + */ +#define PREDECL_DLIST(prefix) \ +struct prefix ## _head { struct dlist_head dh; }; \ +struct prefix ## _item { struct dlist_item di; }; + +#define INIT_DLIST(var) { .dh = { \ + .hitem = { &var.dh.hitem, &var.dh.hitem }, }, } + +#define DECLARE_DLIST(prefix, type, field) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ + h->dh.hitem.prev = &h->dh.hitem; \ + h->dh.hitem.next = &h->dh.hitem; \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _add_head(struct prefix##_head *h, type *item) \ +{ \ + typesafe_dlist_add(&h->dh, &h->dh.hitem, &item->field.di); \ +} \ +macro_inline void prefix ## _add_tail(struct prefix##_head *h, type *item) \ +{ \ + typesafe_dlist_add(&h->dh, h->dh.hitem.prev, &item->field.di); \ +} \ +macro_inline void prefix ## _add_after(struct prefix##_head *h, \ + type *after, type *item) \ +{ \ + struct dlist_item *prev; \ + prev = after ? &after->field.di : &h->dh.hitem; \ + typesafe_dlist_add(&h->dh, prev, &item->field.di); \ +} \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + struct dlist_item *ditem = &item->field.di; \ + ditem->prev->next = ditem->next; \ + ditem->next->prev = ditem->prev; \ + h->dh.count--; \ + ditem->prev = ditem->next = NULL; \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct dlist_item *ditem = h->dh.hitem.next; \ + if (ditem == &h->dh.hitem) \ + return NULL; \ + ditem->prev->next = ditem->next; \ + ditem->next->prev = ditem->prev; \ + h->dh.count--; \ + return container_of(ditem, type, field.di); \ +} \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + struct dlist_item *ditem = h->dh.hitem.next; \ + if (ditem == &h->dh.hitem) \ + return NULL; \ + return container_of(ditem, type, field.di); \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head * h, type *item) \ +{ \ + struct dlist_item *ditem = &item->field.di; \ + if (ditem->next == &h->dh.hitem) \ + return NULL; \ + return container_of(ditem->next, type, field.di); \ +} \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + if (!item) \ + return NULL; \ + return prefix ## _next(h, item); \ +} \ +macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return h->dh.count; \ +} \ +/* ... */ + +/* note: heap currently caps out at 4G items */ + +#define HEAP_NARY 8U +typedef uint32_t heap_index_i; + +struct heap_item { + uint32_t index; +}; + +struct heap_head { + struct heap_item **array; + uint32_t arraysz, count; +}; + +#define HEAP_RESIZE_TRESH_UP(h) \ + (h->hh.count + 1 >= h->hh.arraysz) +#define HEAP_RESIZE_TRESH_DN(h) \ + (h->hh.count == 0 || \ + h->hh.arraysz - h->hh.count > (h->hh.count + 1024) / 2) + +#define PREDECL_HEAP(prefix) \ +struct prefix ## _head { struct heap_head hh; }; \ +struct prefix ## _item { struct heap_item hi; }; + +#define INIT_HEAP(var) { } + +#define DECLARE_HEAP(prefix, type, field, cmpfn) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + assert(h->hh.count == 0); \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline int prefix ## __cmp(const struct heap_item *a, \ + const struct heap_item *b) \ +{ \ + return cmpfn(container_of(a, type, field.hi), \ + container_of(b, type, field.hi)); \ +} \ +macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ +{ \ + if (HEAP_RESIZE_TRESH_UP(h)) \ + typesafe_heap_resize(&h->hh, true); \ + typesafe_heap_pullup(&h->hh, h->hh.count, &item->field.hi, \ + prefix ## __cmp); \ + h->hh.count++; \ + return NULL; \ +} \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + struct heap_item *other; \ + uint32_t index = item->field.hi.index; \ + assert(h->hh.array[index] == &item->field.hi); \ + h->hh.count--; \ + other = h->hh.array[h->hh.count]; \ + if (cmpfn(container_of(other, type, field.hi), item) < 0) \ + typesafe_heap_pullup(&h->hh, index, other, prefix ## __cmp); \ + else \ + typesafe_heap_pushdown(&h->hh, index, other, prefix ## __cmp); \ + if (HEAP_RESIZE_TRESH_DN(h)) \ + typesafe_heap_resize(&h->hh, false); \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct heap_item *hitem, *other; \ + if (h->hh.count == 0) \ + return NULL; \ + hitem = h->hh.array[0]; \ + h->hh.count--; \ + other = h->hh.array[h->hh.count]; \ + typesafe_heap_pushdown(&h->hh, 0, other, prefix ## __cmp); \ + if (HEAP_RESIZE_TRESH_DN(h)) \ + typesafe_heap_resize(&h->hh, false); \ + return container_of(hitem, type, field.hi); \ +} \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + if (h->hh.count == 0) \ + return NULL; \ + return container_of(h->hh.array[0], type, field.hi); \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ \ + uint32_t idx = item->field.hi.index + 1; \ + if (idx >= h->hh.count) \ + return NULL; \ + return container_of(h->hh.array[idx], type, field.hi); \ +} \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + if (!item) \ + return NULL; \ + return prefix ## _next(h, item); \ +} \ +macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return h->hh.count; \ +} \ +/* ... */ + +extern void typesafe_heap_resize(struct heap_head *head, bool grow); +extern void typesafe_heap_pushdown(struct heap_head *head, uint32_t index, + struct heap_item *item, + int (*cmpfn)(const struct heap_item *a, + const struct heap_item *b)); +extern void typesafe_heap_pullup(struct heap_head *head, uint32_t index, + struct heap_item *item, + int (*cmpfn)(const struct heap_item *a, + const struct heap_item *b)); + +/* single-linked list, sorted. + * can be used as priority queue with add / pop + */ + +/* don't use these structs directly */ +struct ssort_item { + struct ssort_item *next; +}; + +struct ssort_head { + struct ssort_item *first; + size_t count; +}; + +/* use as: + * + * PREDECL_SORTLIST(namelist) + * struct name { + * struct namelist_item nlitem; + * } + * DECLARE_SORTLIST(namelist, struct name, nlitem) + */ +#define _PREDECL_SORTLIST(prefix) \ +struct prefix ## _head { struct ssort_head sh; }; \ +struct prefix ## _item { struct ssort_item si; }; + +#define INIT_SORTLIST_UNIQ(var) { } +#define INIT_SORTLIST_NONUNIQ(var) { } + +#define PREDECL_SORTLIST_UNIQ(prefix) \ + _PREDECL_SORTLIST(prefix) +#define PREDECL_SORTLIST_NONUNIQ(prefix) \ + _PREDECL_SORTLIST(prefix) + +#define _DECLARE_SORTLIST(prefix, type, field, cmpfn_nuq, cmpfn_uq) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ +{ \ + struct ssort_item **np = &h->sh.first; \ + int c = 1; \ + while (*np && (c = cmpfn_uq( \ + container_of(*np, type, field.si), item)) < 0) \ + np = &(*np)->next; \ + if (c == 0) \ + return container_of(*np, type, field.si); \ + item->field.si.next = *np; \ + *np = &item->field.si; \ + h->sh.count++; \ + return NULL; \ +} \ +macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ + const type *item) \ +{ \ + struct ssort_item *sitem = h->sh.first; \ + int cmpval = 0; \ + while (sitem && (cmpval = cmpfn_nuq( \ + container_of(sitem, type, field.si), item) < 0)) \ + sitem = sitem->next; \ + return container_of_null(sitem, type, field.si); \ +} \ +macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ + const type *item) \ +{ \ + struct ssort_item *prev = NULL, *sitem = h->sh.first; \ + int cmpval = 0; \ + while (sitem && (cmpval = cmpfn_nuq( \ + container_of(sitem, type, field.si), item) < 0)) \ + sitem = (prev = sitem)->next; \ + return container_of_null(prev, type, field.si); \ +} \ +/* TODO: del_hint */ \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + struct ssort_item **iter = &h->sh.first; \ + while (*iter && *iter != &item->field.si) \ + iter = &(*iter)->next; \ + if (!*iter) \ + return; \ + h->sh.count--; \ + *iter = item->field.si.next; \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct ssort_item *sitem = h->sh.first; \ + if (!sitem) \ + return NULL; \ + h->sh.count--; \ + h->sh.first = sitem->next; \ + return container_of(sitem, type, field.si); \ +} \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + return container_of_null(h->sh.first, type, field.si); \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ \ + struct ssort_item *sitem = &item->field.si; \ + return container_of_null(sitem->next, type, field.si); \ +} \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + struct ssort_item *sitem; \ + if (!item) \ + return NULL; \ + sitem = &item->field.si; \ + return container_of_null(sitem->next, type, field.si); \ +} \ +macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return h->sh.count; \ +} \ +/* ... */ + +#define DECLARE_SORTLIST_UNIQ(prefix, type, field, cmpfn) \ + _DECLARE_SORTLIST(prefix, type, field, cmpfn, cmpfn) \ + \ +macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +{ \ + struct ssort_item *sitem = h->sh.first; \ + int cmpval = 0; \ + while (sitem && (cmpval = cmpfn( \ + container_of(sitem, type, field.si), item) < 0)) \ + sitem = sitem->next; \ + if (!sitem || cmpval > 0) \ + return NULL; \ + return container_of(sitem, type, field.si); \ +} \ +/* ... */ + +#define DECLARE_SORTLIST_NONUNIQ(prefix, type, field, cmpfn) \ +macro_inline int _ ## prefix ## _cmp(const type *a, const type *b) \ +{ \ + int cmpval = cmpfn(a, b); \ + if (cmpval) \ + return cmpval; \ + if (a < b) \ + return -1; \ + if (a > b) \ + return 1; \ + return 0; \ +} \ + _DECLARE_SORTLIST(prefix, type, field, cmpfn, _ ## prefix ## _cmp) \ +/* ... */ + + +/* hash, "sorted" by hash value + */ + +/* don't use these structs directly */ +struct thash_item { + struct thash_item *next; + uint32_t hashval; +}; + +struct thash_head { + struct thash_item **entries; + uint32_t count; + + uint8_t tabshift; + uint8_t minshift, maxshift; +}; + +#define _HASH_SIZE(tabshift) \ + ((1U << (tabshift)) >> 1) +#define HASH_SIZE(head) \ + _HASH_SIZE((head).tabshift) +#define _HASH_KEY(tabshift, val) \ + ((val) >> (33 - (tabshift))) +#define HASH_KEY(head, val) \ + _HASH_KEY((head).tabshift, val) +#define HASH_GROW_THRESHOLD(head) \ + ((head).count >= HASH_SIZE(head)) +#define HASH_SHRINK_THRESHOLD(head) \ + ((head).count <= (HASH_SIZE(head) - 1) / 2) + +extern void typesafe_hash_grow(struct thash_head *head); +extern void typesafe_hash_shrink(struct thash_head *head); + +/* use as: + * + * PREDECL_HASH(namelist) + * struct name { + * struct namelist_item nlitem; + * } + * DECLARE_HASH(namelist, struct name, nlitem, cmpfunc, hashfunc) + */ +#define PREDECL_HASH(prefix) \ +struct prefix ## _head { struct thash_head hh; }; \ +struct prefix ## _item { struct thash_item hi; }; + +#define INIT_HASH(var) { } + +#define DECLARE_HASH(prefix, type, field, cmpfn, hashfn) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + assert(h->hh.count == 0); \ + h->hh.minshift = 0; \ + typesafe_hash_shrink(&h->hh); \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ +{ \ + h->hh.count++; \ + if (!h->hh.tabshift || HASH_GROW_THRESHOLD(h->hh)) \ + typesafe_hash_grow(&h->hh); \ + \ + uint32_t hval = hashfn(item), hbits = HASH_KEY(h->hh, hval); \ + item->field.hi.hashval = hval; \ + struct thash_item **np = &h->hh.entries[hbits]; \ + while (*np && (*np)->hashval < hval) \ + np = &(*np)->next; \ + if (*np && cmpfn(container_of(*np, type, field.hi), item) == 0) { \ + h->hh.count--; \ + return container_of(*np, type, field.hi); \ + } \ + item->field.hi.next = *np; \ + *np = &item->field.hi; \ + return NULL; \ +} \ +macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +{ \ + if (!h->hh.tabshift) \ + return NULL; \ + uint32_t hval = hashfn(item), hbits = HASH_KEY(h->hh, hval); \ + struct thash_item *hitem = h->hh.entries[hbits]; \ + while (hitem && hitem->hashval < hval) \ + hitem = hitem->next; \ + while (hitem && hitem->hashval == hval) { \ + if (!cmpfn(container_of(hitem, type, field.hi), item)) \ + return container_of(hitem, type, field.hi); \ + hitem = hitem->next; \ + } \ + return NULL; \ +} \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + if (!h->hh.tabshift) \ + return; \ + uint32_t hval = item->field.hi.hashval, hbits = HASH_KEY(h->hh, hval); \ + struct thash_item **np = &h->hh.entries[hbits]; \ + while (*np && (*np)->hashval < hval) \ + np = &(*np)->next; \ + while (*np && *np != &item->field.hi && (*np)->hashval == hval) \ + np = &(*np)->next; \ + if (*np != &item->field.hi) \ + return; \ + *np = item->field.hi.next; \ + item->field.hi.next = NULL; \ + h->hh.count--; \ + if (HASH_SHRINK_THRESHOLD(h->hh)) \ + typesafe_hash_shrink(&h->hh); \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + uint32_t i; \ + for (i = 0; i < HASH_SIZE(h->hh); i++) \ + if (h->hh.entries[i]) { \ + struct thash_item *hitem = h->hh.entries[i]; \ + h->hh.entries[i] = hitem->next; \ + h->hh.count--; \ + hitem->next = NULL; \ + if (HASH_SHRINK_THRESHOLD(h->hh)) \ + typesafe_hash_shrink(&h->hh); \ + return container_of(hitem, type, field.hi); \ + } \ + return NULL; \ +} \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + uint32_t i; \ + for (i = 0; i < HASH_SIZE(h->hh); i++) \ + if (h->hh.entries[i]) \ + return container_of(h->hh.entries[i], type, field.hi); \ + return NULL; \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ \ + struct thash_item *hitem = &item->field.hi; \ + if (hitem->next) \ + return container_of(hitem->next, type, field.hi); \ + uint32_t i = HASH_KEY(h->hh, hitem->hashval) + 1; \ + for (; i < HASH_SIZE(h->hh); i++) \ + if (h->hh.entries[i]) \ + return container_of(h->hh.entries[i], type, field.hi); \ + return NULL; \ +} \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + if (!item) \ + return NULL; \ + return prefix ## _next(h, item); \ +} \ +macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return h->hh.count; \ +} \ +/* ... */ + +/* skiplist, sorted. + * can be used as priority queue with add / pop + */ + +/* don't use these structs directly */ +#define SKIPLIST_MAXDEPTH 16 +#define SKIPLIST_EMBED 4 +#define SKIPLIST_OVERFLOW (SKIPLIST_EMBED - 1) + +struct sskip_item { + struct sskip_item *next[SKIPLIST_EMBED]; +}; + +struct sskip_overflow { + struct sskip_item *next[SKIPLIST_MAXDEPTH - SKIPLIST_OVERFLOW]; +}; + +struct sskip_head { + struct sskip_item hitem; + struct sskip_item *overflow[SKIPLIST_MAXDEPTH - SKIPLIST_OVERFLOW]; + size_t count; +}; + +/* use as: + * + * PREDECL_SKIPLIST(namelist) + * struct name { + * struct namelist_item nlitem; + * } + * DECLARE_SKIPLIST(namelist, struct name, nlitem, cmpfunc) + */ +#define _PREDECL_SKIPLIST(prefix) \ +struct prefix ## _head { struct sskip_head sh; }; \ +struct prefix ## _item { struct sskip_item si; }; + +#define INIT_SKIPLIST_UNIQ(var) { } +#define INIT_SKIPLIST_NONUNIQ(var) { } + +#define _DECLARE_SKIPLIST(prefix, type, field, cmpfn_nuq, cmpfn_uq) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ + h->sh.hitem.next[SKIPLIST_OVERFLOW] = (struct sskip_item *) \ + ((uintptr_t)h->sh.overflow | 1); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ +{ \ + struct sskip_item *si; \ + si = typesafe_skiplist_add(&h->sh, &item->field.si, cmpfn_uq); \ + return container_of_null(si, type, field.si); \ +} \ +macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ + const type *item) \ +{ \ + struct sskip_item *sitem = typesafe_skiplist_find_gteq(&h->sh, \ + &item->field.si, cmpfn_nuq); \ + return container_of_null(sitem, type, field.si); \ +} \ +macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ + const type *item) \ +{ \ + struct sskip_item *sitem = typesafe_skiplist_find_lt(&h->sh, \ + &item->field.si, cmpfn_nuq); \ + return container_of_null(sitem, type, field.si); \ +} \ +macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + typesafe_skiplist_del(&h->sh, &item->field.si, cmpfn_uq); \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct sskip_item *sitem = typesafe_skiplist_pop(&h->sh); \ + return container_of_null(sitem, type, field.si); \ +} \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + struct sskip_item *first = h->sh.hitem.next[0]; \ + return container_of_null(first, type, field.si); \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ \ + struct sskip_item *next = item->field.si.next[0]; \ + return container_of_null(next, type, field.si); \ +} \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + struct sskip_item *next; \ + next = item ? item->field.si.next[0] : NULL; \ + return container_of_null(next, type, field.si); \ +} \ +macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +{ \ + return h->sh.count; \ +} \ +/* ... */ + +#define PREDECL_SKIPLIST_UNIQ(prefix) \ + _PREDECL_SKIPLIST(prefix) +#define DECLARE_SKIPLIST_UNIQ(prefix, type, field, cmpfn) \ + \ +macro_inline int prefix ## __cmp(const struct sskip_item *a, \ + const struct sskip_item *b) \ +{ \ + return cmpfn(container_of(a, type, field.si), \ + container_of(b, type, field.si)); \ +} \ +macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +{ \ + struct sskip_item *sitem = typesafe_skiplist_find(&h->sh, \ + &item->field.si, &prefix ## __cmp); \ + return container_of_null(sitem, type, field.si); \ +} \ + \ +_DECLARE_SKIPLIST(prefix, type, field, \ + prefix ## __cmp, prefix ## __cmp) \ +/* ... */ + +#define PREDECL_SKIPLIST_NONUNIQ(prefix) \ + _PREDECL_SKIPLIST(prefix) +#define DECLARE_SKIPLIST_NONUNIQ(prefix, type, field, cmpfn) \ + \ +macro_inline int prefix ## __cmp(const struct sskip_item *a, \ + const struct sskip_item *b) \ +{ \ + return cmpfn(container_of(a, type, field.si), \ + container_of(b, type, field.si)); \ +} \ +macro_inline int prefix ## __cmp_uq(const struct sskip_item *a, \ + const struct sskip_item *b) \ +{ \ + int cmpval = cmpfn(container_of(a, type, field.si), \ + container_of(b, type, field.si)); \ + if (cmpval) \ + return cmpval; \ + if (a < b) \ + return -1; \ + if (a > b) \ + return 1; \ + return 0; \ +} \ + \ +_DECLARE_SKIPLIST(prefix, type, field, \ + prefix ## __cmp, prefix ## __cmp_uq) \ +/* ... */ + + +extern struct sskip_item *typesafe_skiplist_add(struct sskip_head *head, + struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)); +extern struct sskip_item *typesafe_skiplist_find(struct sskip_head *head, + const struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)); +extern struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head, + const struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)); +extern struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head, + const struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)); +extern void typesafe_skiplist_del(struct sskip_head *head, + struct sskip_item *item, int (*cmpfn)( + const struct sskip_item *a, + const struct sskip_item *b)); +extern struct sskip_item *typesafe_skiplist_pop(struct sskip_head *head); + +#ifdef __cplusplus +} +#endif + +/* this needs to stay at the end because both files include each other. + * the resolved order is typesafe.h before typerb.h + */ +#include "typerb.h" + +#endif /* _FRR_TYPESAFE_H */ diff --git a/lib/vrf.c b/lib/vrf.c index de50e6a51743..229f19f29ae8 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -362,9 +362,9 @@ struct vrf_bit_set { bool set; }; -static unsigned int vrf_hash_bitmap_key(void *data) +static unsigned int vrf_hash_bitmap_key(const void *data) { - struct vrf_bit_set *bit = data; + const struct vrf_bit_set *bit = data; return bit->vrf_id; } @@ -860,7 +860,6 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty), void vrf_set_default_name(const char *default_name, bool force) { struct vrf *def_vrf; - struct vrf *vrf_with_default_name = NULL; static bool def_vrf_forced; def_vrf = vrf_lookup_by_id(VRF_DEFAULT); @@ -871,13 +870,7 @@ void vrf_set_default_name(const char *default_name, bool force) def_vrf->vrf_id); return; } - if (vrf_with_default_name && vrf_with_default_name != def_vrf) { - /* vrf name already used by an other VRF */ - zlog_debug("VRF: %s, avoid changing name to %s, same name exists (%u)", - vrf_with_default_name->name, default_name, - vrf_with_default_name->vrf_id); - return; - } + snprintf(vrf_default_name, VRF_NAMSIZ, "%s", default_name); if (def_vrf) { if (force) @@ -911,10 +904,16 @@ vrf_id_t vrf_get_default_id(void) int vrf_bind(vrf_id_t vrf_id, int fd, const char *name) { int ret = 0; + struct interface *ifp; if (fd < 0 || name == NULL) return fd; - if (vrf_is_backend_netns()) + /* the device should exist + * otherwise we should return + * case ifname = vrf in netns mode => return + */ + ifp = if_lookup_by_name(name, vrf_id); + if (!ifp) return fd; #ifdef SO_BINDTODEVICE ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name)+1); diff --git a/lib/vty.c b/lib/vty.c index dae8e825993b..e306e69b873c 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -42,10 +42,15 @@ #include "frrstr.h" #include "lib_errors.h" #include "northbound_cli.h" +#include "printfrr.h" #include #include +#ifndef VTYSH_EXTRACT_PL +#include "lib/vty_clippy.c" +#endif + DEFINE_MTYPE_STATIC(LIB, VTY, "VTY") DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer") DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history") @@ -84,10 +89,7 @@ static char *vty_ipv6_accesslist_name = NULL; static vector Vvty_serv_thread; /* Current directory. */ -char *vty_cwd = NULL; - -/* Exclusive configuration lock. */ -struct vty *vty_exclusive_lock; +char vty_cwd[MAXPATHLEN]; /* Login password check. */ static int no_password_check = 0; @@ -95,7 +97,8 @@ static int no_password_check = 0; /* Integrated configuration file path */ static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; -static int do_log_commands = 0; +static bool do_log_commands; +static bool do_log_commands_perm; void vty_frame(struct vty *vty, const char *format, ...) { @@ -146,8 +149,7 @@ bool vty_set_include(struct vty *vty, const char *regexp) int vty_out(struct vty *vty, const char *format, ...) { va_list args; - int len = 0; - int size = 1024; + ssize_t len; char buf[1024]; char *p = NULL; char *filtered; @@ -157,35 +159,11 @@ int vty_out(struct vty *vty, const char *format, ...) vty_out(vty, "%s", vty->frame); } - /* Try to write to initial buffer. */ va_start(args, format); - len = vsnprintf(buf, sizeof(buf), format, args); + p = vasnprintfrr(MTYPE_VTY_OUT_BUF, buf, sizeof(buf), format, args); va_end(args); - /* Initial buffer is not enough. */ - if (len < 0 || len >= size) { - while (1) { - if (len > -1) - size = len + 1; - else - size = size * 2; - - p = XREALLOC(MTYPE_VTY_OUT_BUF, p, size); - if (!p) - return -1; - - va_start(args, format); - len = vsnprintf(p, size, format, args); - va_end(args); - - if (len > -1 && len < size) - break; - } - } - - /* When initial buffer is enough to store all output. */ - if (!p) - p = buf; + len = strlen(p); /* filter buffer */ if (vty->filter) { @@ -272,8 +250,8 @@ int vty_out(struct vty *vty, const char *format, ...) } static int vty_log_out(struct vty *vty, const char *level, - const char *proto_str, const char *format, - struct timestamp_control *ctl, va_list va) + const char *proto_str, const char *msg, + struct timestamp_control *ctl) { int ret; int len; @@ -298,7 +276,7 @@ static int vty_log_out(struct vty *vty, const char *level, if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf))) return -1; - if (((ret = vsnprintf(buf + len, sizeof(buf) - len, format, va)) < 0) + if (((ret = snprintf(buf + len, sizeof(buf) - len, "%s", msg)) < 0) || ((size_t)((len += ret) + 2) > sizeof(buf))) return -1; @@ -1001,7 +979,7 @@ static void vty_describe_fold(struct vty *vty, int cmd_width, if (pos == 0) break; - strncpy(buf, p, pos); + memcpy(buf, p, pos); buf[pos] = '\0'; vty_out(vty, " %-*s %s\n", cmd_width, cmd, buf); @@ -1662,7 +1640,7 @@ static struct vty *vty_create(int vty_sock, union sockunion *su) /* configurable parameters not part of basic init */ vty->v_timeout = vty_timeout_val; - strcpy(vty->address, buf); + strlcpy(vty->address, buf, sizeof(vty->address)); if (no_password_check) { if (host.advanced) vty->node = ENABLE_NODE; @@ -1798,7 +1776,7 @@ struct vty *vty_stdio(void (*atclose)(int isexit)) */ vty->node = ENABLE_NODE; vty->v_timeout = 0; - strcpy(vty->address, "console"); + strlcpy(vty->address, "console", sizeof(vty->address)); vty_stdio_resume(); return vty; @@ -2369,7 +2347,7 @@ static void vty_read_file(struct nb_config *config, FILE *confp) if (config == NULL && vty->candidate_config && frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) { ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, - true, "Read configuration file", + vty, true, "Read configuration file", NULL); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) zlog_err("%s: failed to read configuration file.", @@ -2387,9 +2365,10 @@ static FILE *vty_use_backup_config(const char *fullpath) int c; char buffer[512]; - fullpath_sav = malloc(strlen(fullpath) + strlen(CONF_BACKUP_EXT) + 1); - strcpy(fullpath_sav, fullpath); - strcat(fullpath_sav, CONF_BACKUP_EXT); + size_t fullpath_sav_sz = strlen(fullpath) + strlen(CONF_BACKUP_EXT) + 1; + fullpath_sav = malloc(fullpath_sav_sz); + strlcpy(fullpath_sav, fullpath, fullpath_sav_sz); + strlcat(fullpath_sav, CONF_BACKUP_EXT, fullpath_sav_sz); sav = open(fullpath_sav, O_RDONLY); if (sav < 0) { @@ -2549,8 +2528,8 @@ bool vty_read_config(struct nb_config *config, const char *config_file, } /* Small utility function which output log to the VTY. */ -void vty_log(const char *level, const char *proto_str, const char *format, - struct timestamp_control *ctl, va_list va) +void vty_log(const char *level, const char *proto_str, const char *msg, + struct timestamp_control *ctl) { unsigned int i; struct vty *vty; @@ -2560,13 +2539,8 @@ void vty_log(const char *level, const char *proto_str, const char *format, for (i = 0; i < vector_active(vtyvec); i++) if ((vty = vector_slot(vtyvec, i)) != NULL) - if (vty->monitor) { - va_list ac; - va_copy(ac, va); - vty_log_out(vty, level, proto_str, format, ctl, - ac); - va_end(ac); - } + if (vty->monitor) + vty_log_out(vty, level, proto_str, msg, ctl); } /* Async-signal-safe version of vty_log for fixed strings. */ @@ -2601,8 +2575,8 @@ void vty_log_fixed(char *buf, size_t len) int vty_config_enter(struct vty *vty, bool private_config, bool exclusive) { - if (exclusive && !vty_config_exclusive_lock(vty)) { - vty_out(vty, "VTY configuration is locked by other VTY\n"); + if (exclusive && nb_running_lock(NB_CLIENT_CLI, vty)) { + vty_out(vty, "%% Configuration is locked by other client\n"); return CMD_WARNING; } @@ -2611,17 +2585,22 @@ int vty_config_enter(struct vty *vty, bool private_config, bool exclusive) vty->private_config = private_config; vty->xpath_index = 0; - if (private_config) { - vty->candidate_config = nb_config_dup(running_config); - vty->candidate_config_base = nb_config_dup(running_config); - vty_out(vty, - "Warning: uncommitted changes will be discarded on exit.\n\n"); - } else { - vty->candidate_config = vty_shared_candidate_config; - if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) + pthread_rwlock_rdlock(&running_config->lock); + { + if (private_config) { + vty->candidate_config = nb_config_dup(running_config); vty->candidate_config_base = nb_config_dup(running_config); + vty_out(vty, + "Warning: uncommitted changes will be discarded on exit.\n\n"); + } else { + vty->candidate_config = vty_shared_candidate_config; + if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) + vty->candidate_config_base = + nb_config_dup(running_config); + } } + pthread_rwlock_unlock(&running_config->lock); return CMD_SUCCESS; } @@ -2636,7 +2615,7 @@ void vty_config_exit(struct vty *vty) nb_cli_confirmed_commit_clean(vty); } - vty_config_exclusive_unlock(vty); + (void)nb_running_unlock(NB_CLIENT_CLI, vty); if (vty->candidate_config) { if (vty->private_config) @@ -2651,21 +2630,6 @@ void vty_config_exit(struct vty *vty) vty->config = false; } -int vty_config_exclusive_lock(struct vty *vty) -{ - if (vty_exclusive_lock == NULL) { - vty_exclusive_lock = vty; - return 1; - } - return 0; -} - -void vty_config_exclusive_unlock(struct vty *vty) -{ - if (vty_exclusive_lock == vty) - vty_exclusive_lock = NULL; -} - /* Master of the threads. */ static struct thread_master *vty_master; @@ -2987,13 +2951,24 @@ DEFUN_NOSH (show_history, } /* vty login. */ -DEFUN (log_commands, +DEFPY (log_commands, log_commands_cmd, - "log commands", + "[no] log commands", + NO_STR "Logging control\n" - "Log all commands (can't be unset without restart)\n") + "Log all commands\n") { - do_log_commands = 1; + if (no) { + if (do_log_commands_perm) { + vty_out(vty, + "Daemon started with permanent logging turned on for commands, ignoring\n"); + return CMD_WARNING; + } + + do_log_commands = false; + } else + do_log_commands = true; + return CMD_SUCCESS; } @@ -3068,10 +3043,9 @@ void vty_reset(void) static void vty_save_cwd(void) { - char cwd[MAXPATHLEN]; char *c; - c = getcwd(cwd, MAXPATHLEN); + c = getcwd(vty_cwd, sizeof(vty_cwd)); if (!c) { /* @@ -3085,15 +3059,12 @@ static void vty_save_cwd(void) SYSCONFDIR, errno); exit(-1); } - if (getcwd(cwd, MAXPATHLEN) == NULL) { + if (getcwd(vty_cwd, sizeof(vty_cwd)) == NULL) { flog_err_sys(EC_LIB_SYSTEM_CALL, "Failure to getcwd, errno: %d", errno); exit(-1); } } - - vty_cwd = XMALLOC(MTYPE_TMP, strlen(cwd) + 1); - strcpy(vty_cwd, cwd); } char *vty_get_cwd(void) @@ -3117,7 +3088,7 @@ void vty_init_vtysh(void) } /* Install vty's own commands like `who' command. */ -void vty_init(struct thread_master *master_thread) +void vty_init(struct thread_master *master_thread, bool do_command_logging) { /* For further configuration read, preserve current directory. */ vty_save_cwd(); @@ -3141,6 +3112,12 @@ void vty_init(struct thread_master *master_thread) install_element(CONFIG_NODE, &no_service_advanced_vty_cmd); install_element(CONFIG_NODE, &show_history_cmd); install_element(CONFIG_NODE, &log_commands_cmd); + + if (do_command_logging) { + do_log_commands = true; + do_log_commands_perm = true; + } + install_element(ENABLE_NODE, &terminal_monitor_cmd); install_element(ENABLE_NODE, &terminal_no_monitor_cmd); install_element(ENABLE_NODE, &no_terminal_monitor_cmd); @@ -3159,7 +3136,7 @@ void vty_init(struct thread_master *master_thread) void vty_terminate(void) { - XFREE(MTYPE_TMP, vty_cwd); + memset(vty_cwd, 0x00, sizeof(vty_cwd)); if (vtyvec && Vvty_serv_thread) { vty_reset(); diff --git a/lib/vty.h b/lib/vty.h index 9c2653e1ac0d..035e75802481 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -289,11 +289,8 @@ struct vty_arg { #define IS_DIRECTORY_SEP(c) ((c) == DIRECTORY_SEP) #endif -/* Exported variables */ -extern struct vty *vty_exclusive_lock; - /* Prototypes. */ -extern void vty_init(struct thread_master *); +extern void vty_init(struct thread_master *, bool do_command_logging); extern void vty_init_vtysh(void); extern void vty_terminate(void); extern void vty_reset(void); @@ -316,13 +313,11 @@ extern void vty_time_print(struct vty *, int); extern void vty_serv_sock(const char *, unsigned short, const char *); extern void vty_close(struct vty *); extern char *vty_get_cwd(void); -extern void vty_log(const char *level, const char *proto, const char *fmt, - struct timestamp_control *, va_list); +extern void vty_log(const char *level, const char *proto, const char *msg, + struct timestamp_control *); extern int vty_config_enter(struct vty *vty, bool private_config, bool exclusive); extern void vty_config_exit(struct vty *); -extern int vty_config_exclusive_lock(struct vty *vty); -extern void vty_config_exclusive_unlock(struct vty *vty); extern int vty_shell(struct vty *); extern int vty_shell_serv(struct vty *); extern void vty_hello(struct vty *); diff --git a/lib/wheel.c b/lib/wheel.c index 69d2fa48dc0b..8e479c931b5a 100644 --- a/lib/wheel.c +++ b/lib/wheel.c @@ -80,7 +80,7 @@ static int wheel_timer_thread(struct thread *t) } struct timer_wheel *wheel_init(struct thread_master *master, int period, - size_t slots, unsigned int (*slot_key)(void *), + size_t slots, unsigned int (*slot_key)(const void *), void (*slot_run)(void *), const char *run_name) { diff --git a/lib/wheel.h b/lib/wheel.h index e66751c1d092..401789e1a01d 100644 --- a/lib/wheel.h +++ b/lib/wheel.h @@ -38,7 +38,7 @@ struct timer_wheel { /* * Key to determine what slot the item belongs in */ - unsigned int (*slot_key)(void *); + unsigned int (*slot_key)(const void *); void (*slot_run)(void *); }; @@ -80,9 +80,9 @@ struct timer_wheel { * of running your code. */ struct timer_wheel *wheel_init(struct thread_master *master, int period, - size_t slots, unsigned int (*slot_key)(void *), - void (*slot_run)(void *), - const char *run_name); + size_t slots, + unsigned int (*slot_key)(const void *), + void (*slot_run)(void *), const char *run_name); /* * Delete the specified timer wheel created diff --git a/lib/workqueue.c b/lib/workqueue.c index 066d81f35020..54090d0d0f82 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -280,7 +280,7 @@ int work_queue_run(struct thread *thread) if (item->ran > wq->spec.max_retries) { /* run error handler, if any */ if (wq->spec.errorfunc) - wq->spec.errorfunc(wq, item->data); + wq->spec.errorfunc(wq, item); work_queue_item_remove(wq, item); continue; } diff --git a/lib/yang.c b/lib/yang.c index 2a2c155deede..2f9a9aa5a3d7 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -617,14 +617,6 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path) zlog(priority, "libyang: %s", msg); } -#if CONFDATE > 20190401 -CPP_NOTICE("lib/yang: time to remove non-LIBYANG_EXT_BUILTIN support") -#endif - -#ifdef LIBYANG_EXT_BUILTIN -extern struct lytype_plugin_list frr_user_types[]; -#endif - struct ly_ctx *yang_ctx_new_setup(void) { struct ly_ctx *ctx; @@ -650,31 +642,10 @@ struct ly_ctx *yang_ctx_new_setup(void) void yang_init(void) { -#ifndef LIBYANG_EXT_BUILTIN -CPP_NOTICE("lib/yang: deprecated libyang <0.16.74 extension loading in use!") - static char ly_plugin_dir[PATH_MAX]; - const char *const *ly_loaded_plugins; - const char *ly_plugin; - bool found_ly_frr_types = false; - - /* Tell libyang where to find its plugins. */ - snprintf(ly_plugin_dir, sizeof(ly_plugin_dir), "%s=%s", - "LIBYANG_USER_TYPES_PLUGINS_DIR", LIBYANG_PLUGINS_PATH); - putenv(ly_plugin_dir); -#endif - /* Initialize libyang global parameters that affect all containers. */ ly_set_log_clb(ly_log_cb, 1); ly_log_options(LY_LOLOG | LY_LOSTORE); -#ifdef LIBYANG_EXT_BUILTIN - if (ly_register_types(frr_user_types, "frr_user_types")) { - flog_err(EC_LIB_LIBYANG_PLUGIN_LOAD, - "ly_register_types() failed"); - exit(1); - } -#endif - /* Initialize libyang container for native models. */ ly_native_ctx = yang_ctx_new_setup(); if (!ly_native_ctx) { @@ -682,22 +653,6 @@ CPP_NOTICE("lib/yang: deprecated libyang <0.16.74 extension loading in use!") exit(1); } -#ifndef LIBYANG_EXT_BUILTIN - /* Detect if the required libyang plugin(s) were loaded successfully. */ - ly_loaded_plugins = ly_get_loaded_plugins(); - for (size_t i = 0; (ly_plugin = ly_loaded_plugins[i]); i++) { - if (strmatch(ly_plugin, "frr_user_types")) { - found_ly_frr_types = true; - break; - } - } - if (!found_ly_frr_types) { - flog_err(EC_LIB_LIBYANG_PLUGIN_LOAD, - "%s: failed to load frr_user_types.so", __func__); - exit(1); - } -#endif - yang_translator_init(); } diff --git a/lib/yang_translator.c b/lib/yang_translator.c index 76a6cc5fd195..341420eeda45 100644 --- a/lib/yang_translator.c +++ b/lib/yang_translator.c @@ -24,6 +24,7 @@ #include "hash.h" #include "yang.h" #include "yang_translator.h" +#include "frrstr.h" DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR, "YANG Translator") DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR_MODULE, "YANG Translator Module") @@ -45,8 +46,6 @@ static struct ly_ctx *ly_translator_ctx; static unsigned int yang_translator_validate(struct yang_translator *translator); static unsigned int yang_module_nodes_count(const struct lys_module *module); -static void str_replace(char *o_string, const char *s_string, - const char *r_string); struct yang_mapping_node { char xpath_from_canonical[XPATH_MAXLEN]; @@ -62,7 +61,7 @@ static bool yang_mapping_hash_cmp(const void *value1, const void *value2) return strmatch(c1->xpath_from_canonical, c2->xpath_from_canonical); } -static unsigned int yang_mapping_hash_key(void *value) +static unsigned int yang_mapping_hash_key(const void *value) { return string_hash_make(value); } @@ -108,14 +107,24 @@ static void yang_mapping_add(struct yang_translator *translator, int dir, sizeof(mapping->xpath_from_fmt)); strlcpy(mapping->xpath_to_fmt, xpath_to_fmt, sizeof(mapping->xpath_to_fmt)); - str_replace(mapping->xpath_from_fmt, "KEY1", "%[^']"); - str_replace(mapping->xpath_from_fmt, "KEY2", "%[^']"); - str_replace(mapping->xpath_from_fmt, "KEY3", "%[^']"); - str_replace(mapping->xpath_from_fmt, "KEY4", "%[^']"); - str_replace(mapping->xpath_to_fmt, "KEY1", "%s"); - str_replace(mapping->xpath_to_fmt, "KEY2", "%s"); - str_replace(mapping->xpath_to_fmt, "KEY3", "%s"); - str_replace(mapping->xpath_to_fmt, "KEY4", "%s"); + + const char *keys[] = {"KEY1", "KEY2", "KEY3", "KEY4"}; + char *xpfmt; + + for (unsigned int i = 0; i < array_size(keys); i++) { + xpfmt = frrstr_replace(mapping->xpath_from_fmt, keys[i], + "%[^']"); + strlcpy(mapping->xpath_from_fmt, xpfmt, + sizeof(mapping->xpath_from_fmt)); + XFREE(MTYPE_TMP, xpfmt); + } + + for (unsigned int i = 0; i < array_size(keys); i++) { + xpfmt = frrstr_replace(mapping->xpath_to_fmt, keys[i], "%s"); + strlcpy(mapping->xpath_to_fmt, xpfmt, + sizeof(mapping->xpath_to_fmt)); + XFREE(MTYPE_TMP, xpfmt); + } } struct yang_translator *yang_translator_load(const char *path) @@ -500,28 +509,6 @@ static unsigned int yang_module_nodes_count(const struct lys_module *module) return total; } -/* TODO: rewrite this function. */ -static void str_replace(char *o_string, const char *s_string, - const char *r_string) -{ - char buffer[BUFSIZ]; - char *ch; - - ch = strstr(o_string, s_string); - if (!ch) - return; - - memcpy(buffer, o_string, ch - o_string); - buffer[ch - o_string] = 0; - - sprintf(buffer + (ch - o_string), "%s%s", r_string, - ch + strlen(s_string)); - - o_string[0] = 0; - strcpy(o_string, buffer); - return str_replace(o_string, s_string, r_string); -} - void yang_translator_init(void) { ly_translator_ctx = yang_ctx_new_setup(); diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c index 7ecea5f44546..0558383823e5 100644 --- a/lib/yang_wrappers.c +++ b/lib/yang_wrappers.c @@ -817,7 +817,7 @@ void yang_dnode_get_ipv4(struct in_addr *addr, const struct lyd_node *dnode, dleaf = (const struct lyd_node_leaf_list *)dnode; assert(dleaf->value_type == LY_TYPE_STRING); - memcpy(addr, dleaf->value.ptr, sizeof(*addr)); + (void)inet_pton(AF_INET, dleaf->value_str, addr); } void yang_get_default_ipv4(struct in_addr *var, const char *xpath_fmt, ...) @@ -874,7 +874,7 @@ void yang_dnode_get_ipv4p(union prefixptr prefix, const struct lyd_node *dnode, dleaf = (const struct lyd_node_leaf_list *)dnode; assert(dleaf->value_type == LY_TYPE_STRING); - memcpy(prefix4, dleaf->value.ptr, sizeof(*prefix4)); + (void)str2prefix_ipv4(dleaf->value_str, prefix4); } void yang_get_default_ipv4p(union prefixptr var, const char *xpath_fmt, ...) @@ -927,7 +927,7 @@ void yang_dnode_get_ipv6(struct in6_addr *addr, const struct lyd_node *dnode, dleaf = (const struct lyd_node_leaf_list *)dnode; assert(dleaf->value_type == LY_TYPE_STRING); - memcpy(addr, dleaf->value.ptr, sizeof(*addr)); + (void)inet_pton(AF_INET6, dleaf->value_str, addr); } void yang_get_default_ipv6(struct in6_addr *var, const char *xpath_fmt, ...) @@ -984,7 +984,7 @@ void yang_dnode_get_ipv6p(union prefixptr prefix, const struct lyd_node *dnode, dleaf = (const struct lyd_node_leaf_list *)dnode; assert(dleaf->value_type == LY_TYPE_STRING); - memcpy(prefix6, dleaf->value.ptr, sizeof(*prefix6)); + (void)str2prefix_ipv6(dleaf->value_str, prefix6); } void yang_get_default_ipv6p(union prefixptr var, const char *xpath_fmt, ...) diff --git a/lib/zclient.c b/lib/zclient.c index 4901c9274356..e9b4f5a58b17 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -62,10 +62,13 @@ struct zclient *zclient_new(struct thread_master *master, struct zclient_options *opt) { struct zclient *zclient; + size_t stream_size = + MAX(ZEBRA_MAX_PACKET_SIZ, sizeof(struct zapi_route)); + zclient = XCALLOC(MTYPE_ZCLIENT, sizeof(struct zclient)); - zclient->ibuf = stream_new(ZEBRA_MAX_PACKET_SIZ); - zclient->obuf = stream_new(ZEBRA_MAX_PACKET_SIZ); + zclient->ibuf = stream_new(stream_size); + zclient->obuf = stream_new(stream_size); zclient->wb = buffer_new(0); zclient->master = master; @@ -555,6 +558,25 @@ void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id, zclient_send_message(zclient); } +int zclient_send_interface_protodown(struct zclient *zclient, vrf_id_t vrf_id, + struct interface *ifp, bool down) +{ + struct stream *s; + + if (zclient->sock < 0) + return -1; + + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_INTERFACE_SET_PROTODOWN, vrf_id); + stream_putl(s, ifp->ifindex); + stream_putc(s, !!down); + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); + + return 0; +} + /* Make connection to zebra daemon. */ int zclient_start(struct zclient *zclient) { @@ -1381,6 +1403,8 @@ void zebra_router_id_update_read(struct stream *s, struct prefix *rid) * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | bandwidth | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | parent ifindex | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Link Layer Type | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Harware Address Length | @@ -1561,6 +1585,7 @@ void zebra_interface_if_set_value(struct stream *s, struct interface *ifp) ifp->mtu = stream_getl(s); ifp->mtu6 = stream_getl(s); ifp->bandwidth = stream_getl(s); + ifp->link_ifindex = stream_getl(s); ifp->ll_type = stream_getl(s); ifp->hw_addr_len = stream_getl(s); if (ifp->hw_addr_len) @@ -2371,9 +2396,7 @@ int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw) /* * Receive PW status update from Zebra and send it to LDE process. */ -void zebra_read_pw_status_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id, - struct zapi_pw_status *pw) +void zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw) { struct stream *s; @@ -2386,8 +2409,7 @@ void zebra_read_pw_status_update(int command, struct zclient *zclient, pw->status = stream_getl(s); } -static void zclient_capability_decode(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static void zclient_capability_decode(ZAPI_CALLBACK_ARGS) { struct zclient_capabilities cap; struct stream *s = zclient->ibuf; diff --git a/lib/zclient.h b/lib/zclient.h index 0926281f2eb0..d651738687ce 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -38,8 +38,17 @@ #include "mlag.h" +/* Zebra types. Used in Zserv message header. */ +typedef uint16_t zebra_size_t; + +/* Marker value used in new Zserv, in the byte location corresponding + * the command value in the old zserv header. To allow old and new + * Zserv headers to be distinguished from each other. + */ +#define ZEBRA_HEADER_MARKER 254 + /* For input/output buffer to zebra. */ -#define ZEBRA_MAX_PACKET_SIZ 16384 +#define ZEBRA_MAX_PACKET_SIZ 16384U /* Zebra header size. */ #define ZEBRA_HEADER_SIZE 10 @@ -76,6 +85,7 @@ typedef enum { ZEBRA_INTERFACE_UP, ZEBRA_INTERFACE_DOWN, ZEBRA_INTERFACE_SET_MASTER, + ZEBRA_INTERFACE_SET_PROTODOWN, ZEBRA_ROUTE_ADD, ZEBRA_ROUTE_DELETE, ZEBRA_ROUTE_NOTIFY_OWNER, @@ -221,66 +231,49 @@ struct zclient { /* Redistribute defauilt. */ vrf_bitmap_t default_information[AFI_MAX]; +#define ZAPI_CALLBACK_ARGS \ + int cmd, struct zclient *zclient, uint16_t length, vrf_id_t vrf_id + /* Pointer to the callback functions. */ void (*zebra_connected)(struct zclient *); void (*zebra_capabilities)(struct zclient_capabilities *cap); - int (*router_id_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_delete)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_up)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_down)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_address_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_address_delete)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*interface_link_params)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_bfd_dest_update)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*interface_nbr_address_add)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*interface_nbr_address_delete)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*interface_vrf_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*nexthop_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*import_check_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*bfd_dest_replay)(int, struct zclient *, uint16_t, vrf_id_t); - int (*redistribute_route_add)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*redistribute_route_del)(int, struct zclient *, uint16_t, - vrf_id_t); + int (*router_id_update)(ZAPI_CALLBACK_ARGS); + int (*interface_add)(ZAPI_CALLBACK_ARGS); + int (*interface_delete)(ZAPI_CALLBACK_ARGS); + int (*interface_up)(ZAPI_CALLBACK_ARGS); + int (*interface_down)(ZAPI_CALLBACK_ARGS); + int (*interface_address_add)(ZAPI_CALLBACK_ARGS); + int (*interface_address_delete)(ZAPI_CALLBACK_ARGS); + int (*interface_link_params)(ZAPI_CALLBACK_ARGS); + int (*interface_bfd_dest_update)(ZAPI_CALLBACK_ARGS); + int (*interface_nbr_address_add)(ZAPI_CALLBACK_ARGS); + int (*interface_nbr_address_delete)(ZAPI_CALLBACK_ARGS); + int (*interface_vrf_update)(ZAPI_CALLBACK_ARGS); + int (*nexthop_update)(ZAPI_CALLBACK_ARGS); + int (*import_check_update)(ZAPI_CALLBACK_ARGS); + int (*bfd_dest_replay)(ZAPI_CALLBACK_ARGS); + int (*redistribute_route_add)(ZAPI_CALLBACK_ARGS); + int (*redistribute_route_del)(ZAPI_CALLBACK_ARGS); int (*fec_update)(int, struct zclient *, uint16_t); - int (*local_es_add)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*local_es_del)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*local_vni_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_vni_del)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_l3vni_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_l3vni_del)(int, struct zclient *, uint16_t, vrf_id_t); - void (*local_ip_prefix_add)(int, struct zclient *, uint16_t, vrf_id_t); - void (*local_ip_prefix_del)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t); - int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*route_notify_owner)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*rule_notify_owner)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - void (*label_chunk)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*ipset_notify_owner)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*ipset_entry_notify_owner)(int command, - struct zclient *zclient, - uint16_t length, - vrf_id_t vrf_id); - int (*iptable_notify_owner)(int command, - struct zclient *zclient, - uint16_t length, - vrf_id_t vrf_id); - int (*vxlan_sg_add)(int command, struct zclient *client, - uint16_t length, vrf_id_t vrf_id); - int (*vxlan_sg_del)(int command, struct zclient *client, - uint16_t length, vrf_id_t vrf_id_t); + int (*local_es_add)(ZAPI_CALLBACK_ARGS); + int (*local_es_del)(ZAPI_CALLBACK_ARGS); + int (*local_vni_add)(ZAPI_CALLBACK_ARGS); + int (*local_vni_del)(ZAPI_CALLBACK_ARGS); + int (*local_l3vni_add)(ZAPI_CALLBACK_ARGS); + int (*local_l3vni_del)(ZAPI_CALLBACK_ARGS); + void (*local_ip_prefix_add)(ZAPI_CALLBACK_ARGS); + void (*local_ip_prefix_del)(ZAPI_CALLBACK_ARGS); + int (*local_macip_add)(ZAPI_CALLBACK_ARGS); + int (*local_macip_del)(ZAPI_CALLBACK_ARGS); + int (*pw_status_update)(ZAPI_CALLBACK_ARGS); + int (*route_notify_owner)(ZAPI_CALLBACK_ARGS); + int (*rule_notify_owner)(ZAPI_CALLBACK_ARGS); + void (*label_chunk)(ZAPI_CALLBACK_ARGS); + int (*ipset_notify_owner)(ZAPI_CALLBACK_ARGS); + int (*ipset_entry_notify_owner)(ZAPI_CALLBACK_ARGS); + int (*iptable_notify_owner)(ZAPI_CALLBACK_ARGS); + int (*vxlan_sg_add)(ZAPI_CALLBACK_ARGS); + int (*vxlan_sg_del)(ZAPI_CALLBACK_ARGS); }; /* Zebra API message flag. */ @@ -338,6 +331,41 @@ struct zapi_route { unsigned short instance; uint32_t flags; +/* + * Cause Zebra to consider this routes nexthops recursively + */ +#define ZEBRA_FLAG_ALLOW_RECURSION 0x01 +/* + * This is a route that is read in on startup that was left around + * from a previous run of FRR + */ +#define ZEBRA_FLAG_SELFROUTE 0x02 +/* + * This flag is used to tell Zebra that the BGP route being passed + * down is a IBGP route + */ +#define ZEBRA_FLAG_IBGP 0x04 +/* + * This is a route that has been selected for FIB installation. + * This flag is set in zebra and can be passed up to routing daemons + */ +#define ZEBRA_FLAG_SELECTED 0x08 +/* + * This is a route that we are telling Zebra that this route *must* + * win and will be installed even over ZEBRA_FLAG_SELECTED + */ +#define ZEBRA_FLAG_FIB_OVERRIDE 0x10 +/* + * This flag tells Zebra that the route is a EVPN route and should + * be treated specially + */ +#define ZEBRA_FLAG_EVPN_ROUTE 0x20 +/* + * This flag tells Zebra that it should treat the distance passed + * down as an additional discriminator for route selection of the + * route entry. This mainly is used for backup static routes. + */ +#define ZEBRA_FLAG_RR_USE_DISTANCE 0x40 uint8_t message; @@ -483,6 +511,9 @@ extern void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id, struct interface *ifp, int enable, int ra_interval); +extern int zclient_send_interface_protodown(struct zclient *zclient, + vrf_id_t vrf_id, + struct interface *ifp, bool down); /* Send redistribute command to zebra daemon. Do not update zclient state. */ extern int zebra_redistribute_send(int command, struct zclient *, afi_t, @@ -597,9 +628,7 @@ extern int tm_release_table_chunk(struct zclient *zclient, uint32_t start, extern int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw); -extern void zebra_read_pw_status_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id, - struct zapi_pw_status *pw); +extern void zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw); extern int zclient_route_send(uint8_t, struct zclient *, struct zapi_route *); extern int zclient_send_rnh(struct zclient *zclient, int command, diff --git a/lib/zebra.h b/lib/zebra.h index b96fb5a20683..2f9ada09beb7 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -202,18 +202,12 @@ typedef unsigned char uint8_t; /* Some systems do not define UINT32_MAX, etc.. from inttypes.h * e.g. this makes life easier for FBSD 4.11 users. */ -#ifndef INT8_MAX -#define INT8_MAX (127) -#endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif -#ifndef UINT8_MAX -#define UINT8_MAX (255U) -#endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif @@ -335,45 +329,6 @@ struct in_pktinfo { #endif /* ndef BYTE_ORDER */ -/* MAX / MIN are not commonly defined, but useful */ -/* note: glibc sys/param.h has #define MIN(a,b) (((a)<(b))?(a):(b)) */ -#ifdef MAX -#undef MAX -#endif -#define MAX(a, b) \ - ({ \ - typeof(a) _max_a = (a); \ - typeof(b) _max_b = (b); \ - _max_a > _max_b ? _max_a : _max_b; \ - }) -#ifdef MIN -#undef MIN -#endif -#define MIN(a, b) \ - ({ \ - typeof(a) _min_a = (a); \ - typeof(b) _min_b = (b); \ - _min_a < _min_b ? _min_a : _min_b; \ - }) - -#ifndef offsetof -#ifdef __compiler_offsetof -#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) -#else -#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) -#endif -#endif - -#ifndef container_of -#define container_of(ptr, type, member) \ - ({ \ - const typeof(((type *)0)->member) *__mptr = (ptr); \ - (type *)((char *)__mptr - offsetof(type, member)); \ - }) -#endif - -#define ZEBRA_NUM_OF(x) (sizeof (x) / sizeof (x[0])) - /* For old definition. */ #ifndef IN6_ARE_ADDR_EQUAL #define IN6_ARE_ADDR_EQUAL IN6_IS_ADDR_EQUAL @@ -382,12 +337,6 @@ struct in_pktinfo { /* default zebra TCP port for zclient */ #define ZEBRA_PORT 2600 -/* Marker value used in new Zserv, in the byte location corresponding - * the command value in the old zserv header. To allow old and new - * Zserv headers to be distinguished from each other. - */ -#define ZEBRA_HEADER_MARKER 254 - /* * The compiler.h header is used for anyone using the CPP_NOTICE * since this is universally needed, let's add it to zebra.h @@ -397,71 +346,20 @@ struct in_pktinfo { /* Zebra route's types are defined in route_types.h */ #include "route_types.h" -/* Note: whenever a new route-type or zserv-command is added the - * corresponding {command,route}_types[] table in lib/log.c MUST be - * updated! */ - -/* Map a route type to a string. For example, ZEBRA_ROUTE_RIPNG -> "ripng". */ -extern const char *zebra_route_string(unsigned int route_type); -/* Map a route type to a char. For example, ZEBRA_ROUTE_RIPNG -> 'R'. */ -extern char zebra_route_char(unsigned int route_type); -/* Map a zserv command type to the same string, - * e.g. ZEBRA_INTERFACE_ADD -> "ZEBRA_INTERFACE_ADD" */ -/* Map a protocol name to its number. e.g. ZEBRA_ROUTE_BGP->9*/ -extern int proto_name2num(const char *s); -/* Map redistribute X argument to protocol number. - * unlike proto_name2num, this accepts shorthands and takes - * an AFI value to restrict input */ -extern int proto_redistnum(int afi, const char *s); - -extern const char *zserv_command_string(unsigned int command); - #define strmatch(a,b) (!strcmp((a), (b))) -/* Zebra message flags */ - -/* - * Cause Zebra to consider this routes nexthops recursively - */ -#define ZEBRA_FLAG_ALLOW_RECURSION 0x01 -/* - * This is a route that is read in on startup that was left around - * from a previous run of FRR - */ -#define ZEBRA_FLAG_SELFROUTE 0x02 -/* - * This flag is used to tell Zebra that the BGP route being passed - * down is a IBGP route - */ -#define ZEBRA_FLAG_IBGP 0x04 -/* - * This is a route that has been selected for FIB installation. - * This flag is set in zebra and can be passed up to routing daemons - */ -#define ZEBRA_FLAG_SELECTED 0x08 -/* - * This is a route that we are telling Zebra that this route *must* - * win and will be installed even over ZEBRA_FLAG_SELECTED - */ -#define ZEBRA_FLAG_FIB_OVERRIDE 0x10 -/* - * This flag tells Zebra that the route is a EVPN route and should - * be treated specially - */ -#define ZEBRA_FLAG_EVPN_ROUTE 0x20 -/* - * This flag tells Zebra that it should treat the distance passed - * down as an additional discriminator for route selection of the - * route entry. This mainly is used for backup static routes. - */ -#define ZEBRA_FLAG_RR_USE_DISTANCE 0x40 - #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */ #endif /* Address family numbers from RFC1700. */ -typedef enum { AFI_IP = 1, AFI_IP6 = 2, AFI_L2VPN = 3, AFI_MAX = 4 } afi_t; +typedef enum { + AFI_UNSPEC = 0, + AFI_IP = 1, + AFI_IP6 = 2, + AFI_L2VPN = 3, + AFI_MAX = 4 +} afi_t; /* Subsequent Address Family Identifier. */ typedef enum { @@ -534,10 +432,6 @@ typedef enum { #define RESET_FLAG_ATOMIC(PV) \ ((atomic_store_explicit(PV, 0, memory_order_seq_cst))) -/* Zebra types. Used in Zserv message header. */ -typedef uint16_t zebra_size_t; -typedef uint16_t zebra_command_t; - /* VRF ID type. */ typedef uint32_t vrf_id_t; diff --git a/m4/.gitignore b/m4/.gitignore index 2c04163659b4..cc778b9e99a1 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -4,6 +4,7 @@ !ax_compare_version.m4 !ax_prog_perl_modules.m4 !ax_pthread.m4 +!ax_python.m4 !ax_sys_weak_alias.m4 !ax_sys_weak_alias.m4 !pkg.m4 diff --git a/m4/ax_lua.m4 b/m4/ax_lua.m4 new file mode 100644 index 000000000000..9feb352255bd --- /dev/null +++ b/m4/ax_lua.m4 @@ -0,0 +1,664 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_lua.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# AX_LUA_HEADERS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# AX_LUA_LIBS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# AX_LUA_READLINE[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# +# DESCRIPTION +# +# Detect a Lua interpreter, optionally specifying a minimum and maximum +# version number. Set up important Lua paths, such as the directories in +# which to install scripts and modules (shared libraries). +# +# Also detect Lua headers and libraries. The Lua version contained in the +# header is checked to match the Lua interpreter version exactly. When +# searching for Lua libraries, the version number is used as a suffix. +# This is done with the goal of supporting multiple Lua installs (5.1, +# 5.2, and 5.3 side-by-side). +# +# A note on compatibility with previous versions: This file has been +# mostly rewritten for serial 18. Most developers should be able to use +# these macros without needing to modify configure.ac. Care has been taken +# to preserve each macro's behavior, but there are some differences: +# +# 1) AX_WITH_LUA is deprecated; it now expands to the exact same thing as +# AX_PROG_LUA with no arguments. +# +# 2) AX_LUA_HEADERS now checks that the version number defined in lua.h +# matches the interpreter version. AX_LUA_HEADERS_VERSION is therefore +# unnecessary, so it is deprecated and does not expand to anything. +# +# 3) The configure flag --with-lua-suffix no longer exists; the user +# should instead specify the LUA precious variable on the command line. +# See the AX_PROG_LUA description for details. +# +# Please read the macro descriptions below for more information. +# +# This file was inspired by Andrew Dalke's and James Henstridge's +# python.m4 and Tom Payne's, Matthieu Moy's, and Reuben Thomas's ax_lua.m4 +# (serial 17). Basically, this file is a mash-up of those two files. I +# like to think it combines the best of the two! +# +# AX_PROG_LUA: Search for the Lua interpreter, and set up important Lua +# paths. Adds precious variable LUA, which may contain the path of the Lua +# interpreter. If LUA is blank, the user's path is searched for an +# suitable interpreter. +# +# If MINIMUM-VERSION is supplied, then only Lua interpreters with a +# version number greater or equal to MINIMUM-VERSION will be accepted. If +# TOO-BIG-VERSION is also supplied, then only Lua interpreters with a +# version number greater or equal to MINIMUM-VERSION and less than +# TOO-BIG-VERSION will be accepted. +# +# The Lua version number, LUA_VERSION, is found from the interpreter, and +# substituted. LUA_PLATFORM is also found, but not currently supported (no +# standard representation). +# +# Finally, the macro finds four paths: +# +# luadir Directory to install Lua scripts. +# pkgluadir $luadir/$PACKAGE +# luaexecdir Directory to install Lua modules. +# pkgluaexecdir $luaexecdir/$PACKAGE +# +# These paths are found based on $prefix, $exec_prefix, Lua's +# package.path, and package.cpath. The first path of package.path +# beginning with $prefix is selected as luadir. The first path of +# package.cpath beginning with $exec_prefix is used as luaexecdir. This +# should work on all reasonable Lua installations. If a path cannot be +# determined, a default path is used. Of course, the user can override +# these later when invoking make. +# +# luadir Default: $prefix/share/lua/$LUA_VERSION +# luaexecdir Default: $exec_prefix/lib/lua/$LUA_VERSION +# +# These directories can be used by Automake as install destinations. The +# variable name minus 'dir' needs to be used as a prefix to the +# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES. +# +# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is +# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT- +# FOUND is blank, then it will default to printing an error. To prevent +# the default behavior, give ':' as an action. +# +# AX_LUA_HEADERS: Search for Lua headers. Requires that AX_PROG_LUA be +# expanded before this macro. Adds precious variable LUA_INCLUDE, which +# may contain Lua specific include flags, e.g. -I/usr/include/lua5.1. If +# LUA_INCLUDE is blank, then this macro will attempt to find suitable +# flags. +# +# LUA_INCLUDE can be used by Automake to compile Lua modules or +# executables with embedded interpreters. The *_CPPFLAGS variables should +# be used for this purpose, e.g. myprog_CPPFLAGS = $(LUA_INCLUDE). +# +# This macro searches for the header lua.h (and others). The search is +# performed with a combination of CPPFLAGS, CPATH, etc, and LUA_INCLUDE. +# If the search is unsuccessful, then some common directories are tried. +# If the headers are then found, then LUA_INCLUDE is set accordingly. +# +# The paths automatically searched are: +# +# * /usr/include/luaX.Y +# * /usr/include/lua/X.Y +# * /usr/include/luaXY +# * /usr/local/include/luaX.Y +# * /usr/local/include/lua-X.Y +# * /usr/local/include/lua/X.Y +# * /usr/local/include/luaXY +# +# (Where X.Y is the Lua version number, e.g. 5.1.) +# +# The Lua version number found in the headers is always checked to match +# the Lua interpreter's version number. Lua headers with mismatched +# version numbers are not accepted. +# +# If headers are found, then ACTION-IF-FOUND is performed, otherwise +# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then +# it will default to printing an error. To prevent the default behavior, +# set the action to ':'. +# +# AX_LUA_LIBS: Search for Lua libraries. Requires that AX_PROG_LUA be +# expanded before this macro. Adds precious variable LUA_LIB, which may +# contain Lua specific linker flags, e.g. -llua5.1. If LUA_LIB is blank, +# then this macro will attempt to find suitable flags. +# +# LUA_LIB can be used by Automake to link Lua modules or executables with +# embedded interpreters. The *_LIBADD and *_LDADD variables should be used +# for this purpose, e.g. mymod_LIBADD = $(LUA_LIB). +# +# This macro searches for the Lua library. More technically, it searches +# for a library containing the function lua_load. The search is performed +# with a combination of LIBS, LIBRARY_PATH, and LUA_LIB. +# +# If the search determines that some linker flags are missing, then those +# flags will be added to LUA_LIB. +# +# If libraries are found, then ACTION-IF-FOUND is performed, otherwise +# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then +# it will default to printing an error. To prevent the default behavior, +# set the action to ':'. +# +# AX_LUA_READLINE: Search for readline headers and libraries. Requires the +# AX_LIB_READLINE macro, which is provided by ax_lib_readline.m4 from the +# Autoconf Archive. +# +# If a readline compatible library is found, then ACTION-IF-FOUND is +# performed, otherwise ACTION-IF-NOT-FOUND is performed. +# +# LICENSE +# +# Copyright (c) 2015 Reuben Thomas +# Copyright (c) 2014 Tim Perkins +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 39 + +dnl ========================================================================= +dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION], +dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ========================================================================= +AC_DEFUN([AX_PROG_LUA], +[ + dnl Check for required tools. + AC_REQUIRE([AC_PROG_GREP]) + AC_REQUIRE([AC_PROG_SED]) + + dnl Make LUA a precious variable. + AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1]) + + dnl Find a Lua interpreter. + m4_define_default([_AX_LUA_INTERPRETER_LIST], + [lua lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua50]) + + m4_if([$1], [], + [ dnl No version check is needed. Find any Lua interpreter. + AS_IF([test "x$LUA" = 'x'], + [AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])]) + ax_display_LUA='lua' + + AS_IF([test "x$LUA" != 'x:'], + [ dnl At least check if this is a Lua interpreter. + AC_MSG_CHECKING([if $LUA is a Lua interpreter]) + _AX_LUA_CHK_IS_INTRP([$LUA], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([not a Lua interpreter]) + ]) + ]) + ], + [ dnl A version check is needed. + AS_IF([test "x$LUA" != 'x'], + [ dnl Check if this is a Lua interpreter. + AC_MSG_CHECKING([if $LUA is a Lua interpreter]) + _AX_LUA_CHK_IS_INTRP([$LUA], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([not a Lua interpreter]) + ]) + dnl Check the version. + m4_if([$2], [], + [_ax_check_text="whether $LUA version >= $1"], + [_ax_check_text="whether $LUA version >= $1, < $2"]) + AC_MSG_CHECKING([$_ax_check_text]) + _AX_LUA_CHK_VER([$LUA], [$1], [$2], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([version is out of range for specified LUA])]) + ax_display_LUA=$LUA + ], + [ dnl Try each interpreter until we find one that satisfies VERSION. + m4_if([$2], [], + [_ax_check_text="for a Lua interpreter with version >= $1"], + [_ax_check_text="for a Lua interpreter with version >= $1, < $2"]) + AC_CACHE_CHECK([$_ax_check_text], + [ax_cv_pathless_LUA], + [ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do + test "x$ax_cv_pathless_LUA" = 'xnone' && break + _AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue]) + _AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break]) + done + ]) + dnl Set $LUA to the absolute path of $ax_cv_pathless_LUA. + AS_IF([test "x$ax_cv_pathless_LUA" = 'xnone'], + [LUA=':'], + [AC_PATH_PROG([LUA], [$ax_cv_pathless_LUA])]) + ax_display_LUA=$ax_cv_pathless_LUA + ]) + ]) + + AS_IF([test "x$LUA" = 'x:'], + [ dnl Run any user-specified action, or abort. + m4_default([$4], [AC_MSG_ERROR([cannot find suitable Lua interpreter])]) + ], + [ dnl Query Lua for its version number. + AC_CACHE_CHECK([for $ax_display_LUA version], + [ax_cv_lua_version], + [ dnl Get the interpreter version in X.Y format. This should work for + dnl interpreters version 5.0 and beyond. + ax_cv_lua_version=[`$LUA -e ' + -- return a version number in X.Y format + local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)") + print(ver)'`] + ]) + AS_IF([test "x$ax_cv_lua_version" = 'x'], + [AC_MSG_ERROR([invalid Lua version number])]) + AC_SUBST([LUA_VERSION], [$ax_cv_lua_version]) + AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`]) + + dnl The following check is not supported: + dnl At times (like when building shared libraries) you may want to know + dnl which OS platform Lua thinks this is. + AC_CACHE_CHECK([for $ax_display_LUA platform], + [ax_cv_lua_platform], + [ax_cv_lua_platform=[`$LUA -e 'print("unknown")'`]]) + AC_SUBST([LUA_PLATFORM], [$ax_cv_lua_platform]) + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of LUA_PREFIX and LUA_EXEC_PREFIX. These are made distinct + dnl variables so they can be overridden if need be. However, the general + dnl consensus is that you shouldn't need this ability. + AC_SUBST([LUA_PREFIX], ['${prefix}']) + AC_SUBST([LUA_EXEC_PREFIX], ['${exec_prefix}']) + + dnl Lua provides no way to query the script directory, and instead + dnl provides LUA_PATH. However, we should be able to make a safe educated + dnl guess. If the built-in search path contains a directory which is + dnl prefixed by $prefix, then we can store scripts there. The first + dnl matching path will be used. + AC_CACHE_CHECK([for $ax_display_LUA script directory], + [ax_cv_lua_luadir], + [ AS_IF([test "x$prefix" = 'xNONE'], + [ax_lua_prefix=$ac_default_prefix], + [ax_lua_prefix=$prefix]) + + dnl Initialize to the default path. + ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION" + + dnl Try to find a path with the prefix. + _AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [script]) + AS_IF([test "x$ax_lua_prefixed_path" != 'x'], + [ dnl Fix the prefix. + _ax_strip_prefix=`echo "$ax_lua_prefix" | $SED 's|.|.|g'` + ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \ + $SED "s|^$_ax_strip_prefix|$LUA_PREFIX|"` + ]) + ]) + AC_SUBST([luadir], [$ax_cv_lua_luadir]) + AC_SUBST([pkgluadir], [\${luadir}/$PACKAGE]) + + dnl Lua provides no way to query the module directory, and instead + dnl provides LUA_PATH. However, we should be able to make a safe educated + dnl guess. If the built-in search path contains a directory which is + dnl prefixed by $exec_prefix, then we can store modules there. The first + dnl matching path will be used. + AC_CACHE_CHECK([for $ax_display_LUA module directory], + [ax_cv_lua_luaexecdir], + [ AS_IF([test "x$exec_prefix" = 'xNONE'], + [ax_lua_exec_prefix=$ax_lua_prefix], + [ax_lua_exec_prefix=$exec_prefix]) + + dnl Initialize to the default path. + ax_cv_lua_luaexecdir="$LUA_EXEC_PREFIX/lib/lua/$LUA_VERSION" + + dnl Try to find a path with the prefix. + _AX_LUA_FND_PRFX_PTH([$LUA], + [$ax_lua_exec_prefix], [module]) + AS_IF([test "x$ax_lua_prefixed_path" != 'x'], + [ dnl Fix the prefix. + _ax_strip_prefix=`echo "$ax_lua_exec_prefix" | $SED 's|.|.|g'` + ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \ + $SED "s|^$_ax_strip_prefix|$LUA_EXEC_PREFIX|"` + ]) + ]) + AC_SUBST([luaexecdir], [$ax_cv_lua_luaexecdir]) + AC_SUBST([pkgluaexecdir], [\${luaexecdir}/$PACKAGE]) + + dnl Run any user specified action. + $3 + ]) +]) + +dnl AX_WITH_LUA is now the same thing as AX_PROG_LUA. +AC_DEFUN([AX_WITH_LUA], +[ + AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA instead]]) + AX_PROG_LUA +]) + + +dnl ========================================================================= +dnl _AX_LUA_CHK_IS_INTRP(PROG, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +dnl ========================================================================= +AC_DEFUN([_AX_LUA_CHK_IS_INTRP], +[ + dnl A minimal Lua factorial to prove this is an interpreter. This should work + dnl for Lua interpreters version 5.0 and beyond. + _ax_lua_factorial=[`$1 2>/dev/null -e ' + -- a simple factorial + function fact (n) + if n == 0 then + return 1 + else + return n * fact(n-1) + end + end + print("fact(5) is " .. fact(5))'`] + AS_IF([test "$_ax_lua_factorial" = 'fact(5) is 120'], + [$2], [$3]) +]) + + +dnl ========================================================================= +dnl _AX_LUA_CHK_VER(PROG, MINIMUM-VERSION, [TOO-BIG-VERSION], +dnl [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +dnl ========================================================================= +AC_DEFUN([_AX_LUA_CHK_VER], +[ + dnl Check that the Lua version is within the bounds. Only the major and minor + dnl version numbers are considered. This should work for Lua interpreters + dnl version 5.0 and beyond. + _ax_lua_good_version=[`$1 -e ' + -- a script to compare versions + function verstr2num(verstr) + local _, _, majorver, minorver = string.find(verstr, "^(%d+)%.(%d+)") + if majorver and minorver then + return tonumber(majorver) * 100 + tonumber(minorver) + end + end + local minver = verstr2num("$2") + local _, _, trimver = string.find(_VERSION, "^Lua (.*)") + local ver = verstr2num(trimver) + local maxver = verstr2num("$3") or 1e9 + if minver <= ver and ver < maxver then + print("yes") + else + print("no") + end'`] + AS_IF([test "x$_ax_lua_good_version" = "xyes"], + [$4], [$5]) +]) + + +dnl ========================================================================= +dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, SCRIPT-OR-MODULE-DIR) +dnl ========================================================================= +AC_DEFUN([_AX_LUA_FND_PRFX_PTH], +[ + dnl Get the script or module directory by querying the Lua interpreter, + dnl filtering on the given prefix, and selecting the shallowest path. If no + dnl path is found matching the prefix, the result will be an empty string. + dnl The third argument determines the type of search, it can be 'script' or + dnl 'module'. Supplying 'script' will perform the search with package.path + dnl and LUA_PATH, and supplying 'module' will search with package.cpath and + dnl LUA_CPATH. This is done for compatibility with Lua 5.0. + + ax_lua_prefixed_path=[`$1 -e ' + -- get the path based on search type + local searchtype = "$3" + local paths = "" + if searchtype == "script" then + paths = (package and package.path) or LUA_PATH + elseif searchtype == "module" then + paths = (package and package.cpath) or LUA_CPATH + end + -- search for the prefix + local prefix = "'$2'" + local minpath = "" + local mindepth = 1e9 + string.gsub(paths, "(@<:@^;@:>@+)", + function (path) + path = string.gsub(path, "%?.*$", "") + path = string.gsub(path, "/@<:@^/@:>@*$", "") + if string.find(path, prefix) then + local depth = string.len(string.gsub(path, "@<:@^/@:>@", "")) + if depth < mindepth then + minpath = path + mindepth = depth + end + end + end) + print(minpath)'`] +]) + + +dnl ========================================================================= +dnl AX_LUA_HEADERS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ========================================================================= +AC_DEFUN([AX_LUA_HEADERS], +[ + dnl Check for LUA_VERSION. + AC_MSG_CHECKING([if LUA_VERSION is defined]) + AS_IF([test "x$LUA_VERSION" != 'x'], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION]) + ]) + + dnl Make LUA_INCLUDE a precious variable. + AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1]) + + dnl Some default directories to search. + LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'` + m4_define_default([_AX_LUA_INCLUDE_LIST], + [ /usr/include/lua$LUA_VERSION \ + /usr/include/lua-$LUA_VERSION \ + /usr/include/lua/$LUA_VERSION \ + /usr/include/lua$LUA_SHORT_VERSION \ + /usr/local/include/lua$LUA_VERSION \ + /usr/local/include/lua-$LUA_VERSION \ + /usr/local/include/lua/$LUA_VERSION \ + /usr/local/include/lua$LUA_SHORT_VERSION \ + ]) + + dnl Try to find the headers. + _ax_lua_saved_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" + AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h]) + CPPFLAGS=$_ax_lua_saved_cppflags + + dnl Try some other directories if LUA_INCLUDE was not set. + AS_IF([test "x$LUA_INCLUDE" = 'x' && + test "x$ac_cv_header_lua_h" != 'xyes'], + [ dnl Try some common include paths. + for _ax_include_path in _AX_LUA_INCLUDE_LIST; do + test ! -d "$_ax_include_path" && continue + + AC_MSG_CHECKING([for Lua headers in]) + AC_MSG_RESULT([$_ax_include_path]) + + AS_UNSET([ac_cv_header_lua_h]) + AS_UNSET([ac_cv_header_lualib_h]) + AS_UNSET([ac_cv_header_lauxlib_h]) + AS_UNSET([ac_cv_header_luaconf_h]) + + _ax_lua_saved_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS -I$_ax_include_path" + AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h]) + CPPFLAGS=$_ax_lua_saved_cppflags + + AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'], + [ LUA_INCLUDE="-I$_ax_include_path" + break + ]) + done + ]) + + AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'], + [ dnl Make a program to print LUA_VERSION defined in the header. + dnl TODO It would be really nice if we could do this without compiling a + dnl program, then it would work when cross compiling. But I'm not sure how + dnl to do this reliably. For now, assume versions match when cross compiling. + + AS_IF([test "x$cross_compiling" != 'xyes'], + [ AC_CACHE_CHECK([for Lua header version], + [ax_cv_lua_header_version], + [ _ax_lua_saved_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" + AC_RUN_IFELSE( + [ AC_LANG_SOURCE([[ +#include +#include +#include +int main(int argc, char ** argv) +{ + if(argc > 1) printf("%s", LUA_VERSION); + exit(EXIT_SUCCESS); +} +]]) + ], + [ ax_cv_lua_header_version=`./conftest$EXEEXT p | \ + $SED -n "s|^Lua \(@<:@0-9@:>@\{1,\}\.@<:@0-9@:>@\{1,\}\).\{0,\}|\1|p"` + ], + [ax_cv_lua_header_version='unknown']) + CPPFLAGS=$_ax_lua_saved_cppflags + ]) + + dnl Compare this to the previously found LUA_VERSION. + AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION]) + AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"], + [ AC_MSG_RESULT([yes]) + ax_header_version_match='yes' + ], + [ AC_MSG_RESULT([no]) + ax_header_version_match='no' + ]) + ], + [ AC_MSG_WARN([cross compiling so assuming header version number matches]) + ax_header_version_match='yes' + ]) + ]) + + dnl Was LUA_INCLUDE specified? + AS_IF([test "x$ax_header_version_match" != 'xyes' && + test "x$LUA_INCLUDE" != 'x'], + [AC_MSG_ERROR([cannot find headers for specified LUA_INCLUDE])]) + + dnl Test the final result and run user code. + AS_IF([test "x$ax_header_version_match" = 'xyes'], [$1], + [m4_default([$2], [AC_MSG_ERROR([cannot find Lua includes])])]) +]) + +dnl AX_LUA_HEADERS_VERSION no longer exists, use AX_LUA_HEADERS. +AC_DEFUN([AX_LUA_HEADERS_VERSION], +[ + AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS instead]]) +]) + + +dnl ========================================================================= +dnl AX_LUA_LIBS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ========================================================================= +AC_DEFUN([AX_LUA_LIBS], +[ + dnl TODO Should this macro also check various -L flags? + + dnl Check for LUA_VERSION. + AC_MSG_CHECKING([if LUA_VERSION is defined]) + AS_IF([test "x$LUA_VERSION" != 'x'], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([cannot check Lua libs without knowing LUA_VERSION]) + ]) + + dnl Make LUA_LIB a precious variable. + AC_ARG_VAR([LUA_LIB], [The Lua library, e.g. -llua5.1]) + + AS_IF([test "x$LUA_LIB" != 'x'], + [ dnl Check that LUA_LIBS works. + _ax_lua_saved_libs=$LIBS + LIBS="$LIBS $LUA_LIB" + AC_SEARCH_LIBS([lua_load], [], + [_ax_found_lua_libs='yes'], + [_ax_found_lua_libs='no']) + LIBS=$_ax_lua_saved_libs + + dnl Check the result. + AS_IF([test "x$_ax_found_lua_libs" != 'xyes'], + [AC_MSG_ERROR([cannot find libs for specified LUA_LIB])]) + ], + [ dnl First search for extra libs. + _ax_lua_extra_libs='' + + _ax_lua_saved_libs=$LIBS + LIBS="$LIBS $LUA_LIB" + AC_SEARCH_LIBS([exp], [m]) + AC_SEARCH_LIBS([dlopen], [dl]) + LIBS=$_ax_lua_saved_libs + + AS_IF([test "x$ac_cv_search_exp" != 'xno' && + test "x$ac_cv_search_exp" != 'xnone required'], + [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_exp"]) + + AS_IF([test "x$ac_cv_search_dlopen" != 'xno' && + test "x$ac_cv_search_dlopen" != 'xnone required'], + [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_dlopen"]) + + dnl Try to find the Lua libs. + _ax_lua_saved_libs=$LIBS + LIBS="$LIBS $LUA_LIB" + AC_SEARCH_LIBS([lua_load], + [ lua$LUA_VERSION \ + lua$LUA_SHORT_VERSION \ + lua-$LUA_VERSION \ + lua-$LUA_SHORT_VERSION \ + lua \ + ], + [_ax_found_lua_libs='yes'], + [_ax_found_lua_libs='no'], + [$_ax_lua_extra_libs]) + LIBS=$_ax_lua_saved_libs + + AS_IF([test "x$ac_cv_search_lua_load" != 'xno' && + test "x$ac_cv_search_lua_load" != 'xnone required'], + [LUA_LIB="$ac_cv_search_lua_load $_ax_lua_extra_libs"]) + ]) + + dnl Test the result and run user code. + AS_IF([test "x$_ax_found_lua_libs" = 'xyes'], [$1], + [m4_default([$2], [AC_MSG_ERROR([cannot find Lua libs])])]) +]) + + +dnl ========================================================================= +dnl AX_LUA_READLINE([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ========================================================================= +AC_DEFUN([AX_LUA_READLINE], +[ + AX_LIB_READLINE + AS_IF([test "x$ac_cv_header_readline_readline_h" != 'x' && + test "x$ac_cv_header_readline_history_h" != 'x'], + [ LUA_LIBS_CFLAGS="-DLUA_USE_READLINE $LUA_LIBS_CFLAGS" + $1 + ], + [$2]) +]) diff --git a/m4/ax_python.m4 b/m4/ax_python.m4 new file mode 100644 index 000000000000..32043c81ae23 --- /dev/null +++ b/m4/ax_python.m4 @@ -0,0 +1,284 @@ +dnl FRR Python autoconf magic +dnl 2019 David Lamparter for NetDEF, Inc. +dnl SPDX-License-Identifier: GPL-2.0-or-later + +dnl the _ at the beginning will be cut off (to support the empty version string) +m4_define_default([_FRR_PY_VERS], [_3 _ _2 _3.7 _3.6 _3.5 _3.4 _3.3 _3.2 _2.7]) + +dnl check basic interpreter properties (py2/py3) +dnl doubles as simple check whether the interpreter actually works +dnl also swaps in the full path to the interpreter +dnl arg1: if-true, arg2: if-false +AC_DEFUN([_FRR_PYTHON_INTERP], [dnl +AC_ARG_VAR([PYTHON], [Python interpreter to use])dnl + AC_MSG_CHECKING([python interpreter $PYTHON]) + AC_RUN_LOG(["$PYTHON" -c 'import sys; open("conftest.pyver", "w").write(sys.executable or ""); sys.exit(not (sys.version_info.major == 2 and sys.version_info.minor >= 7))']) + py2=$ac_status + _py2_full="`cat conftest.pyver 2>/dev/null`" + rm -f "conftest.pyver" >/dev/null 2>/dev/null + + AC_RUN_LOG(["$PYTHON" -c 'import sys; open("conftest.pyver", "w").write(sys.executable or ""); sys.exit(not ((sys.version_info.major == 3 and sys.version_info.minor >= 2) or sys.version_info.major > 3))']) + py3=$ac_status + _py3_full="`cat conftest.pyver 2>/dev/null`" + rm -f "conftest.pyver" >/dev/null 2>/dev/null + + case "p${py2}p${py3}" in + p0p1) frr_cv_python=python2 + _python_full="$_py2_full" ;; + p1p0) frr_cv_python=python3 + _python_full="$_py3_full" ;; + *) frr_cv_python=none ;; + esac + + if test "$frr_cv_python" = none; then + AC_MSG_RESULT([not working]) + $2 + else + test -n "$_python_full" -a -x "$_python_full" && PYTHON="$_python_full" + AC_MSG_RESULT([$PYTHON ($frr_cv_python)]) + $1 + fi + + dnl return value + test "$frr_cv_python" != none +]) + +dnl check whether $PYTHON has modules available +dnl arg1: list of modules (space separated) +dnl arg2: if all true, arg3: if any missing +dnl also sets frr_py_mod_ to "true" or "false" +AC_DEFUN([FRR_PYTHON_MODULES], [ + result=true + for pymod in $1; do + AC_MSG_CHECKING([whether $PYTHON module $pymod is available]) + AC_RUN_LOG(["$PYTHON" -c "import $pymod"]) + sane="`echo \"$pymod\" | tr -c '[a-zA-Z0-9\n]' '_'`" + if test "$ac_status" -eq 0; then + AC_MSG_RESULT([yes]) + eval frr_py_mod_$sane=true + else + AC_MSG_RESULT([no]) + eval frr_py_mod_$sane=false + result=false + fi + done + if $result; then + m4_default([$2], [:]) + else + m4_default([$3], [:]) + fi + $result +]) + +dnl check whether $PYTHON has modules available +dnl arg1: list of modules (space separated) +dnl arg2: command line parameters for executing +dnl arg3: if all true, arg4: if any missing +dnl also sets frr_py_modexec_ to "true" or "false" +AC_DEFUN([FRR_PYTHON_MOD_EXEC], [ + result=true + for pymod in $1; do + AC_MSG_CHECKING([whether $PYTHON module $pymod is executable]) + AC_RUN_LOG(["$PYTHON" -m "$pymod" $2 > /dev/null]) + sane="`echo \"$pymod\" | tr -c '[a-zA-Z0-9\n]' '_'`" + if test "$ac_status" -eq 0; then + AC_MSG_RESULT([yes]) + eval frr_py_modexec_$sane=true + else + AC_MSG_RESULT([no]) + eval frr_py_modexec_$sane=false + result=false + fi + done + if $result; then + m4_default([$3], [:]) + else + m4_default([$4], [:]) + fi + $result +]) + +dnl check whether we can build & link python bits +dnl input: PYTHON_CFLAGS and PYTHON_LIBS +AC_DEFUN([_FRR_PYTHON_DEVENV], [ + result=true + AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ +#include +#if PY_VERSION_HEX < 0x02070000 +#error python too old +#endif +int main(void); +], +[ +{ + Py_Initialize(); + return 0; +} +])], [ + # some python installs are missing the zlib dependency... + PYTHON_LIBS="${PYTHON_LIBS} -lz" + AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ +#include +#if PY_VERSION_HEX < 0x02070000 +#error python too old +#endif +int main(void); +], +[ +{ + Py_Initialize(); + return 0; +} +])], [ + result=false + AC_MSG_RESULT([no]) + ], [:]) + ], [:]) + + if $result; then + AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ +#include +#if PY_VERSION_HEX != $1 +#error python version mismatch +#endif +int main(void); +], +[ +{ + Py_Initialize(); + return 0; +} +])], [ + result=false + AC_MSG_RESULT([version mismatch]) + ], [ + AC_MSG_RESULT([yes]) + ]) + fi + + if $result; then + m4_default([$2], [:]) + else + m4_default([$3], [ + unset PYTHON_LIBS + unset PYTHON_CFLAGS + ]) + fi +]) + +AC_DEFUN([_FRR_PYTHON_GETDEV], [dnl +AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl + + py_abi="` \"$1\" -c \"import sys; print(getattr(sys, 'abiflags', ''))\"`" + py_hex="` \"$1\" -c \"import sys; print(hex(sys.hexversion))\"`" + py_ldver="` \"$1\" -c \"import sysconfig; print(sysconfig.get_config_var('LDVERSION') or '')\"`" + py_ver="` \"$1\" -c \"import sysconfig; print(sysconfig.get_config_var('VERSION') or '')\"`" + py_bindir="`\"$1\" -c \"import sysconfig; print(sysconfig.get_config_var('BINDIR') or '')\"`" + test -z "$py_bindir" || py_bindir="$py_bindir/" + echo "py_abi=${py_abi} py_ldver=${py_ldver} py_ver=${py_ver} py_bindir=${py_bindir}" >&AS_MESSAGE_LOG_FD + + py_found=false + + for tryver in "${py_ldver}" "${py_ver}"; do + pycfg="${py_bindir}python${tryver}-config" + AC_MSG_CHECKING([whether ${pycfg} is available]) + if "$pycfg" --configdir >/dev/null 2>/dev/null; then + AC_MSG_RESULT([yes]) + + PYTHON_CFLAGS="`\"$pycfg\" --includes`" + PYTHON_LIBS="`\"$pycfg\" --ldflags`" + + AC_MSG_CHECKING([whether ${pycfg} provides a working build environment]) + _FRR_PYTHON_DEVENV([$py_hex], [ + py_found=true + break + ]) + else + AC_MSG_RESULT([no]) + fi + + pkg_failed=no + AC_MSG_CHECKING([whether pkg-config python-${tryver} is available]) + unset PYTHON_CFLAGS + unset PYTHON_LIBS + pkg="python-${tryver}" + pkg="${pkg%-}" + _PKG_CONFIG([PYTHON_CFLAGS], [cflags], [${pkg}]) + _PKG_CONFIG([PYTHON_LIBS], [libs], [${pkg}]) + if test $pkg_failed = no; then + AC_MSG_RESULT([yes]) + + PYTHON_CFLAGS=$pkg_cv_PYTHON_CFLAGS + PYTHON_LIBS=$pkg_cv_PYTHON_LIBS + + AC_MSG_CHECKING([whether pkg-config python-${tryver} provides a working build environment]) + _FRR_PYTHON_DEVENV([$py_hex], [ + py_found=true + break + ]) + else + AC_MSG_RESULT([no]) + fi + done + + if $py_found; then + m4_default([$2], [:]) + else + unset PYTHON_CFLAGS + unset PYTHON_LIBS + m4_default([$3], [:]) + fi +]) + +dnl just find python without checking headers/libs +AC_DEFUN([FRR_PYTHON], [ + dnl user override + if test "x$PYTHON" != "x"; then + _FRR_PYTHON_INTERP([], [ + AC_MSG_ERROR([PYTHON ($PYTHON) explicitly specified but not working]) + ]) + else + for frr_pyver in _FRR_PY_VERS; do + PYTHON="python${frr_pyver#_}" + _FRR_PYTHON_INTERP([break]) + PYTHON=":" + done + if test "$PYTHON" = ":"; then + AC_MSG_ERROR([no working python version found]) + fi + fi + AC_SUBST([PYTHON]) +]) + +dnl find python with checking headers/libs +AC_DEFUN([FRR_PYTHON_DEV], [dnl +AC_ARG_VAR([PYTHON_CFLAGS], [C compiler flags for Python])dnl +AC_ARG_VAR([PYTHON_LIBS], [linker flags for Python])dnl + + dnl user override + if test "x$PYTHON" != "x"; then + _FRR_PYTHON_INTERP([], [ + AC_MSG_ERROR([PYTHON ($PYTHON) explicitly specified but not working]) + ]) + _FRR_PYTHON_GETDEV([$PYTHON], [], [ + AC_MSG_ERROR([PYTHON ($PYTHON) explicitly specified but development environment not working]) + ]) + else + for frr_pyver in _FRR_PY_VERS; do + PYTHON="python${frr_pyver#_}" + _FRR_PYTHON_INTERP([ + _FRR_PYTHON_GETDEV([$PYTHON], [ + break + ]) + ]) + PYTHON=":" + done + if test "$PYTHON" = ":"; then + AC_MSG_ERROR([no working python version found]) + fi + fi + + AC_SUBST([PYTHON_CFLAGS]) + AC_SUBST([PYTHON_LIBS]) + AC_SUBST([PYTHON]) +]) diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c index 5508778243d5..cc18b36f6ae5 100644 --- a/nhrpd/nhrp_cache.c +++ b/nhrpd/nhrp_cache.c @@ -30,9 +30,9 @@ const char *const nhrp_cache_type_str[] = { [NHRP_CACHE_LOCAL] = "local", }; -static unsigned int nhrp_cache_protocol_key(void *peer_data) +static unsigned int nhrp_cache_protocol_key(const void *peer_data) { - struct nhrp_cache *p = peer_data; + const struct nhrp_cache *p = peer_data; return sockunion_hash(&p->remote_addr); } diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c index b33eaa047849..8f1ba14fe4f2 100644 --- a/nhrpd/nhrp_interface.c +++ b/nhrpd/nhrp_interface.c @@ -296,13 +296,12 @@ void nhrp_interface_update(struct interface *ifp) } } -int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; /* read and add the interface in the iflist. */ - ifp = zebra_interface_add_read(client->ibuf, vrf_id); + ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); if (ifp == NULL) return 0; @@ -315,13 +314,12 @@ int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, return 0; } -int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; - s = client->ibuf; + s = zclient->ibuf; ifp = zebra_interface_state_read(s, vrf_id); if (ifp == NULL) return 0; @@ -335,12 +333,11 @@ int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length, return 0; } -int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_interface_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; - ifp = zebra_interface_state_read(client->ibuf, vrf_id); + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); if (ifp == NULL) return 0; @@ -350,12 +347,11 @@ int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length, return 0; } -int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_interface_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; - ifp = zebra_interface_state_read(client->ibuf, vrf_id); + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); if (ifp == NULL) return 0; @@ -364,13 +360,12 @@ int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length, return 0; } -int nhrp_interface_address_add(int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *ifc; char buf[PREFIX_STRLEN]; - ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -383,13 +378,12 @@ int nhrp_interface_address_add(int cmd, struct zclient *client, return 0; } -int nhrp_interface_address_delete(int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; char buf[PREFIX_STRLEN]; - ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c index 9b8599eded57..d7c485f0a050 100644 --- a/nhrpd/nhrp_main.c +++ b/nhrpd/nhrp_main.c @@ -55,7 +55,7 @@ struct zebra_privs_t nhrpd_privs = { .vty_group = VTY_GROUP, #endif .caps_p = _caps_p, - .cap_num_p = ZEBRA_NUM_OF(_caps_p), + .cap_num_p = array_size(_caps_p), }; static void parse_arguments(int argc, char **argv) diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c index db2f72ac2211..ca309f25061e 100644 --- a/nhrpd/nhrp_peer.c +++ b/nhrpd/nhrp_peer.c @@ -151,9 +151,9 @@ static void nhrp_peer_ifp_notify(struct notifier_block *n, unsigned long cmd) nhrp_peer_unref(p); } -static unsigned int nhrp_peer_key(void *peer_data) +static unsigned int nhrp_peer_key(const void *peer_data) { - struct nhrp_peer *p = peer_data; + const struct nhrp_peer *p = peer_data; return sockunion_hash(&p->vc->remote.nbma); } diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index dae00bbcea00..a788eb2efb0f 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -184,8 +184,7 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, &api); } -int nhrp_route_read(int cmd, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_route_read(ZAPI_CALLBACK_ARGS) { struct zapi_route api; struct zapi_nexthop *api_nh; diff --git a/nhrpd/nhrp_vc.c b/nhrpd/nhrp_vc.c index f92ea4ac900e..605aa34ff9bd 100644 --- a/nhrpd/nhrp_vc.c +++ b/nhrpd/nhrp_vc.c @@ -28,9 +28,9 @@ struct child_sa { static struct hash *nhrp_vc_hash; static struct list_head childlist_head[512]; -static unsigned int nhrp_vc_key(void *peer_data) +static unsigned int nhrp_vc_key(const void *peer_data) { - struct nhrp_vc *vc = peer_data; + const struct nhrp_vc *vc = peer_data; return jhash_2words(sockunion_hash(&vc->local.nbma), sockunion_hash(&vc->remote.nbma), 0); } @@ -102,7 +102,7 @@ int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc) { char buf[2][SU_ADDRSTRLEN]; struct child_sa *sa = NULL, *lsa; - uint32_t child_hash = child_id % ZEBRA_NUM_OF(childlist_head); + uint32_t child_hash = child_id % array_size(childlist_head); int abort_migration = 0; list_for_each_entry(lsa, &childlist_head[child_hash], childlist_entry) @@ -202,7 +202,7 @@ void nhrp_vc_init(void) size_t i; nhrp_vc_hash = hash_create(nhrp_vc_key, nhrp_vc_cmp, "NHRP VC hash"); - for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) + for (i = 0; i < array_size(childlist_head); i++) list_init(&childlist_head[i]); } @@ -211,7 +211,7 @@ void nhrp_vc_reset(void) struct child_sa *sa, *n; size_t i; - for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) { + for (i = 0; i < array_size(childlist_head); i++) { list_for_each_entry_safe(sa, n, &childlist_head[i], childlist_entry) nhrp_vc_ipsec_updown(sa->id, 0); diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h index 8f1c63457aa6..89de145e6501 100644 --- a/nhrpd/nhrpd.h +++ b/nhrpd/nhrpd.h @@ -314,18 +314,12 @@ void nhrp_interface_init(void); void nhrp_interface_update(struct interface *ifp); void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi); -int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id); -int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id); -int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id); -int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id); -int nhrp_interface_address_add(int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id); -int nhrp_interface_address_delete(int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id); +int nhrp_interface_add(ZAPI_CALLBACK_ARGS); +int nhrp_interface_delete(ZAPI_CALLBACK_ARGS); +int nhrp_interface_up(ZAPI_CALLBACK_ARGS); +int nhrp_interface_down(ZAPI_CALLBACK_ARGS); +int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS); +int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS); void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn); @@ -349,8 +343,7 @@ void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp); void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix *p, struct interface *ifp, const union sockunion *nexthop, uint32_t mtu); -int nhrp_route_read(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id); +int nhrp_route_read(ZAPI_CALLBACK_ARGS); int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, union sockunion *via, struct interface **ifp); enum nhrp_route_type nhrp_route_address(struct interface *in_ifp, diff --git a/nhrpd/reqid.c b/nhrpd/reqid.c index 08a007bdf933..e56bbe3bf779 100644 --- a/nhrpd/reqid.c +++ b/nhrpd/reqid.c @@ -2,9 +2,9 @@ #include "hash.h" #include "nhrpd.h" -static unsigned int nhrp_reqid_key(void *data) +static unsigned int nhrp_reqid_key(const void *data) { - struct nhrp_reqid *r = data; + const struct nhrp_reqid *r = data; return r->request_id; } diff --git a/nhrpd/resolver.c b/nhrpd/resolver.c index 830f0e1c84f8..64b16e7ee34d 100644 --- a/nhrpd/resolver.c +++ b/nhrpd/resolver.c @@ -171,7 +171,7 @@ static void ares_address_cb(void *arg, int status, int timeouts, return; } - for (i = 0; i < ZEBRA_NUM_OF(addr) && he->h_addr_list[i] != NULL; i++) { + for (i = 0; i < array_size(addr) && he->h_addr_list[i] != NULL; i++) { memset(&addr[i], 0, sizeof(addr[i])); addr[i].sa.sa_family = he->h_addrtype; switch (he->h_addrtype) { diff --git a/nhrpd/vici.c b/nhrpd/vici.c index 3de4609a2bc4..d6105b71d427 100644 --- a/nhrpd/vici.c +++ b/nhrpd/vici.c @@ -207,7 +207,7 @@ static void parse_sa_message(struct vici_message_ctx *ctx, } break; default: - if (!key) + if (!key || !key->ptr) break; switch (key->ptr[0]) { @@ -550,7 +550,7 @@ int sock_open_unix(const char *path) memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr.sun_family) + strlen(addr.sun_path)); diff --git a/nhrpd/zbuf.c b/nhrpd/zbuf.c index c662295083e7..7f1475cc69e1 100644 --- a/nhrpd/zbuf.c +++ b/nhrpd/zbuf.c @@ -196,7 +196,7 @@ int zbufq_write(struct zbuf_queue *zbq, int fd) iov[iovcnt++] = (struct iovec){ .iov_base = zb->head, .iov_len = zbuf_used(zb), }; - if (iovcnt >= ZEBRA_NUM_OF(iov)) + if (iovcnt >= array_size(iov)) break; } diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 2795bb9abdfb..946bbf8cc942 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -956,7 +956,7 @@ static void ospf6_asbr_routemap_update(const char *mapname) } } -static void ospf6_asbr_routemap_event(route_map_event_t event, const char *name) +static void ospf6_asbr_routemap_event(const char *name) { int type; diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c index e7284a665935..f0500601b090 100644 --- a/ospf6d/ospf6_bfd.c +++ b/ospf6d/ospf6_bfd.c @@ -74,6 +74,7 @@ void ospf6_bfd_reg_dereg_nbr(struct ospf6_neighbor *on, int command) struct interface *ifp = oi->interface; struct bfd_info *bfd_info; char src[64]; + int cbit; if (!oi->bfd_info || !on->bfd_info) return; @@ -85,9 +86,11 @@ void ospf6_bfd_reg_dereg_nbr(struct ospf6_neighbor *on, int command) bfd_get_command_dbg_str(command), src); } + cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); + bfd_peer_sendmsg(zclient, bfd_info, AF_INET6, &on->linklocal_addr, - on->ospf6_if->linklocal_addr, ifp->name, 0, 0, command, - 0, VRF_DEFAULT); + on->ospf6_if->linklocal_addr, ifp->name, 0, 0, + cbit, command, 0, VRF_DEFAULT); if (command == ZEBRA_BFD_DEST_DEREGISTER) bfd_info_free((struct bfd_info **)&on->bfd_info); @@ -138,8 +141,7 @@ static void ospf6_bfd_reg_dereg_all_nbr(struct ospf6_interface *oi, int command) * ospf6_bfd_nbr_replay - Replay all the neighbors that have BFD enabled * to zebra */ -static int ospf6_bfd_nbr_replay(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) { struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); struct listnode *node; @@ -152,7 +154,7 @@ static int ospf6_bfd_nbr_replay(int command, struct zclient *zclient, zlog_debug("Zebra: BFD Dest replay request"); /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); /* Replay the neighbor, if BFD is enabled on the interface*/ FOR_ALL_INTERFACES (vrf, ifp) { @@ -182,8 +184,7 @@ static int ospf6_bfd_nbr_replay(int command, struct zclient *zclient, * has changed and bring down the neighbor * connectivity if BFD down is received. */ -static int ospf6_bfd_interface_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct ospf6_interface *oi; @@ -197,7 +198,8 @@ static int ospf6_bfd_interface_dest_update(int command, struct zclient *zclient, struct bfd_info *bfd_info; struct timeval tv; - ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status, vrf_id); + ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status, + NULL, vrf_id); if ((ifp == NULL) || (dp.family != AF_INET6)) return 0; diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index f08426fb4799..aa4a99517319 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -27,7 +27,6 @@ #include "command.h" #include "vty.h" #include "prefix.h" -#include "pqueue.h" #include "linklist.h" #include "thread.h" #include "lib_errors.h" @@ -76,16 +75,18 @@ static unsigned int ospf6_spf_get_ifindex_from_nh(struct ospf6_vertex *v) return 0; } -static int ospf6_vertex_cmp(void *a, void *b) +static int ospf6_vertex_cmp(const struct ospf6_vertex *va, + const struct ospf6_vertex *vb) { - struct ospf6_vertex *va = (struct ospf6_vertex *)a; - struct ospf6_vertex *vb = (struct ospf6_vertex *)b; - /* ascending order */ if (va->cost != vb->cost) return (va->cost - vb->cost); - return (va->hops - vb->hops); + if (va->hops != vb->hops) + return (va->hops - vb->hops); + return 0; } +DECLARE_SKIPLIST_NONUNIQ(vertex_pqueue, struct ospf6_vertex, pqi, + ospf6_vertex_cmp) static int ospf6_vertex_id_cmp(void *a, void *b) { @@ -461,7 +462,7 @@ void ospf6_spf_calculation(uint32_t router_id, struct ospf6_route_table *result_table, struct ospf6_area *oa) { - struct pqueue *candidate_list; + struct vertex_pqueue_head candidate_list; struct ospf6_vertex *root, *v, *w; int size; caddr_t lsdesc; @@ -481,8 +482,7 @@ void ospf6_spf_calculation(uint32_t router_id, } /* initialize */ - candidate_list = pqueue_create(); - candidate_list->cmp = ospf6_vertex_cmp; + vertex_pqueue_init(&candidate_list); root = ospf6_vertex_create(lsa); root->area = oa; @@ -492,13 +492,10 @@ void ospf6_spf_calculation(uint32_t router_id, inet_pton(AF_INET6, "::1", &address); /* Actually insert root to the candidate-list as the only candidate */ - pqueue_enqueue(root, candidate_list); + vertex_pqueue_add(&candidate_list, root); /* Iterate until candidate-list becomes empty */ - while (candidate_list->size) { - /* get closest candidate from priority queue */ - v = pqueue_dequeue(candidate_list); - + while ((v = vertex_pqueue_pop(&candidate_list))) { /* installing may result in merging or rejecting of the vertex */ if (ospf6_spf_install(v, result_table) < 0) @@ -557,12 +554,11 @@ void ospf6_spf_calculation(uint32_t router_id, zlog_debug( " New candidate: %s hops %d cost %d", w->name, w->hops, w->cost); - pqueue_enqueue(w, candidate_list); + vertex_pqueue_add(&candidate_list, w); } } - - pqueue_delete(candidate_list); + //vertex_pqueue_fini(&candidate_list); ospf6_remove_temp_router_lsa(oa); diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h index da95ec80a32c..a387d40a577e 100644 --- a/ospf6d/ospf6_spf.h +++ b/ospf6d/ospf6_spf.h @@ -21,6 +21,7 @@ #ifndef OSPF6_SPF_H #define OSPF6_SPF_H +#include "typesafe.h" #include "ospf6_top.h" /* Debug option */ @@ -33,6 +34,7 @@ extern unsigned char conf_debug_ospf6_spf; #define IS_OSPF6_DEBUG_SPF(level) \ (conf_debug_ospf6_spf & OSPF6_DEBUG_SPF_##level) +PREDECL_SKIPLIST_NONUNIQ(vertex_pqueue) /* Transit Vertex */ struct ospf6_vertex { /* type of this vertex */ @@ -41,6 +43,8 @@ struct ospf6_vertex { /* Vertex Identifier */ struct prefix vertex_id; + struct vertex_pqueue_item pqi; + /* Identifier String */ char name[128]; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index abdc82a73841..af16c5aa7cde 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -48,8 +48,7 @@ unsigned char conf_debug_ospf6_zebra = 0; struct zclient *zclient = NULL; /* Router-id update message from zebra. */ -static int ospf6_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct prefix router_id; struct ospf6 *o = ospf6; @@ -99,8 +98,7 @@ void ospf6_zebra_no_redistribute(int type) } /* Inteface addition message from zebra. */ -static int ospf6_zebra_if_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_zebra_if_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -112,8 +110,7 @@ static int ospf6_zebra_if_add(int command, struct zclient *zclient, return 0; } -static int ospf6_zebra_if_del(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_zebra_if_del(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -132,8 +129,7 @@ static int ospf6_zebra_if_del(int command, struct zclient *zclient, return 0; } -static int ospf6_zebra_if_state_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_zebra_if_state_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -152,10 +148,7 @@ static int ospf6_zebra_if_state_update(int command, struct zclient *zclient, return 0; } -static int ospf6_zebra_if_address_update_add(int command, - struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static int ospf6_zebra_if_address_update_add(ZAPI_CALLBACK_ARGS) { struct connected *c; char buf[128]; @@ -179,10 +172,7 @@ static int ospf6_zebra_if_address_update_add(int command, return 0; } -static int ospf6_zebra_if_address_update_delete(int command, - struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static int ospf6_zebra_if_address_update_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; char buf[128]; @@ -209,8 +199,7 @@ static int ospf6_zebra_if_address_update_delete(int command, return 0; } -static int ospf6_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; unsigned long ifindex; @@ -240,13 +229,13 @@ static int ospf6_zebra_read_route(int command, struct zclient *zclient, zlog_debug( "Zebra Receive route %s: %s %s nexthop %s ifindex %ld tag %" ROUTE_TAG_PRI, - (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD ? "add" - : "delete"), + (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD ? "add" + : "delete"), zebra_route_string(api.type), prefixstr, nexthopstr, ifindex, api.tag); } - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) ospf6_asbr_redistribute_add(api.type, ifindex, &api.prefix, api.nexthop_num, nexthop, api.tag); else @@ -582,7 +571,7 @@ uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, struct ospf6_route * or) static void ospf6_zebra_connected(struct zclient *zclient) { /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); zclient_send_reg_requests(zclient, VRF_DEFAULT); } diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index ea919017d36c..921a81388975 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -148,6 +148,7 @@ ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance, new = ospf_external_info_new(type, instance); new->p = p; new->ifindex = ifindex; + new->vrf_id = ospf->vrf_id; new->nexthop = nexthop; new->tag = tag; diff --git a/ospfd/ospf_asbr.h b/ospfd/ospf_asbr.h index ac7bd68b5f3d..40ad544c8e97 100644 --- a/ospfd/ospf_asbr.h +++ b/ospfd/ospf_asbr.h @@ -49,6 +49,8 @@ struct external_info { struct route_map_set_values route_map_set; #define ROUTEMAP_METRIC(E) (E)->route_map_set.metric #define ROUTEMAP_METRIC_TYPE(E) (E)->route_map_set.metric_type + + vrf_id_t vrf_id; }; #define OSPF_ASBR_CHECK_DELAY 30 diff --git a/ospfd/ospf_bfd.c b/ospfd/ospf_bfd.c index 594735a08f80..a17975270af9 100644 --- a/ospfd/ospf_bfd.c +++ b/ospfd/ospf_bfd.c @@ -65,6 +65,7 @@ static void ospf_bfd_reg_dereg_nbr(struct ospf_neighbor *nbr, int command) struct interface *ifp = oi->ifp; struct ospf_if_params *params; struct bfd_info *bfd_info; + int cbit; /* Check if BFD is enabled */ params = IF_DEF_PARAMS(ifp); @@ -80,8 +81,10 @@ static void ospf_bfd_reg_dereg_nbr(struct ospf_neighbor *nbr, int command) inet_ntoa(nbr->src), ospf_vrf_id_to_name(oi->ospf->vrf_id)); + cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); + bfd_peer_sendmsg(zclient, bfd_info, AF_INET, &nbr->src, NULL, ifp->name, - 0, 0, command, 0, oi->ospf->vrf_id); + 0, 0, cbit, command, 0, oi->ospf->vrf_id); } /* @@ -141,8 +144,7 @@ static int ospf_bfd_reg_dereg_all_nbr(struct interface *ifp, int command) * ospf_bfd_nbr_replay - Replay all the neighbors that have BFD enabled * to zebra */ -static int ospf_bfd_nbr_replay(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) { struct listnode *inode, *node, *onode; struct ospf *ospf; @@ -157,7 +159,7 @@ static int ospf_bfd_nbr_replay(int command, struct zclient *zclient, } /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); /* Replay the neighbor, if BFD is enabled in OSPF */ for (ALL_LIST_ELEMENTS(om->ospf, node, onode, ospf)) { @@ -195,8 +197,7 @@ static int ospf_bfd_nbr_replay(int command, struct zclient *zclient, * connectivity if the BFD status changed to * down. */ -static int ospf_bfd_interface_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct ospf_interface *oi; @@ -209,7 +210,8 @@ static int ospf_bfd_interface_dest_update(int command, struct zclient *zclient, struct bfd_info *bfd_info; struct timeval tv; - ifp = bfd_get_peer_info(zclient->ibuf, &p, NULL, &status, vrf_id); + ifp = bfd_get_peer_info(zclient->ibuf, &p, NULL, &status, + NULL, vrf_id); if ((ifp == NULL) || (p.family != AF_INET)) return 0; @@ -251,6 +253,13 @@ static int ospf_bfd_interface_dest_update(int command, struct zclient *zclient, OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer); } + if ((status == BFD_STATUS_UP) + && (old_status == BFD_STATUS_DOWN)) { + if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) + zlog_debug("NSM[%s:%s]: BFD Up", + IF_NAME(nbr->oi), + inet_ntoa(nbr->address.u.prefix4)); + } } return 0; diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index a381cf7145fd..5e3dabc27a1f 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -69,6 +69,8 @@ struct lsa_header { uint16_t length; }; +struct vertex; + /* OSPF LSA. */ struct ospf_lsa { /* LSA origination flag. */ @@ -95,10 +97,7 @@ struct ospf_lsa { int lock; /* Flags for the SPF calculation. */ - int stat; -#define LSA_SPF_NOT_EXPLORED -1 -#define LSA_SPF_IN_SPFTREE -2 - /* If stat >= 0, stat is LSA position in candidates heap. */ + struct vertex *stat; /* References to this LSA in neighbor retransmission lists*/ int retransmit_counter; diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c index 2e850c4e2670..86eb141312eb 100644 --- a/ospfd/ospf_lsdb.c +++ b/ospfd/ospf_lsdb.c @@ -169,21 +169,6 @@ void ospf_lsdb_delete_all(struct ospf_lsdb *lsdb) } } -void ospf_lsdb_clean_stat(struct ospf_lsdb *lsdb) -{ - struct route_table *table; - struct route_node *rn; - struct ospf_lsa *lsa; - int i; - - for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) { - table = lsdb->type[i].db; - for (rn = route_top(table); rn; rn = route_next(rn)) - if ((lsa = (rn->info)) != NULL) - lsa->stat = LSA_SPF_NOT_EXPLORED; - } -} - struct ospf_lsa *ospf_lsdb_lookup(struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) { struct route_table *table; diff --git a/ospfd/ospf_lsdb.h b/ospfd/ospf_lsdb.h index 65c7e28fed37..5cf5d05449ea 100644 --- a/ospfd/ospf_lsdb.h +++ b/ospfd/ospf_lsdb.h @@ -67,8 +67,6 @@ extern void ls_prefix_set(struct prefix_ls *lp, struct ospf_lsa *lsa); extern void ospf_lsdb_add(struct ospf_lsdb *, struct ospf_lsa *); extern void ospf_lsdb_delete(struct ospf_lsdb *, struct ospf_lsa *); extern void ospf_lsdb_delete_all(struct ospf_lsdb *); -/* Set all stats to -1 (LSA_SPF_NOT_EXPLORED). */ -extern void ospf_lsdb_clean_stat(struct ospf_lsdb *lsdb); extern struct ospf_lsa *ospf_lsdb_lookup(struct ospf_lsdb *, struct ospf_lsa *); extern struct ospf_lsa *ospf_lsdb_lookup_by_id(struct ospf_lsdb *, uint8_t, struct in_addr, struct in_addr); diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 43c5e338b089..6bc8c25153bf 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -2107,7 +2107,6 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, dump_lsa_key(lsa)); DISCARD_LSA(lsa, 4); - continue; } /* Actual flooding procedure. */ diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c index 30b2a50bb38f..1d17dbf40d5d 100644 --- a/ospfd/ospf_routemap.c +++ b/ospfd/ospf_routemap.c @@ -97,7 +97,7 @@ static void ospf_route_map_update(const char *name) } } -static void ospf_route_map_event(route_map_event_t event, const char *name) +static void ospf_route_map_event(const char *name) { struct ospf *ospf; int type; @@ -298,7 +298,7 @@ static route_map_result_t route_match_interface(void *rule, if (type == RMAP_OSPF) { ei = object; - ifp = if_lookup_by_name_all_vrf((char *)rule); + ifp = if_lookup_by_name((char *)rule, ei->vrf_id); if (ifp == NULL || ifp->ifindex != ei->ifindex) return RMAP_NOMATCH; diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index 6e03fa9bdeb0..296a05bdf141 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -30,7 +30,6 @@ #include "table.h" #include "log.h" #include "sockunion.h" /* for inet_ntop () */ -#include "pqueue.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -53,6 +52,11 @@ static unsigned int spf_reason_flags = 0; +/* dummy vertex to flag "in spftree" */ +static const struct vertex vertex_in_spftree = {}; +#define LSA_SPF_IN_SPFTREE (struct vertex *)&vertex_in_spftree +#define LSA_SPF_NOT_EXPLORED NULL + static void ospf_clear_spf_reason_flags(void) { spf_reason_flags = 0; @@ -72,35 +76,36 @@ static struct list vertex_list = {.del = ospf_vertex_free}; /* Heap related functions, for the managment of the candidates, to * be used with pqueue. */ -static int cmp(void *node1, void *node2) +static int vertex_cmp(const struct vertex *v1, const struct vertex *v2) { - struct vertex *v1 = (struct vertex *)node1; - struct vertex *v2 = (struct vertex *)node2; - if (v1 != NULL && v2 != NULL) { - /* network vertices must be chosen before router vertices of - * same - * cost in order to find all shortest paths - */ - if (((v1->distance - v2->distance) == 0) - && (v1->type != v2->type)) { - switch (v1->type) { - case OSPF_VERTEX_NETWORK: - return -1; - case OSPF_VERTEX_ROUTER: - return 1; - } - } else - return (v1->distance - v2->distance); + if (v1->distance != v2->distance) + return v1->distance - v2->distance; + + if (v1->type != v2->type) { + switch (v1->type) { + case OSPF_VERTEX_NETWORK: + return -1; + case OSPF_VERTEX_ROUTER: + return 1; + } } return 0; } +DECLARE_SKIPLIST_NONUNIQ(vertex_pqueue, struct vertex, pqi, vertex_cmp) -static void update_stat(void *node, int position) +static void lsdb_clean_stat(struct ospf_lsdb *lsdb) { - struct vertex *v = node; - - /* Set the status of the vertex, when its position changes. */ - *(v->stat) = position; + struct route_table *table; + struct route_node *rn; + struct ospf_lsa *lsa; + int i; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) { + table = lsdb->type[i].db; + for (rn = route_top(table); rn; rn = route_next(rn)) + if ((lsa = (rn->info)) != NULL) + lsa->stat = LSA_SPF_NOT_EXPLORED; + } } static struct vertex_nexthop *vertex_nexthop_new(void) @@ -179,7 +184,6 @@ static struct vertex *ospf_vertex_new(struct ospf_lsa *lsa) new = XCALLOC(MTYPE_OSPF_VERTEX, sizeof(struct vertex)); new->flags = 0; - new->stat = &(lsa->stat); new->type = lsa->data->type; new->id = lsa->data->id; new->lsa = lsa->data; @@ -187,6 +191,9 @@ static struct vertex *ospf_vertex_new(struct ospf_lsa *lsa) new->parents = list_new(); new->parents->del = vertex_parent_free; new->parents->cmp = vertex_parent_cmp; + new->lsa_p = lsa; + + lsa->stat = new; listnode_add(&vertex_list, new); @@ -786,7 +793,8 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, * path is found to a vertex already on the candidate list, store the new cost. */ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, - struct ospf_area *area, struct pqueue *candidate) + struct ospf_area *area, + struct vertex_pqueue_head *candidate) { struct ospf_lsa *w_lsa = NULL; uint8_t *p; @@ -935,13 +943,11 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, /* Calculate nexthop to W. */ if (ospf_nexthop_calculation(area, v, w, l, distance, lsa_pos)) - pqueue_enqueue(w, candidate); + vertex_pqueue_add(candidate, w); else if (IS_DEBUG_OSPF_EVENT) zlog_debug("Nexthop Calc failed"); - } else if (w_lsa->stat >= 0) { - /* Get the vertex from candidates. */ - w = candidate->array[w_lsa->stat]; - + } else if (w_lsa->stat != LSA_SPF_IN_SPFTREE) { + w = w_lsa->stat; /* if D is greater than. */ if (w->distance < distance) { continue; @@ -962,18 +968,10 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, * which * will flush the old parents */ - if (ospf_nexthop_calculation(area, v, w, l, - distance, lsa_pos)) - /* Decrease the key of the node in the - * heap. - * trickle-sort it up towards root, just - * in case this - * node should now be the new root due - * the cost change. - * (next pqueu_{de,en}queue will fully - * re-heap the queue). - */ - trickle_up(w_lsa->stat, candidate); + vertex_pqueue_del(candidate, w); + ospf_nexthop_calculation(area, v, w, l, + distance, lsa_pos); + vertex_pqueue_add(candidate, w); } } /* end W is already on the candidate list */ } /* end loop over the links in V's LSA */ @@ -1169,7 +1167,7 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, struct route_table *new_table, struct route_table *new_rtrs) { - struct pqueue *candidate; + struct vertex_pqueue_head candidate; struct vertex *v; if (IS_DEBUG_OSPF_EVENT) { @@ -1194,11 +1192,9 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, /* This function scans all the LSA database and set the stat field to * LSA_SPF_NOT_EXPLORED. */ - ospf_lsdb_clean_stat(area->lsdb); + lsdb_clean_stat(area->lsdb); /* Create a new heap for the candidates. */ - candidate = pqueue_create(); - candidate->cmp = cmp; - candidate->update = update_stat; + vertex_pqueue_init(&candidate); /* Initialize the shortest-path tree to only the root (which is the router doing the calculation). */ @@ -1207,7 +1203,7 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, /* Set LSA position to LSA_SPF_IN_SPFTREE. This vertex is the root of * the * spanning tree. */ - *(v->stat) = LSA_SPF_IN_SPFTREE; + v->lsa_p->stat = LSA_SPF_IN_SPFTREE; /* Set Area A's TransitCapability to FALSE. */ area->transit = OSPF_TRANSIT_FALSE; @@ -1215,23 +1211,22 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, for (;;) { /* RFC2328 16.1. (2). */ - ospf_spf_next(v, ospf, area, candidate); + ospf_spf_next(v, ospf, area, &candidate); /* RFC2328 16.1. (3). */ /* If at this step the candidate list is empty, the shortest- path tree (of transit vertices) has been completely built and this stage of the procedure terminates. */ - if (candidate->size == 0) - break; - /* Otherwise, choose the vertex belonging to the candidate list that is closest to the root, and add it to the shortest-path tree (removing it from the candidate list in the process). */ /* Extract from the candidates the node with the lower key. */ - v = (struct vertex *)pqueue_dequeue(candidate); + v = vertex_pqueue_pop(&candidate); + if (!v) + break; /* Update stat field in vertex. */ - *(v->stat) = LSA_SPF_IN_SPFTREE; + v->lsa_p->stat = LSA_SPF_IN_SPFTREE; ospf_vertex_add_parent(v); @@ -1255,7 +1250,7 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, ospf_spf_process_stubs(area, area->spf, new_table, 0); /* Free candidate queue. */ - pqueue_delete(candidate); + //vertex_pqueue_fini(&candidate); ospf_vertex_dump(__func__, area->spf, 0, 1); /* Free nexthop information, canonical versions of which are attached diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h index 85f42bcd1829..09a0b6f1b7eb 100644 --- a/ospfd/ospf_spf.h +++ b/ospfd/ospf_spf.h @@ -22,6 +22,8 @@ #ifndef _QUAGGA_OSPF_SPF_H #define _QUAGGA_OSPF_SPF_H +#include "typesafe.h" + /* values for vertex->type */ #define OSPF_VERTEX_ROUTER 1 /* for a Router-LSA */ #define OSPF_VERTEX_NETWORK 2 /* for a Network-LSA */ @@ -31,13 +33,15 @@ /* The "root" is the node running the SPF calculation */ +PREDECL_SKIPLIST_NONUNIQ(vertex_pqueue) /* A router or network in an area */ struct vertex { + struct vertex_pqueue_item pqi; uint8_t flags; uint8_t type; /* copied from LSA header */ struct in_addr id; /* copied from LSA header */ + struct ospf_lsa *lsa_p; struct lsa_header *lsa; /* Router or Network LSA */ - int *stat; /* Link to LSA status. */ uint32_t distance; /* from root to this vertex */ struct list *parents; /* list of parents in SPF tree */ struct list *children; /* list of children in SPF tree*/ diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c index a49352086828..6947393a6086 100644 --- a/ospfd/ospf_sr.c +++ b/ospfd/ospf_sr.c @@ -86,7 +86,7 @@ static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe); */ /* Hash function for Segment Routing entry */ -static unsigned int sr_hash(void *p) +static unsigned int sr_hash(const void *p) { const struct in_addr *rid = p; diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c index bd8cbee11ab2..e68363663949 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -397,53 +397,13 @@ static void set_linkparams_link_type(struct ospf_interface *oi, return; } -static void set_linkparams_link_id(struct ospf_interface *oi, - struct mpls_te_link *lp) +static void set_linkparams_link_id(struct mpls_te_link *lp, + struct in_addr link_id) { - struct ospf_neighbor *nbr; - int done = 0; lp->link_id.header.type = htons(TE_LINK_SUBTLV_LINK_ID); lp->link_id.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); - - /* - * The Link ID is identical to the contents of the Link ID field - * in the Router LSA for these link types. - */ - switch (oi->type) { - case OSPF_IFTYPE_POINTOPOINT: - /* Take the router ID of the neighbor. */ - if ((nbr = ospf_nbr_lookup_ptop(oi)) - && nbr->state == NSM_Full) { - lp->link_id.value = nbr->router_id; - done = 1; - } - break; - case OSPF_IFTYPE_BROADCAST: - case OSPF_IFTYPE_NBMA: - /* Take the interface address of the designated router. */ - if ((nbr = ospf_nbr_lookup_by_addr(oi->nbrs, &DR(oi))) == NULL) - break; - - if (nbr->state == NSM_Full - || (IPV4_ADDR_SAME(&oi->address->u.prefix4, &DR(oi)) - && ospf_nbr_count(oi, NSM_Full) > 0)) { - lp->link_id.value = DR(oi); - done = 1; - } - break; - default: - /* Not supported yet. */ /* XXX */ - lp->link_id.header.type = htons(0); - break; - } - - if (!done) { - struct in_addr mask; - masklen2ip(oi->address->prefixlen, &mask); - lp->link_id.value.s_addr = - oi->address->u.prefix4.s_addr & mask.s_addr; - } + lp->link_id.value = link_id; return; } @@ -958,40 +918,33 @@ void ospf_mpls_te_update_if(struct interface *ifp) return; } +/* + * Just add interface and set available information. Other information + * and flooding of LSA will be done later when adjacency will be up + * See ospf_mpls_te_nsm_change() after + */ static void ospf_mpls_te_ism_change(struct ospf_interface *oi, int old_state) { - struct te_link_subtlv_link_type old_type; - struct te_link_subtlv_link_id old_id; + struct mpls_te_link *lp; - if ((lp = lookup_linkparams_by_ifp(oi->ifp)) == NULL) { + lp = lookup_linkparams_by_ifp(oi->ifp); + if (lp == NULL) { flog_warn( EC_OSPF_TE_UNEXPECTED, - "ospf_mpls_te_ism_change: Cannot get linkparams from OI(%s)?", - IF_NAME(oi)); + "MPLS-TE (%s): Cannot get linkparams from OI(%s)?", + __func__, IF_NAME(oi)); return; } if (oi->area == NULL || oi->area->ospf == NULL) { flog_warn( EC_OSPF_TE_UNEXPECTED, - "ospf_mpls_te_ism_change: Cannot refer to OSPF from OI(%s)?", - IF_NAME(oi)); + "MPLS-TE (%s): Cannot refer to OSPF from OI(%s)?", + __func__, IF_NAME(oi)); return; } -#ifdef notyet - if ((lp->area != NULL - && !IPV4_ADDR_SAME(&lp->area->area_id, &oi->area->area_id)) - || (lp->area != NULL && oi->area == NULL)) { - /* How should we consider this case? */ - flog_warn( - EC_OSPF_TE_UNEXPECTED, - "MPLS-TE: Area for OI(%s) has changed to [%s], flush previous LSAs", - IF_NAME(oi), - oi->area ? inet_ntoa(oi->area->area_id) : "N/A"); - ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); - } -#endif + /* Keep Area information in combination with linkparams. */ lp->area = oi->area; @@ -1003,55 +956,103 @@ static void ospf_mpls_te_ism_change(struct ospf_interface *oi, int old_state) case ISM_DROther: case ISM_Backup: case ISM_DR: - old_type = lp->link_type; - old_id = lp->link_id; - - /* Set Link type, Link ID, Local and Remote IP addr */ + /* Set Link type and Local IP addr */ set_linkparams_link_type(oi, lp); - set_linkparams_link_id(oi, lp); set_linkparams_lclif_ipaddr(lp, oi->address->u.prefix4); - if (oi->type == LINK_TYPE_SUBTLV_VALUE_PTP) { - struct prefix *pref = CONNECTED_PREFIX(oi->connected); - if (pref != NULL) - set_linkparams_rmtif_ipaddr(lp, - pref->u.prefix4); - } - - /* Update TE parameters */ - update_linkparams(lp); - - /* Try to Schedule LSA */ - if ((ntohs(old_type.header.type) - != ntohs(lp->link_type.header.type) - || old_type.link_type.value - != lp->link_type.link_type.value) - || (ntohs(old_id.header.type) - != ntohs(lp->link_id.header.type) - || ntohl(old_id.value.s_addr) - != ntohl(lp->link_id.value.s_addr))) { - if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) - ospf_mpls_te_lsa_schedule(lp, REFRESH_THIS_LSA); - else - ospf_mpls_te_lsa_schedule(lp, - REORIGINATE_THIS_LSA); - } break; default: - lp->link_type.header.type = htons(0); - lp->link_id.header.type = htons(0); - + /* State is undefined: Flush LSA if engaged */ if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); break; } + if (IS_DEBUG_OSPF_TE) + zlog_debug( + "MPLS-TE(%s): Update Link parameters for interface %s", + __func__, IF_NAME(oi)); + return; } +/* + * Complete TE info and schedule LSA flooding + * Link-ID and Remote IP address must be set with neighbor info + * which are only valid once NSM state is FULL + */ static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_state) { - /* Nothing to do here */ + struct ospf_interface *oi = nbr->oi; + struct mpls_te_link *lp; + + /* Process Neighbor only when its state is NSM Full */ + if (nbr->state != NSM_Full) + return; + + /* Get interface information for Traffic Engineering */ + lp = lookup_linkparams_by_ifp(oi->ifp); + if (lp == NULL) { + flog_warn( + EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Cannot get linkparams from OI(%s)?", + __func__, IF_NAME(oi)); + return; + } + + if (oi->area == NULL || oi->area->ospf == NULL) { + flog_warn( + EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Cannot refer to OSPF from OI(%s)?", + __func__, IF_NAME(oi)); + return; + } + + /* Keep Area information in combination with SR info. */ + lp->area = oi->area; + + /* Keep interface MPLS-TE status */ + lp->flags = HAS_LINK_PARAMS(oi->ifp); + + /* + * The Link ID is identical to the contents of the Link ID field + * in the Router LSA for these link types. + */ + switch (oi->state) { + case ISM_PointToPoint: + /* Set Link ID with neighbor Router ID */ + set_linkparams_link_id(lp, nbr->router_id); + /* Set Remote IP address */ + set_linkparams_rmtif_ipaddr(lp, nbr->address.u.prefix4); + break; + + case ISM_DR: + case ISM_DROther: + case ISM_Backup: + /* Set Link ID with the Designated Router ID */ + set_linkparams_link_id(lp, DR(oi)); + break; + + default: + /* State is undefined: Flush LSA if engaged */ + if (OspfMplsTE.enabled && + CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) + ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + return; + } + + if (IS_DEBUG_OSPF_TE) + zlog_debug( + "MPLS-TE (%s): Add Link-ID %s for interface %s ", + __func__, inet_ntoa(lp->link_id.value), oi->ifp->name); + + /* Try to Schedule LSA */ + if (OspfMplsTE.enabled) { + if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) + ospf_mpls_te_lsa_schedule(lp, REFRESH_THIS_LSA); + else + ospf_mpls_te_lsa_schedule(lp, REORIGINATE_THIS_LSA); + } return; } @@ -2534,7 +2535,7 @@ DEFUN (show_ip_ospf_mpls_te_link, struct interface *ifp = NULL; struct listnode *node; char *vrf_name = NULL; - bool all_vrf; + bool all_vrf = false; int inst = 0; int idx_vrf = 0; struct ospf *ospf = NULL; diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 4cbd817ad871..c178e367d390 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -65,8 +65,7 @@ struct zclient *zclient = NULL; extern struct thread_master *master; /* Router-id update message from zebra. */ -static int ospf_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct ospf *ospf = NULL; struct prefix router_id; @@ -99,8 +98,7 @@ static int ospf_router_id_update_zebra(int command, struct zclient *zclient, } /* Inteface addition message from zebra. */ -static int ospf_interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp = NULL; struct ospf *ospf = NULL; @@ -138,8 +136,7 @@ static int ospf_interface_add(int command, struct zclient *zclient, return 0; } -static int ospf_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -181,8 +178,7 @@ static struct interface *zebra_interface_if_lookup(struct stream *s, return if_lookup_by_name(ifname_tmp, vrf_id); } -static int ospf_interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_state_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct ospf_interface *oi; @@ -238,8 +234,7 @@ static int ospf_interface_state_up(int command, struct zclient *zclient, return 0; } -static int ospf_interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_state_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct ospf_interface *oi; @@ -263,14 +258,13 @@ static int ospf_interface_state_down(int command, struct zclient *zclient, return 0; } -static int ospf_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; struct ospf *ospf = NULL; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -294,8 +288,7 @@ static int ospf_interface_address_add(int command, struct zclient *zclient, return 0; } -static int ospf_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; struct interface *ifp; @@ -303,7 +296,7 @@ static int ospf_interface_address_delete(int command, struct zclient *zclient, struct route_node *rn; struct prefix p; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -339,8 +332,7 @@ static int ospf_interface_address_delete(int command, struct zclient *zclient, return 0; } -static int ospf_interface_link_params(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_link_params(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -356,8 +348,7 @@ static int ospf_interface_link_params(int command, struct zclient *zclient, } /* VRF update for an interface. */ -static int ospf_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp = NULL; vrf_id_t new_vrf_id; @@ -1003,8 +994,7 @@ void ospf_routemap_unset(struct ospf_redist *red) } /* Zebra route add and delete treatment. */ -static int ospf_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; struct prefix_ipv4 p; @@ -1047,7 +1037,7 @@ static int ospf_zebra_read_route(int command, struct zclient *zclient, zebra_route_string(api.type), vrf_id, buf_prefix); } - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { /* XXX|HACK|TODO|FIXME: * Maybe we should ignore reject/blackhole routes? Testing * shows that there is no problems though and this is only way @@ -1108,7 +1098,7 @@ static int ospf_zebra_read_route(int command, struct zclient *zclient, } } } - } else /* if (command == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ + } else /* if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ { ospf_external_info_delete(ospf, rt_type, api.instance, p); if (is_prefix_default(&p)) @@ -1575,7 +1565,7 @@ void ospf_zebra_vrf_deregister(struct ospf *ospf) static void ospf_zebra_connected(struct zclient *zclient) { /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); zclient_send_reg_requests(zclient, VRF_DEFAULT); } diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 7504752725df..22dd6f1a3833 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -66,6 +66,8 @@ static void *pbr_nhrc_hash_alloc(void *p) { struct nhrc *nhrc = XCALLOC(MTYPE_PBR_NHG, sizeof(struct nhrc)); nhrc->nexthop = *(struct nexthop *)p; + nhrc->nexthop.next = NULL; + nhrc->nexthop.prev = NULL; return nhrc; } @@ -129,10 +131,10 @@ static void pbr_nh_delete_iterate(struct hash_bucket *b, void *p) pbr_nh_delete((struct pbr_nexthop_cache **)&b->data); } -static uint32_t pbr_nh_hash_key(void *arg) +static uint32_t pbr_nh_hash_key(const void *arg) { uint32_t key; - struct pbr_nexthop_cache *pbrnc = (struct pbr_nexthop_cache *)arg; + const struct pbr_nexthop_cache *pbrnc = arg; key = nexthop_hash(pbrnc->nexthop); @@ -191,7 +193,7 @@ static void *pbr_nhgc_alloc(void *p) new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new)); - strcpy(new->name, pnhgc->name); + strlcpy(new->name, pnhgc->name, sizeof(pnhgc->name)); new->table_id = pbr_nht_get_next_tableid(false); DEBUGD(&pbr_dbg_nht, "%s: NHT: %s assigned Table ID: %u", @@ -712,10 +714,30 @@ static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket *b, pnhi->valid += 1; } +static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket *b, + void *data) +{ + struct pbr_nexthop_cache *pnhc = b->data; + struct nexthop_group *nhg = data; + struct nexthop *nh = NULL; + + copy_nexthops(&nh, pnhc->nexthop, NULL); + + nexthop_add(&nhg->nexthop, nh); +} + +static void +pbr_nexthop_group_cache_to_nexthop_group(struct nexthop_group *nhg, + struct pbr_nexthop_group_cache *pnhgc) +{ + hash_iterate(pnhgc->nhh, pbr_nexthop_group_cache_iterate_to_group, nhg); +} + static void pbr_nht_nexthop_update_lookup(struct hash_bucket *b, void *data) { struct pbr_nexthop_group_cache *pnhgc = b->data; struct pbr_nht_individual pnhi; + struct nexthop_group nhg = {}; bool old_valid; old_valid = pnhgc->valid; @@ -730,6 +752,13 @@ static void pbr_nht_nexthop_update_lookup(struct hash_bucket *b, void *data) */ pnhgc->valid = !!pnhi.valid; + if (pnhgc->valid) { + pbr_nexthop_group_cache_to_nexthop_group(&nhg, pnhgc); + pbr_nht_install_nexthop_group(pnhgc, nhg); + /* Don't need copied nexthops anymore */ + nexthops_free(nhg.nexthop); + } + if (old_valid != pnhgc->valid) pbr_map_check_nh_group_change(pnhgc->name); } @@ -789,10 +818,9 @@ void pbr_nht_nexthop_interface_update(struct interface *ifp) ifp); } -static uint32_t pbr_nhg_hash_key(void *arg) +static uint32_t pbr_nhg_hash_key(const void *arg) { - struct pbr_nexthop_group_cache *nhgc = - (struct pbr_nexthop_group_cache *)arg; + const struct pbr_nexthop_group_cache *nhgc = arg; return jhash(&nhgc->name, strlen(nhgc->name), 0x52c34a96); } @@ -940,7 +968,7 @@ void pbr_nht_init(void) pbr_nhg_hash = hash_create_size( 16, pbr_nhg_hash_key, pbr_nhg_hash_equal, "PBR NHG Cache Hash"); pbr_nhrc_hash = - hash_create_size(16, (unsigned int (*)(void *))nexthop_hash, + hash_create_size(16, (unsigned int (*)(const void *))nexthop_hash, pbr_nhrc_hash_equal, "PBR NH Hash"); pbr_nhg_low_table = PBR_NHT_DEFAULT_LOW_TABLEID; diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 067d5c01fdfa..a71c712ea71e 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -38,7 +38,7 @@ #include "pbrd/pbr_vty_clippy.c" #endif -DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-700)", +DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map PBRMAP seq (1-700)", "Create pbr-map or enter pbr-map command mode\n" "The name of the PBR MAP\n" "Sequence to insert in existing pbr-map entry\n" @@ -54,7 +54,7 @@ DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-700)", return CMD_SUCCESS; } -DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-700)]", +DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]", NO_STR "Delete pbr-map\n" "The name of the PBR MAP\n" @@ -172,7 +172,7 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, } DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, - "[no] set nexthop-group NAME$name", + "[no] set nexthop-group NHGNAME$name", NO_STR "Set for the PBR-MAP\n" "nexthop-group to use\n" @@ -348,7 +348,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, DEFPY (pbr_policy, pbr_policy_cmd, - "[no] pbr-policy NAME$mapname", + "[no] pbr-policy PBRMAP$mapname", NO_STR "Policy to use\n" "Name of the pbr-map to apply\n") @@ -661,8 +661,27 @@ static int pbr_vty_map_config_write(struct vty *vty) return 1; } +static void pbr_map_completer(vector comps, struct cmd_token *token) +{ + struct pbr_map *pbrm; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, pbrm->name)); +} + +static const struct cmd_variable_handler pbr_map_name[] = { + { + .tokenname = "PBRMAP", .completions = pbr_map_completer, + }, + { + .completions = NULL + } +}; + void pbr_vty_init(void) { + cmd_variable_handler_register(pbr_map_name); + install_node(&interface_node, pbr_interface_config_write); if_cmd_init(); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 4f8f50556bb5..466a9a13ae1a 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -59,8 +59,7 @@ struct pbr_interface *pbr_if_new(struct interface *ifp) } /* Inteface addition message from zebra. */ -static int interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -80,8 +79,7 @@ static int interface_add(int command, struct zclient *zclient, return 0; } -static int interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -102,28 +100,27 @@ static int interface_delete(int command, struct zclient *zclient, return 0; } -static int interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; char buf[PREFIX_STRLEN]; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); DEBUGD(&pbr_dbg_zebra, - "%s: %s added %s", __PRETTY_FUNCTION__, c->ifp->name, - prefix2str(c->address, buf, sizeof(buf))); + "%s: %s added %s", __PRETTY_FUNCTION__, + c ? c->ifp->name : "Unknown", + c ? prefix2str(c->address, buf, sizeof(buf)) : "Unknown"); return 0; } -static int interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; char buf[PREFIX_STRLEN]; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; @@ -136,8 +133,7 @@ static int interface_address_delete(int command, struct zclient *zclient, return 0; } -static int interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_state_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -151,8 +147,7 @@ static int interface_state_up(int command, struct zclient *zclient, return 0; } -static int interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_state_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -166,8 +161,7 @@ static int interface_state_down(int command, struct zclient *zclient, return 0; } -static int route_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int route_notify_owner(ZAPI_CALLBACK_ARGS) { struct prefix p; enum zapi_route_notify_owner note; @@ -212,8 +206,7 @@ static int route_notify_owner(int command, struct zclient *zclient, return 0; } -static int rule_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int rule_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t seqno, priority, unique; enum zapi_rule_notify_owner note; @@ -356,6 +349,11 @@ void route_add(struct pbr_nexthop_group_cache *pnhgc, struct nexthop_group nhg, "%s: Asked to install unsupported route type: L2VPN", __PRETTY_FUNCTION__); break; + case AFI_UNSPEC: + DEBUGD(&pbr_dbg_zebra, + "%s: Asked to install unspecified route type", + __PRETTY_FUNCTION__); + break; } } @@ -398,11 +396,15 @@ void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi) "%s: Asked to delete unsupported route type: L2VPN", __PRETTY_FUNCTION__); break; + case AFI_UNSPEC: + DEBUGD(&pbr_dbg_zebra, + "%s: Asked to delete unspecified route type", + __PRETTY_FUNCTION__); + break; } } -static int pbr_zebra_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pbr_zebra_nexthop_update(ZAPI_CALLBACK_ARGS) { struct zapi_route nhr; char buf[PREFIX2STR_BUFFER]; diff --git a/pimd/mtracebis.c b/pimd/mtracebis.c index 65c495eff045..dd9576275411 100644 --- a/pimd/mtracebis.c +++ b/pimd/mtracebis.c @@ -17,9 +17,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include #ifdef __linux__ diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c index 0a450834e3ef..438a0c9b64db 100644 --- a/pimd/pim_assert.c +++ b/pimd/pim_assert.c @@ -418,7 +418,7 @@ int pim_assert_build_msg(uint8_t *pim_msg, int buf_size, struct interface *ifp, Add PIM header */ pim_msg_size = pim_msg_curr - pim_msg; - pim_msg_build_header(pim_msg, pim_msg_size, PIM_MSG_TYPE_ASSERT); + pim_msg_build_header(pim_msg, pim_msg_size, PIM_MSG_TYPE_ASSERT, false); return pim_msg_size; } diff --git a/pimd/pim_bfd.c b/pimd/pim_bfd.c index 466cc60643ac..87d0f9fa22d1 100644 --- a/pimd/pim_bfd.c +++ b/pimd/pim_bfd.c @@ -111,6 +111,7 @@ static void pim_bfd_reg_dereg_nbr(struct pim_neighbor *nbr, int command) struct pim_interface *pim_ifp = NULL; struct bfd_info *bfd_info = NULL; struct zclient *zclient = NULL; + int cbit; zclient = pim_zebra_zclient_get(); @@ -127,8 +128,12 @@ static void pim_bfd_reg_dereg_nbr(struct pim_neighbor *nbr, int command) zlog_debug("%s Nbr %s %s with BFD", __PRETTY_FUNCTION__, str, bfd_get_command_dbg_str(command)); } + + cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); + bfd_peer_sendmsg(zclient, bfd_info, AF_INET, &nbr->source_addr, NULL, - nbr->interface->name, 0, 0, command, 0, VRF_DEFAULT); + nbr->interface->name, 0, 0, cbit, + command, 0, VRF_DEFAULT); } /* @@ -208,8 +213,7 @@ void pim_bfd_if_param_set(struct interface *ifp, uint32_t min_rx, * connectivity if the BFD status changed to * down. */ -static int pim_bfd_interface_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp = NULL; struct pim_interface *pim_ifp = NULL; @@ -223,7 +227,8 @@ static int pim_bfd_interface_dest_update(int command, struct zclient *zclient, struct listnode *neigh_nextnode = NULL; struct pim_neighbor *neigh = NULL; - ifp = bfd_get_peer_info(zclient->ibuf, &p, NULL, &status, vrf_id); + ifp = bfd_get_peer_info(zclient->ibuf, &p, NULL, &status, + NULL, vrf_id); if ((ifp == NULL) || (p.family != AF_INET)) return 0; @@ -288,8 +293,7 @@ static int pim_bfd_interface_dest_update(int command, struct zclient *zclient, * pim_bfd_nbr_replay - Replay all the neighbors that have BFD enabled * to zebra */ -static int pim_bfd_nbr_replay(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) { struct interface *ifp = NULL; struct pim_interface *pim_ifp = NULL; @@ -299,7 +303,7 @@ static int pim_bfd_nbr_replay(int command, struct zclient *zclient, struct vrf *vrf = NULL; /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { FOR_ALL_INTERFACES (vrf, ifp) { diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c new file mode 100644 index 000000000000..4ba8d08fe3a3 --- /dev/null +++ b/pimd/pim_bsm.c @@ -0,0 +1,1404 @@ +/* + * pim_bsm.c: PIM BSM handling routines + * + * Copyright (C) 2018-19 Vmware, Inc. + * Saravanan K + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#include "if.h" +#include "pimd.h" +#include "pim_iface.h" +#include "pim_instance.h" +#include "pim_rpf.h" +#include "pim_hello.h" +#include "pim_pim.h" +#include "pim_nht.h" +#include "pim_bsm.h" +#include "pim_time.h" + +/* Functions forward declaration */ +static void pim_bs_timer_start(struct bsm_scope *scope, int bs_timeout); +static void pim_g2rp_timer_start(struct bsm_rpinfo *bsrp, int hold_time); +static inline void pim_g2rp_timer_restart(struct bsm_rpinfo *bsrp, + int hold_time); + +/* Memory Types */ +DEFINE_MTYPE_STATIC(PIMD, PIM_BSGRP_NODE, "PIM BSR advertised grp info") +DEFINE_MTYPE_STATIC(PIMD, PIM_BSRP_NODE, "PIM BSR advertised RP info") +DEFINE_MTYPE_STATIC(PIMD, PIM_BSM_INFO, "PIM BSM Info") +DEFINE_MTYPE_STATIC(PIMD, PIM_BSM_PKT_VAR_MEM, "PIM BSM Packet") + +/* All bsm packets forwarded shall be fit within ip mtu less iphdr(max) */ +#define MAX_IP_HDR_LEN 24 + +/* pim_bsm_write_config - Write the interface pim bsm configuration.*/ +void pim_bsm_write_config(struct vty *vty, struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + + if (pim_ifp) { + if (!pim_ifp->bsm_enable) + vty_out(vty, " no ip pim bsm\n"); + if (!pim_ifp->ucast_bsm_accept) + vty_out(vty, " no ip pim unicast-bsm\n"); + } +} + +static void pim_free_bsgrp_data(struct bsgrp_node *bsgrp_node) +{ + if (bsgrp_node->bsrp_list) + list_delete(&bsgrp_node->bsrp_list); + if (bsgrp_node->partial_bsrp_list) + list_delete(&bsgrp_node->partial_bsrp_list); + XFREE(MTYPE_PIM_BSGRP_NODE, bsgrp_node); +} + +static void pim_free_bsgrp_node(struct route_table *rt, struct prefix *grp) +{ + struct route_node *rn; + + rn = route_node_lookup(rt, grp); + if (rn) { + rn->info = NULL; + route_unlock_node(rn); + route_unlock_node(rn); + } +} + +static void pim_bsm_node_free(struct bsm_info *bsm) +{ + if (bsm->bsm) + XFREE(MTYPE_PIM_BSM_PKT_VAR_MEM, bsm->bsm); + XFREE(MTYPE_PIM_BSM_INFO, bsm); +} + +static int pim_g2rp_list_compare(struct bsm_rpinfo *node1, + struct bsm_rpinfo *node2) +{ + /* RP election Algo : + * Step-1 : Loweset Rp priority will have higher precedance. + * Step-2 : If priority same then higher hash val will have + * higher precedance. + * Step-3 : If Hash val is same then highest rp address will + * become elected RP. + */ + if (node1->rp_prio < node2->rp_prio) + return -1; + if (node1->rp_prio > node2->rp_prio) + return 1; + if (node1->hash < node2->hash) + return 1; + if (node1->hash > node2->hash) + return -1; + if (node1->rp_address.s_addr < node2->rp_address.s_addr) + return 1; + if (node1->rp_address.s_addr > node2->rp_address.s_addr) + return -1; + return 0; +} + +static void pim_free_bsrp_node(struct bsm_rpinfo *bsrp_info) +{ + if (bsrp_info->g2rp_timer) + THREAD_OFF(bsrp_info->g2rp_timer); + XFREE(MTYPE_PIM_BSRP_NODE, bsrp_info); +} + +static struct list *pim_alloc_bsrp_list(void) +{ + struct list *new_list = NULL; + + new_list = list_new(); + + if (!new_list) + return NULL; + + new_list->cmp = (int (*)(void *, void *))pim_g2rp_list_compare; + new_list->del = (void (*)(void *))pim_free_bsrp_node; + + return new_list; +} + +static struct bsgrp_node *pim_bsm_new_bsgrp_node(struct route_table *rt, + struct prefix *grp) +{ + struct route_node *rn; + struct bsgrp_node *bsgrp; + + rn = route_node_get(rt, grp); + if (!rn) { + zlog_warn("%s: route node creation failed", + __PRETTY_FUNCTION__); + return NULL; + } + bsgrp = XCALLOC(MTYPE_PIM_BSGRP_NODE, sizeof(struct bsgrp_node)); + + if (!bsgrp) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: bsgrp alloc failed", + __PRETTY_FUNCTION__); + route_unlock_node(rn); + return NULL; + } + + rn->info = bsgrp; + bsgrp->bsrp_list = pim_alloc_bsrp_list(); + bsgrp->partial_bsrp_list = pim_alloc_bsrp_list(); + + if ((!bsgrp->bsrp_list) || (!bsgrp->partial_bsrp_list)) { + route_unlock_node(rn); + pim_free_bsgrp_data(bsgrp); + return NULL; + } + + prefix_copy(&bsgrp->group, grp); + return bsgrp; +} + +static int pim_on_bs_timer(struct thread *t) +{ + struct route_node *rn; + struct bsm_scope *scope; + struct bsgrp_node *bsgrp_node; + struct bsm_rpinfo *bsrp; + struct prefix nht_p; + char buf[PREFIX2STR_BUFFER]; + bool is_bsr_tracking = true; + + scope = THREAD_ARG(t); + THREAD_OFF(scope->bs_timer); + + if (PIM_DEBUG_BSM) + zlog_debug("%s: Bootstrap Timer expired for scope: %d", + __PRETTY_FUNCTION__, scope->sz_id); + + /* Remove next hop tracking for the bsr */ + nht_p.family = AF_INET; + nht_p.prefixlen = IPV4_MAX_BITLEN; + nht_p.u.prefix4 = scope->current_bsr; + if (PIM_DEBUG_BSM) { + prefix2str(&nht_p, buf, sizeof(buf)); + zlog_debug("%s: Deregister BSR addr %s with Zebra NHT", + __PRETTY_FUNCTION__, buf); + } + pim_delete_tracked_nexthop(scope->pim, &nht_p, NULL, NULL, + is_bsr_tracking); + + /* Reset scope zone data */ + scope->accept_nofwd_bsm = false; + scope->state = ACCEPT_ANY; + scope->current_bsr.s_addr = INADDR_ANY; + scope->current_bsr_prio = 0; + scope->current_bsr_first_ts = 0; + scope->current_bsr_last_ts = 0; + scope->bsm_frag_tag = 0; + list_delete_all_node(scope->bsm_list); + + for (rn = route_top(scope->bsrp_table); rn; rn = route_next(rn)) { + + bsgrp_node = (struct bsgrp_node *)rn->info; + if (!bsgrp_node) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: bsgrp_node is null", + __PRETTY_FUNCTION__); + continue; + } + /* Give grace time for rp to continue for another hold time */ + if ((bsgrp_node->bsrp_list) && (bsgrp_node->bsrp_list->count)) { + bsrp = listnode_head(bsgrp_node->bsrp_list); + pim_g2rp_timer_restart(bsrp, bsrp->rp_holdtime); + } + /* clear pending list */ + if ((bsgrp_node->partial_bsrp_list) + && (bsgrp_node->partial_bsrp_list->count)) { + list_delete_all_node(bsgrp_node->partial_bsrp_list); + bsgrp_node->pend_rp_cnt = 0; + } + } + return 0; +} + +static void pim_bs_timer_stop(struct bsm_scope *scope) +{ + if (PIM_DEBUG_BSM) + zlog_debug("%s : BS timer being stopped of sz: %d", + __PRETTY_FUNCTION__, scope->sz_id); + THREAD_OFF(scope->bs_timer); +} + +static void pim_bs_timer_start(struct bsm_scope *scope, int bs_timeout) +{ + if (!scope) { + if (PIM_DEBUG_BSM) + zlog_debug("%s : Invalid scope(NULL).", + __PRETTY_FUNCTION__); + return; + } + THREAD_OFF(scope->bs_timer); + if (PIM_DEBUG_BSM) + zlog_debug("%s : starting bs timer for scope %d with timeout %d secs", + __PRETTY_FUNCTION__, scope->sz_id, bs_timeout); + thread_add_timer(router->master, pim_on_bs_timer, scope, bs_timeout, + &scope->bs_timer); +} + +static inline void pim_bs_timer_restart(struct bsm_scope *scope, int bs_timeout) +{ + pim_bs_timer_start(scope, bs_timeout); +} + +void pim_bsm_proc_init(struct pim_instance *pim) +{ + memset(&pim->global_scope, 0, sizeof(struct bsm_scope)); + + pim->global_scope.sz_id = PIM_GBL_SZ_ID; + pim->global_scope.bsrp_table = route_table_init(); + pim->global_scope.accept_nofwd_bsm = true; + pim->global_scope.state = NO_INFO; + pim->global_scope.pim = pim; + pim->global_scope.bsm_list = list_new(); + pim->global_scope.bsm_list->del = (void (*)(void *))pim_bsm_node_free; + pim_bs_timer_start(&pim->global_scope, PIM_BS_TIME); +} + +void pim_bsm_proc_free(struct pim_instance *pim) +{ + struct route_node *rn; + struct bsgrp_node *bsgrp; + + pim_bs_timer_stop(&pim->global_scope); + + if (pim->global_scope.bsm_list) + list_delete(&pim->global_scope.bsm_list); + + for (rn = route_top(pim->global_scope.bsrp_table); rn; + rn = route_next(rn)) { + bsgrp = rn->info; + if (!bsgrp) + continue; + pim_free_bsgrp_data(bsgrp); + } + + if (pim->global_scope.bsrp_table) + route_table_finish(pim->global_scope.bsrp_table); +} + +static bool is_hold_time_elapsed(void *data) +{ + struct bsm_rpinfo *bsrp; + + bsrp = data; + + if (bsrp->elapse_time < bsrp->rp_holdtime) + return false; + else + return true; +} + +static int pim_on_g2rp_timer(struct thread *t) +{ + struct bsm_rpinfo *bsrp; + struct bsm_rpinfo *bsrp_node; + struct bsgrp_node *bsgrp_node; + struct listnode *bsrp_ln; + struct pim_instance *pim; + struct rp_info *rp_info; + struct route_node *rn; + uint16_t elapse; + struct in_addr bsrp_addr; + + bsrp = THREAD_ARG(t); + THREAD_OFF(bsrp->g2rp_timer); + bsgrp_node = bsrp->bsgrp_node; + + /* elapse time is the hold time of expired node */ + elapse = bsrp->rp_holdtime; + bsrp_addr = bsrp->rp_address; + + /* update elapse for all bsrp nodes */ + for (ALL_LIST_ELEMENTS_RO(bsgrp_node->bsrp_list, bsrp_ln, bsrp_node)) + bsrp_node->elapse_time += elapse; + + /* remove the expired nodes from the list */ + list_filter_out_nodes(bsgrp_node->bsrp_list, is_hold_time_elapsed); + + /* Get the next elected rp node */ + bsrp = listnode_head(bsgrp_node->bsrp_list); + pim = bsgrp_node->scope->pim; + rn = route_node_lookup(pim->rp_table, &bsgrp_node->group); + + if (!rn) { + zlog_warn("%s: Route node doesn't exist", __PRETTY_FUNCTION__); + return 0; + } + + rp_info = (struct rp_info *)rn->info; + + if (!rp_info) { + route_unlock_node(rn); + return 0; + } + + if (rp_info->rp_src != RP_SRC_STATIC) { + /* If new rp available, change it else delete the existing */ + if (bsrp) { + bsrp_addr = bsrp->rp_address; + pim_g2rp_timer_start( + bsrp, (bsrp->rp_holdtime - bsrp->elapse_time)); + pim_rp_change(pim, bsrp_addr, bsgrp_node->group, + RP_SRC_BSR); + } else { + pim_rp_del(pim, bsrp_addr, bsgrp_node->group, NULL, + RP_SRC_BSR); + } + } + + if ((!bsgrp_node->bsrp_list->count) + && (!bsgrp_node->partial_bsrp_list->count)) { + pim_free_bsgrp_node(pim->global_scope.bsrp_table, + &bsgrp_node->group); + pim_free_bsgrp_data(bsgrp_node); + } + + return 0; +} + +static void pim_g2rp_timer_start(struct bsm_rpinfo *bsrp, int hold_time) +{ + if (!bsrp) { + if (PIM_DEBUG_BSM) + zlog_debug("%s : Invalid brsp(NULL).", + __PRETTY_FUNCTION__); + return; + } + THREAD_OFF(bsrp->g2rp_timer); + if (PIM_DEBUG_BSM) { + char buf[48]; + + zlog_debug( + "%s : starting g2rp timer for grp: %s - rp: %s with timeout %d secs(Actual Hold time : %d secs)", + __PRETTY_FUNCTION__, + prefix2str(&bsrp->bsgrp_node->group, buf, 48), + inet_ntoa(bsrp->rp_address), hold_time, + bsrp->rp_holdtime); + } + + thread_add_timer(router->master, pim_on_g2rp_timer, bsrp, hold_time, + &bsrp->g2rp_timer); +} + +static inline void pim_g2rp_timer_restart(struct bsm_rpinfo *bsrp, + int hold_time) +{ + pim_g2rp_timer_start(bsrp, hold_time); +} + +static void pim_g2rp_timer_stop(struct bsm_rpinfo *bsrp) +{ + if (!bsrp) + return; + + if (PIM_DEBUG_BSM) { + char buf[48]; + + zlog_debug("%s : stopping g2rp timer for grp: %s - rp: %s", + __PRETTY_FUNCTION__, + prefix2str(&bsrp->bsgrp_node->group, buf, 48), + inet_ntoa(bsrp->rp_address)); + } + + THREAD_OFF(bsrp->g2rp_timer); +} + +static bool is_hold_time_zero(void *data) +{ + struct bsm_rpinfo *bsrp; + + bsrp = data; + + if (bsrp->rp_holdtime) + return false; + else + return true; +} + +static void pim_instate_pend_list(struct bsgrp_node *bsgrp_node) +{ + struct bsm_rpinfo *active; + struct bsm_rpinfo *pend; + struct list *temp; + struct rp_info *rp_info; + struct route_node *rn; + struct pim_instance *pim; + struct rp_info *rp_all; + struct prefix group_all; + bool had_rp_node = true; + + pim = bsgrp_node->scope->pim; + active = listnode_head(bsgrp_node->bsrp_list); + + /* Remove nodes with hold time 0 & check if list still has a head */ + list_filter_out_nodes(bsgrp_node->partial_bsrp_list, is_hold_time_zero); + pend = listnode_head(bsgrp_node->partial_bsrp_list); + + if (!str2prefix("224.0.0.0/4", &group_all)) + return; + + rp_all = pim_rp_find_match_group(pim, &group_all); + rn = route_node_lookup(pim->rp_table, &bsgrp_node->group); + + if (pend) + pim_g2rp_timer_start(pend, pend->rp_holdtime); + + /* if rp node doesn't exist or exist but not configured(rp_all), + * install the rp from head(if exists) of partial list. List is + * is sorted such that head is the elected RP for the group. + */ + if (!rn || (prefix_same(&rp_all->group, &bsgrp_node->group) + && pim_rpf_addr_is_inaddr_none(&rp_all->rp))) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: Route node doesn't exist", + __PRETTY_FUNCTION__); + if (pend) + pim_rp_new(pim, pend->rp_address, bsgrp_node->group, + NULL, RP_SRC_BSR); + had_rp_node = false; + } else { + rp_info = (struct rp_info *)rn->info; + if (!rp_info) { + route_unlock_node(rn); + if (pend) + pim_rp_new(pim, pend->rp_address, + bsgrp_node->group, NULL, RP_SRC_BSR); + had_rp_node = false; + } + } + + /* We didn't have rp node and pending list is empty(unlikely), cleanup*/ + if ((!had_rp_node) && (!pend)) { + pim_free_bsgrp_node(bsgrp_node->scope->bsrp_table, + &bsgrp_node->group); + pim_free_bsgrp_data(bsgrp_node); + return; + } + + if ((had_rp_node) && (rp_info->rp_src != RP_SRC_STATIC)) { + /* This means we searched and got rp node, needs unlock */ + route_unlock_node(rn); + + if (active && pend) { + if ((active->rp_address.s_addr + != pend->rp_address.s_addr)) + pim_rp_change(pim, pend->rp_address, + bsgrp_node->group, RP_SRC_BSR); + } + + /* Possible when the first BSM has group with 0 rp count */ + if ((!active) && (!pend)) { + if (PIM_DEBUG_BSM) { + zlog_debug( + "%s: Both bsrp and partial list are empty", + __PRETTY_FUNCTION__); + } + pim_free_bsgrp_node(bsgrp_node->scope->bsrp_table, + &bsgrp_node->group); + pim_free_bsgrp_data(bsgrp_node); + return; + } + + /* Possible when a group with 0 rp count received in BSM */ + if ((active) && (!pend)) { + pim_rp_del(pim, active->rp_address, bsgrp_node->group, + NULL, RP_SRC_BSR); + pim_free_bsgrp_node(bsgrp_node->scope->bsrp_table, + &bsgrp_node->group); + if (PIM_DEBUG_BSM) { + zlog_debug("%s:Pend List is null,del grp node", + __PRETTY_FUNCTION__); + } + pim_free_bsgrp_data(bsgrp_node); + return; + } + } + + if ((had_rp_node) && (rp_info->rp_src == RP_SRC_STATIC)) { + /* We need to unlock rn this case */ + route_unlock_node(rn); + /* there is a chance that static rp exist and bsrp cleaned + * so clean bsgrp node if pending list empty + */ + if (!pend) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: Partial list is empty, static rp exists", + __PRETTY_FUNCTION__); + pim_free_bsgrp_node(bsgrp_node->scope->bsrp_table, + &bsgrp_node->group); + pim_free_bsgrp_data(bsgrp_node); + return; + } + } + + /* swap the list & delete all nodes in partial list (old bsrp_list) + * before swap + * active is head of bsrp list + * pend is head of partial list + * After swap + * active is head of partial list + * pend is head of bsrp list + * So check appriate head after swap and clean the new partial list + */ + temp = bsgrp_node->bsrp_list; + bsgrp_node->bsrp_list = bsgrp_node->partial_bsrp_list; + bsgrp_node->partial_bsrp_list = temp; + + if (active) { + pim_g2rp_timer_stop(active); + list_delete_all_node(bsgrp_node->partial_bsrp_list); + } +} + +static bool pim_bsr_rpf_check(struct pim_instance *pim, struct in_addr bsr, + struct in_addr ip_src_addr) +{ + struct pim_nexthop nexthop; + int result; + + memset(&nexthop, 0, sizeof(nexthop)); + + /* New BSR recived */ + if (bsr.s_addr != pim->global_scope.current_bsr.s_addr) { + result = pim_nexthop_match(pim, bsr, ip_src_addr); + + /* Nexthop lookup pass for the new BSR address */ + if (result) + return true; + + if (PIM_DEBUG_BSM) { + char bsr_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", bsr, bsr_str, sizeof(bsr_str)); + zlog_debug("%s : No route to BSR address %s", + __PRETTY_FUNCTION__, bsr_str); + } + return false; + } + + return pim_nexthop_match_nht_cache(pim, bsr, ip_src_addr); +} + +static bool is_preferred_bsr(struct pim_instance *pim, struct in_addr bsr, + uint32_t bsr_prio) +{ + if (bsr.s_addr == pim->global_scope.current_bsr.s_addr) + return true; + + if (bsr_prio > pim->global_scope.current_bsr_prio) + return true; + + else if (bsr_prio == pim->global_scope.current_bsr_prio) { + if (bsr.s_addr >= pim->global_scope.current_bsr.s_addr) + return true; + else + return false; + } else + return false; +} + +static void pim_bsm_update(struct pim_instance *pim, struct in_addr bsr, + uint32_t bsr_prio) +{ + struct pim_nexthop_cache pnc; + + if (bsr.s_addr != pim->global_scope.current_bsr.s_addr) { + struct prefix nht_p; + char buf[PREFIX2STR_BUFFER]; + bool is_bsr_tracking = true; + + /* De-register old BSR and register new BSR with Zebra NHT */ + nht_p.family = AF_INET; + nht_p.prefixlen = IPV4_MAX_BITLEN; + + if (pim->global_scope.current_bsr.s_addr != INADDR_ANY) { + nht_p.u.prefix4 = pim->global_scope.current_bsr; + if (PIM_DEBUG_BSM) { + prefix2str(&nht_p, buf, sizeof(buf)); + zlog_debug( + "%s: Deregister BSR addr %s with Zebra NHT", + __PRETTY_FUNCTION__, buf); + } + pim_delete_tracked_nexthop(pim, &nht_p, NULL, NULL, + is_bsr_tracking); + } + + nht_p.u.prefix4 = bsr; + if (PIM_DEBUG_BSM) { + prefix2str(&nht_p, buf, sizeof(buf)); + zlog_debug( + "%s: NHT Register BSR addr %s with Zebra NHT", + __PRETTY_FUNCTION__, buf); + } + + memset(&pnc, 0, sizeof(struct pim_nexthop_cache)); + pim_find_or_track_nexthop(pim, &nht_p, NULL, NULL, + is_bsr_tracking, &pnc); + pim->global_scope.current_bsr = bsr; + pim->global_scope.current_bsr_first_ts = + pim_time_monotonic_sec(); + pim->global_scope.state = ACCEPT_PREFERRED; + } + pim->global_scope.current_bsr_prio = bsr_prio; + pim->global_scope.current_bsr_last_ts = pim_time_monotonic_sec(); +} + +static bool pim_bsm_send_intf(uint8_t *buf, int len, struct interface *ifp, + struct in_addr dst_addr) +{ + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + + if (!pim_ifp) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: Pim interface not available for %s", + __PRETTY_FUNCTION__, ifp->name); + return false; + } + + if (pim_ifp->pim_sock_fd == -1) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: Pim sock not available for %s", + __PRETTY_FUNCTION__, ifp->name); + return false; + } + + pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address, dst_addr, + buf, len, ifp->name); + pim_ifp->pim_ifstat_bsm_tx++; + pim_ifp->pim->bsm_sent++; + return true; +} + +static bool pim_bsm_frag_send(uint8_t *buf, uint32_t len, struct interface *ifp, + uint32_t pim_mtu, struct in_addr dst_addr, + bool no_fwd) +{ + struct bsmmsg_grpinfo *grpinfo, *curgrp; + uint8_t *firstgrp_ptr; + uint8_t *pkt; + uint8_t *pak_start; + uint32_t parsed_len = 0; + uint32_t this_pkt_rem; + uint32_t copy_byte_count; + uint32_t this_pkt_len; + uint8_t total_rp_cnt; + uint8_t this_rp_cnt; + uint8_t frag_rp_cnt; + uint8_t rp_fit_cnt; + bool pak_pending = false; + + /* MTU passed here is PIM MTU (IP MTU less IP Hdr) */ + if (pim_mtu < (PIM_MIN_BSM_LEN)) { + zlog_warn( + "%s: mtu(pim mtu: %d) size less than minimum bootsrap len", + __PRETTY_FUNCTION__, pim_mtu); + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: mtu (pim mtu:%d) less than minimum bootsrap len", + __PRETTY_FUNCTION__, pim_mtu); + return false; + } + + pak_start = XCALLOC(MTYPE_PIM_BSM_PKT_VAR_MEM, pim_mtu); + + if (!pak_start) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: malloc failed", __PRETTY_FUNCTION__); + return false; + } + + pkt = pak_start; + + /* Fill PIM header later before sending packet to calc checksum */ + pkt += PIM_MSG_HEADER_LEN; + buf += PIM_MSG_HEADER_LEN; + + /* copy bsm header to new packet at offset of pim hdr */ + memcpy(pkt, buf, PIM_BSM_HDR_LEN); + pkt += PIM_BSM_HDR_LEN; + buf += PIM_BSM_HDR_LEN; + parsed_len += (PIM_MSG_HEADER_LEN + PIM_BSM_HDR_LEN); + + /* Store the position of first grp ptr, which can be reused for + * next packet to start filling group. old bsm header and pim hdr + * remains. So need not be filled again for next packet onwards. + */ + firstgrp_ptr = pkt; + + /* we received mtu excluding IP hdr len as param + * now this_pkt_rem is mtu excluding + * PIM_BSM_HDR_LEN + PIM_MSG_HEADER_LEN + */ + this_pkt_rem = pim_mtu - (PIM_BSM_HDR_LEN + PIM_MSG_HEADER_LEN); + + /* For each group till the packet length parsed */ + while (parsed_len < len) { + /* pkt ---> fragment's current pointer + * buf ---> input buffer's current pointer + * mtu ---> size of the pim packet - PIM header + * curgrp ---> current group on the fragment + * grpinfo ---> current group on the input buffer + * this_pkt_rem ---> bytes remaing on the current fragment + * rp_fit_cnt ---> num of rp for current grp that + * fits this frag + * total_rp_cnt ---> total rp present for the group in the buf + * frag_rp_cnt ---> no of rp for the group to be fit in + * the frag + * this_rp_cnt ---> how many rp have we parsed + */ + grpinfo = (struct bsmmsg_grpinfo *)buf; + memcpy(pkt, buf, PIM_BSM_GRP_LEN); + curgrp = (struct bsmmsg_grpinfo *)pkt; + parsed_len += PIM_BSM_GRP_LEN; + pkt += PIM_BSM_GRP_LEN; + buf += PIM_BSM_GRP_LEN; + this_pkt_rem -= PIM_BSM_GRP_LEN; + + /* initialize rp count and total_rp_cnt before the rp loop */ + this_rp_cnt = 0; + total_rp_cnt = grpinfo->frag_rp_count; + + /* Loop till all RPs for the group parsed */ + while (this_rp_cnt < total_rp_cnt) { + /* All RP from a group processed here. + * group is pointed by grpinfo. + * At this point make sure buf pointing to a RP + * within a group + */ + rp_fit_cnt = this_pkt_rem / PIM_BSM_RP_LEN; + + /* calculate how many rp am i going to copy in + * this frag + */ + if (rp_fit_cnt > (total_rp_cnt - this_rp_cnt)) + frag_rp_cnt = total_rp_cnt - this_rp_cnt; + else + frag_rp_cnt = rp_fit_cnt; + + /* populate the frag rp count for the current grp */ + curgrp->frag_rp_count = frag_rp_cnt; + copy_byte_count = frag_rp_cnt * PIM_BSM_RP_LEN; + + /* copy all the rp that we are fitting in this + * frag for the grp + */ + memcpy(pkt, buf, copy_byte_count); + this_rp_cnt += frag_rp_cnt; + buf += copy_byte_count; + pkt += copy_byte_count; + parsed_len += copy_byte_count; + this_pkt_rem -= copy_byte_count; + + /* Either we couldn't fit all rp for the group or the + * mtu reached + */ + if ((this_rp_cnt < total_rp_cnt) + || (this_pkt_rem + < (PIM_BSM_GRP_LEN + PIM_BSM_RP_LEN))) { + /* No space to fit in more rp, send this pkt */ + this_pkt_len = pim_mtu - this_pkt_rem; + pim_msg_build_header(pak_start, this_pkt_len, + PIM_MSG_TYPE_BOOTSTRAP, + no_fwd); + pim_bsm_send_intf(pak_start, this_pkt_len, ifp, + dst_addr); + + /* Construct next fragment. Reuse old packet */ + pkt = firstgrp_ptr; + this_pkt_rem = pim_mtu - (PIM_BSM_HDR_LEN + + PIM_MSG_HEADER_LEN); + + /* If pkt can't accomodate next group + atleast + * one rp, we must break out of this inner loop + * and process next RP + */ + if (total_rp_cnt == this_rp_cnt) + break; + + /* If some more RPs for the same group pending, + * fill grp hdr + */ + memcpy(pkt, (uint8_t *)grpinfo, + PIM_BSM_GRP_LEN); + curgrp = (struct bsmmsg_grpinfo *)pkt; + pkt += PIM_BSM_GRP_LEN; + this_pkt_rem -= PIM_BSM_GRP_LEN; + pak_pending = false; + } else { + /* We filled something but not yet sent out */ + pak_pending = true; + } + } /* while RP count */ + } /*while parsed len */ + + /* Send if we have any unsent packet */ + if (pak_pending) { + this_pkt_len = pim_mtu - this_pkt_rem; + pim_msg_build_header(pak_start, this_pkt_len, + PIM_MSG_TYPE_BOOTSTRAP, no_fwd); + pim_bsm_send_intf(pak_start, (pim_mtu - this_pkt_rem), ifp, + dst_addr); + } + XFREE(MTYPE_PIM_BSM_PKT_VAR_MEM, pak_start); + return true; +} + +static void pim_bsm_fwd_whole_sz(struct pim_instance *pim, uint8_t *buf, + uint32_t len, int sz) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + struct in_addr dst_addr; + uint32_t pim_mtu; + bool no_fwd = FALSE; + bool ret = FALSE; + + /* For now only global scope zone is supported, so send on all + * pim interfaces in the vrf + */ + dst_addr = qpim_all_pim_routers_addr; + FOR_ALL_INTERFACES (pim->vrf, ifp) { + pim_ifp = ifp->info; + if ((!pim_ifp) || (!pim_ifp->bsm_enable)) + continue; + pim_hello_require(ifp); + pim_mtu = ifp->mtu - MAX_IP_HDR_LEN; + if (pim_mtu < len) { + ret = pim_bsm_frag_send(buf, len, ifp, pim_mtu, + dst_addr, no_fwd); + if (PIM_DEBUG_BSM) + zlog_debug("%s: pim_bsm_frag_send returned %s", + __PRETTY_FUNCTION__, + ret ? "TRUE" : "FALSE"); + } else { + pim_msg_build_header(buf, len, PIM_MSG_TYPE_BOOTSTRAP, + no_fwd); + if (!pim_bsm_send_intf(buf, len, ifp, dst_addr)) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: pim_bsm_send_intf returned FALSE", + __PRETTY_FUNCTION__); + } + } + } +} + +bool pim_bsm_new_nbr_fwd(struct pim_neighbor *neigh, struct interface *ifp) +{ + struct in_addr dst_addr; + struct pim_interface *pim_ifp; + struct bsm_scope *scope; + struct listnode *bsm_ln; + struct bsm_info *bsminfo; + char neigh_src_str[INET_ADDRSTRLEN]; + uint32_t pim_mtu; + bool no_fwd = true; + bool ret = false; + + if (PIM_DEBUG_BSM) { + pim_inet4_dump("", neigh->source_addr, neigh_src_str, + sizeof(neigh_src_str)); + zlog_debug("%s: New neighbor %s seen on %s", + __PRETTY_FUNCTION__, neigh_src_str, ifp->name); + } + + pim_ifp = ifp->info; + + /* DR only forwards BSM packet */ + if (pim_ifp->pim_dr_addr.s_addr == pim_ifp->primary_address.s_addr) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: It is not DR, so don't forward BSM packet", + __PRETTY_FUNCTION__); + } + + if (!pim_ifp->bsm_enable) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: BSM proc not enabled on %s", + __PRETTY_FUNCTION__, ifp->name); + return ret; + } + + scope = &pim_ifp->pim->global_scope; + + if (!scope->bsm_list->count) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: BSM list for the scope is empty", + __PRETTY_FUNCTION__); + return ret; + } + + if (!pim_ifp->ucast_bsm_accept) { + dst_addr = qpim_all_pim_routers_addr; + if (PIM_DEBUG_BSM) + zlog_debug("%s: Sending BSM mcast to %s", + __PRETTY_FUNCTION__, neigh_src_str); + } else { + dst_addr = neigh->source_addr; + if (PIM_DEBUG_BSM) + zlog_debug("%s: Sending BSM ucast to %s", + __PRETTY_FUNCTION__, neigh_src_str); + } + pim_mtu = ifp->mtu - MAX_IP_HDR_LEN; + pim_hello_require(ifp); + + for (ALL_LIST_ELEMENTS_RO(scope->bsm_list, bsm_ln, bsminfo)) { + if (pim_mtu < bsminfo->size) { + ret = pim_bsm_frag_send(bsminfo->bsm, bsminfo->size, + ifp, pim_mtu, dst_addr, no_fwd); + if (!ret) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: pim_bsm_frag_send failed", + __PRETTY_FUNCTION__); + } + } else { + /* Pim header needs to be constructed */ + pim_msg_build_header(bsminfo->bsm, bsminfo->size, + PIM_MSG_TYPE_BOOTSTRAP, no_fwd); + ret = pim_bsm_send_intf(bsminfo->bsm, bsminfo->size, + ifp, dst_addr); + if (!ret) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: pim_bsm_frag_send failed", + __PRETTY_FUNCTION__); + } + } + } + return ret; +} + +struct bsgrp_node *pim_bsm_get_bsgrp_node(struct bsm_scope *scope, + struct prefix *grp) +{ + struct route_node *rn; + struct bsgrp_node *bsgrp; + + rn = route_node_lookup(scope->bsrp_table, grp); + if (!rn) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: Route node doesn't exist for the group", + __PRETTY_FUNCTION__); + return NULL; + } + bsgrp = rn->info; + route_unlock_node(rn); + + return bsgrp; +} + +static uint32_t hash_calc_on_grp_rp(struct prefix group, struct in_addr rp, + uint8_t hashmasklen) +{ + uint64_t temp; + uint32_t hash; + uint32_t grpaddr; + uint32_t rp_add; + uint32_t mask = 0xffffffff; + + /* mask to be made zero if hashmasklen is 0 because mask << 32 + * may not give 0. hashmasklen can be 0 to 32. + */ + if (hashmasklen == 0) + mask = 0; + + /* in_addr stores ip in big endian, hence network byte order + * convert to uint32 before processing hash + */ + grpaddr = ntohl(group.u.prefix4.s_addr); + /* Avoid shifting by 32 bit on a 32 bit register */ + if (hashmasklen) + grpaddr = grpaddr & ((mask << (32 - hashmasklen))); + else + grpaddr = grpaddr & mask; + rp_add = ntohl(rp.s_addr); + temp = 1103515245 * ((1103515245 * grpaddr + 12345) ^ rp_add) + 12345; + hash = temp & (0x7fffffff); + return hash; +} + +static bool pim_install_bsm_grp_rp(struct pim_instance *pim, + struct bsgrp_node *grpnode, + struct bsmmsg_rpinfo *rp) +{ + struct bsm_rpinfo *bsm_rpinfo; + uint8_t hashMask_len = pim->global_scope.hashMasklen; + + /*memory allocation for bsm_rpinfo */ + bsm_rpinfo = XCALLOC(MTYPE_PIM_BSRP_NODE, sizeof(*bsm_rpinfo)); + + if (!bsm_rpinfo) { + if (PIM_DEBUG_BSM) + zlog_debug("%s, Memory allocation failed.\r\n", + __PRETTY_FUNCTION__); + return false; + } + + bsm_rpinfo->rp_prio = rp->rp_pri; + bsm_rpinfo->rp_holdtime = rp->rp_holdtime; + memcpy(&bsm_rpinfo->rp_address, &rp->rpaddr.addr, + sizeof(struct in_addr)); + bsm_rpinfo->elapse_time = 0; + + /* Back pointer to the group node. */ + bsm_rpinfo->bsgrp_node = grpnode; + + /* update hash for this rp node */ + bsm_rpinfo->hash = hash_calc_on_grp_rp(grpnode->group, rp->rpaddr.addr, + hashMask_len); + if (listnode_add_sort_nodup(grpnode->partial_bsrp_list, bsm_rpinfo)) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s, bs_rpinfo node added to the partial bs_rplist.\r\n", + __PRETTY_FUNCTION__); + return true; + } + + if (PIM_DEBUG_BSM) + zlog_debug("%s: list node not added\n", __PRETTY_FUNCTION__); + + XFREE(MTYPE_PIM_BSRP_NODE, bsm_rpinfo); + return false; +} + +static void pim_update_pending_rp_cnt(struct bsm_scope *sz, + struct bsgrp_node *bsgrp, + uint16_t bsm_frag_tag, + uint32_t total_rp_count) +{ + if (bsgrp->pend_rp_cnt) { + /* received bsm is different packet , + * it is not same fragment. + */ + if (bsm_frag_tag != bsgrp->frag_tag) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s,Received a new BSM ,so clear the pending bs_rpinfo list.\r\n", + __PRETTY_FUNCTION__); + list_delete_all_node(bsgrp->partial_bsrp_list); + bsgrp->pend_rp_cnt = total_rp_count; + } + } else + bsgrp->pend_rp_cnt = total_rp_count; + + bsgrp->frag_tag = bsm_frag_tag; +} + +/* Parsing BSR packet and adding to partial list of corresponding bsgrp node */ +static bool pim_bsm_parse_install_g2rp(struct bsm_scope *scope, uint8_t *buf, + int buflen, uint16_t bsm_frag_tag) +{ + struct bsmmsg_grpinfo grpinfo; + struct bsmmsg_rpinfo rpinfo; + struct prefix group; + struct bsgrp_node *bsgrp = NULL; + int frag_rp_cnt = 0; + int offset = 0; + int ins_count = 0; + + while (buflen > offset) { + /* Extract Group tlv from BSM */ + memcpy(&grpinfo, buf, sizeof(struct bsmmsg_grpinfo)); + + if (PIM_DEBUG_BSM) { + char grp_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", grpinfo.group.addr, grp_str, + sizeof(grp_str)); + zlog_debug( + "%s, Group %s Rpcount:%d Fragment-Rp-count:%d\r\n", + __PRETTY_FUNCTION__, grp_str, grpinfo.rp_count, + grpinfo.frag_rp_count); + } + + buf += sizeof(struct bsmmsg_grpinfo); + offset += sizeof(struct bsmmsg_grpinfo); + + if (grpinfo.rp_count == 0) { + if (PIM_DEBUG_BSM) { + char grp_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", grpinfo.group.addr, + grp_str, sizeof(grp_str)); + zlog_debug( + "%s, Rp count is zero for group: %s\r\n", + __PRETTY_FUNCTION__, grp_str); + } + return false; + } + + group.family = AF_INET; + group.prefixlen = grpinfo.group.mask; + group.u.prefix4.s_addr = grpinfo.group.addr.s_addr; + + /* Get the Group node for the BSM rp table */ + bsgrp = pim_bsm_get_bsgrp_node(scope, &group); + + if (!bsgrp) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s, Create new BSM Group node.\r\n", + __PRETTY_FUNCTION__); + + /* create a new node to be added to the tree. */ + bsgrp = pim_bsm_new_bsgrp_node(scope->bsrp_table, + &group); + + if (!bsgrp) { + zlog_debug( + "%s, Failed to get the BSM group node.\r\n", + __PRETTY_FUNCTION__); + continue; + } + + bsgrp->scope = scope; + } + + pim_update_pending_rp_cnt(scope, bsgrp, bsm_frag_tag, + grpinfo.rp_count); + frag_rp_cnt = grpinfo.frag_rp_count; + ins_count = 0; + + while (frag_rp_cnt--) { + /* Extract RP address tlv from BSM */ + memcpy(&rpinfo, buf, sizeof(struct bsmmsg_rpinfo)); + rpinfo.rp_holdtime = ntohs(rpinfo.rp_holdtime); + buf += sizeof(struct bsmmsg_rpinfo); + offset += sizeof(struct bsmmsg_rpinfo); + + if (PIM_DEBUG_BSM) { + char rp_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", rpinfo.rpaddr.addr, + rp_str, sizeof(rp_str)); + zlog_debug( + "%s, Rp address - %s; pri:%d hold:%d\r\n", + __PRETTY_FUNCTION__, rp_str, + rpinfo.rp_pri, rpinfo.rp_holdtime); + } + + /* Call Install api to update grp-rp mappings */ + if (pim_install_bsm_grp_rp(scope->pim, bsgrp, &rpinfo)) + ins_count++; + } + + bsgrp->pend_rp_cnt -= ins_count; + + if (!bsgrp->pend_rp_cnt) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s, Recvd all the rps for this group, so bsrp list with penidng rp list.", + __PRETTY_FUNCTION__); + /* replace the bsrp_list with pending list */ + pim_instate_pend_list(bsgrp); + } + } + return true; +} + +int pim_bsm_process(struct interface *ifp, struct ip *ip_hdr, uint8_t *buf, + uint32_t buf_size, bool no_fwd) +{ + struct bsm_hdr *bshdr; + int sz = PIM_GBL_SZ_ID; + struct bsmmsg_grpinfo *msg_grp; + struct pim_interface *pim_ifp = NULL; + struct bsm_info *bsminfo; + struct pim_instance *pim; + char bsr_str[INET_ADDRSTRLEN]; + uint16_t frag_tag; + bool empty_bsm = FALSE; + + /* BSM Packet acceptance validation */ + pim_ifp = ifp->info; + if (!pim_ifp) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: multicast not enabled on interface %s", + __PRETTY_FUNCTION__, ifp->name); + return -1; + } + + pim_ifp->pim_ifstat_bsm_rx++; + pim = pim_ifp->pim; + pim->bsm_rcvd++; + + /* Drop if bsm processing is disabled on interface */ + if (!pim_ifp->bsm_enable) { + zlog_warn("%s: BSM not enabled on interface %s", + __PRETTY_FUNCTION__, ifp->name); + pim_ifp->pim_ifstat_bsm_cfg_miss++; + pim->bsm_dropped++; + return -1; + } + + bshdr = (struct bsm_hdr *)(buf + PIM_MSG_HEADER_LEN); + pim_inet4_dump("", bshdr->bsr_addr.addr, bsr_str, + sizeof(bsr_str)); + pim->global_scope.hashMasklen = bshdr->hm_len; + frag_tag = ntohs(bshdr->frag_tag); + + /* Identify empty BSM */ + if ((buf_size - PIM_BSM_HDR_LEN - PIM_MSG_HEADER_LEN) < PIM_BSM_GRP_LEN) + empty_bsm = true; + + if (!empty_bsm) { + msg_grp = (struct bsmmsg_grpinfo *)(buf + PIM_MSG_HEADER_LEN + + PIM_BSM_HDR_LEN); + /* Currently we don't support scope zoned BSM */ + if (msg_grp->group.sz) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s : Administratively scoped range BSM received", + __PRETTY_FUNCTION__); + pim_ifp->pim_ifstat_bsm_invalid_sz++; + pim->bsm_dropped++; + return -1; + } + } + + /* Drop if bsr is not preferred bsr */ + if (!is_preferred_bsr(pim, bshdr->bsr_addr.addr, bshdr->bsr_prio)) { + if (PIM_DEBUG_BSM) + zlog_debug("%s : Received a non-preferred BSM", + __PRETTY_FUNCTION__); + pim->bsm_dropped++; + return -1; + } + + if (no_fwd) { + /* only accept no-forward BSM if quick refresh on startup */ + if ((pim->global_scope.accept_nofwd_bsm) + || (frag_tag == pim->global_scope.bsm_frag_tag)) { + pim->global_scope.accept_nofwd_bsm = false; + } else { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s : nofwd_bsm received on %s when accpt_nofwd_bsm false", + __PRETTY_FUNCTION__, bsr_str); + pim->bsm_dropped++; + pim_ifp->pim_ifstat_ucast_bsm_cfg_miss++; + return -1; + } + } + + /* Mulicast BSM received */ + if (ip_hdr->ip_dst.s_addr == qpim_all_pim_routers_addr.s_addr) { + if (!no_fwd) { + if (!pim_bsr_rpf_check(pim, bshdr->bsr_addr.addr, + ip_hdr->ip_src)) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s : RPF check fail for BSR address %s", + __PRETTY_FUNCTION__, bsr_str); + pim->bsm_dropped++; + return -1; + } + } + } else if (if_lookup_exact_address(&ip_hdr->ip_dst, AF_INET, + pim->vrf_id)) { + /* Unicast BSM received - if ucast bsm not enabled on + * the interface, drop it + */ + if (!pim_ifp->ucast_bsm_accept) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s : Unicast BSM not enabled on interface %s", + __PRETTY_FUNCTION__, ifp->name); + pim_ifp->pim_ifstat_ucast_bsm_cfg_miss++; + pim->bsm_dropped++; + return -1; + } + + } else { + if (PIM_DEBUG_BSM) + zlog_debug("%s : Invalid destination address", + __PRETTY_FUNCTION__); + pim->bsm_dropped++; + return -1; + } + + if (empty_bsm) { + if (PIM_DEBUG_BSM) + zlog_debug("%s : Empty Pref BSM received", + __PRETTY_FUNCTION__); + } + /* Parse Update bsm rp table and install/uninstall rp if required */ + if (!pim_bsm_parse_install_g2rp( + &pim_ifp->pim->global_scope, + (buf + PIM_BSM_HDR_LEN + PIM_MSG_HEADER_LEN), + (buf_size - PIM_BSM_HDR_LEN - PIM_MSG_HEADER_LEN), + frag_tag)) { + if (PIM_DEBUG_BSM) { + zlog_debug("%s, Parsing BSM failed.\r\n", + __PRETTY_FUNCTION__); + } + pim->bsm_dropped++; + return -1; + } + /* Restart the bootstrap timer */ + pim_bs_timer_restart(&pim_ifp->pim->global_scope, + PIM_BSR_DEFAULT_TIMEOUT); + + /* If new BSM received, clear the old bsm database */ + if (pim_ifp->pim->global_scope.bsm_frag_tag != frag_tag) { + if (PIM_DEBUG_BSM) { + zlog_debug("%s: Current frag tag: %d Frag teg rcvd: %d", + __PRETTY_FUNCTION__, + pim_ifp->pim->global_scope.bsm_frag_tag, + frag_tag); + } + list_delete_all_node(pim_ifp->pim->global_scope.bsm_list); + pim_ifp->pim->global_scope.bsm_frag_tag = frag_tag; + } + + /* update the scope information from bsm */ + pim_bsm_update(pim, bshdr->bsr_addr.addr, bshdr->bsr_prio); + + if (!no_fwd) { + pim_bsm_fwd_whole_sz(pim_ifp->pim, buf, buf_size, sz); + bsminfo = XCALLOC(MTYPE_PIM_BSM_INFO, sizeof(struct bsm_info)); + if (!bsminfo) { + zlog_warn("%s: bsminfo alloc failed", + __PRETTY_FUNCTION__); + return 0; + } + + bsminfo->bsm = XCALLOC(MTYPE_PIM_BSM_PKT_VAR_MEM, buf_size); + if (!bsminfo->bsm) { + zlog_warn("%s: bsm alloc failed", __PRETTY_FUNCTION__); + XFREE(MTYPE_PIM_BSM_INFO, bsminfo); + return 0; + } + + bsminfo->size = buf_size; + memcpy(bsminfo->bsm, buf, buf_size); + listnode_add(pim_ifp->pim->global_scope.bsm_list, bsminfo); + } + + return 0; +} diff --git a/pimd/pim_bsm.h b/pimd/pim_bsm.h new file mode 100644 index 000000000000..0758c94f1947 --- /dev/null +++ b/pimd/pim_bsm.h @@ -0,0 +1,198 @@ +/* + * pim_bsm.h: PIM BSM handling related + * + * Copyright (C) 2018-19 Vmware, Inc. + * Saravanan K + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef __PIM_BSM_H__ +#define __PIM_BSM_H__ + +#include "if.h" +#include "vty.h" +#include "linklist.h" +#include "table.h" +#include "pim_rp.h" +#include "pim_msg.h" + +/* Defines */ +#define PIM_GBL_SZ_ID 0 /* global scope zone id set to 0 */ +#define PIM_BS_TIME 60 /* RFC 5059 - Sec 5 */ +#define PIM_BSR_DEFAULT_TIMEOUT 130 /* RFC 5059 - Sec 5 */ + +/* These structures are only encoded IPv4 specific */ +#define PIM_BSM_HDR_LEN sizeof(struct bsm_hdr) +#define PIM_BSM_GRP_LEN sizeof(struct bsmmsg_grpinfo) +#define PIM_BSM_RP_LEN sizeof(struct bsmmsg_rpinfo) + +#define PIM_MIN_BSM_LEN \ + (PIM_HDR_LEN + PIM_BSM_HDR_LEN + PIM_BSM_GRP_LEN + PIM_BSM_RP_LEN) + +/* Datastructures + * ============== + */ + +/* Non candidate BSR states */ +enum ncbsr_state { + NO_INFO = 0, + ACCEPT_ANY, + ACCEPT_PREFERRED +}; + +/* BSM scope - bsm processing is per scope */ +struct bsm_scope { + int sz_id; /* scope zone id */ + enum ncbsr_state state; /* non candidate BSR state */ + bool accept_nofwd_bsm; /* no fwd bsm accepted for scope */ + struct in_addr current_bsr; /* current elected BSR for the sz */ + uint32_t current_bsr_prio; /* current BSR priority */ + int64_t current_bsr_first_ts; /* current BSR elected time */ + int64_t current_bsr_last_ts; /* Last BSM received from E-BSR */ + uint16_t bsm_frag_tag; /* Last received frag tag from E-BSR */ + uint8_t hashMasklen; /* Mask in hash calc RFC 7761 4.7.2 */ + struct pim_instance *pim; /* Back pointer to pim instance */ + struct list *bsm_list; /* list of bsm frag for frowarding */ + struct route_table *bsrp_table; /* group2rp mapping rcvd from BSR */ + struct thread *bs_timer; /* Boot strap timer */ + struct thread *sz_timer; +}; + +/* BSM packet - this is stored as list in bsm_list inside scope + * This is used for forwarding to new neighbors or restarting mcast routers + */ +struct bsm_info { + uint32_t size; /* size of the packet */ + unsigned char *bsm; /* Actual packet */ +}; + +/* This is the group node of the bsrp table in scope. + * this node maintains the list of rp for the group. + */ +struct bsgrp_node { + struct prefix group; /* Group range */ + struct bsm_scope *scope; /* Back ptr to scope */ + struct list *bsrp_list; /* list of RPs adv by BSR */ + struct list *partial_bsrp_list; /* maintained until all RPs received */ + int pend_rp_cnt; /* Total RP - Received RP */ + uint16_t frag_tag; /* frag tag to identify the fragment */ +}; + +/* This is the list node of bsrp_list and partial bsrp list in + * bsgrp_node. Hold info of each RP received for the group + */ +struct bsm_rpinfo { + uint32_t hash; /* Hash Value as per RFC 7761 4.7.2 */ + uint32_t elapse_time; /* upd at expiry of elected RP node */ + uint16_t rp_prio; /* RP priority */ + uint16_t rp_holdtime; /* RP holdtime - g2rp timer value */ + struct in_addr rp_address; /* RP Address */ + struct bsgrp_node *bsgrp_node; /* Back ptr to bsgrp_node */ + struct thread *g2rp_timer; /* Run only for elected RP node */ +}; + +/* Structures to extract Bootstrap Message header and Grp to RP Mappings + * ===================================================================== + * BSM Format: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |PIM Ver| Type |N| Reserved | Checksum | PIM HDR + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Fragment Tag | Hash Mask Len | BSR Priority | BS HDR(1) + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | BSR Address (Encoded-Unicast format) | BS HDR(2) + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Group Address 1 (Encoded-Group format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Count 1 | Frag RP Cnt 1 | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Address 1 (Encoded-Unicast format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP1 Holdtime | RP1 Priority | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Address 2 (Encoded-Unicast format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP2 Holdtime | RP2 Priority | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | . | + * | . | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Address m (Encoded-Unicast format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RPm Holdtime | RPm Priority | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Group Address 2 (Encoded-Group format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | . | + * | . | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Group Address n (Encoded-Group format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Count n | Frag RP Cnt n | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Address 1 (Encoded-Unicast format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP1 Holdtime | RP1 Priority | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Address 2 (Encoded-Unicast format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP2 Holdtime | RP2 Priority | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | . | + * | . | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Address m (Encoded-Unicast format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RPm Holdtime | RPm Priority | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct bsm_hdr { + uint16_t frag_tag; + uint8_t hm_len; + uint8_t bsr_prio; + struct pim_encoded_ipv4_unicast bsr_addr; +} __attribute__((packed)); + +struct bsmmsg_grpinfo { + struct pim_encoded_group_ipv4 group; + uint8_t rp_count; + uint8_t frag_rp_count; + uint16_t reserved; +} __attribute__((packed)); + +struct bsmmsg_rpinfo { + struct pim_encoded_ipv4_unicast rpaddr; + uint16_t rp_holdtime; + uint8_t rp_pri; + uint8_t reserved; +} __attribute__((packed)); + +/* API */ +void pim_bsm_proc_init(struct pim_instance *pim); +void pim_bsm_proc_free(struct pim_instance *pim); +void pim_bsm_write_config(struct vty *vty, struct interface *ifp); +int pim_bsm_process(struct interface *ifp, + struct ip *ip_hdr, + uint8_t *buf, + uint32_t buf_size, + bool no_fwd); +bool pim_bsm_new_nbr_fwd(struct pim_neighbor *neigh, struct interface *ifp); +struct bsgrp_node *pim_bsm_get_bsgrp_node(struct bsm_scope *scope, + struct prefix *grp); +#endif diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 77ea314d54d5..18e84fef23c7 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -62,6 +62,7 @@ #include "pim_bfd.h" #include "pim_vxlan.h" #include "bfd.h" +#include "pim_bsm.h" #ifndef VTYSH_EXTRACT_PL #include "pimd/pim_cmd_clippy.c" @@ -657,6 +658,7 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, long oqpi_msec; /* Other Querier Present Interval */ long qri_msec; time_t now; + int lmqc; json_object *json = NULL; json_object *json_row = NULL; @@ -701,8 +703,8 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, pim_ifp->igmp_query_max_response_time_dsec); lmqt_msec = PIM_IGMP_LMQT_MSEC( - pim_ifp->igmp_query_max_response_time_dsec, - igmp->querier_robustness_variable); + pim_ifp->igmp_specific_query_max_response_time_dsec, + pim_ifp->igmp_last_member_query_count); ohpi_msec = PIM_IGMP_OHPI_DSEC( @@ -718,6 +720,7 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, pim_ifp->pim_sock_fd); else mloop = 0; + lmqc = pim_ifp->igmp_last_member_query_count; if (uj) { json_row = json_object_new_object(); @@ -742,6 +745,9 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, json_row, "timerGroupMembershipIntervalMsec", gmi_msec); + json_object_int_add(json_row, + "lastMemberQueryCount", + lmqc); json_object_int_add(json_row, "timerLastMemberQueryMsec", lmqt_msec); @@ -808,6 +814,9 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, vty_out(vty, "Group Membership Interval : %lis\n", gmi_msec / 1000); + vty_out(vty, + "Last Member Query Count : %d\n", + lmqc); vty_out(vty, "Last Member Query Time : %lis\n", lmqt_msec / 1000); @@ -1207,6 +1216,8 @@ static void pim_show_interfaces_single(struct pim_instance *pim, print_header = 1; for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + if (!up->rpf.source_nexthop.interface) + continue; if (strcmp(ifp->name, up->rpf.source_nexthop @@ -1475,13 +1486,14 @@ static void pim_show_interface_traffic(struct pim_instance *pim, json = json_object_new_object(); else { vty_out(vty, "\n"); - vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s\n", + vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s%-17s\n", "Interface", " HELLO", " JOIN", " PRUNE", " REGISTER", "REGISTER-STOP", - " ASSERT"); - vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s\n", "", + " ASSERT", " BSM"); + vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s%-17s\n", "", " Rx/Tx", " Rx/Tx", " Rx/Tx", - " Rx/Tx", " Rx/Tx", " Rx/Tx"); + " Rx/Tx", " Rx/Tx", " Rx/Tx", + " Rx/Tx"); vty_out(vty, "---------------------------------------------------------------------------------------------------------------\n"); } @@ -1516,12 +1528,15 @@ static void pim_show_interface_traffic(struct pim_instance *pim, json_object_int_add(json_row, "assertRx", pim_ifp->pim_ifstat_assert_recv); json_object_int_add(json_row, "assertTx", - pim_ifp->pim_ifstat_assert_send); - + pim_ifp->pim_ifstat_assert_send); + json_object_int_add(json_row, "bsmRx", + pim_ifp->pim_ifstat_bsm_rx); + json_object_int_add(json_row, "bsmTx", + pim_ifp->pim_ifstat_bsm_tx); json_object_object_add(json, ifp->name, json_row); } else { vty_out(vty, - "%-16s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u \n", + "%-16s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7" PRIu64 "/%-7" PRIu64 "\n", ifp->name, pim_ifp->pim_ifstat_hello_recv, pim_ifp->pim_ifstat_hello_sent, pim_ifp->pim_ifstat_join_recv, @@ -1533,7 +1548,9 @@ static void pim_show_interface_traffic(struct pim_instance *pim, pim_ifp->pim_ifstat_reg_stop_recv, pim_ifp->pim_ifstat_reg_stop_send, pim_ifp->pim_ifstat_assert_recv, - pim_ifp->pim_ifstat_assert_send); + pim_ifp->pim_ifstat_assert_send, + pim_ifp->pim_ifstat_bsm_rx, + pim_ifp->pim_ifstat_bsm_tx); } } if (uj) { @@ -1557,14 +1574,15 @@ static void pim_show_interface_traffic_single(struct pim_instance *pim, json = json_object_new_object(); else { vty_out(vty, "\n"); - vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s\n", + vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s%-17s\n", "Interface", " HELLO", " JOIN", " PRUNE", - " REGISTER", " REGISTER-STOP", " ASSERT"); - vty_out(vty, "%-14s%-18s%-17s%-17s%-17s%-17s%-17s\n", "", + " REGISTER", " REGISTER-STOP", " ASSERT", + " BSM"); + vty_out(vty, "%-14s%-18s%-17s%-17s%-17s%-17s%-17s%-17s\n", "", " Rx/Tx", " Rx/Tx", " Rx/Tx", " Rx/Tx", - " Rx/Tx", " Rx/Tx"); + " Rx/Tx", " Rx/Tx", " Rx/Tx"); vty_out(vty, - "---------------------------------------------------------------------------------------------------------------------\n"); + "-------------------------------------------------------------------------------------------------------------------------------\n"); } FOR_ALL_INTERFACES (pim->vrf, ifp) { @@ -1603,11 +1621,15 @@ static void pim_show_interface_traffic_single(struct pim_instance *pim, pim_ifp->pim_ifstat_assert_recv); json_object_int_add(json_row, "assertTx", pim_ifp->pim_ifstat_assert_send); + json_object_int_add(json_row, "bsmRx", + pim_ifp->pim_ifstat_bsm_rx); + json_object_int_add(json_row, "bsmTx", + pim_ifp->pim_ifstat_bsm_tx); json_object_object_add(json, ifp->name, json_row); } else { vty_out(vty, - "%-16s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u \n", + "%-16s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7" PRIu64 "/%-7" PRIu64 "\n", ifp->name, pim_ifp->pim_ifstat_hello_recv, pim_ifp->pim_ifstat_hello_sent, pim_ifp->pim_ifstat_join_recv, @@ -1619,7 +1641,9 @@ static void pim_show_interface_traffic_single(struct pim_instance *pim, pim_ifp->pim_ifstat_reg_stop_recv, pim_ifp->pim_ifstat_reg_stop_send, pim_ifp->pim_ifstat_assert_recv, - pim_ifp->pim_ifstat_assert_send); + pim_ifp->pim_ifstat_assert_send, + pim_ifp->pim_ifstat_bsm_rx, + pim_ifp->pim_ifstat_bsm_tx); } } if (uj) { @@ -1985,9 +2009,9 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, ifp_in = pim_if_find_by_vif_index(pim, c_oil->oil.mfcc_parent); if (ifp_in) - strcpy(in_ifname, ifp_in->name); + strlcpy(in_ifname, ifp_in->name, sizeof(in_ifname)); else - strcpy(in_ifname, ""); + strlcpy(in_ifname, "", sizeof(in_ifname)); if (src_or_group) { if (strcmp(src_or_group, src_str) @@ -2069,9 +2093,9 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, now - c_oil->oif_creation[oif_vif_index]); if (ifp_out) - strcpy(out_ifname, ifp_out->name); + strlcpy(out_ifname, ifp_out->name, sizeof(out_ifname)); else - strcpy(out_ifname, ""); + strlcpy(out_ifname, "", sizeof(out_ifname)); if (uj) { json_ifp_out = json_object_new_object(); @@ -2093,7 +2117,7 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, } else { if (first_oif) { first_oif = 0; - vty_out(vty, "%s(%c%c%c%c%c)", out_ifname, + vty_out(vty, "%s(%c%c%c%c)", out_ifname, (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) ? 'I' @@ -2106,16 +2130,12 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, & PIM_OIF_FLAG_PROTO_VXLAN) ? 'V' : ' ', - (c_oil->oif_flags[oif_vif_index] - & PIM_OIF_FLAG_PROTO_SOURCE) - ? 'S' - : ' ', (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_STAR) ? '*' : ' '); } else - vty_out(vty, ", %s(%c%c%c%c%c)", + vty_out(vty, ", %s(%c%c%c%c)", out_ifname, (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) @@ -2129,10 +2149,6 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, & PIM_OIF_FLAG_PROTO_VXLAN) ? 'V' : ' ', - (c_oil->oif_flags[oif_vif_index] - & PIM_OIF_FLAG_PROTO_SOURCE) - ? 'S' - : ' ', (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_STAR) ? '*' @@ -2311,41 +2327,76 @@ static void json_object_pim_upstream_add(json_object *json, /* XXX: need to print ths flag in the plain text display as well */ if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_MSDP) json_object_boolean_true_add(json, "sourceMsdp"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SEND_SG_RPT_PRUNE) + json_object_boolean_true_add(json, "sendSGRptPrune"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_LHR) + json_object_boolean_true_add(json, "lastHopRouter"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY) + json_object_boolean_true_add(json, "disableKATExpiry"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_STATIC_IIF) + json_object_boolean_true_add(json, "staticIncomingInterface"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL) + json_object_boolean_true_add(json, + "allowIncomingInterfaceinOil"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA) + json_object_boolean_true_add(json, "noPimRegistrationData"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG) + json_object_boolean_true_add(json, "forcePimRegistration"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG) + json_object_boolean_true_add(json, "sourceVxlanOrigination"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) + json_object_boolean_true_add(json, "sourceVxlanTermination"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) + json_object_boolean_true_add(json, "mlagVxlan"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) + json_object_boolean_true_add(json, + "mlagNonDesignatedForwarder"); } static const char * pim_upstream_state2brief_str(enum pim_upstream_state join_state, - char *state_str) + char *state_str, size_t state_str_len) { switch (join_state) { case PIM_UPSTREAM_NOTJOINED: - strcpy(state_str, "NotJ"); + strlcpy(state_str, "NotJ", state_str_len); break; case PIM_UPSTREAM_JOINED: - strcpy(state_str, "J"); + strlcpy(state_str, "J", state_str_len); break; default: - strcpy(state_str, "Unk"); + strlcpy(state_str, "Unk", state_str_len); } return state_str; } static const char *pim_reg_state2brief_str(enum pim_reg_state reg_state, - char *state_str) + char *state_str, size_t state_str_len) { switch (reg_state) { case PIM_REG_NOINFO: - strcpy(state_str, "RegNI"); + strlcpy(state_str, "RegNI", state_str_len); break; case PIM_REG_JOIN: - strcpy(state_str, "RegJ"); + strlcpy(state_str, "RegJ", state_str_len); break; case PIM_REG_JOIN_PENDING: case PIM_REG_PRUNE: - strcpy(state_str, "RegP"); + strlcpy(state_str, "RegP", state_str_len); break; default: - strcpy(state_str, "Unk"); + strlcpy(state_str, "Unk", state_str_len); } return state_str; } @@ -2413,13 +2464,13 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, pim_time_timer_to_hhmmss(msdp_reg_timer, sizeof(msdp_reg_timer), up->t_msdp_reg_timer); - pim_upstream_state2brief_str(up->join_state, state_str); + pim_upstream_state2brief_str(up->join_state, state_str, sizeof(state_str)); if (up->reg_state != PIM_REG_NOINFO) { char tmp_str[PIM_REG_STATE_STR_LEN]; sprintf(state_str + strlen(state_str), ",%s", - pim_reg_state2brief_str(up->reg_state, - tmp_str)); + pim_reg_state2brief_str(up->reg_state, tmp_str, + sizeof(tmp_str))); } if (uj) { @@ -2470,7 +2521,7 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, pim_upstream_state2str(up->join_state)); json_object_string_add( json_row, "regState", - pim_reg_state2str(up->reg_state, state_str)); + pim_reg_state2str(up->reg_state, state_str, sizeof(state_str))); json_object_string_add(json_row, "upTime", uptime); json_object_string_add(json_row, "joinTimer", join_timer); @@ -2851,6 +2902,415 @@ static void pim_show_nexthop(struct pim_instance *pim, struct vty *vty) hash_walk(pim->rpf_hash, pim_print_pnc_cache_walkcb, &cwd); } +/* Display the bsm database details */ +static void pim_show_bsm_db(struct pim_instance *pim, struct vty *vty, bool uj) +{ + struct listnode *bsmnode; + int count = 0; + int fragment = 1; + struct bsm_info *bsm; + json_object *json = NULL; + json_object *json_group = NULL; + json_object *json_row = NULL; + + count = pim->global_scope.bsm_list->count; + + if (uj) { + json = json_object_new_object(); + json_object_int_add(json, "Number of the fragments", count); + } else { + vty_out(vty, "Scope Zone: Global\n"); + vty_out(vty, "Number of the fragments: %d\n", count); + vty_out(vty, "\n"); + } + + for (ALL_LIST_ELEMENTS_RO(pim->global_scope.bsm_list, bsmnode, bsm)) { + char grp_str[INET_ADDRSTRLEN]; + char rp_str[INET_ADDRSTRLEN]; + char bsr_str[INET_ADDRSTRLEN]; + struct bsmmsg_grpinfo *group; + struct bsmmsg_rpinfo *rpaddr; + struct prefix grp; + struct bsm_hdr *hdr; + uint32_t offset = 0; + uint8_t *buf; + uint32_t len = 0; + uint32_t frag_rp_cnt = 0; + + buf = bsm->bsm; + len = bsm->size; + + /* skip pim header */ + buf += PIM_MSG_HEADER_LEN; + len -= PIM_MSG_HEADER_LEN; + + hdr = (struct bsm_hdr *)buf; + + /* BSM starts with bsr header */ + buf += sizeof(struct bsm_hdr); + len -= sizeof(struct bsm_hdr); + + pim_inet4_dump("", hdr->bsr_addr.addr, bsr_str, + sizeof(bsr_str)); + + + if (uj) { + json_object_string_add(json, "BSR address", bsr_str); + json_object_int_add(json, "BSR priority", + hdr->bsr_prio); + json_object_int_add(json, "Hashmask Length", + hdr->hm_len); + json_object_int_add(json, "Fragment Tag", + ntohs(hdr->frag_tag)); + } else { + vty_out(vty, "BSM Fragment : %d\n", fragment); + vty_out(vty, "------------------\n"); + vty_out(vty, "%-15s %-15s %-15s %-15s\n", "BSR-Address", + "BSR-Priority", "Hashmask-len", "Fragment-Tag"); + vty_out(vty, "%-15s %-15d %-15d %-15d\n", bsr_str, + hdr->bsr_prio, hdr->hm_len, + ntohs(hdr->frag_tag)); + } + + vty_out(vty, "\n"); + + while (offset < len) { + group = (struct bsmmsg_grpinfo *)buf; + + if (group->group.family == PIM_MSG_ADDRESS_FAMILY_IPV4) + grp.family = AF_INET; + + grp.prefixlen = group->group.mask; + grp.u.prefix4.s_addr = group->group.addr.s_addr; + + prefix2str(&grp, grp_str, sizeof(grp_str)); + + buf += sizeof(struct bsmmsg_grpinfo); + offset += sizeof(struct bsmmsg_grpinfo); + + if (uj) { + json_object_object_get_ex(json, grp_str, + &json_group); + if (!json_group) { + json_group = json_object_new_object(); + json_object_int_add(json_group, + "Rp Count", + group->rp_count); + json_object_int_add( + json_group, "Fragment Rp count", + group->frag_rp_count); + json_object_object_add(json, grp_str, + json_group); + } + } else { + vty_out(vty, "Group : %s\n", grp_str); + vty_out(vty, "-------------------\n"); + vty_out(vty, "Rp Count:%d\n", group->rp_count); + vty_out(vty, "Fragment Rp Count : %d\n", + group->frag_rp_count); + } + + frag_rp_cnt = group->frag_rp_count; + + if (!frag_rp_cnt) + continue; + + if (!uj) + vty_out(vty, + "RpAddress HoldTime Priority\n"); + + while (frag_rp_cnt--) { + rpaddr = (struct bsmmsg_rpinfo *)buf; + + buf += sizeof(struct bsmmsg_rpinfo); + offset += sizeof(struct bsmmsg_rpinfo); + + pim_inet4_dump("", + rpaddr->rpaddr.addr, rp_str, + sizeof(rp_str)); + + if (uj) { + json_row = json_object_new_object(); + json_object_string_add( + json_row, "Rp Address", rp_str); + json_object_int_add( + json_row, "Rp HoldTime", + ntohs(rpaddr->rp_holdtime)); + json_object_int_add(json_row, + "Rp Priority", + rpaddr->rp_pri); + json_object_object_add( + json_group, rp_str, json_row); + } else { + vty_out(vty, "%-15s %-12d %d\n", rp_str, + ntohs(rpaddr->rp_holdtime), + rpaddr->rp_pri); + } + } + vty_out(vty, "\n"); + } + + fragment++; + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +/*Display the group-rp mappings */ +static void pim_show_group_rp_mappings_info(struct pim_instance *pim, + struct vty *vty, bool uj) +{ + struct bsgrp_node *bsgrp; + struct listnode *rpnode; + struct bsm_rpinfo *bsm_rp; + struct route_node *rn; + char bsr_str[INET_ADDRSTRLEN]; + json_object *json = NULL; + json_object *json_group = NULL; + json_object *json_row = NULL; + + if (pim->global_scope.current_bsr.s_addr == INADDR_ANY) + strlcpy(bsr_str, "0.0.0.0", sizeof(bsr_str)); + + else + pim_inet4_dump("", pim->global_scope.current_bsr, bsr_str, + sizeof(bsr_str)); + + if (uj) { + json = json_object_new_object(); + json_object_string_add(json, "BSR Address", bsr_str); + } else { + vty_out(vty, "BSR Address %s\n", bsr_str); + } + + for (rn = route_top(pim->global_scope.bsrp_table); rn; + rn = route_next(rn)) { + bsgrp = (struct bsgrp_node *)rn->info; + + if (!bsgrp) + continue; + + char grp_str[INET_ADDRSTRLEN]; + + prefix2str(&bsgrp->group, grp_str, sizeof(grp_str)); + + if (uj) { + json_object_object_get_ex(json, grp_str, &json_group); + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, + json_group); + } + } else { + vty_out(vty, "Group Address %s\n", grp_str); + vty_out(vty, "--------------------------\n"); + vty_out(vty, "%-15s %-15s %-15s %-15s\n", "Rp Address", + "priority", "Holdtime", "Hash"); + + vty_out(vty, "(ACTIVE)\n"); + } + + if (bsgrp->bsrp_list) { + for (ALL_LIST_ELEMENTS_RO(bsgrp->bsrp_list, rpnode, + bsm_rp)) { + char rp_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", + bsm_rp->rp_address, rp_str, + sizeof(rp_str)); + + if (uj) { + json_row = json_object_new_object(); + json_object_string_add( + json_row, "Rp Address", rp_str); + json_object_int_add( + json_row, "Rp HoldTime", + bsm_rp->rp_holdtime); + json_object_int_add(json_row, + "Rp Priority", + bsm_rp->rp_prio); + json_object_int_add(json_row, + "Hash Val", + bsm_rp->hash); + json_object_object_add( + json_group, rp_str, json_row); + + } else { + vty_out(vty, + "%-15s %-15u %-15u %-15u\n", + rp_str, bsm_rp->rp_prio, + bsm_rp->rp_holdtime, + bsm_rp->hash); + } + } + if (!bsgrp->bsrp_list->count && !uj) + vty_out(vty, "Active List is empty.\n"); + } + + if (uj) { + json_object_int_add(json_group, "Pending RP count", + bsgrp->pend_rp_cnt); + } else { + vty_out(vty, "(PENDING)\n"); + vty_out(vty, "Pending RP count :%d\n", + bsgrp->pend_rp_cnt); + if (bsgrp->pend_rp_cnt) + vty_out(vty, "%-15s %-15s %-15s %-15s\n", + "Rp Address", "priority", "Holdtime", + "Hash"); + } + + if (bsgrp->partial_bsrp_list) { + for (ALL_LIST_ELEMENTS_RO(bsgrp->partial_bsrp_list, + rpnode, bsm_rp)) { + char rp_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", bsm_rp->rp_address, + rp_str, sizeof(rp_str)); + + if (uj) { + json_row = json_object_new_object(); + json_object_string_add( + json_row, "Rp Address", rp_str); + json_object_int_add( + json_row, "Rp HoldTime", + bsm_rp->rp_holdtime); + json_object_int_add(json_row, + "Rp Priority", + bsm_rp->rp_prio); + json_object_int_add(json_row, + "Hash Val", + bsm_rp->hash); + json_object_object_add( + json_group, rp_str, json_row); + } else { + vty_out(vty, + "%-15s %-15u %-15u %-15u\n", + rp_str, bsm_rp->rp_prio, + bsm_rp->rp_holdtime, + bsm_rp->hash); + } + } + if (!bsgrp->partial_bsrp_list->count && !uj) + vty_out(vty, "Partial List is empty\n"); + } + + if (!uj) + vty_out(vty, "\n"); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +/* pim statistics - just adding only bsm related now. + * We can continue to add all pim related stats here. + */ +static void pim_show_statistics(struct pim_instance *pim, struct vty *vty, + const char *ifname, bool uj) +{ + json_object *json = NULL; + struct interface *ifp; + + if (uj) { + json = json_object_new_object(); + json_object_int_add(json, "Number of Received BSMs", + pim->bsm_rcvd); + json_object_int_add(json, "Number of Forwared BSMs", + pim->bsm_sent); + json_object_int_add(json, "Number of Dropped BSMs", + pim->bsm_dropped); + } else { + vty_out(vty, "BSM Statistics :\n"); + vty_out(vty, "----------------\n"); + vty_out(vty, "Number of Received BSMs : %" PRIu64 "\n", + pim->bsm_rcvd); + vty_out(vty, "Number of Forwared BSMs : %" PRIu64 "\n", + pim->bsm_sent); + vty_out(vty, "Number of Dropped BSMs : %" PRIu64 "\n", + pim->bsm_dropped); + } + + vty_out(vty, "\n"); + + /* scan interfaces */ + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + + if (ifname && strcmp(ifname, ifp->name)) + continue; + + if (!pim_ifp) + continue; + + if (!uj) { + vty_out(vty, "Interface : %s\n", ifp->name); + vty_out(vty, "-------------------\n"); + vty_out(vty, + "Number of BSMs dropped due to config miss : %u\n", + pim_ifp->pim_ifstat_bsm_cfg_miss); + vty_out(vty, "Number of unicast BSMs dropped : %u\n", + pim_ifp->pim_ifstat_ucast_bsm_cfg_miss); + vty_out(vty, + "Number of BSMs dropped due to invalid scope zone : %u\n", + pim_ifp->pim_ifstat_bsm_invalid_sz); + } else { + + json_object *json_row = NULL; + + json_row = json_object_new_object(); + + json_object_string_add(json_row, "If Name", ifp->name); + json_object_int_add( + json_row, + "Number of BSMs dropped due to config miss", + pim_ifp->pim_ifstat_bsm_cfg_miss); + json_object_int_add( + json_row, "Number of unicast BSMs dropped", + pim_ifp->pim_ifstat_ucast_bsm_cfg_miss); + json_object_int_add(json_row, + "Number of BSMs dropped due to invalid scope zone", + pim_ifp->pim_ifstat_bsm_invalid_sz); + json_object_object_add(json, ifp->name, json_row); + } + vty_out(vty, "\n"); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +static void clear_pim_statistics(struct pim_instance *pim) +{ + struct interface *ifp; + + pim->bsm_rcvd = 0; + pim->bsm_sent = 0; + pim->bsm_dropped = 0; + + /* scan interfaces */ + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + pim_ifp->pim_ifstat_bsm_cfg_miss = 0; + pim_ifp->pim_ifstat_ucast_bsm_cfg_miss = 0; + pim_ifp->pim_ifstat_bsm_invalid_sz = 0; + } +} + static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj) { struct interface *ifp; @@ -3170,6 +3630,82 @@ static void igmp_show_source_retransmission(struct pim_instance *pim, } /* scan interfaces */ } +static void pim_show_bsr(struct pim_instance *pim, + struct vty *vty, + bool uj) +{ + char uptime[10]; + char last_bsm_seen[10]; + time_t now; + char bsr_state[20]; + char bsr_str[PREFIX_STRLEN]; + json_object *json = NULL; + + vty_out(vty, "PIMv2 Bootstrap information\n"); + + if (pim->global_scope.current_bsr.s_addr == INADDR_ANY) { + strlcpy(bsr_str, "0.0.0.0", sizeof(bsr_str)); + pim_time_uptime(uptime, sizeof(uptime), + pim->global_scope.current_bsr_first_ts); + pim_time_uptime(last_bsm_seen, sizeof(last_bsm_seen), + pim->global_scope.current_bsr_last_ts); + } + + else { + pim_inet4_dump("", pim->global_scope.current_bsr, + bsr_str, sizeof(bsr_str)); + now = pim_time_monotonic_sec(); + pim_time_uptime(uptime, sizeof(uptime), + (now - pim->global_scope.current_bsr_first_ts)); + pim_time_uptime(last_bsm_seen, sizeof(last_bsm_seen), + now - pim->global_scope.current_bsr_last_ts); + } + + switch (pim->global_scope.state) { + case NO_INFO: + strlcpy(bsr_state, "NO_INFO", sizeof(bsr_state)); + break; + case ACCEPT_ANY: + strlcpy(bsr_state, "ACCEPT_ANY", sizeof(bsr_state)); + break; + case ACCEPT_PREFERRED: + strlcpy(bsr_state, "ACCEPT_PREFERRED", sizeof(bsr_state)); + break; + default: + strlcpy(bsr_state, "", sizeof(bsr_state)); + } + + if (uj) { + json = json_object_new_object(); + json_object_string_add(json, "bsr", bsr_str); + json_object_int_add(json, "priority", + pim->global_scope.current_bsr_prio); + json_object_int_add(json, "fragment_tag", + pim->global_scope.bsm_frag_tag); + json_object_string_add(json, "state", bsr_state); + json_object_string_add(json, "upTime", uptime); + json_object_string_add(json, "last_bsm_seen", last_bsm_seen); + } + + else { + vty_out(vty, "Current preferred BSR address: %s\n", bsr_str); + vty_out(vty, + "Priority Fragment-Tag State UpTime\n"); + vty_out(vty, " %-12d %-12d %-13s %7s\n", + pim->global_scope.current_bsr_prio, + pim->global_scope.bsm_frag_tag, + bsr_state, + uptime); + vty_out(vty, "Last BSM seen: %s\n", last_bsm_seen); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + static void clear_igmp_interfaces(struct pim_instance *pim) { struct interface *ifp; @@ -3245,44 +3781,70 @@ DEFUN (clear_ip_igmp_interfaces, return CMD_SUCCESS; } -static void mroute_add_all(struct pim_instance *pim) +DEFUN (clear_ip_pim_statistics, + clear_ip_pim_statistics_cmd, + "clear ip pim statistics [vrf NAME]", + CLEAR_STR + IP_STR + CLEAR_IP_PIM_STR + VRF_CMD_HELP_STR + "Reset PIM statistics\n") { - struct listnode *node; - struct channel_oil *c_oil; + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { - if (pim_mroute_add(c_oil, __PRETTY_FUNCTION__)) { - /* just log warning */ - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC", - __FILE__, __PRETTY_FUNCTION__, source_str, - group_str); - } - } + if (!vrf) + return CMD_WARNING; + + clear_pim_statistics(vrf->info); + return CMD_SUCCESS; } -static void mroute_del_all(struct pim_instance *pim) +static void clear_mroute(struct pim_instance *pim) { - struct listnode *node; - struct channel_oil *c_oil; + struct pim_upstream *up; + struct interface *ifp; - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { - if (pim_mroute_del(c_oil, __PRETTY_FUNCTION__)) { - /* just log warning */ - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC", - __FILE__, __PRETTY_FUNCTION__, source_str, - group_str); + /* scan interfaces */ + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + struct pim_ifchannel *ch; + + if (!pim_ifp) + continue; + + /* deleting all ifchannels */ + while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) { + ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb); + + pim_ifchannel_delete(ch); + } + + /* clean up all igmp groups */ + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, + igmp)) { + + struct igmp_group *grp; + + if (igmp->igmp_group_list) { + while (igmp->igmp_group_list->count) { + grp = listnode_head( + igmp->igmp_group_list); + igmp_group_delete(grp); + } + } + + } + } + + /* clean up all upstreams*/ + if (pim->upstream_list) { + while (pim->upstream_list->count) { + up = listnode_head(pim->upstream_list); + pim_upstream_del(pim, up, __PRETTY_FUNCTION__); } } } @@ -3301,8 +3863,7 @@ DEFUN (clear_ip_mroute, if (!vrf) return CMD_WARNING; - mroute_del_all(vrf->info); - mroute_add_all(vrf->info); + clear_mroute(vrf->info); return CMD_SUCCESS; } @@ -3363,6 +3924,8 @@ DEFUN (clear_ip_pim_interface_traffic, pim_ifp->pim_ifstat_reg_stop_send = 0; pim_ifp->pim_ifstat_assert_recv = 0; pim_ifp->pim_ifstat_assert_send = 0; + pim_ifp->pim_ifstat_bsm_rx = 0; + pim_ifp->pim_ifstat_bsm_tx = 0; } return CMD_SUCCESS; @@ -4422,6 +4985,76 @@ DEFUN (show_ip_pim_interface_traffic, return CMD_SUCCESS; } +DEFUN (show_ip_pim_bsm_db, + show_ip_pim_bsm_db_cmd, + "show ip pim bsm-database [vrf NAME] [json]", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "PIM cached bsm packets information\n" + JSON_STR) +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + bool uj = use_json(argc, argv); + + if (!vrf) + return CMD_WARNING; + + pim_show_bsm_db(vrf->info, vty, uj); + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_bsrp, + show_ip_pim_bsrp_cmd, + "show ip pim bsrp-info [vrf NAME] [json]", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "PIM cached group-rp mappings information\n" + JSON_STR) +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + bool uj = use_json(argc, argv); + + if (!vrf) + return CMD_WARNING; + + pim_show_group_rp_mappings_info(vrf->info, vty, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_statistics, + show_ip_pim_statistics_cmd, + "show ip pim [vrf NAME] statistics [interface WORD] [json]", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "PIM statistics\n" + "interface\n" + "PIM interface\n" + JSON_STR) +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + bool uj = use_json(argc, argv); + + if (!vrf) + return CMD_WARNING; + + if (argv_find(argv, argc, "WORD", &idx)) + pim_show_statistics(vrf->info, vty, argv[idx]->arg, uj); + else + pim_show_statistics(vrf->info, vty, NULL, uj); + + return CMD_SUCCESS; +} + static void show_multicast_interfaces(struct pim_instance *pim, struct vty *vty) { struct interface *ifp; @@ -4609,9 +5242,9 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, ifp_in = pim_if_find_by_vif_index(pim, c_oil->oil.mfcc_parent); if (ifp_in) - strcpy(in_ifname, ifp_in->name); + strlcpy(in_ifname, ifp_in->name, sizeof(in_ifname)); else - strcpy(in_ifname, ""); + strlcpy(in_ifname, "", sizeof(in_ifname)); if (uj) { @@ -4666,9 +5299,9 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, found_oif = 1; if (ifp_out) - strcpy(out_ifname, ifp_out->name); + strlcpy(out_ifname, ifp_out->name, sizeof(out_ifname)); else - strcpy(out_ifname, ""); + strlcpy(out_ifname, "", sizeof(out_ifname)); if (uj) { json_ifp_out = json_object_new_object(); @@ -4692,11 +5325,6 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, json_object_boolean_true_add( json_ifp_out, "protocolVxlan"); - if (c_oil->oif_flags[oif_vif_index] - & PIM_OIF_FLAG_PROTO_SOURCE) - json_object_boolean_true_add( - json_ifp_out, "protocolSource"); - if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_STAR) json_object_boolean_true_add( @@ -4726,27 +5354,22 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, } else { if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM) { - strcpy(proto, "PIM"); + strlcpy(proto, "PIM", sizeof(proto)); } if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) { - strcpy(proto, "IGMP"); + strlcpy(proto, "IGMP", sizeof(proto)); } if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_VXLAN) { - strcpy(proto, "VxLAN"); - } - - if (c_oil->oif_flags[oif_vif_index] - & PIM_OIF_FLAG_PROTO_SOURCE) { - strcpy(proto, "SRC"); + strlcpy(proto, "VxLAN", sizeof(proto)); } if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_STAR) { - strcpy(proto, "STAR"); + strlcpy(proto, "STAR", sizeof(proto)); } vty_out(vty, @@ -4785,9 +5408,9 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, found_oif = 0; if (ifp_in) - strcpy(in_ifname, ifp_in->name); + strlcpy(in_ifname, ifp_in->name, sizeof(in_ifname)); else - strcpy(in_ifname, ""); + strlcpy(in_ifname, "", sizeof(in_ifname)); if (uj) { @@ -4814,7 +5437,7 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, json_object_string_add(json_source, "iif", in_ifname); json_oil = NULL; } else { - strcpy(proto, "STATIC"); + strlcpy(proto, "STATIC", sizeof(proto)); } for (oif_vif_index = 0; oif_vif_index < MAXVIFS; @@ -4836,9 +5459,9 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, found_oif = 1; if (ifp_out) - strcpy(out_ifname, ifp_out->name); + strlcpy(out_ifname, ifp_out->name, sizeof(out_ifname)); else - strcpy(out_ifname, ""); + strlcpy(out_ifname, "", sizeof(out_ifname)); if (uj) { json_ifp_out = json_object_new_object(); @@ -4956,24 +5579,67 @@ DEFUN (show_ip_mroute_vrf_all, bool first = true; bool fill = false; - if (argv_find(argv, argc, "fill", &idx)) - fill = true; + if (argv_find(argv, argc, "fill", &idx)) + fill = true; + + if (uj) + vty_out(vty, "{ "); + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + if (uj) { + if (!first) + vty_out(vty, ", "); + vty_out(vty, " \"%s\": ", vrf->name); + first = false; + } else + vty_out(vty, "VRF: %s\n", vrf->name); + show_mroute(vrf->info, vty, &sg, fill, uj); + } + if (uj) + vty_out(vty, "}\n"); + + return CMD_SUCCESS; +} + +DEFUN (clear_ip_mroute_count, + clear_ip_mroute_count_cmd, + "clear ip mroute [vrf NAME] count", + CLEAR_STR + IP_STR + MROUTE_STR + VRF_CMD_HELP_STR + "Route and packet count data\n") +{ + int idx = 2; + struct listnode *node; + struct channel_oil *c_oil; + struct static_route *sr; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct pim_instance *pim; + + if (!vrf) + return CMD_WARNING; - if (uj) - vty_out(vty, "{ "); - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - if (uj) { - if (!first) - vty_out(vty, ", "); - vty_out(vty, " \"%s\": ", vrf->name); - first = false; - } else - vty_out(vty, "VRF: %s\n", vrf->name); - show_mroute(vrf->info, vty, &sg, fill, uj); + pim = vrf->info; + for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { + if (!c_oil->installed) + continue; + + pim_mroute_update_counters(c_oil); + c_oil->cc.origpktcnt = c_oil->cc.pktcnt; + c_oil->cc.origbytecnt = c_oil->cc.bytecnt; + c_oil->cc.origwrong_if = c_oil->cc.wrong_if; } - if (uj) - vty_out(vty, "}\n"); + for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sr)) { + if (!sr->c_oil.installed) + continue; + + pim_mroute_update_counters(&sr->c_oil); + + sr->c_oil.cc.origpktcnt = sr->c_oil.cc.pktcnt; + sr->c_oil.cc.origbytecnt = sr->c_oil.cc.bytecnt; + sr->c_oil.cc.origwrong_if = sr->c_oil.cc.wrong_if; + } return CMD_SUCCESS; } @@ -4981,7 +5647,7 @@ static void show_mroute_count(struct pim_instance *pim, struct vty *vty) { struct listnode *node; struct channel_oil *c_oil; - struct static_route *s_route; + struct static_route *sr; vty_out(vty, "\n"); @@ -5005,28 +5671,30 @@ static void show_mroute_count(struct pim_instance *pim, struct vty *vty) vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld\n", source_str, group_str, c_oil->cc.lastused / 100, - c_oil->cc.pktcnt, c_oil->cc.bytecnt, - c_oil->cc.wrong_if); + c_oil->cc.pktcnt - c_oil->cc.origpktcnt, + c_oil->cc.bytecnt - c_oil->cc.origbytecnt, + c_oil->cc.wrong_if - c_oil->cc.origwrong_if); } - for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, s_route)) { + for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sr)) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; - if (!s_route->c_oil.installed) + if (!sr->c_oil.installed) continue; - pim_mroute_update_counters(&s_route->c_oil); + pim_mroute_update_counters(&sr->c_oil); - pim_inet4_dump("", s_route->c_oil.oil.mfcc_mcastgrp, + pim_inet4_dump("", sr->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", s_route->c_oil.oil.mfcc_origin, + pim_inet4_dump("", sr->c_oil.oil.mfcc_origin, source_str, sizeof(source_str)); vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld\n", - source_str, group_str, s_route->c_oil.cc.lastused, - s_route->c_oil.cc.pktcnt, s_route->c_oil.cc.bytecnt, - s_route->c_oil.cc.wrong_if); + source_str, group_str, sr->c_oil.cc.lastused, + sr->c_oil.cc.pktcnt - sr->c_oil.cc.origpktcnt, + sr->c_oil.cc.bytecnt - sr->c_oil.cc.origbytecnt, + sr->c_oil.cc.wrong_if - sr->c_oil.cc.origwrong_if); } } @@ -5080,6 +5748,97 @@ DEFUN (show_ip_mroute_count_vrf_all, return CMD_SUCCESS; } +static void show_mroute_summary(struct pim_instance *pim, struct vty *vty) +{ + struct listnode *node; + struct channel_oil *c_oil; + struct static_route *s_route; + uint32_t starg_sw_mroute_cnt = 0; + uint32_t sg_sw_mroute_cnt = 0; + uint32_t starg_hw_mroute_cnt = 0; + uint32_t sg_hw_mroute_cnt = 0; + + vty_out(vty, "Mroute Type Installed/Total\n"); + + for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { + if (!c_oil->installed) { + if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) + starg_sw_mroute_cnt++; + else + sg_sw_mroute_cnt++; + } else { + if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) + starg_hw_mroute_cnt++; + else + sg_hw_mroute_cnt++; + } + } + + for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, s_route)) { + if (!s_route->c_oil.installed) { + if (s_route->c_oil.oil.mfcc_origin.s_addr == INADDR_ANY) + starg_sw_mroute_cnt++; + else + sg_sw_mroute_cnt++; + } else { + if (s_route->c_oil.oil.mfcc_origin.s_addr == INADDR_ANY) + starg_hw_mroute_cnt++; + else + sg_hw_mroute_cnt++; + } + } + + vty_out(vty, "%-20s %d/%d\n", "(*, G)", starg_hw_mroute_cnt, + starg_sw_mroute_cnt + starg_hw_mroute_cnt); + vty_out(vty, "%-20s %d/%d\n", "(S, G)", sg_hw_mroute_cnt, + sg_sw_mroute_cnt + sg_hw_mroute_cnt); + vty_out(vty, "------\n"); + vty_out(vty, "%-20s %d/%d\n", "Total", + (starg_hw_mroute_cnt + sg_hw_mroute_cnt), + (starg_sw_mroute_cnt + + starg_hw_mroute_cnt + + sg_sw_mroute_cnt + + sg_hw_mroute_cnt)); +} + +DEFUN (show_ip_mroute_summary, + show_ip_mroute_summary_cmd, + "show ip mroute [vrf NAME] summary", + SHOW_STR + IP_STR + MROUTE_STR + VRF_CMD_HELP_STR + "Summary of all mroutes\n") +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + + if (!vrf) + return CMD_WARNING; + + show_mroute_summary(vrf->info, vty); + return CMD_SUCCESS; +} + +DEFUN (show_ip_mroute_summary_vrf_all, + show_ip_mroute_summary_vrf_all_cmd, + "show ip mroute vrf all summary", + SHOW_STR + IP_STR + MROUTE_STR + VRF_CMD_HELP_STR + "Summary of all mroutes\n") +{ + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + vty_out(vty, "VRF: %s\n", vrf->name); + show_mroute_summary(vrf->info, vty); + } + + return CMD_SUCCESS; +} + DEFUN (show_ip_rib, show_ip_rib_cmd, "show ip rib [vrf NAME] A.B.C.D", @@ -5196,7 +5955,13 @@ static int pim_rp_cmd_worker(struct pim_instance *pim, struct vty *vty, { int result; - result = pim_rp_new(pim, rp, group, plist); + result = pim_rp_new_config(pim, rp, group, plist); + + if (result == PIM_GROUP_BAD_ADDR_MASK_COMBO) { + vty_out(vty, "%% Inconsistent address and mask: %s\n", + group); + return CMD_WARNING_CONFIG_FAILED; + } if (result == PIM_GROUP_BAD_ADDRESS) { vty_out(vty, "%% Bad group address specified: %s\n", group); @@ -5522,7 +6287,7 @@ static int pim_no_rp_cmd_worker(struct pim_instance *pim, struct vty *vty, const char *rp, const char *group, const char *plist) { - int result = pim_rp_del(pim, rp, group, plist); + int result = pim_rp_del_config(pim, rp, group, plist); if (result == PIM_GROUP_BAD_ADDRESS) { vty_out(vty, "%% Bad group address specified: %s\n", group); @@ -5741,6 +6506,27 @@ DEFUN (show_ip_pim_group_type, return CMD_SUCCESS; } +DEFUN (show_ip_pim_bsr, + show_ip_pim_bsr_cmd, + "show ip pim bsr [json]", + SHOW_STR + IP_STR + PIM_STR + "boot-strap router information\n" + JSON_STR) +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + bool uj = use_json(argc, argv); + + if (!vrf) + return CMD_WARNING; + + pim_show_bsr(vrf->info, vty, uj); + + return CMD_SUCCESS; +} + DEFUN (ip_ssmpingd, ip_ssmpingd_cmd, "ip ssmpingd [A.B.C.D]", @@ -6418,6 +7204,106 @@ DEFUN_HIDDEN (interface_no_ip_igmp_query_max_response_time_dsec, return CMD_SUCCESS; } +#define IGMP_LAST_MEMBER_QUERY_COUNT_MIN (1) +#define IGMP_LAST_MEMBER_QUERY_COUNT_MAX (7) + +DEFUN (interface_ip_igmp_last_member_query_count, + interface_ip_igmp_last_member_query_count_cmd, + "ip igmp last-member-query-count (1-7)", + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR + "Last member query count\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + int last_member_query_count; + int ret; + + if (!pim_ifp) { + ret = pim_cmd_igmp_start(vty, ifp); + if (ret != CMD_SUCCESS) + return ret; + pim_ifp = ifp->info; + } + + last_member_query_count = atoi(argv[3]->arg); + + pim_ifp->igmp_last_member_query_count = last_member_query_count; + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_last_member_query_count, + interface_no_ip_igmp_last_member_query_count_cmd, + "no ip igmp last-member-query-count", + NO_STR + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + return CMD_SUCCESS; + + pim_ifp->igmp_last_member_query_count = + IGMP_DEFAULT_ROBUSTNESS_VARIABLE; + + return CMD_SUCCESS; +} + +#define IGMP_LAST_MEMBER_QUERY_INTERVAL_MIN (1) +#define IGMP_LAST_MEMBER_QUERY_INTERVAL_MAX (255) + +DEFUN (interface_ip_igmp_last_member_query_interval, + interface_ip_igmp_last_member_query_interval_cmd, + "ip igmp last-member-query-interval (1-255)", + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR + "Last member query interval in deciseconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + int last_member_query_interval; + int ret; + + if (!pim_ifp) { + ret = pim_cmd_igmp_start(vty, ifp); + if (ret != CMD_SUCCESS) + return ret; + pim_ifp = ifp->info; + } + + last_member_query_interval = atoi(argv[3]->arg); + pim_ifp->igmp_specific_query_max_response_time_dsec + = last_member_query_interval; + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_last_member_query_interval, + interface_no_ip_igmp_last_member_query_interval_cmd, + "no ip igmp last-member-query-interval", + NO_STR + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + return CMD_SUCCESS; + + pim_ifp->igmp_specific_query_max_response_time_dsec = + IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC; + + return CMD_SUCCESS; +} + DEFUN (interface_ip_pim_drprio, interface_ip_pim_drprio_cmd, "ip pim drpriority (1-4294967295)", @@ -7189,6 +8075,7 @@ DEFUN (debug_pim, PIM_DO_DEBUG_PIM_TRACE; PIM_DO_DEBUG_MSDP_EVENTS; PIM_DO_DEBUG_MSDP_PACKETS; + PIM_DO_DEBUG_BSM; return CMD_SUCCESS; } @@ -7207,6 +8094,7 @@ DEFUN (no_debug_pim, PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND; PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV; + PIM_DONT_DEBUG_BSM; return CMD_SUCCESS; } @@ -7592,6 +8480,30 @@ DEFUN (no_debug_mtrace, return CMD_SUCCESS; } +DEFUN (debug_bsm, + debug_bsm_cmd, + "debug pim bsm", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_BSM_STR) +{ + PIM_DO_DEBUG_BSM; + return CMD_SUCCESS; +} + +DEFUN (no_debug_bsm, + no_debug_bsm_cmd, + "no debug pim bsm", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_BSM_STR) +{ + PIM_DONT_DEBUG_BSM; + return CMD_SUCCESS; +} + + DEFUN_NOSH (show_debugging_pim, show_debugging_pim_cmd, "show debugging [pim]", @@ -7715,6 +8627,94 @@ DEFUN (no_ip_pim_bfd, return CMD_SUCCESS; } +DEFUN (ip_pim_bsm, + ip_pim_bsm_cmd, + "ip pim bsm", + IP_STR + PIM_STR + "Enables BSM support on the interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) { + if (!pim_cmd_interface_add(ifp)) { + vty_out(vty, "Could not enable PIM SM on interface\n"); + return CMD_WARNING; + } + } + + pim_ifp = ifp->info; + pim_ifp->bsm_enable = true; + + return CMD_SUCCESS; +} + +DEFUN (no_ip_pim_bsm, + no_ip_pim_bsm_cmd, + "no ip pim bsm", + NO_STR + IP_STR + PIM_STR + "Disables BSM support\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, "Pim not enabled on this interface\n"); + return CMD_WARNING; + } + + pim_ifp->bsm_enable = false; + + return CMD_SUCCESS; +} + +DEFUN (ip_pim_ucast_bsm, + ip_pim_ucast_bsm_cmd, + "ip pim unicast-bsm", + IP_STR + PIM_STR + "Accept/Send unicast BSM on the interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) { + if (!pim_cmd_interface_add(ifp)) { + vty_out(vty, "Could not enable PIM SM on interface\n"); + return CMD_WARNING; + } + } + + pim_ifp = ifp->info; + pim_ifp->ucast_bsm_accept = true; + + return CMD_SUCCESS; +} + +DEFUN (no_ip_pim_ucast_bsm, + no_ip_pim_ucast_bsm_cmd, + "no ip pim unicast-bsm", + NO_STR + IP_STR + PIM_STR + "Block send/receive unicast BSM on this interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, "Pim not enabled on this interface\n"); + return CMD_WARNING; + } + + pim_ifp->ucast_bsm_accept = false; + + return CMD_SUCCESS; +} + #if HAVE_BFDD > 0 DEFUN_HIDDEN( #else @@ -8232,7 +9232,7 @@ static void ip_msdp_show_peers(struct pim_instance *pim, struct vty *vty, pim_time_uptime(timebuf, sizeof(timebuf), now - mp->uptime); } else { - strcpy(timebuf, "-"); + strlcpy(timebuf, "-", sizeof(timebuf)); } pim_inet4_dump("", mp->peer, peer_str, sizeof(peer_str)); pim_inet4_dump("", mp->local, local_str, @@ -8289,7 +9289,7 @@ static void ip_msdp_show_peers_detail(struct pim_instance *pim, struct vty *vty, pim_time_uptime(timebuf, sizeof(timebuf), now - mp->uptime); } else { - strcpy(timebuf, "-"); + strlcpy(timebuf, "-", sizeof(timebuf)); } pim_inet4_dump("", mp->local, local_str, sizeof(local_str)); @@ -8468,18 +9468,18 @@ static void ip_msdp_show_sa(struct pim_instance *pim, struct vty *vty, bool uj) if (sa->flags & PIM_MSDP_SAF_PEER) { pim_inet4_dump("", sa->rp, rp_str, sizeof(rp_str)); if (sa->up) { - strcpy(spt_str, "yes"); + strlcpy(spt_str, "yes", sizeof(spt_str)); } else { - strcpy(spt_str, "no"); + strlcpy(spt_str, "no", sizeof(spt_str)); } } else { - strcpy(rp_str, "-"); - strcpy(spt_str, "-"); + strlcpy(rp_str, "-", sizeof(rp_str)); + strlcpy(spt_str, "-", sizeof(spt_str)); } if (sa->flags & PIM_MSDP_SAF_LOCAL) { - strcpy(local_str, "yes"); + strlcpy(local_str, "yes", sizeof(local_str)); } else { - strcpy(local_str, "no"); + strlcpy(local_str, "no", sizeof(local_str)); } if (uj) { json_object_object_get_ex(json, grp_str, &json_group); @@ -8533,19 +9533,19 @@ static void ip_msdp_show_sa_entry_detail(struct pim_msdp_sa *sa, pim_inet4_dump("", sa->rp, rp_str, sizeof(rp_str)); pim_inet4_dump("", sa->peer, peer_str, sizeof(peer_str)); if (sa->up) { - strcpy(spt_str, "yes"); + strlcpy(spt_str, "yes", sizeof(spt_str)); } else { - strcpy(spt_str, "no"); + strlcpy(spt_str, "no", sizeof(spt_str)); } } else { - strcpy(rp_str, "-"); - strcpy(peer_str, "-"); - strcpy(spt_str, "-"); + strlcpy(rp_str, "-", sizeof(rp_str)); + strlcpy(peer_str, "-", sizeof(peer_str)); + strlcpy(spt_str, "-", sizeof(spt_str)); } if (sa->flags & PIM_MSDP_SAF_LOCAL) { - strcpy(local_str, "yes"); + strlcpy(local_str, "yes", sizeof(local_str)); } else { - strcpy(local_str, "no"); + strlcpy(local_str, "no", sizeof(local_str)); } pim_time_timer_to_hhmmss(statetimer, sizeof(statetimer), sa->sa_state_timer); @@ -9259,6 +10259,14 @@ void pim_cmd_init(void) &interface_ip_igmp_query_max_response_time_dsec_cmd); install_element(INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_dsec_cmd); + install_element(INTERFACE_NODE, + &interface_ip_igmp_last_member_query_count_cmd); + install_element(INTERFACE_NODE, + &interface_no_ip_igmp_last_member_query_count_cmd); + install_element(INTERFACE_NODE, + &interface_ip_igmp_last_member_query_interval_cmd); + install_element(INTERFACE_NODE, + &interface_no_ip_igmp_last_member_query_interval_cmd); install_element(INTERFACE_NODE, &interface_ip_pim_activeactive_cmd); install_element(INTERFACE_NODE, &interface_ip_pim_ssm_cmd); install_element(INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd); @@ -9312,24 +10320,32 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_pim_upstream_rpf_cmd); install_element(VIEW_NODE, &show_ip_pim_rp_cmd); install_element(VIEW_NODE, &show_ip_pim_rp_vrf_all_cmd); + install_element(VIEW_NODE, &show_ip_pim_bsr_cmd); install_element(VIEW_NODE, &show_ip_multicast_cmd); install_element(VIEW_NODE, &show_ip_multicast_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_mroute_cmd); install_element(VIEW_NODE, &show_ip_mroute_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_mroute_count_cmd); install_element(VIEW_NODE, &show_ip_mroute_count_vrf_all_cmd); + install_element(VIEW_NODE, &show_ip_mroute_summary_cmd); + install_element(VIEW_NODE, &show_ip_mroute_summary_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_rib_cmd); install_element(VIEW_NODE, &show_ip_ssmpingd_cmd); install_element(VIEW_NODE, &show_debugging_pim_cmd); install_element(VIEW_NODE, &show_ip_pim_nexthop_cmd); install_element(VIEW_NODE, &show_ip_pim_nexthop_lookup_cmd); + install_element(VIEW_NODE, &show_ip_pim_bsrp_cmd); + install_element(VIEW_NODE, &show_ip_pim_bsm_db_cmd); + install_element(VIEW_NODE, &show_ip_pim_statistics_cmd); + install_element(ENABLE_NODE, &clear_ip_mroute_count_cmd); install_element(ENABLE_NODE, &clear_ip_interfaces_cmd); install_element(ENABLE_NODE, &clear_ip_igmp_interfaces_cmd); install_element(ENABLE_NODE, &clear_ip_mroute_cmd); install_element(ENABLE_NODE, &clear_ip_pim_interfaces_cmd); install_element(ENABLE_NODE, &clear_ip_pim_interface_traffic_cmd); install_element(ENABLE_NODE, &clear_ip_pim_oil_cmd); + install_element(ENABLE_NODE, &clear_ip_pim_statistics_cmd); install_element(ENABLE_NODE, &debug_igmp_cmd); install_element(ENABLE_NODE, &no_debug_igmp_cmd); @@ -9377,6 +10393,8 @@ void pim_cmd_init(void) install_element(ENABLE_NODE, &no_debug_msdp_packets_cmd); install_element(ENABLE_NODE, &debug_mtrace_cmd); install_element(ENABLE_NODE, &no_debug_mtrace_cmd); + install_element(ENABLE_NODE, &debug_bsm_cmd); + install_element(ENABLE_NODE, &no_debug_bsm_cmd); install_element(CONFIG_NODE, &debug_igmp_cmd); install_element(CONFIG_NODE, &no_debug_igmp_cmd); @@ -9420,6 +10438,8 @@ void pim_cmd_init(void) install_element(CONFIG_NODE, &no_debug_msdp_packets_cmd); install_element(CONFIG_NODE, &debug_mtrace_cmd); install_element(CONFIG_NODE, &no_debug_mtrace_cmd); + install_element(CONFIG_NODE, &debug_bsm_cmd); + install_element(CONFIG_NODE, &no_debug_bsm_cmd); install_element(CONFIG_NODE, &ip_msdp_mesh_group_member_cmd); install_element(VRF_NODE, &ip_msdp_mesh_group_member_cmd); @@ -9443,6 +10463,11 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_pim_vxlan_sg_work_cmd); install_element(INTERFACE_NODE, &interface_pim_use_source_cmd); install_element(INTERFACE_NODE, &interface_no_pim_use_source_cmd); + /* Install BSM command */ + install_element(INTERFACE_NODE, &ip_pim_bsm_cmd); + install_element(INTERFACE_NODE, &no_ip_pim_bsm_cmd); + install_element(INTERFACE_NODE, &ip_pim_ucast_bsm_cmd); + install_element(INTERFACE_NODE, &no_ip_pim_ucast_bsm_cmd); /* Install BFD command */ install_element(INTERFACE_NODE, &ip_pim_bfd_cmd); install_element(INTERFACE_NODE, &ip_pim_bfd_param_cmd); diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index 67d6e43c34a5..f5bb316a7ae3 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -35,6 +35,8 @@ #define IFACE_IGMP_QUERY_INTERVAL_STR "IGMP host query interval\n" #define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR "IGMP max query response value (seconds)\n" #define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR "IGMP max query response value (deciseconds)\n" +#define IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR "IGMP last member query interval\n" +#define IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR "IGMP last member query count\n" #define DEBUG_IGMP_STR "IGMP protocol activity\n" #define DEBUG_IGMP_EVENTS_STR "IGMP protocol events\n" #define DEBUG_IGMP_PACKETS_STR "IGMP protocol packets\n" @@ -65,6 +67,8 @@ #define DEBUG_MSDP_INTERNAL_STR "MSDP protocol internal\n" #define DEBUG_MSDP_PACKETS_STR "MSDP protocol packets\n" #define DEBUG_MTRACE_STR "Mtrace protocol activity\n" +#define DEBUG_PIM_BSM_STR "BSR message processing activity\n" + void pim_cmd_init(void); diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c index e482d321a41b..721d153d76f7 100644 --- a/pimd/pim_hello.c +++ b/pimd/pim_hello.c @@ -31,6 +31,7 @@ #include "pim_iface.h" #include "pim_neighbor.h" #include "pim_upstream.h" +#include "pim_bsm.h" static void on_trace(const char *label, struct interface *ifp, struct in_addr src) @@ -367,6 +368,12 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, } FREE_ADDR_LIST_THEN_RETURN(-8); } + /* Forward BSM if required */ + if (!pim_bsm_new_nbr_fwd(neigh, ifp)) { + if (PIM_DEBUG_PIM_HELLO) + zlog_debug("%s: forwarding bsm to new nbr failed", + __PRETTY_FUNCTION__); + } /* actual addr list has been saved under neighbor */ return 0; @@ -420,6 +427,12 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, } FREE_ADDR_LIST_THEN_RETURN(-9); } + /* Forward BSM if required */ + if (!pim_bsm_new_nbr_fwd(neigh, ifp)) { + if (PIM_DEBUG_PIM_HELLO) + zlog_debug("%s: forwarding bsm to new nbr failed", + __PRETTY_FUNCTION__); + } /* actual addr list is saved under neighbor */ return 0; diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 0fb7f176ce8e..7b8f04569788 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -131,6 +131,12 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC; pim_ifp->igmp_specific_query_max_response_time_dsec = IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC; + pim_ifp->igmp_last_member_query_count = + IGMP_DEFAULT_ROBUSTNESS_VARIABLE; + + /* BSM config on interface: TRUE by default */ + pim_ifp->bsm_enable = true; + pim_ifp->ucast_bsm_accept = true; /* RFC 3376: 8.3. Query Response Interval diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index fe96c077582c..1c11e8570518 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -88,8 +88,14 @@ struct pim_interface { int igmp_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs for general queries */ int igmp_specific_query_max_response_time_dsec; /* IGMPv3 Max Response - Time in dsecs for - specific queries */ + Time in dsecs called + as last member query + interval, defines the + maximum response time + advertised in IGMP + group-specific + queries */ + int igmp_last_member_query_count; /* IGMP last member query count */ struct list *igmp_socket_list; /* list of struct igmp_sock */ struct list *igmp_join_list; /* list of struct igmp_join */ @@ -128,6 +134,8 @@ struct pim_interface { bool activeactive; int64_t pim_ifstat_start; /* start timestamp for stats */ + uint64_t pim_ifstat_bsm_rx; + uint64_t pim_ifstat_bsm_tx; uint32_t pim_ifstat_hello_sent; uint32_t pim_ifstat_hello_sendfail; uint32_t pim_ifstat_hello_recv; @@ -142,7 +150,12 @@ struct pim_interface { uint32_t pim_ifstat_reg_stop_send; uint32_t pim_ifstat_assert_recv; uint32_t pim_ifstat_assert_send; + uint32_t pim_ifstat_bsm_cfg_miss; + uint32_t pim_ifstat_ucast_bsm_cfg_miss; + uint32_t pim_ifstat_bsm_invalid_sz; struct bfd_info *bfd_info; + bool bsm_enable; /* bsm processing enable */ + bool ucast_bsm_accept; /* ucast bsm processing */ }; /* diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index cbc3c6a640c7..9b242e9be5fd 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -135,9 +135,20 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) if (ch->upstream->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) mask = PIM_OIF_FLAG_PROTO_IGMP; - /* SGRpt entry could have empty oil */ - pim_channel_del_oif(ch->upstream->channel_oil, ch->interface, - mask); + /* + * A S,G RPT channel can have an empty oil, we also + * need to take into account the fact that a ifchannel + * might have been suppressing a *,G ifchannel from + * being inherited. So let's figure out what + * needs to be done here + */ + if (pim_upstream_evaluate_join_desired_interface( + ch->upstream, ch, ch->parent)) + pim_channel_add_oif(ch->upstream->channel_oil, + ch->interface, mask); + else + pim_channel_del_oif(ch->upstream->channel_oil, + ch->interface, mask); /* * Do we have any S,G's that are inheriting? * Nuke from on high too. @@ -1393,7 +1404,9 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom, PIM_IF_FLAG_UNSET_S_G_RPT(child->flags); child->ifjoin_state = PIM_IFJOIN_NOINFO; - if (I_am_RP(pim, child->sg.grp)) { + if ((I_am_RP(pim, child->sg.grp)) && + (!pim_upstream_empty_inherited_olist( + child->upstream))) { pim_channel_add_oif( child->upstream->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_STAR); @@ -1414,9 +1427,9 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom, pim_jp_agg_single_upstream_send(&starup->rpf, starup, true); } -unsigned int pim_ifchannel_hash_key(void *arg) +unsigned int pim_ifchannel_hash_key(const void *arg) { - struct pim_ifchannel *ch = (struct pim_ifchannel *)arg; + const struct pim_ifchannel *ch = arg; return jhash_2words(ch->sg.src.s_addr, ch->sg.grp.s_addr, 0); } diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h index b9d4d291d854..b36c3236b0bd 100644 --- a/pimd/pim_ifchannel.h +++ b/pimd/pim_ifchannel.h @@ -155,5 +155,5 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom, int pim_ifchannel_compare(const struct pim_ifchannel *ch1, const struct pim_ifchannel *ch2); -unsigned int pim_ifchannel_hash_key(void *arg); +unsigned int pim_ifchannel_hash_key(const void *arg); #endif /* PIM_IFCHANNEL_H */ diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index cdd156b96fa3..4ae6f69d3eee 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -741,7 +741,7 @@ static void igmp_group_free(struct igmp_group *group) XFREE(MTYPE_PIM_IGMP_GROUP, group); } -static void igmp_group_delete(struct igmp_group *group) +void igmp_group_delete(struct igmp_group *group) { struct listnode *src_node; struct listnode *src_nextnode; @@ -829,9 +829,9 @@ void igmp_sock_delete_all(struct interface *ifp) } } -static unsigned int igmp_group_hash_key(void *arg) +static unsigned int igmp_group_hash_key(const void *arg) { - struct igmp_group *group = (struct igmp_group *)arg; + const struct igmp_group *group = arg; return jhash_1word(group->group_addr.s_addr, 0); } diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index c8b880ddd7e0..7db568dcfe21 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -197,4 +197,5 @@ void igmp_send_query(int igmp_version, struct igmp_group *group, int fd, int query_max_response_time_dsec, uint8_t s_flag, uint8_t querier_robustness_variable, uint16_t querier_query_interval); +void igmp_group_delete(struct igmp_group *group); #endif /* PIM_IGMP_H */ diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index b845f54f066d..bc0460fa0397 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -997,7 +997,7 @@ static void group_retransmit_group(struct igmp_group *group) char query_buf[query_buf_size]; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; lmqt_msec = lmqc * lmqi_msec; @@ -1076,7 +1076,7 @@ static int group_retransmit_sources(struct igmp_group *group, igmp = group->group_igmp_sock; pim_ifp = igmp->interface->info; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; lmqt_msec = lmqc * lmqi_msec; @@ -1314,9 +1314,13 @@ static long igmp_source_timer_remain_msec(struct igmp_source *source) */ static void group_query_send(struct igmp_group *group) { + struct pim_interface *pim_ifp; + struct igmp_sock *igmp; long lmqc; /* Last Member Query Count */ - lmqc = group->group_igmp_sock->querier_robustness_variable; + igmp = group->group_igmp_sock; + pim_ifp = igmp->interface->info; + lmqc = pim_ifp->igmp_last_member_query_count; /* lower group timer to lmqt */ igmp_group_timer_lower_to_lmqt(group); @@ -1351,7 +1355,7 @@ static void source_query_send_by_flag(struct igmp_group *group, igmp = group->group_igmp_sock; pim_ifp = igmp->interface->info; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; lmqt_msec = lmqc * lmqi_msec; @@ -1509,7 +1513,7 @@ void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) ifname = ifp->name; lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqt_msec = PIM_IGMP_LMQT_MSEC( lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ @@ -1546,7 +1550,7 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source) ifname = ifp->name; lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqt_msec = PIM_IGMP_LMQT_MSEC( lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ diff --git a/pimd/pim_instance.c b/pimd/pim_instance.c index a2bf3d27839b..6848d2dabb73 100644 --- a/pimd/pim_instance.c +++ b/pimd/pim_instance.c @@ -33,6 +33,7 @@ #include "pim_static.h" #include "pim_ssmpingd.h" #include "pim_vty.h" +#include "pim_bsm.h" static void pim_instance_terminate(struct pim_instance *pim) { @@ -50,6 +51,8 @@ static void pim_instance_terminate(struct pim_instance *pim) pim_rp_free(pim); + pim_bsm_proc_free(pim); + /* Traverse and cleanup rpf_hash */ if (pim->rpf_hash) { hash_clean(pim->rpf_hash, (void *)pim_rp_list_hash_clean); @@ -106,6 +109,8 @@ static struct pim_instance *pim_instance_init(struct vrf *vrf) pim_rp_init(pim); + pim_bsm_proc_init(pim); + pim_oil_init(pim); pim_upstream_init(pim); diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h index 1740bcc7900d..06d41c4b537d 100644 --- a/pimd/pim_instance.h +++ b/pimd/pim_instance.h @@ -26,6 +26,7 @@ #include "pim_str.h" #include "pim_msdp.h" #include "pim_assert.h" +#include "pim_bsm.h" #include "pim_vxlan_instance.h" #if defined(HAVE_LINUX_MROUTE_H) @@ -121,6 +122,11 @@ struct pim_instance { bool ecmp_enable; bool ecmp_rebalance_enable; + /* Bsm related */ + struct bsm_scope global_scope; + uint64_t bsm_rcvd; + uint64_t bsm_sent; + uint64_t bsm_dropped; /* If we need to rescan all our upstreams */ struct thread *rpf_cache_refresher; diff --git a/pimd/pim_join.c b/pimd/pim_join.c index cbacaf3ea872..5e1a4f0c5e6a 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -519,7 +519,7 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups) group_size = pim_msg_get_jp_group_size(group->sources); if (group_size > packet_left) { pim_msg_build_header(pim_msg, packet_size, - PIM_MSG_TYPE_JOIN_PRUNE); + PIM_MSG_TYPE_JOIN_PRUNE, false); if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address, qpim_all_pim_routers_addr, pim_msg, @@ -576,7 +576,7 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups) if (packet_left < sizeof(struct pim_jp_groups) || msg->num_groups == 255) { pim_msg_build_header(pim_msg, packet_size, - PIM_MSG_TYPE_JOIN_PRUNE); + PIM_MSG_TYPE_JOIN_PRUNE, false); if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address, qpim_all_pim_routers_addr, pim_msg, @@ -596,7 +596,7 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups) if (!new_packet) { // msg->num_groups = htons (msg->num_groups); pim_msg_build_header(pim_msg, packet_size, - PIM_MSG_TYPE_JOIN_PRUNE); + PIM_MSG_TYPE_JOIN_PRUNE, false); if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address, qpim_all_pim_routers_addr, pim_msg, packet_size, diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index 866a19fc9831..ca0582434768 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -897,11 +897,11 @@ int pim_mroute_add(struct channel_oil *c_oil, const char *name) int err; int orig = 0; int orig_iif_vif = 0; - struct pim_interface *pim_reg_ifp; - int orig_pimreg_ttl; + struct pim_interface *pim_reg_ifp = NULL; + int orig_pimreg_ttl = 0; bool pimreg_ttl_reset = false; - struct pim_interface *vxlan_ifp; - int orig_term_ttl; + struct pim_interface *vxlan_ifp = NULL; + int orig_term_ttl = 0; bool orig_term_ttl_reset = false; pim->mroute_add_last = pim_time_monotonic_sec(); @@ -985,9 +985,11 @@ int pim_mroute_add(struct channel_oil *c_oil, const char *name) if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig; - if (pimreg_ttl_reset) + if (pimreg_ttl_reset) { + assert(pim_reg_ifp); c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] = orig_pimreg_ttl; + } if (orig_term_ttl_reset) c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] = diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 395c4af35f94..74a3a9836bc6 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -680,9 +680,9 @@ void pim_msdp_up_del(struct pim_instance *pim, struct prefix_sg *sg) } /* sa hash and peer list helpers */ -static unsigned int pim_msdp_sa_hash_key_make(void *p) +static unsigned int pim_msdp_sa_hash_key_make(const void *p) { - struct pim_msdp_sa *sa = p; + const struct pim_msdp_sa *sa = p; return (jhash_2words(sa->sg.src.s_addr, sa->sg.grp.s_addr, 0)); } @@ -1078,7 +1078,7 @@ static enum pim_msdp_err pim_msdp_peer_new(struct pim_instance *pim, mp->mesh_group_name = XSTRDUP(MTYPE_PIM_MSDP_MG_NAME, mesh_group_name); mp->state = PIM_MSDP_INACTIVE; mp->fd = -1; - strcpy(mp->last_reset, "-"); + strlcpy(mp->last_reset, "-", sizeof(mp->last_reset)); /* higher IP address is listener */ if (ntohl(mp->local.s_addr) > ntohl(mp->peer.s_addr)) { mp->flags |= PIM_MSDP_PEERF_LISTENER; @@ -1215,9 +1215,9 @@ enum pim_msdp_err pim_msdp_peer_del(struct pim_instance *pim, } /* peer hash and peer list helpers */ -static unsigned int pim_msdp_peer_hash_key_make(void *p) +static unsigned int pim_msdp_peer_hash_key_make(const void *p) { - struct pim_msdp_peer *mp = p; + const struct pim_msdp_peer *mp = p; return (jhash_1word(mp->peer.s_addr, 0)); } diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c index 63688f87e055..2e467502b1fe 100644 --- a/pimd/pim_msg.c +++ b/pimd/pim_msg.c @@ -39,7 +39,7 @@ #include "pim_oil.h" void pim_msg_build_header(uint8_t *pim_msg, size_t pim_msg_size, - uint8_t pim_msg_type) + uint8_t pim_msg_type, bool no_fwd) { struct pim_msg_header *header = (struct pim_msg_header *)pim_msg; @@ -48,6 +48,7 @@ void pim_msg_build_header(uint8_t *pim_msg, size_t pim_msg_size, */ header->ver = PIM_PROTO_VERSION; header->type = pim_msg_type; + header->Nbit = no_fwd; header->reserved = 0; diff --git a/pimd/pim_msg.h b/pimd/pim_msg.h index ad9b5d9c01e6..5f5030396750 100644 --- a/pimd/pim_msg.h +++ b/pimd/pim_msg.h @@ -23,6 +23,8 @@ #include #include "pim_jp_agg.h" + +#define PIM_HDR_LEN sizeof(struct pim_msg_header) /* Number Description ---------- ------------------ @@ -41,11 +43,20 @@ enum pim_msg_address_family { /* * Network Order pim_msg_hdr + * ========================= + * PIM Header definition as per RFC 5059. N bit introduced to indicate + * do-not-forward option in PIM Boot strap Message. + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |PIM Ver| Type |N| Reserved | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct pim_msg_header { uint8_t type : 4; uint8_t ver : 4; - uint8_t reserved; + uint8_t Nbit : 1; /* No Fwd Bit */ + uint8_t reserved : 7; uint16_t checksum; } __attribute__((packed)); @@ -58,7 +69,9 @@ struct pim_encoded_ipv4_unicast { struct pim_encoded_group_ipv4 { uint8_t ne; uint8_t family; - uint8_t reserved; + uint8_t bidir : 1; /* Bidir bit */ + uint8_t reserved : 6; /* Reserved */ + uint8_t sz : 1; /* scope zone bit */ uint8_t mask; struct in_addr addr; } __attribute__((packed)); @@ -88,7 +101,7 @@ struct pim_jp { } __attribute__((packed)); void pim_msg_build_header(uint8_t *pim_msg, size_t pim_msg_size, - uint8_t pim_msg_type); + uint8_t pim_msg_type, bool no_fwd); uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf, struct in_addr addr); uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf, struct in_addr addr); diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index 8a459fe86e57..65ea858cb60b 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -121,6 +121,7 @@ static struct pim_nexthop_cache *pim_nexthop_cache_add(struct pim_instance *pim, */ int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, struct pim_upstream *up, struct rp_info *rp, + bool bsr_track_needed, struct pim_nexthop_cache *out_pnc) { struct pim_nexthop_cache *pnc = NULL; @@ -157,6 +158,9 @@ int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, if (up != NULL) hash_get(pnc->upstream_hash, up, hash_alloc_intern); + if (bsr_track_needed) + pnc->bsr_tracking = true; + if (CHECK_FLAG(pnc->flags, PIM_NEXTHOP_VALID)) { if (out_pnc) memcpy(out_pnc, pnc, sizeof(struct pim_nexthop_cache)); @@ -167,7 +171,8 @@ int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, } void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, - struct pim_upstream *up, struct rp_info *rp) + struct pim_upstream *up, struct rp_info *rp, + bool del_bsr_tracking) { struct pim_nexthop_cache *pnc = NULL; struct pim_nexthop_cache lookup; @@ -208,6 +213,9 @@ void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, if (up) hash_release(pnc->upstream_hash, up); + if (del_bsr_tracking) + pnc->bsr_tracking = false; + if (PIM_DEBUG_PIM_NHT) { char buf[PREFIX_STRLEN]; prefix2str(addr, buf, sizeof buf); @@ -218,7 +226,8 @@ void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, } if (pnc->rp_list->count == 0 - && pnc->upstream_hash->count == 0) { + && pnc->upstream_hash->count == 0 + && pnc->bsr_tracking == false) { pim_sendmsg_zebra_rnh(pim, zclient, pnc, ZEBRA_NEXTHOP_UNREGISTER); @@ -233,6 +242,169 @@ void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, } } +/* Given a source address and a neighbor address, check if the neighbor is one + * of the next hop to reach the source. search from zebra route database + */ +bool pim_nexthop_match(struct pim_instance *pim, struct in_addr addr, + struct in_addr ip_src) +{ + struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM]; + int i = 0; + ifindex_t first_ifindex = 0; + struct interface *ifp = NULL; + struct pim_neighbor *nbr = NULL; + int num_ifindex; + + if (addr.s_addr == INADDR_NONE) + return 0; + + memset(nexthop_tab, 0, + sizeof(struct pim_zlookup_nexthop) * MULTIPATH_NUM); + num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, MULTIPATH_NUM, + addr, PIM_NEXTHOP_LOOKUP_MAX); + if (num_ifindex < 1) { + char addr_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn( + "%s %s: could not find nexthop ifindex for address %s", + __FILE__, __PRETTY_FUNCTION__, addr_str); + return 0; + } + + while (i < num_ifindex) { + first_ifindex = nexthop_tab[i].ifindex; + + ifp = if_lookup_by_index(first_ifindex, pim->vrf_id); + if (!ifp) { + if (PIM_DEBUG_ZEBRA) { + char addr_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", addr, addr_str, + sizeof(addr_str)); + zlog_debug( + "%s %s: could not find interface for ifindex %d (address %s)", + __FILE__, __PRETTY_FUNCTION__, + first_ifindex, addr_str); + } + i++; + continue; + } + + if (!ifp->info) { + if (PIM_DEBUG_ZEBRA) { + char addr_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", addr, addr_str, + sizeof(addr_str)); + zlog_debug( + "%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)", + __PRETTY_FUNCTION__, ifp->name, + first_ifindex, addr_str); + } + i++; + continue; + } + + if (!pim_if_connected_to_source(ifp, addr)) { + nbr = pim_neighbor_find( + ifp, nexthop_tab[i].nexthop_addr.u.prefix4); + if (PIM_DEBUG_PIM_TRACE_DETAIL) + zlog_debug("ifp name: %s, pim nbr: %p", + ifp->name, nbr); + if (!nbr && !if_is_loopback(ifp)) { + i++; + continue; + } + } + + if (nexthop_tab[i].nexthop_addr.u.prefix4.s_addr + == ip_src.s_addr) + return 1; + + i++; + } + + return 0; +} + +/* Given a source address and a neighbor address, check if the neighbor is one + * of the next hop to reach the source. search from pim next hop cache + */ +bool pim_nexthop_match_nht_cache(struct pim_instance *pim, struct in_addr addr, + struct in_addr ip_src) +{ + struct pim_rpf rpf; + ifindex_t first_ifindex; + struct interface *ifp = NULL; + uint8_t nh_iter = 0; + struct pim_neighbor *nbr = NULL; + struct nexthop *nh_node = NULL; + struct pim_nexthop_cache *pnc = NULL; + + memset(&rpf, 0, sizeof(struct pim_rpf)); + rpf.rpf_addr.family = AF_INET; + rpf.rpf_addr.prefixlen = IPV4_MAX_BITLEN; + rpf.rpf_addr.u.prefix4 = addr; + + pnc = pim_nexthop_cache_find(pim, &rpf); + if (!pnc || !pnc->nexthop_num) + return 0; + + for (nh_node = pnc->nexthop; nh_node; nh_node = nh_node->next) { + first_ifindex = nh_node->ifindex; + ifp = if_lookup_by_index(first_ifindex, pim->vrf_id); + if (!ifp) { + if (PIM_DEBUG_PIM_NHT) { + char addr_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", addr, addr_str, + sizeof(addr_str)); + zlog_debug( + "%s %s: could not find interface for ifindex %d (address %s(%s))", + __FILE__, __PRETTY_FUNCTION__, + first_ifindex, addr_str, + pim->vrf->name); + } + nh_iter++; + continue; + } + if (!ifp->info) { + if (PIM_DEBUG_PIM_NHT) { + char addr_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", addr, addr_str, + sizeof(addr_str)); + zlog_debug( + "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)", + __PRETTY_FUNCTION__, ifp->name, + pim->vrf->name, first_ifindex, + addr_str); + } + nh_iter++; + continue; + } + + if (!pim_if_connected_to_source(ifp, addr)) { + nbr = pim_neighbor_find(ifp, nh_node->gate.ipv4); + if (!nbr && !if_is_loopback(ifp)) { + if (PIM_DEBUG_PIM_NHT) + zlog_debug( + "%s: pim nbr not found on input interface %s(%s)", + __PRETTY_FUNCTION__, ifp->name, + pim->vrf->name); + nh_iter++; + continue; + } + } + + if (nh_node->gate.ipv4.s_addr == ip_src.s_addr) + return 1; + } + + return 0; +} + void pim_rp_nexthop_del(struct rp_info *rp_info) { rp_info->rp.source_nexthop.interface = NULL; @@ -275,7 +447,7 @@ static int pim_update_upstream_nh_helper(struct hash_bucket *bucket, void *arg) struct pim_rpf old; old.source_nexthop.interface = up->rpf.source_nexthop.interface; - rpf_result = pim_rpf_update(pim, up, &old, 0); + rpf_result = pim_rpf_update(pim, up, &old); if (rpf_result == PIM_RPF_FAILURE) { pim_upstream_rpf_clear(pim, up); return HASHWALK_CONTINUE; @@ -553,8 +725,7 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, /* This API is used to parse Registered address nexthop update coming from Zebra */ -int pim_parse_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) { struct nexthop *nexthop; struct nexthop *nhlist_head = NULL; @@ -581,7 +752,7 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient, return 0; } - if (command == ZEBRA_NEXTHOP_UPDATE) { + if (cmd == ZEBRA_NEXTHOP_UPDATE) { prefix_copy(&rpf.rpf_addr, &nhr.prefix); pnc = pim_nexthop_cache_find(pim, &rpf); if (!pnc) { diff --git a/pimd/pim_nht.h b/pimd/pim_nht.h index 13bb0fcb55cf..12dbf167d1fc 100644 --- a/pimd/pim_nht.h +++ b/pimd/pim_nht.h @@ -45,15 +45,22 @@ struct pim_nexthop_cache { struct list *rp_list; struct hash *upstream_hash; + /* Ideally this has to be list of scope zone. But for now we can just + * have as a bool variable to say bsr_tracking. + * Later this variable can be changed as a list of scope zones for + * tracking same bsr for multiple scope zones. + */ + bool bsr_tracking; }; -int pim_parse_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id); +int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS); int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, struct pim_upstream *up, struct rp_info *rp, + bool bsr_track_needed, struct pim_nexthop_cache *out_pnc); void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, - struct pim_upstream *up, struct rp_info *rp); + struct pim_upstream *up, struct rp_info *rp, + bool del_bsr_tracking); struct pim_nexthop_cache *pim_nexthop_cache_find(struct pim_instance *pim, struct pim_rpf *rpf); uint32_t pim_compute_ecmp_hash(struct prefix *src, struct prefix *grp); @@ -65,4 +72,9 @@ void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zclient, int pim_ecmp_fib_lookup_if_vif_index(struct pim_instance *pim, struct prefix *src, struct prefix *grp); void pim_rp_nexthop_del(struct rp_info *rp_info); +bool pim_nexthop_match(struct pim_instance *pim, struct in_addr addr, + struct in_addr ip_src); +bool pim_nexthop_match_nht_cache(struct pim_instance *pim, struct in_addr addr, + struct in_addr ip_src); + #endif diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index 5945bc55fd92..22045c2d337c 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -91,9 +91,9 @@ static bool pim_oil_equal(const void *arg1, const void *arg2) return false; } -static unsigned int pim_oil_hash_key(void *arg) +static unsigned int pim_oil_hash_key(const void *arg) { - struct channel_oil *oil = (struct channel_oil *)arg; + const struct channel_oil *oil = arg; return jhash_2words(oil->oil.mfcc_mcastgrp.s_addr, oil->oil.mfcc_origin.s_addr, 0); diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index c5106d01cbe0..d097da6b3eb7 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -32,13 +32,11 @@ */ #define PIM_OIF_FLAG_PROTO_IGMP (1 << 0) #define PIM_OIF_FLAG_PROTO_PIM (1 << 1) -#define PIM_OIF_FLAG_PROTO_SOURCE (1 << 2) -#define PIM_OIF_FLAG_PROTO_STAR (1 << 3) -#define PIM_OIF_FLAG_PROTO_VXLAN (1 << 4) +#define PIM_OIF_FLAG_PROTO_STAR (1 << 2) +#define PIM_OIF_FLAG_PROTO_VXLAN (1 << 3) #define PIM_OIF_FLAG_PROTO_ANY \ (PIM_OIF_FLAG_PROTO_IGMP | PIM_OIF_FLAG_PROTO_PIM \ - | PIM_OIF_FLAG_PROTO_SOURCE | PIM_OIF_FLAG_PROTO_STAR \ - | PIM_OIF_FLAG_PROTO_VXLAN) + | PIM_OIF_FLAG_PROTO_STAR | PIM_OIF_FLAG_PROTO_VXLAN) /* * We need a pimreg vif id from the kernel. @@ -53,10 +51,13 @@ struct channel_counts { unsigned long long lastused; + unsigned long origpktcnt; unsigned long pktcnt; unsigned long oldpktcnt; + unsigned long origbytecnt; unsigned long bytecnt; unsigned long oldbytecnt; + unsigned long origwrong_if; unsigned long wrong_if; unsigned long oldwrong_if; }; diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index 71b0d479281b..12b28ed9af68 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -39,6 +39,7 @@ #include "pim_msg.h" #include "pim_register.h" #include "pim_errors.h" +#include "pim_bsm.h" static int on_pim_hello_send(struct thread *t); static int pim_hello_send(struct interface *ifp, uint16_t holdtime); @@ -148,6 +149,7 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) uint16_t checksum; /* computed checksum */ struct pim_neighbor *neigh; struct pim_msg_header *header; + bool no_fwd; if (len < sizeof(*ip_hdr)) { if (PIM_DEBUG_PIM_PACKETS) @@ -185,6 +187,7 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) /* for computing checksum */ header->checksum = 0; + no_fwd = header->Nbit; if (header->type == PIM_MSG_TYPE_REGISTER) { /* First 8 byte header checksum */ @@ -273,6 +276,11 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) pim_msg + PIM_MSG_HEADER_LEN, pim_msg_len - PIM_MSG_HEADER_LEN); break; + case PIM_MSG_TYPE_BOOTSTRAP: + return pim_bsm_process(ifp, ip_hdr, pim_msg, pim_msg_len, + no_fwd); + break; + default: if (PIM_DEBUG_PIM_PACKETS) { zlog_debug( @@ -634,7 +642,7 @@ static int hello_send(struct interface *ifp, uint16_t holdtime) zassert(pim_msg_size >= PIM_PIM_MIN_LEN); zassert(pim_msg_size <= PIM_PIM_BUFSIZE_WRITE); - pim_msg_build_header(pim_msg, pim_msg_size, PIM_MSG_TYPE_HELLO); + pim_msg_build_header(pim_msg, pim_msg_size, PIM_MSG_TYPE_HELLO, false); if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address, qpim_all_pim_routers_addr, pim_msg, pim_msg_size, diff --git a/pimd/pim_register.c b/pimd/pim_register.c index 431236eebe72..3fe7e8bf640e 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -94,7 +94,7 @@ void pim_register_stop_send(struct interface *ifp, struct prefix_sg *sg, b1length += length; pim_msg_build_header(buffer, b1length + PIM_MSG_REGISTER_STOP_LEN, - PIM_MSG_TYPE_REG_STOP); + PIM_MSG_TYPE_REG_STOP, false); pinfo = (struct pim_interface *)ifp->info; if (!pinfo) { @@ -208,7 +208,7 @@ void pim_register_send(const uint8_t *buf, int buf_size, struct in_addr src, memcpy(b1, (const unsigned char *)buf, buf_size); pim_msg_build_header(buffer, buf_size + PIM_MSG_REGISTER_LEN, - PIM_MSG_TYPE_REGISTER); + PIM_MSG_TYPE_REGISTER, false); ++pinfo->pim_ifstat_reg_send; diff --git a/pimd/pim_routemap.c b/pimd/pim_routemap.c index 4230c127ad31..2de94e9031eb 100644 --- a/pimd/pim_routemap.c +++ b/pimd/pim_routemap.c @@ -36,7 +36,7 @@ static void pim_route_map_delete(const char *rmap_name) route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED); } -static void pim_route_map_event(route_map_event_t event, const char *rmap_name) +static void pim_route_map_event(const char *rmap_name) { route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_ADDED); } diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index 14643743ad8f..35c040c64cec 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -48,6 +48,7 @@ #include "pim_mroute.h" #include "pim_oil.h" #include "pim_zebra.h" +#include "pim_bsm.h" /* Cleanup pim->rpf_hash each node data */ void pim_rp_list_hash_clean(void *data) @@ -372,7 +373,7 @@ void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up) zlog_debug("%s: Deregister upstream %s addr %s with Zebra NHT", __PRETTY_FUNCTION__, up->sg_str, buf); } - pim_delete_tracked_nexthop(pim, &nht_p, up, NULL); + pim_delete_tracked_nexthop(pim, &nht_p, up, NULL, false); } /* Update the upstream address */ @@ -380,7 +381,7 @@ void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up) old_rpf.source_nexthop.interface = up->rpf.source_nexthop.interface; - rpf_result = pim_rpf_update(pim, up, &old_rpf, 1); + rpf_result = pim_rpf_update(pim, up, &old_rpf); if (rpf_result == PIM_RPF_FAILURE) pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__); @@ -406,10 +407,45 @@ void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up) pim_zebra_update_all_interfaces(pim); } -int pim_rp_new(struct pim_instance *pim, const char *rp, - const char *group_range, const char *plist) +int pim_rp_new_config(struct pim_instance *pim, const char *rp, + const char *group_range, const char *plist) { int result = 0; + struct prefix group; + struct in_addr rp_addr; + + if (group_range == NULL) + result = str2prefix("224.0.0.0/4", &group); + else { + result = str2prefix(group_range, &group); + if (result) { + struct prefix temp; + + prefix_copy(&temp, &group); + apply_mask(&temp); + if (!prefix_same(&group, &temp)) + return PIM_GROUP_BAD_ADDR_MASK_COMBO; + } + } + + if (!result) + return PIM_GROUP_BAD_ADDRESS; + + result = inet_pton(AF_INET, rp, &rp_addr); + + if (result <= 0) + return PIM_RP_BAD_ADDRESS; + + result = pim_rp_new(pim, rp_addr, group, plist, RP_SRC_STATIC); + return result; +} + +int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, + struct prefix group, const char *plist, + enum rp_source rp_src_flag) +{ + int result = 0; + char rp[INET_ADDRSTRLEN]; struct rp_info *rp_info; struct rp_info *rp_all; struct prefix group_all; @@ -417,41 +453,19 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, struct rp_info *tmp_rp_info; char buffer[BUFSIZ]; struct prefix nht_p; - struct prefix temp; struct route_node *rn; struct pim_upstream *up; struct listnode *upnode; rp_info = XCALLOC(MTYPE_PIM_RP, sizeof(*rp_info)); - if (group_range == NULL) - result = str2prefix("224.0.0.0/4", &rp_info->group); - else { - result = str2prefix(group_range, &rp_info->group); - if (result) { - prefix_copy(&temp, &rp_info->group); - apply_mask(&temp); - if (!prefix_same(&rp_info->group, &temp)) { - XFREE(MTYPE_PIM_RP, rp_info); - return PIM_GROUP_BAD_ADDR_MASK_COMBO; - } - } - } - - if (!result) { - XFREE(MTYPE_PIM_RP, rp_info); - return PIM_GROUP_BAD_ADDRESS; - } - rp_info->rp.rpf_addr.family = AF_INET; rp_info->rp.rpf_addr.prefixlen = IPV4_MAX_PREFIXLEN; - result = inet_pton(rp_info->rp.rpf_addr.family, rp, - &rp_info->rp.rpf_addr.u.prefix4); + rp_info->rp.rpf_addr.u.prefix4 = rp_addr; + prefix_copy(&rp_info->group, &group); + rp_info->rp_src = rp_src_flag; - if (result <= 0) { - XFREE(MTYPE_PIM_RP, rp_info); - return PIM_RP_BAD_ADDRESS; - } + inet_ntop(AF_INET, &rp_info->rp.rpf_addr.u.prefix4, rp, sizeof(rp)); if (plist) { /* @@ -479,10 +493,10 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, if (rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4.s_addr) { if (tmp_rp_info->plist) - pim_rp_del(pim, rp, NULL, - tmp_rp_info->plist); + pim_rp_del_config(pim, rp, NULL, + tmp_rp_info->plist); else - pim_rp_del( + pim_rp_del_config( pim, rp, prefix2str(&tmp_rp_info->group, buffer, BUFSIZ), @@ -516,7 +530,8 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, && rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4 .s_addr) { - pim_rp_del(pim, rp, NULL, tmp_rp_info->plist); + pim_rp_del_config(pim, rp, NULL, + tmp_rp_info->plist); } } @@ -526,6 +541,7 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, if (prefix_same(&rp_all->group, &rp_info->group) && pim_rpf_addr_is_inaddr_none(&rp_all->rp)) { rp_all->rp.rpf_addr = rp_info->rp.rpf_addr; + rp_all->rp_src = rp_src_flag; XFREE(MTYPE_PIM_RP, rp_info); /* Register addr with Zebra NHT */ @@ -556,8 +572,8 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, grp.family = AF_INET; grp.prefixlen = IPV4_MAX_BITLEN; grp.u.prefix4 = up->sg.grp; - trp_info = pim_rp_find_match_group(pim, - &grp); + trp_info = pim_rp_find_match_group( + pim, &grp); if (trp_info == rp_all) pim_upstream_update(pim, up); } @@ -565,24 +581,27 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, pim_rp_check_interfaces(pim, rp_all); pim_rp_refresh_group_to_rp_mapping(pim); - pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_all, - NULL); + false, NULL); + if (!pim_ecmp_nexthop_lookup(pim, &rp_all->rp.source_nexthop, &nht_p, &rp_all->group, 1)) return PIM_RP_NO_PATH; - return PIM_SUCCESS; } /* * Return if the group is already configured for this RP */ - if (pim_rp_find_exact(pim, rp_info->rp.rpf_addr.u.prefix4, - &rp_info->group)) { + tmp_rp_info = pim_rp_find_exact( + pim, rp_info->rp.rpf_addr.u.prefix4, &rp_info->group); + if (tmp_rp_info) { + if ((tmp_rp_info->rp_src != rp_src_flag) + && (rp_src_flag == RP_SRC_STATIC)) + tmp_rp_info->rp_src = rp_src_flag; XFREE(MTYPE_PIM_RP, rp_info); - return PIM_SUCCESS; + return result; } /* @@ -604,8 +623,20 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, */ if (prefix_same(&rp_info->group, &tmp_rp_info->group)) { + if ((rp_src_flag == RP_SRC_STATIC) + && (tmp_rp_info->rp_src + == RP_SRC_STATIC)) { + XFREE(MTYPE_PIM_RP, rp_info); + return PIM_GROUP_OVERLAP; + } + + result = pim_rp_change( + pim, + rp_info->rp.rpf_addr.u.prefix4, + tmp_rp_info->group, + rp_src_flag); XFREE(MTYPE_PIM_RP, rp_info); - return PIM_GROUP_OVERLAP; + return result; } } } @@ -654,8 +685,7 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, zlog_debug("%s: NHT Register RP addr %s grp %s with Zebra ", __PRETTY_FUNCTION__, buf, buf1); } - - pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, NULL); + pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, false, NULL); if (!pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop, &nht_p, &rp_info->group, 1)) return PIM_RP_NO_PATH; @@ -663,21 +693,12 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, return PIM_SUCCESS; } -int pim_rp_del(struct pim_instance *pim, const char *rp, - const char *group_range, const char *plist) +int pim_rp_del_config(struct pim_instance *pim, const char *rp, + const char *group_range, const char *plist) { struct prefix group; struct in_addr rp_addr; - struct prefix g_all; - struct rp_info *rp_info; - struct rp_info *rp_all; int result; - struct prefix nht_p; - struct route_node *rn; - bool was_plist = false; - struct rp_info *trp_info; - struct pim_upstream *up; - struct listnode *upnode; if (group_range == NULL) result = str2prefix("224.0.0.0/4", &group); @@ -691,6 +712,32 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, if (result <= 0) return PIM_RP_BAD_ADDRESS; + result = pim_rp_del(pim, rp_addr, group, plist, RP_SRC_STATIC); + return result; +} + +int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, + struct prefix group, const char *plist, + enum rp_source rp_src_flag) +{ + struct prefix g_all; + struct rp_info *rp_info; + struct rp_info *rp_all; + struct prefix nht_p; + struct route_node *rn; + bool was_plist = false; + struct rp_info *trp_info; + struct pim_upstream *up; + struct listnode *upnode; + struct bsgrp_node *bsgrp = NULL; + struct bsm_rpinfo *bsrp = NULL; + char grp_str[PREFIX2STR_BUFFER]; + char rp_str[INET_ADDRSTRLEN]; + + if (!inet_ntop(AF_INET, &rp_addr, rp_str, sizeof(rp_str))) + sprintf(rp_str, ""); + prefix2str(&group, grp_str, sizeof(grp_str)); + if (plist) rp_info = pim_rp_find_prefix_list(pim, rp_addr, plist); else @@ -704,6 +751,42 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, was_plist = true; } + if (PIM_DEBUG_TRACE) + zlog_debug("%s: Delete RP %s for the group %s", + __PRETTY_FUNCTION__, rp_str, grp_str); + + /* While static RP is getting deleted, we need to check if dynamic RP + * present for the same group in BSM RP table, then install the dynamic + * RP for the group node into the main rp table + */ + if (rp_src_flag == RP_SRC_STATIC) { + bsgrp = pim_bsm_get_bsgrp_node(&pim->global_scope, &group); + + if (bsgrp) { + bsrp = listnode_head(bsgrp->bsrp_list); + if (bsrp) { + if (PIM_DEBUG_TRACE) { + char bsrp_str[INET_ADDRSTRLEN]; + + if (!inet_ntop(AF_INET, bsrp, bsrp_str, + sizeof(bsrp_str))) + sprintf(bsrp_str, ""); + + zlog_debug("%s: BSM RP %s found for the group %s", + __PRETTY_FUNCTION__, + bsrp_str, grp_str); + } + return pim_rp_change(pim, bsrp->rp_address, + group, RP_SRC_BSR); + } + } else { + if (PIM_DEBUG_TRACE) + zlog_debug( + "%s: BSM RP not found for the group %s", + __PRETTY_FUNCTION__, grp_str); + } + } + /* Deregister addr with Zebra NHT */ nht_p.family = AF_INET; nht_p.prefixlen = IPV4_MAX_BITLEN; @@ -714,7 +797,7 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, zlog_debug("%s: Deregister RP addr %s with Zebra ", __PRETTY_FUNCTION__, buf); } - pim_delete_tracked_nexthop(pim, &nht_p, NULL, rp_info); + pim_delete_tracked_nexthop(pim, &nht_p, NULL, rp_info, false); if (!str2prefix("224.0.0.0/4", &g_all)) return PIM_RP_BAD_ADDRESS; @@ -726,8 +809,9 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, /* Find the upstream (*, G) whose upstream address is * same as the deleted RP */ - if ((up->upstream_addr.s_addr == rp_addr.s_addr) && - (up->sg.src.s_addr == INADDR_ANY)) { + if ((up->upstream_addr.s_addr + == rp_info->rp.rpf_addr.u.prefix4.s_addr) + && (up->sg.src.s_addr == INADDR_ANY)) { struct prefix grp; grp.family = AF_INET; grp.prefixlen = IPV4_MAX_BITLEN; @@ -777,8 +861,9 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, /* Find the upstream (*, G) whose upstream address is same as * the deleted RP */ - if ((up->upstream_addr.s_addr == rp_addr.s_addr) && - (up->sg.src.s_addr == INADDR_ANY)) { + if ((up->upstream_addr.s_addr + == rp_info->rp.rpf_addr.u.prefix4.s_addr) + && (up->sg.src.s_addr == INADDR_ANY)) { struct prefix grp; grp.family = AF_INET; @@ -790,9 +875,9 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, /* RP not found for the group grp */ if (pim_rpf_addr_is_inaddr_none(&trp_info->rp)) { pim_upstream_rpf_clear(pim, up); - pim_rp_set_upstream_addr(pim, - &up->upstream_addr, - up->sg.src, up->sg.grp); + pim_rp_set_upstream_addr( + pim, &up->upstream_addr, up->sg.src, + up->sg.grp); } /* RP found for the group grp */ @@ -805,6 +890,105 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, return PIM_SUCCESS; } +int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr, + struct prefix group, enum rp_source rp_src_flag) +{ + struct prefix nht_p; + struct route_node *rn; + int result = 0; + struct rp_info *rp_info = NULL; + struct pim_upstream *up; + struct listnode *upnode; + + rn = route_node_lookup(pim->rp_table, &group); + if (!rn) { + result = pim_rp_new(pim, new_rp_addr, group, NULL, rp_src_flag); + return result; + } + + rp_info = rn->info; + + if (!rp_info) { + route_unlock_node(rn); + result = pim_rp_new(pim, new_rp_addr, group, NULL, rp_src_flag); + return result; + } + + if (rp_info->rp.rpf_addr.u.prefix4.s_addr == new_rp_addr.s_addr) { + if (rp_info->rp_src != rp_src_flag) { + rp_info->rp_src = rp_src_flag; + route_unlock_node(rn); + return PIM_SUCCESS; + } + } + + /* Deregister old RP addr with Zebra NHT */ + if (rp_info->rp.rpf_addr.u.prefix4.s_addr != INADDR_ANY) { + nht_p.family = AF_INET; + nht_p.prefixlen = IPV4_MAX_BITLEN; + nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; + if (PIM_DEBUG_PIM_NHT_RP) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&nht_p, buf, sizeof(buf)); + zlog_debug("%s: Deregister RP addr %s with Zebra ", + __PRETTY_FUNCTION__, buf); + } + pim_delete_tracked_nexthop(pim, &nht_p, NULL, rp_info, false); + } + + pim_rp_nexthop_del(rp_info); + listnode_delete(pim->rp_list, rp_info); + /* Update the new RP address*/ + rp_info->rp.rpf_addr.u.prefix4 = new_rp_addr; + rp_info->rp_src = rp_src_flag; + rp_info->i_am_rp = 0; + + listnode_add_sort(pim->rp_list, rp_info); + + for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + if (up->sg.src.s_addr == INADDR_ANY) { + struct prefix grp; + struct rp_info *trp_info; + + grp.family = AF_INET; + grp.prefixlen = IPV4_MAX_BITLEN; + grp.u.prefix4 = up->sg.grp; + trp_info = pim_rp_find_match_group(pim, &grp); + + if (trp_info == rp_info) + pim_upstream_update(pim, up); + } + } + + /* Register new RP addr with Zebra NHT */ + nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; + if (PIM_DEBUG_PIM_NHT_RP) { + char buf[PREFIX2STR_BUFFER]; + char buf1[PREFIX2STR_BUFFER]; + + prefix2str(&nht_p, buf, sizeof(buf)); + prefix2str(&rp_info->group, buf1, sizeof(buf1)); + zlog_debug("%s: NHT Register RP addr %s grp %s with Zebra ", + __PRETTY_FUNCTION__, buf, buf1); + } + + pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, false, NULL); + if (!pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop, &nht_p, + &rp_info->group, 1)) { + route_unlock_node(rn); + return PIM_RP_NO_PATH; + } + + pim_rp_check_interfaces(pim, rp_info); + + route_unlock_node(rn); + + pim_rp_refresh_group_to_rp_mapping(pim); + + return result; +} + void pim_rp_setup(struct pim_instance *pim) { struct listnode *node; @@ -819,7 +1003,8 @@ void pim_rp_setup(struct pim_instance *pim) nht_p.prefixlen = IPV4_MAX_BITLEN; nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; - pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, NULL); + pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, false, + NULL); if (!pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop, &nht_p, &rp_info->group, 1)) if (PIM_DEBUG_PIM_NHT_RP) @@ -969,8 +1154,8 @@ struct pim_rpf *pim_rp_g(struct pim_instance *pim, struct in_addr group) "%s: NHT Register RP addr %s grp %s with Zebra", __PRETTY_FUNCTION__, buf, buf1); } - - pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, NULL); + pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, false, + NULL); pim_rpf_set_refresh_time(pim); (void)pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop, &nht_p, &rp_info->group, 1); @@ -1030,6 +1215,9 @@ int pim_rp_config_write(struct pim_instance *pim, struct vty *vty, if (pim_rpf_addr_is_inaddr_none(&rp_info->rp)) continue; + if (rp_info->rp_src == RP_SRC_BSR) + continue; + if (rp_info->plist) vty_out(vty, "%sip pim rp %s prefix-list %s\n", spaces, inet_ntop(AF_INET, @@ -1062,6 +1250,7 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj) struct rp_info *rp_info; struct rp_info *prev_rp_info = NULL; struct listnode *node; + char source[7]; json_object *json = NULL; json_object *json_rp_rows = NULL; @@ -1071,12 +1260,17 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj) json = json_object_new_object(); else vty_out(vty, - "RP address group/prefix-list OIF I am RP\n"); - + "RP address group/prefix-list OIF I am RP Source\n"); for (ALL_LIST_ELEMENTS_RO(pim->rp_list, node, rp_info)) { if (!pim_rpf_addr_is_inaddr_none(&rp_info->rp)) { char buf[48]; + if (rp_info->rp_src == RP_SRC_STATIC) + strcpy(source, "Static"); + else if (rp_info->rp_src == RP_SRC_BSR) + strcpy(source, "BSR"); + else + strcpy(source, "None"); if (uj) { /* * If we have moved on to a new RP then add the @@ -1119,6 +1313,8 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj) json_row, "group", prefix2str(&rp_info->group, buf, 48)); + json_object_string_add(json_row, "source", + source); json_object_array_add(json_rp_rows, json_row); } else { @@ -1143,9 +1339,10 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj) if (rp_info->i_am_rp) vty_out(vty, "yes\n"); else - vty_out(vty, "no\n"); - } + vty_out(vty, "no"); + vty_out(vty, "%14s\n", source); + } prev_rp_info = rp_info; } } @@ -1180,7 +1377,7 @@ void pim_resolve_rp_nh(struct pim_instance *pim, struct pim_neighbor *nbr) nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; memset(&pnc, 0, sizeof(struct pim_nexthop_cache)); if (!pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, - &pnc)) + false, &pnc)) continue; for (nh_node = pnc.nexthop; nh_node; nh_node = nh_node->next) { diff --git a/pimd/pim_rp.h b/pimd/pim_rp.h index 402ec30aba27..6dc26c07a95c 100644 --- a/pimd/pim_rp.h +++ b/pimd/pim_rp.h @@ -27,9 +27,16 @@ #include "pim_iface.h" #include "pim_rpf.h" +enum rp_source { + RP_SRC_NONE = 0, + RP_SRC_STATIC, + RP_SRC_BSR +}; + struct rp_info { struct prefix group; struct pim_rpf rp; + enum rp_source rp_src; int i_am_rp; char *plist; }; @@ -39,10 +46,18 @@ void pim_rp_free(struct pim_instance *pim); void pim_rp_list_hash_clean(void *data); -int pim_rp_new(struct pim_instance *pim, const char *rp, const char *group, - const char *plist); -int pim_rp_del(struct pim_instance *pim, const char *rp, const char *group, - const char *plist); +int pim_rp_new_config(struct pim_instance *pim, const char *rp, + const char *group, const char *plist); +int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, + struct prefix group, const char *plist, + enum rp_source rp_src_flag); +int pim_rp_del_config(struct pim_instance *pim, const char *rp, + const char *group, const char *plist); +int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, + struct prefix group, const char *plist, + enum rp_source rp_src_flag); +int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr, + struct prefix group, enum rp_source rp_src_flag); void pim_rp_prefix_list_update(struct pim_instance *pim, struct prefix_list *plist); diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index afe3886aa5b9..b1a2e717d6ab 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -195,8 +195,7 @@ static int nexthop_mismatch(const struct pim_nexthop *nh1, } enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, - struct pim_upstream *up, struct pim_rpf *old, - uint8_t is_new) + struct pim_upstream *up, struct pim_rpf *old) { struct pim_rpf *rpf = &up->rpf; struct pim_rpf saved; @@ -216,14 +215,6 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, saved.source_nexthop = rpf->source_nexthop; saved.rpf_addr = rpf->rpf_addr; - if (is_new && PIM_DEBUG_ZEBRA) { - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", up->upstream_addr, source_str, - sizeof(source_str)); - zlog_debug("%s: NHT Register upstream %s addr %s with Zebra.", - __PRETTY_FUNCTION__, up->sg_str, source_str); - } - /* Register addr with Zebra NHT */ nht_p.family = AF_INET; nht_p.prefixlen = IPV4_MAX_BITLEN; nht_p.u.prefix4.s_addr = up->upstream_addr.s_addr; @@ -238,7 +229,7 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, if ((up->sg.src.s_addr == INADDR_ANY && I_am_RP(pim, up->sg.grp)) || PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) neigh_needed = FALSE; - pim_find_or_track_nexthop(pim, &nht_p, up, NULL, NULL); + pim_find_or_track_nexthop(pim, &nht_p, up, NULL, false, NULL); if (!pim_ecmp_nexthop_lookup(pim, &rpf->source_nexthop, &src, &grp, neigh_needed)) return PIM_RPF_FAILURE; @@ -426,9 +417,9 @@ int pim_rpf_is_same(struct pim_rpf *rpf1, struct pim_rpf *rpf2) return 0; } -unsigned int pim_rpf_hash_key(void *arg) +unsigned int pim_rpf_hash_key(const void *arg) { - struct pim_nexthop_cache *r = (struct pim_nexthop_cache *)arg; + const struct pim_nexthop_cache *r = arg; return jhash_1word(r->rpf.rpf_addr.u.prefix4.s_addr, 0); } diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h index 57bb22674fa6..df7b249289ee 100644 --- a/pimd/pim_rpf.h +++ b/pimd/pim_rpf.h @@ -56,14 +56,14 @@ enum pim_rpf_result { PIM_RPF_OK = 0, PIM_RPF_CHANGED, PIM_RPF_FAILURE }; struct pim_upstream; -unsigned int pim_rpf_hash_key(void *arg); +unsigned int pim_rpf_hash_key(const void *arg); bool pim_rpf_equal(const void *arg1, const void *arg2); bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, struct in_addr addr, int neighbor_needed); enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, - struct pim_upstream *up, struct pim_rpf *old, - uint8_t is_new); + struct pim_upstream *up, + struct pim_rpf *old); void pim_upstream_rpf_clear(struct pim_instance *pim, struct pim_upstream *up); int pim_rpf_addr_is_inaddr_none(struct pim_rpf *rpf); diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index b708e86a2050..50c68c66bdc2 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -237,7 +237,7 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, zlog_debug("%s: Deregister upstream %s addr %s with Zebra NHT", __PRETTY_FUNCTION__, up->sg_str, buf); } - pim_delete_tracked_nexthop(pim, &nht_p, up, NULL); + pim_delete_tracked_nexthop(pim, &nht_p, up, NULL, false); } XFREE(MTYPE_PIM_UPSTREAM, up); @@ -749,7 +749,7 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, up->channel_oil = pim_channel_oil_add(pim, &up->sg, MAXVIFS); } else { - rpf_result = pim_rpf_update(pim, up, NULL, 1); + rpf_result = pim_rpf_update(pim, up, NULL); if (rpf_result == PIM_RPF_FAILURE) { if (PIM_DEBUG_TRACE) zlog_debug( @@ -1181,8 +1181,16 @@ struct pim_upstream *pim_upstream_keep_alive_timer_proc( "kat expired on %s[%s]; remove stream reference", up->sg_str, pim->vrf->name); PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up->flags); - up = pim_upstream_del(pim, up, __PRETTY_FUNCTION__); - } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags)) { + + /* Return if upstream entry got deleted.*/ + if (!pim_upstream_del(pim, up, __PRETTY_FUNCTION__)) + return NULL; + } + /* upstream reference would have been added to track the local + * membership if it is LHR. We have to clear it when KAT expires. + * Otherwise would result in stale entry with uncleared ref count. + */ + if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags)) { struct pim_upstream *parent = up->parent; PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up->flags); @@ -1391,23 +1399,24 @@ const char *pim_upstream_state2str(enum pim_upstream_state join_state) return "Unknown"; } -const char *pim_reg_state2str(enum pim_reg_state reg_state, char *state_str) +const char *pim_reg_state2str(enum pim_reg_state reg_state, char *state_str, + size_t state_str_len) { switch (reg_state) { case PIM_REG_NOINFO: - strcpy(state_str, "RegNoInfo"); + strlcpy(state_str, "RegNoInfo", state_str_len); break; case PIM_REG_JOIN: - strcpy(state_str, "RegJoined"); + strlcpy(state_str, "RegJoined", state_str_len); break; case PIM_REG_JOIN_PENDING: - strcpy(state_str, "RegJoinPend"); + strlcpy(state_str, "RegJoinPend", state_str_len); break; case PIM_REG_PRUNE: - strcpy(state_str, "RegPrune"); + strlcpy(state_str, "RegPrune", state_str_len); break; default: - strcpy(state_str, "RegUnknown"); + strlcpy(state_str, "RegUnknown", state_str_len); } return state_str; } @@ -1424,7 +1433,7 @@ static int pim_upstream_register_stop_timer(struct thread *t) char state_str[PIM_REG_STATE_STR_LEN]; zlog_debug("%s: (S,G)=%s[%s] upstream register stop timer %s", __PRETTY_FUNCTION__, up->sg_str, pim->vrf->name, - pim_reg_state2str(up->reg_state, state_str)); + pim_reg_state2str(up->reg_state, state_str, sizeof(state_str))); } switch (up->reg_state) { @@ -1616,14 +1625,14 @@ void pim_upstream_find_new_rpf(struct pim_instance *pim) zlog_debug( "%s: Upstream %s without a path to send join, checking", __PRETTY_FUNCTION__, up->sg_str); - pim_rpf_update(pim, up, NULL, 1); + pim_rpf_update(pim, up, NULL); } } } -unsigned int pim_upstream_hash_key(void *arg) +unsigned int pim_upstream_hash_key(const void *arg) { - struct pim_upstream *up = (struct pim_upstream *)arg; + const struct pim_upstream *up = arg; return jhash_2words(up->sg.src.s_addr, up->sg.grp.s_addr, 0); } diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index 13a3dcdf8ca7..02ae99829057 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -283,7 +283,8 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, const char *pim_upstream_state2str(enum pim_upstream_state join_state); #define PIM_REG_STATE_STR_LEN 12 -const char *pim_reg_state2str(enum pim_reg_state state, char *state_str); +const char *pim_reg_state2str(enum pim_reg_state state, char *state_str, + size_t state_str_len); int pim_upstream_inherited_olist_decide(struct pim_instance *pim, struct pim_upstream *up); @@ -308,7 +309,7 @@ void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim, void pim_upstream_spt_prefix_list_update(struct pim_instance *pim, struct prefix_list *pl); -unsigned int pim_upstream_hash_key(void *arg); +unsigned int pim_upstream_hash_key(const void *arg); bool pim_upstream_equal(const void *arg1, const void *arg2); struct pim_upstream *pim_upstream_keep_alive_timer_proc( struct pim_upstream *up); diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 2654ebc588b6..468cd56ee55b 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -39,6 +39,7 @@ #include "pim_msdp.h" #include "pim_ssm.h" #include "pim_bfd.h" +#include "pim_bsm.h" #include "pim_vxlan.h" int pim_debug_config_write(struct vty *vty) @@ -120,6 +121,11 @@ int pim_debug_config_write(struct vty *vty) ++writes; } + if (PIM_DEBUG_BSM) { + vty_out(vty, "debug pim bsm\n"); + ++writes; + } + if (PIM_DEBUG_VXLAN) { vty_out(vty, "debug pim vxlan\n"); ++writes; @@ -344,6 +350,24 @@ int pim_interface_config_write(struct vty *vty) ++writes; } + /* IF ip igmp last-member_query-count */ + if (pim_ifp->igmp_last_member_query_count + != IGMP_DEFAULT_ROBUSTNESS_VARIABLE) { + vty_out(vty, + " ip igmp last-member-query-count %d\n", + pim_ifp->igmp_last_member_query_count); + ++writes; + } + + /* IF ip igmp last-member_query-interval */ + if (pim_ifp->igmp_specific_query_max_response_time_dsec + != IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC) { + vty_out(vty, + " ip igmp last-member-query-interval %d\n", + pim_ifp->igmp_specific_query_max_response_time_dsec); + ++writes; + } + /* IF ip igmp join */ if (pim_ifp->igmp_join_list) { struct listnode *node; @@ -383,7 +407,10 @@ int pim_interface_config_write(struct vty *vty) writes += pim_static_write_mroute(pim, vty, ifp); + pim_bsm_write_config(vty, ifp); + ++writes; pim_bfd_write_config(vty, ifp); + ++writes; } vty_endframe(vty, "!\n"); ++writes; diff --git a/pimd/pim_vxlan.c b/pimd/pim_vxlan.c index af76c6d732cb..d2648fad50a1 100644 --- a/pimd/pim_vxlan.c +++ b/pimd/pim_vxlan.c @@ -245,7 +245,7 @@ static void pim_vxlan_orig_mr_up_del(struct pim_vxlan_sg *vxlan_sg) * for nht */ if (up) - pim_rpf_update(vxlan_sg->pim, up, NULL, 1 /* is_new */); + pim_rpf_update(vxlan_sg->pim, up, NULL); } } @@ -344,7 +344,7 @@ static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg) nht_p.prefixlen = IPV4_MAX_BITLEN; nht_p.u.prefix4 = up->upstream_addr; pim_delete_tracked_nexthop(vxlan_sg->pim, - &nht_p, up, NULL); + &nht_p, up, NULL, false); } pim_upstream_ref(up, flags, __PRETTY_FUNCTION__); vxlan_sg->up = up; @@ -623,9 +623,9 @@ static void pim_vxlan_term_mr_del(struct pim_vxlan_sg *vxlan_sg) } /************************** vxlan SG cache management ************************/ -static unsigned int pim_vxlan_sg_hash_key_make(void *p) +static unsigned int pim_vxlan_sg_hash_key_make(const void *p) { - struct pim_vxlan_sg *vxlan_sg = p; + const struct pim_vxlan_sg *vxlan_sg = p; return (jhash_2words(vxlan_sg->sg.src.s_addr, vxlan_sg->sg.grp.s_addr, 0)); diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index aeaea7d69f08..25ac307ac43d 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -54,8 +54,7 @@ static struct zclient *zclient = NULL; /* Router-id update message from zebra. */ -static int pim_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct prefix router_id; @@ -64,8 +63,7 @@ static int pim_router_id_update_zebra(int command, struct zclient *zclient, return 0; } -static int pim_zebra_if_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct pim_instance *pim; @@ -126,8 +124,7 @@ static int pim_zebra_if_add(int command, struct zclient *zclient, return 0; } -static int pim_zebra_if_del(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_del(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct pim_instance *pim; @@ -166,8 +163,7 @@ static int pim_zebra_if_del(int command, struct zclient *zclient, return 0; } -static int pim_zebra_if_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_state_up(ZAPI_CALLBACK_ARGS) { struct pim_instance *pim; struct interface *ifp; @@ -235,8 +231,7 @@ static int pim_zebra_if_state_up(int command, struct zclient *zclient, return 0; } -static int pim_zebra_if_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_state_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -280,8 +275,7 @@ static int pim_zebra_if_state_down(int command, struct zclient *zclient, return 0; } -static int pim_zebra_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t new_vrf_id; @@ -326,8 +320,7 @@ static void dump_if_address(struct interface *ifp) } #endif -static int pim_zebra_if_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; struct prefix *p; @@ -342,7 +335,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, will add address to interface list by calling connected_add_by_prefix() */ - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; @@ -406,8 +399,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, return 0; } -static int pim_zebra_if_address_del(int command, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_address_del(ZAPI_CALLBACK_ARGS) { struct connected *c; struct prefix *p; @@ -426,7 +418,7 @@ static int pim_zebra_if_address_del(int command, struct zclient *client, will remove address from interface list by calling connected_delete_by_prefix() */ - c = zebra_interface_address_read(command, client->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; @@ -554,8 +546,7 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, pim_upstream_update_join_desired(pim, up); } -static int pim_zebra_vxlan_sg_proc(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_vxlan_sg_proc(ZAPI_CALLBACK_ARGS) { struct stream *s; struct pim_instance *pim; @@ -577,11 +568,11 @@ static int pim_zebra_vxlan_sg_proc(int command, struct zclient *zclient, pim_str_sg_set(&sg, sg_str); zlog_debug("%u:recv SG %s %s", vrf_id, - (command == ZEBRA_VXLAN_SG_ADD)?"add":"del", + (cmd == ZEBRA_VXLAN_SG_ADD)?"add":"del", sg_str); } - if (command == ZEBRA_VXLAN_SG_ADD) + if (cmd == ZEBRA_VXLAN_SG_ADD) pim_vxlan_sg_add(pim, &sg); else pim_vxlan_sg_del(pim, &sg); @@ -789,7 +780,7 @@ void sched_rpf_cache_refresh(struct pim_instance *pim) static void pim_zebra_connected(struct zclient *zclient) { /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, router->vrf_id); zclient_send_reg_requests(zclient, router->vrf_id); } @@ -1305,8 +1296,16 @@ void pim_forward_stop(struct pim_ifchannel *ch, bool install_it) install_it, up->channel_oil->installed); } - pim_channel_del_oif(up->channel_oil, ch->interface, - PIM_OIF_FLAG_PROTO_PIM); + /* + * If a channel is being removed, check to see if we still need + * to inherit the interface. If so make sure it is added in + */ + if (pim_upstream_evaluate_join_desired_interface(up, ch, ch->parent)) + pim_channel_add_oif(up->channel_oil, ch->interface, + PIM_OIF_FLAG_PROTO_PIM); + else + pim_channel_del_oif(up->channel_oil, ch->interface, + PIM_OIF_FLAG_PROTO_PIM); if (install_it && !up->channel_oil->installed) pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index 6d93a40b913d..77526281d18c 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -37,8 +37,10 @@ #include "pim_zlookup.h" static struct zclient *zlookup = NULL; +struct thread *zlookup_read; static void zclient_lookup_sched(struct zclient *zlookup, int delay); +static int zclient_lookup_read_pipe(struct thread *thread); /* Connect to zebra for nexthop lookup. */ static int zclient_lookup_connect(struct thread *t) @@ -65,6 +67,8 @@ static int zclient_lookup_connect(struct thread *t) return -1; } + thread_add_timer(router->master, zclient_lookup_read_pipe, zlookup, 60, + &zlookup_read); return 0; } @@ -113,6 +117,7 @@ static void zclient_lookup_failed(struct zclient *zlookup) void zclient_lookup_free(void) { + thread_cancel(zlookup_read); zclient_stop(zlookup); zclient_free(zlookup); zlookup = NULL; @@ -357,6 +362,20 @@ static int zclient_lookup_nexthop_once(struct pim_instance *pim, return zclient_read_nexthop(pim, zlookup, nexthop_tab, tab_size, addr); } +int zclient_lookup_read_pipe(struct thread *thread) +{ + struct zclient *zlookup = THREAD_ARG(thread); + struct pim_instance *pim = pim_get_pim_instance(VRF_DEFAULT); + struct pim_zlookup_nexthop nexthop_tab[10]; + struct in_addr l = {.s_addr = INADDR_ANY}; + + zclient_lookup_nexthop_once(pim, nexthop_tab, 10, l); + thread_add_timer(router->master, zclient_lookup_read_pipe, zlookup, 60, + &zlookup_read); + + return 1; +} + int zclient_lookup_nexthop(struct pim_instance *pim, struct pim_zlookup_nexthop nexthop_tab[], const int tab_size, struct in_addr addr, diff --git a/pimd/pimd.h b/pimd/pimd.h index 2f2a87037139..175936e0a756 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -114,6 +114,7 @@ #define PIM_MASK_PIM_NHT_RP (1 << 24) #define PIM_MASK_MTRACE (1 << 25) #define PIM_MASK_VXLAN (1 << 26) +#define PIM_MASK_BSM_PROC (1 << 27) /* Remember 32 bits!!! */ /* PIM error codes */ @@ -153,23 +154,25 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DEBUG_PIM_EVENTS (router->debugs & PIM_MASK_PIM_EVENTS) #define PIM_DEBUG_PIM_EVENTS_DETAIL \ - (router->debugs & PIM_MASK_PIM_EVENTS_DETAIL) + (router->debugs & (PIM_MASK_PIM_EVENTS_DETAIL | PIM_MASK_PIM_EVENTS)) #define PIM_DEBUG_PIM_PACKETS (router->debugs & PIM_MASK_PIM_PACKETS) #define PIM_DEBUG_PIM_PACKETDUMP_SEND \ (router->debugs & PIM_MASK_PIM_PACKETDUMP_SEND) #define PIM_DEBUG_PIM_PACKETDUMP_RECV \ (router->debugs & PIM_MASK_PIM_PACKETDUMP_RECV) #define PIM_DEBUG_PIM_TRACE (router->debugs & PIM_MASK_PIM_TRACE) -#define PIM_DEBUG_PIM_TRACE_DETAIL (router->debugs & PIM_MASK_PIM_TRACE_DETAIL) +#define PIM_DEBUG_PIM_TRACE_DETAIL \ + (router->debugs & (PIM_MASK_PIM_TRACE_DETAIL | PIM_MASK_PIM_TRACE)) #define PIM_DEBUG_IGMP_EVENTS (router->debugs & PIM_MASK_IGMP_EVENTS) #define PIM_DEBUG_IGMP_PACKETS (router->debugs & PIM_MASK_IGMP_PACKETS) #define PIM_DEBUG_IGMP_TRACE (router->debugs & PIM_MASK_IGMP_TRACE) #define PIM_DEBUG_IGMP_TRACE_DETAIL \ - (router->debugs & PIM_MASK_IGMP_TRACE_DETAIL) + (router->debugs & (PIM_MASK_IGMP_TRACE_DETAIL | PIM_MASK_IGMP_TRACE)) #define PIM_DEBUG_ZEBRA (router->debugs & PIM_MASK_ZEBRA) #define PIM_DEBUG_SSMPINGD (router->debugs & PIM_MASK_SSMPINGD) #define PIM_DEBUG_MROUTE (router->debugs & PIM_MASK_MROUTE) -#define PIM_DEBUG_MROUTE_DETAIL (router->debugs & PIM_MASK_MROUTE_DETAIL) +#define PIM_DEBUG_MROUTE_DETAIL \ + (router->debugs & (PIM_MASK_MROUTE_DETAIL | PIM_MASK_MROUTE)) #define PIM_DEBUG_PIM_HELLO (router->debugs & PIM_MASK_PIM_HELLO) #define PIM_DEBUG_PIM_J_P (router->debugs & PIM_MASK_PIM_J_P) #define PIM_DEBUG_PIM_REG (router->debugs & PIM_MASK_PIM_REG) @@ -178,15 +181,17 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DEBUG_MSDP_PACKETS (router->debugs & PIM_MASK_MSDP_PACKETS) #define PIM_DEBUG_MSDP_INTERNAL (router->debugs & PIM_MASK_MSDP_INTERNAL) #define PIM_DEBUG_PIM_NHT (router->debugs & PIM_MASK_PIM_NHT) -#define PIM_DEBUG_PIM_NHT_DETAIL (router->debugs & PIM_MASK_PIM_NHT_DETAIL) +#define PIM_DEBUG_PIM_NHT_DETAIL \ + (router->debugs & (PIM_MASK_PIM_NHT_DETAIL | PIM_MASK_PIM_NHT)) #define PIM_DEBUG_PIM_NHT_RP (router->debugs & PIM_MASK_PIM_NHT_RP) #define PIM_DEBUG_MTRACE (router->debugs & PIM_MASK_MTRACE) #define PIM_DEBUG_VXLAN (router->debugs & PIM_MASK_VXLAN) +#define PIM_DEBUG_BSM (router->debugs & PIM_MASK_BSM_PROC) #define PIM_DEBUG_EVENTS \ (router->debugs \ & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS \ - | PIM_MASK_MSDP_EVENTS)) + | PIM_MASK_MSDP_EVENTS | PIM_MASK_BSM_PROC)) #define PIM_DEBUG_PACKETS \ (router->debugs \ & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS \ @@ -212,6 +217,7 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DO_DEBUG_SSMPINGD (router->debugs |= PIM_MASK_SSMPINGD) #define PIM_DO_DEBUG_MROUTE (router->debugs |= PIM_MASK_MROUTE) #define PIM_DO_DEBUG_MROUTE_DETAIL (router->debugs |= PIM_MASK_MROUTE_DETAIL) +#define PIM_DO_DEBUG_BSM (router->debugs |= PIM_MASK_BSM_PROC) #define PIM_DO_DEBUG_PIM_HELLO (router->debugs |= PIM_MASK_PIM_HELLO) #define PIM_DO_DEBUG_PIM_J_P (router->debugs |= PIM_MASK_PIM_J_P) #define PIM_DO_DEBUG_PIM_REG (router->debugs |= PIM_MASK_PIM_REG) @@ -253,6 +259,7 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DONT_DEBUG_PIM_NHT_RP (router->debugs &= ~PIM_MASK_PIM_NHT_RP) #define PIM_DONT_DEBUG_MTRACE (router->debugs &= ~PIM_MASK_MTRACE) #define PIM_DONT_DEBUG_VXLAN (router->debugs &= ~PIM_MASK_VXLAN) +#define PIM_DONT_DEBUG_BSM (router->debugs &= ~PIM_MASK_BSM_PROC) void pim_router_init(void); void pim_router_terminate(void); diff --git a/pimd/subdir.am b/pimd/subdir.am index 7f4810722b85..240b62804fb7 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -17,6 +17,7 @@ pimd_libpim_a_SOURCES = \ pimd/pim_assert.c \ pimd/pim_bfd.c \ pimd/pim_br.c \ + pimd/pim_bsm.c \ pimd/pim_cmd.c \ pimd/pim_errors.c \ pimd/pim_hello.c \ @@ -68,6 +69,7 @@ noinst_HEADERS += \ pimd/pim_assert.h \ pimd/pim_bfd.h \ pimd/pim_br.h \ + pimd/pim_bsm.h \ pimd/pim_cmd.h \ pimd/pim_errors.h \ pimd/pim_hello.h \ diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 36f92598651c..27042e197ce5 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -24,6 +24,7 @@ %{!?with_pam: %global with_pam 0 } %{!?with_pbrd: %global with_pbrd 1 } %{!?with_pimd: %global with_pimd 1 } +%{!?with_vrrpd: %global with_vrrpd 1 } %{!?with_rpki: %global with_rpki 0 } %{!?with_rtadv: %global with_rtadv 1 } %{!?with_watchfrr: %global with_watchfrr 1 } @@ -43,12 +44,6 @@ # defines for configure %define rundir %{_localstatedir}/run/%{name} -# define for sphinx-build binary -%if 0%{?rhel} && 0%{?rhel} < 7 - %define sphinx sphinx-build2.7 -%else - %define sphinx sphinx-build -%endif ############################################################################ #### Version String tweak @@ -124,6 +119,12 @@ %define daemon_babeld "" %endif +%if %{with_vrrpd} + %define daemon_vrrpd vrrpd +%else + %define daemon_vrrpd "" +%endif + %if %{with_watchfrr} %define daemon_watchfrr watchfrr %else @@ -136,7 +137,7 @@ %define daemon_bfdd "" %endif -%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd} +%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd} %{daemon_vrrpd} #release sub-revision (the two digits after the CONFDATE) %{!?release_rev: %global release_rev 01 } @@ -306,6 +307,11 @@ developing OSPF-API and frr applications. %else --disable-babeld \ %endif +%if %{with_vrrpd} + --enable-vrrpd \ +%else + --disable-vrrpd \ +%endif %if %{with_pam} --with-libpam \ %endif @@ -348,7 +354,7 @@ developing OSPF-API and frr applications. %else --disable-bfdd \ %endif - SPHINXBUILD=%{sphinx} + # end make %{?_smp_mflags} MAKEINFO="makeinfo --no-split" @@ -461,6 +467,9 @@ zebra_spec_add_service isisd 2608/tcp "ISISd vty" zebra_spec_add_service bfdd 2617/tcp "BFDd vty" %endif zebra_spec_add_service fabricd 2618/tcp "Fabricd vty" +%if %{with_vrrpd} + zebra_spec_add_service vrrpd 2619/tcp "VRRPd vty" +%endif %if "%{initsystem}" == "systemd" for daemon in %all_daemons ; do @@ -596,6 +605,9 @@ fi %if %{with_pbrd} %{_sbindir}/pbrd %endif +%if %{with_vrrpd} + %{_sbindir}/vrrpd +%endif %{_sbindir}/isisd %{_sbindir}/fabricd %if %{with_ldpd} diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index b909cbcb2b81..634fee0b3025 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -344,8 +344,7 @@ int if_check_address(struct rip *rip, struct in_addr addr) } /* Inteface link down message processing. */ -int rip_interface_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int rip_interface_down(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -372,8 +371,7 @@ int rip_interface_down(int command, struct zclient *zclient, } /* Inteface link up message processing */ -int rip_interface_up(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +int rip_interface_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -405,8 +403,7 @@ int rip_interface_up(int command, struct zclient *zclient, zebra_size_t length, } /* Inteface addition message from zebra. */ -int rip_interface_add(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +int rip_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -436,8 +433,7 @@ int rip_interface_add(int command, struct zclient *zclient, zebra_size_t length, return 0; } -int rip_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int rip_interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -468,8 +464,7 @@ int rip_interface_delete(int command, struct zclient *zclient, } /* VRF update for an interface. */ -int rip_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int rip_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t new_vrf_id; @@ -615,8 +610,7 @@ static void rip_apply_address_add(struct connected *ifc) 0); } -int rip_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int rip_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct prefix *p; @@ -669,8 +663,7 @@ static void rip_apply_address_del(struct connected *ifc) &address, ifc->ifp->ifindex); } -int rip_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int rip_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct prefix *p; diff --git a/ripd/rip_interface.h b/ripd/rip_interface.h index 303be0315d96..6befda0e2860 100644 --- a/ripd/rip_interface.h +++ b/ripd/rip_interface.h @@ -30,8 +30,7 @@ extern int rip_interface_address_add(int, struct zclient *, zebra_size_t, vrf_id_t); extern int rip_interface_address_delete(int, struct zclient *, zebra_size_t, vrf_id_t); -extern int rip_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id); +extern int rip_interface_vrf_update(ZAPI_CALLBACK_ARGS); extern void rip_interface_sync(struct interface *ifp); #endif /* _QUAGGA_RIP_INTERFACE_H */ diff --git a/ripd/rip_main.c b/ripd/rip_main.c index 65da51f83abd..773cb1d0fe6e 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -41,10 +41,7 @@ #include "ripd/rip_errors.h" /* ripd options. */ -#if CONFDATE > 20190521 - CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") -#endif -static struct option longopts[] = {{"retain", no_argument, NULL, 'r'}, {0}}; +static struct option longopts[] = {{0}}; /* ripd privileges */ zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN}; @@ -129,10 +126,7 @@ FRR_DAEMON_INFO(ripd, RIP, .vty_port = RIP_VTY_PORT, .privs = &ripd_privs, .yang_modules = ripd_yang_modules, .n_yang_modules = array_size(ripd_yang_modules), ) -#if CONFDATE > 20190521 -CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") -#endif -#define DEPRECATED_OPTIONS "r" +#define DEPRECATED_OPTIONS "" /* Main routine of ripd. */ int main(int argc, char **argv) diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 4f0df122329c..0c88cb202bd4 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -118,8 +118,7 @@ void rip_zebra_ipv4_delete(struct rip *rip, struct route_node *rp) } /* Zebra route add and delete treatment. */ -static int rip_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int rip_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct rip *rip; struct zapi_route api; @@ -138,11 +137,11 @@ static int rip_zebra_read_route(int command, struct zclient *zclient, nh.ifindex = api.nexthops[0].ifindex; /* Then fetch IPv4 prefixes. */ - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) rip_redistribute_add(rip, api.type, RIP_ROUTE_REDISTRIBUTE, (struct prefix_ipv4 *)&api.prefix, &nh, api.metric, api.distance, api.tag); - else if (command == ZEBRA_REDISTRIBUTE_ROUTE_DEL) + else if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) rip_redistribute_delete(rip, api.type, RIP_ROUTE_REDISTRIBUTE, (struct prefix_ipv4 *)&api.prefix, nh.ifindex); diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index e35652b1acd0..d83f4d279145 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -184,7 +184,8 @@ static int ripng_if_down(struct interface *ifp) zlog_debug("turn off %s", ifp->name); /* Leave from multicast group. */ - ripng_multicast_leave(ifp, ripng->sock); + if (ripng) + ripng_multicast_leave(ifp, ripng->sock); ri->running = 0; } @@ -193,8 +194,7 @@ static int ripng_if_down(struct interface *ifp) } /* Inteface link up message processing. */ -int ripng_interface_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_up(ZAPI_CALLBACK_ARGS) { struct stream *s; struct interface *ifp; @@ -228,8 +228,7 @@ int ripng_interface_up(int command, struct zclient *zclient, } /* Inteface link down message processing. */ -int ripng_interface_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_down(ZAPI_CALLBACK_ARGS) { struct stream *s; struct interface *ifp; @@ -255,8 +254,7 @@ int ripng_interface_down(int command, struct zclient *zclient, } /* Inteface addition message from zebra. */ -int ripng_interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -281,8 +279,7 @@ int ripng_interface_add(int command, struct zclient *zclient, return 0; } -int ripng_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -313,8 +310,7 @@ int ripng_interface_delete(int command, struct zclient *zclient, } /* VRF update for an interface. */ -int ripng_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t new_vrf_id; @@ -383,8 +379,7 @@ static void ripng_apply_address_add(struct connected *ifc) ifc->ifp->ifindex, NULL, 0); } -int ripng_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; struct prefix *p; @@ -450,8 +445,7 @@ static void ripng_apply_address_del(struct connected *ifc) ifc->ifp->ifindex); } -int ripng_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct prefix *p; diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index c755bd83ce5c..4b027019c097 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -41,10 +41,7 @@ #include "ripngd/ripngd.h" /* RIPngd options. */ -#if CONFDATE > 20190521 - CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") -#endif -struct option longopts[] = {{"retain", no_argument, NULL, 'r'}, {0}}; +struct option longopts[] = {{0}}; /* ripngd privileges */ zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN}; @@ -132,10 +129,7 @@ FRR_DAEMON_INFO(ripngd, RIPNG, .vty_port = RIPNG_VTY_PORT, .yang_modules = ripngd_yang_modules, .n_yang_modules = array_size(ripngd_yang_modules), ) -#if CONFDATE > 20190521 -CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") -#endif -#define DEPRECATED_OPTIONS "r" +#define DEPRECATED_OPTIONS "" /* RIPngd main routine. */ int main(int argc, char **argv) diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index cf60de2de914..a557a90c829a 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -113,8 +113,7 @@ void ripng_zebra_ipv6_delete(struct ripng *ripng, struct agg_node *rp) } /* Zebra route add and delete treatment. */ -static int ripng_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ripng_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct ripng *ripng; struct zapi_route api; @@ -138,7 +137,7 @@ static int ripng_zebra_read_route(int command, struct zclient *zclient, nexthop = api.nexthops[0].gate.ipv6; ifindex = api.nexthops[0].ifindex; - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) ripng_redistribute_add(ripng, api.type, RIPNG_ROUTE_REDISTRIBUTE, (struct prefix_ipv6 *)&api.prefix, diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 411689a7a720..71bc43049ab0 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -2011,26 +2011,26 @@ static char *ripng_route_subtype_print(struct ripng_info *rinfo) memset(str, 0, 3); if (rinfo->suppress) - strcat(str, "S"); + strlcat(str, "S", sizeof(str)); switch (rinfo->sub_type) { case RIPNG_ROUTE_RTE: - strcat(str, "n"); + strlcat(str, "n", sizeof(str)); break; case RIPNG_ROUTE_STATIC: - strcat(str, "s"); + strlcat(str, "s", sizeof(str)); break; case RIPNG_ROUTE_DEFAULT: - strcat(str, "d"); + strlcat(str, "d", sizeof(str)); break; case RIPNG_ROUTE_REDISTRIBUTE: - strcat(str, "r"); + strlcat(str, "r", sizeof(str)); break; case RIPNG_ROUTE_INTERFACE: - strcat(str, "i"); + strlcat(str, "i", sizeof(str)); break; default: - strcat(str, "?"); + strlcat(str, "?", sizeof(str)); break; } diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h index dc425b6958c6..a2686304fcfa 100644 --- a/ripngd/ripngd.h +++ b/ripngd/ripngd.h @@ -468,20 +468,13 @@ extern int ripng_send_packet(caddr_t buf, int bufsize, struct sockaddr_in6 *to, extern void ripng_packet_dump(struct ripng_packet *packet, int size, const char *sndrcv); -extern int ripng_interface_up(int command, struct zclient *, zebra_size_t, - vrf_id_t); -extern int ripng_interface_down(int command, struct zclient *, zebra_size_t, - vrf_id_t); -extern int ripng_interface_add(int command, struct zclient *, zebra_size_t, - vrf_id_t); -extern int ripng_interface_delete(int command, struct zclient *, zebra_size_t, - vrf_id_t); -extern int ripng_interface_address_add(int command, struct zclient *, - zebra_size_t, vrf_id_t); -extern int ripng_interface_address_delete(int command, struct zclient *, - zebra_size_t, vrf_id_t); -extern int ripng_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id); +extern int ripng_interface_up(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_down(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_add(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_delete(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_address_add(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_address_delete(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_vrf_update(ZAPI_CALLBACK_ARGS); extern void ripng_interface_sync(struct interface *ifp); extern struct ripng *ripng_lookup_by_vrf_id(vrf_id_t vrf_id); diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index fbcbbe3fdca7..a7552547e968 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -145,19 +145,19 @@ DEFPY (install_routes_data_dump, struct timeval r; timersub(&sg.r.t_end, &sg.r.t_start, &r); - vty_out(vty, "Prefix: %s Total: %u %u %u Time: %ld.%ld\n", + vty_out(vty, "Prefix: %s Total: %u %u %u Time: %jd.%ld\n", prefix2str(&sg.r.orig_prefix, buf, sizeof(buf)), sg.r.total_routes, sg.r.installed_routes, sg.r.removed_routes, - r.tv_sec, r.tv_usec); + (intmax_t)r.tv_sec, (long)r.tv_usec); return CMD_SUCCESS; } DEFPY (install_routes, install_routes_cmd, - "sharp install routes [vrf NAME$name] |nexthop-group NAME$nexthop_group> (1-1000000)$routes [instance (0-255)$instance] [repeat (2-1000)$rpt]", + "sharp install routes [vrf NAME$name] |nexthop-group NHGNAME$nexthop_group> (1-1000000)$routes [instance (0-255)$instance] [repeat (2-1000)$rpt]", "Sharp routing Protocol\n" "install some routes\n" "Routes to install\n" diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index f1e83628c2ee..cd6f9565802d 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -58,8 +58,7 @@ static struct interface *zebra_interface_if_lookup(struct stream *s) } /* Inteface addition message from zebra. */ -static int interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -71,8 +70,7 @@ static int interface_add(int command, struct zclient *zclient, return 0; } -static int interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -90,21 +88,19 @@ static int interface_delete(int command, struct zclient *zclient, return 0; } -static int interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_add(ZAPI_CALLBACK_ARGS) { - zebra_interface_address_read(command, zclient->ibuf, vrf_id); + zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); return 0; } -static int interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; @@ -113,8 +109,7 @@ static int interface_address_delete(int command, struct zclient *zclient, return 0; } -static int interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_state_up(ZAPI_CALLBACK_ARGS) { zebra_interface_if_lookup(zclient->ibuf); @@ -122,8 +117,7 @@ static int interface_state_up(int command, struct zclient *zclient, return 0; } -static int interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_state_down(ZAPI_CALLBACK_ARGS) { zebra_interface_state_read(zclient->ibuf, vrf_id); @@ -202,8 +196,7 @@ static void handle_repeated(bool installed) } } -static int route_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int route_notify_owner(ZAPI_CALLBACK_ARGS) { struct timeval r; struct prefix p; @@ -219,8 +212,8 @@ static int route_notify_owner(int command, struct zclient *zclient, if (sg.r.total_routes == sg.r.installed_routes) { monotime(&sg.r.t_end); timersub(&sg.r.t_end, &sg.r.t_start, &r); - zlog_debug("Installed All Items %ld.%ld", r.tv_sec, - r.tv_usec); + zlog_debug("Installed All Items %jd.%ld", + (intmax_t)r.tv_sec, (long)r.tv_usec); handle_repeated(true); } break; @@ -235,8 +228,8 @@ static int route_notify_owner(int command, struct zclient *zclient, if (sg.r.total_routes == sg.r.removed_routes) { monotime(&sg.r.t_end); timersub(&sg.r.t_end, &sg.r.t_start, &r); - zlog_debug("Removed all Items %ld.%ld", r.tv_sec, - r.tv_usec); + zlog_debug("Removed all Items %jd.%ld", + (intmax_t)r.tv_sec, (long)r.tv_usec); handle_repeated(false); } break; @@ -345,8 +338,7 @@ void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import, __PRETTY_FUNCTION__); } -static int sharp_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int sharp_nexthop_update(ZAPI_CALLBACK_ARGS) { struct sharp_nh_tracker *nht; struct zapi_route nhr; diff --git a/snapcraft/README.snap_build.md b/snapcraft/README.snap_build.md index 7c4284845183..e43f63f2d904 100644 --- a/snapcraft/README.snap_build.md +++ b/snapcraft/README.snap_build.md @@ -92,6 +92,14 @@ All the commands are prefixed with frr. frr.ripngd-debug frr.ldp-debug frr.zebra-debug + frr.pimd-debug + frr.nhrpd-debug + frr.babeld-debug + frr.eigrpd-debug + frr.pbrd-debug + frr.staticd-debug + frr.bfdd-debug + frr.fabricd-debug vtysh can be accessed as frr.vtysh (Make sure you have /snap/bin in your path). If access as `vtysh` instead of `frr.vtysh` is needed, you can enable it diff --git a/snapcraft/README.usage.md b/snapcraft/README.usage.md index a7b51a5656f8..6a0864c8c5f2 100644 --- a/snapcraft/README.usage.md +++ b/snapcraft/README.usage.md @@ -66,6 +66,8 @@ depend on them). These are mainly intended to debug the Snap Starts staticd daemon in foreground - `frr.bfdd-debug`: Starts bfdd daemon in foreground +- `frr.fabricd-debug`: + Starts fabricd daemon in foreground MPLS (LDP) ---------- diff --git a/snapcraft/defaults/fabricd.conf.default b/snapcraft/defaults/fabricd.conf.default new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/snapcraft/scripts/Makefile b/snapcraft/scripts/Makefile index e3a7708f2304..7ddb7f0769fe 100644 --- a/snapcraft/scripts/Makefile +++ b/snapcraft/scripts/Makefile @@ -17,6 +17,7 @@ install: install -D -m 0755 pbrd-service $(DESTDIR)/bin/ install -D -m 0755 staticd-service $(DESTDIR)/bin/ install -D -m 0755 bfdd-service $(DESTDIR)/bin/ + install -D -m 0755 fabricd-service $(DESTDIR)/bin/ install -D -m 0755 set-options $(DESTDIR)/bin/ install -D -m 0755 show_version $(DESTDIR)/bin/ diff --git a/snapcraft/scripts/bgpd-service b/snapcraft/scripts/bgpd-service index 6c3a6f595955..64273d9f80cb 100644 --- a/snapcraft/scripts/bgpd-service +++ b/snapcraft/scripts/bgpd-service @@ -10,7 +10,7 @@ fi if ! [ -e $SNAP_DATA/rpki.conf ]; then echo "-M rpki" > $SNAP_DATA/rpki.conf fi -EXTRA_OPTIONS="`cat $SNAP_DATA/rpki.conf`" +EXTRA_OPTIONS="`$SNAP/bin/cat $SNAP_DATA/rpki.conf`" exec $SNAP/sbin/bgpd \ -f $SNAP_DATA/bgpd.conf \ --pid_file $SNAP_DATA/bgpd.pid \ diff --git a/snapcraft/scripts/fabricd-service b/snapcraft/scripts/fabricd-service new file mode 100644 index 000000000000..586f061ef00e --- /dev/null +++ b/snapcraft/scripts/fabricd-service @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e -x + +if ! [ -e $SNAP_DATA/fabricd.conf ]; then + cp $SNAP/etc/frr/fabricd.conf.default $SNAP_DATA/fabricd.conf +fi +exec $SNAP/sbin/fabricd \ + -f $SNAP_DATA/fabricd.conf \ + --pid_file $SNAP_DATA/fabricd.pid \ + --socket $SNAP_DATA/zsock \ + --vty_socket $SNAP_DATA + diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in index b70d6efee2c0..d8071e8cfee5 100644 --- a/snapcraft/snapcraft.yaml.in +++ b/snapcraft/snapcraft.yaml.in @@ -4,8 +4,8 @@ summary: FRRouting BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM/LDP/EIGRP/BFD routing da description: BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM/LDP/EIGRP/BFD routing daemon FRRouting (FRR) is free software which manages TCP/IP based routing protocols. It supports BGP4, BGP4+, OSPFv2, OSPFv3, IS-IS, RIPv1, RIPv2, - RIPng, PIM, LDP, Babel, EIGRP, PBR (Policy-based routing) and BFD as well as - the IPv6 versions of these. + RIPng, PIM, LDP, Babel, EIGRP, PBR (Policy-based routing), BFD and OpenFabric + as well as the IPv6 versions of these. FRRouting (frr) is a fork of Quagga. confinement: strict grade: devel @@ -127,6 +127,13 @@ apps: - network - network-bind - network-control + fabricd: + command: bin/fabricd-service + daemon: simple + plugs: + - network + - network-bind + - network-control set: command: bin/set-options zebra-debug: @@ -136,7 +143,7 @@ apps: - network-bind - network-control bgpd-debug: - command: sbin/bgpd -f $SNAP_DATA/bgpd.conf --pid_file $SNAP_DATA/bgpd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA --moduledir $SNAP/lib/frr/modules `cat $SNAP_DATA/rpki.conf 2> /dev/null` + command: sbin/bgpd -f $SNAP_DATA/bgpd.conf --pid_file $SNAP_DATA/bgpd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA --moduledir $SNAP/lib/frr/modules plugs: - network - network-bind @@ -219,6 +226,12 @@ apps: - network - network-bind - network-control + fabricd-debug: + command: sbin/fabricd -f $SNAP_DATA/fabricd.conf --pid_file $SNAP_DATA/fabricd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA + plugs: + - network + - network-bind + - network-control parts: rtrlib: @@ -230,16 +243,33 @@ parts: stage-packages: - libssh-4 prime: - - lib/x86_64-linux-gnu/librtr.so* + - lib/librtr.so* - usr/lib/x86_64-linux-gnu/libssh.so* source: https://github.com/rtrlib/rtrlib.git source-type: git - source-tag: v0.5.0 + source-tag: v0.6.3 plugin: cmake configflags: - -DCMAKE_BUILD_TYPE=Release + libyang: + build-packages: + - cmake + - make + - gcc + - libpcre3-dev + stage-packages: + - libpcre3 + source: https://github.com/CESNET/libyang.git + source-type: git + source-tag: v0.16-r3 + plugin: cmake + configflags: + - -DCMAKE_INSTALL_PREFIX:PATH=/usr + - -DENABLE_LYD_PRIV=ON + - -DENABLE_CACHE=OFF + - -DCMAKE_BUILD_TYPE:String="Release" frr: - after: [rtrlib] + after: [rtrlib,libyang] build-packages: - gcc - autoconf @@ -272,13 +302,13 @@ parts: - iproute2 - logrotate - libcap2 - - libc6 - libtinfo5 - libreadline6 - libjson-c2 - libc-ares2 - libatm1 - libprotobuf-c1 + - libdb5.3 plugin: autotools source: ../frr-@PACKAGE_VERSION@.tar.gz configflags: @@ -322,7 +352,9 @@ parts: eigrpd.conf.default: etc/frr/eigrpd.conf.default pbrd.conf.default: etc/frr/pbrd.conf.default bfdd.conf.default: etc/frr/bfdd.conf.default + fabricd.conf.default: etc/frr/fabricd.conf.default vtysh.conf.default: etc/frr/vtysh.conf.default + staticd.conf.default: etc/frr/staticd.conf.default frr-scripts: plugin: make source: scripts @@ -344,3 +376,8 @@ parts: README.snap_build.md: doc/README.snap_build.md extra_version_info.txt: doc/extra_version_info.txt +passthrough: + layout: + /usr/lib/x86_64-linux-gnu/libyang: + bind: $SNAP/usr/lib/x86_64-linux-gnu/libyang + diff --git a/staticd/static_routes.c b/staticd/static_routes.c index cde31df14fec..5f9ecad69462 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -127,7 +127,7 @@ int static_add_route(afi_t afi, safi_t safi, uint8_t type, struct prefix *p, si->tag = tag; si->vrf_id = svrf->vrf->vrf_id; si->nh_vrf_id = nh_svrf->vrf->vrf_id; - strcpy(si->nh_vrfname, nh_svrf->vrf->name); + strlcpy(si->nh_vrfname, nh_svrf->vrf->name, sizeof(si->nh_vrfname)); si->table_id = table_id; si->onlink = onlink; diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index 3f3117752439..c6da00418b4a 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -59,8 +59,7 @@ static struct interface *zebra_interface_if_lookup(struct stream *s) } /* Inteface addition message from zebra. */ -static int interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_add(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -73,8 +72,7 @@ static int interface_add(int command, struct zclient *zclient, return 0; } -static int interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_delete(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct stream *s; @@ -93,20 +91,18 @@ static int interface_delete(int command, struct zclient *zclient, return 0; } -static int interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_add(ZAPI_CALLBACK_ARGS) { - zebra_interface_address_read(command, zclient->ibuf, vrf_id); + zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); return 0; } -static int interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; @@ -115,8 +111,7 @@ static int interface_address_delete(int command, struct zclient *zclient, return 0; } -static int interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_state_up(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -138,16 +133,14 @@ static int interface_state_up(int command, struct zclient *zclient, return 0; } -static int interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_state_down(ZAPI_CALLBACK_ARGS) { zebra_interface_state_read(zclient->ibuf, vrf_id); return 0; } -static int route_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int route_notify_owner(ZAPI_CALLBACK_ARGS) { struct prefix p; enum zapi_route_notify_owner note; @@ -194,8 +187,7 @@ struct static_nht_data { uint8_t nh_num; }; -static int static_zebra_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int static_zebra_nexthop_update(ZAPI_CALLBACK_ARGS) { struct static_nht_data *nhtd, lookup; struct zapi_route nhr; @@ -231,9 +223,9 @@ static void static_zebra_capabilities(struct zclient_capabilities *cap) mpls_enabled = cap->mpls_enabled; } -static unsigned int static_nht_hash_key(void *data) +static unsigned int static_nht_hash_key(const void *data) { - struct static_nht_data *nhtd = data; + const struct static_nht_data *nhtd = data; unsigned int key = 0; key = prefix_hash_key(nhtd->nh); diff --git a/tests/.gitignore b/tests/.gitignore index de648015f1d9..4c6a51d475f4 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -20,6 +20,7 @@ /lib/cli/test_commands_defun.c /lib/northbound/test_oper_data /lib/cxxcompat +/lib/test_atomlist /lib/test_buffer /lib/test_checksum /lib/test_graph @@ -29,9 +30,12 @@ /lib/test_idalloc /lib/test_memory /lib/test_nexthop_iter +/lib/test_ntop +/lib/test_printfrr /lib/test_privs /lib/test_ringbuf /lib/test_segv +/lib/test_seqlock /lib/test_sig /lib/test_srcdest_table /lib/test_stream @@ -39,6 +43,7 @@ /lib/test_timer_correctness /lib/test_timer_performance /lib/test_ttable +/lib/test_typelist /lib/test_zlog /lib/test_zmq /ospf6d/test_lsdb diff --git a/tests/helpers/c/main.c b/tests/helpers/c/main.c index 11db2dabc38f..b1dcfcf7075c 100644 --- a/tests/helpers/c/main.c +++ b/tests/helpers/c/main.c @@ -153,7 +153,7 @@ int main(int argc, char **argv) /* Library inits. */ cmd_init(1); - vty_init(master); + vty_init(master, false); memory_init(); yang_init(); nb_init(master, NULL, 0); diff --git a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz index 4a89bda84eac..6f8bc2218e2f 100644 Binary files a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz and b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz differ diff --git a/tests/isisd/test_isis_lspdb.c b/tests/isisd/test_isis_lspdb.c index b9c6f2bbb21d..f0baa482c78a 100644 --- a/tests/isisd/test_isis_lspdb.c +++ b/tests/isisd/test_isis_lspdb.c @@ -28,21 +28,22 @@ static void test_lsp_build_list_nonzero_ht(void) area->lsp_mtu = 1500; - dict_t *lspdb = lsp_db_init(); + struct lspdb_head _lspdb, *lspdb = &_lspdb; + lsp_db_init(&_lspdb); struct isis_lsp *lsp1 = lsp_new(area, lsp_id1, 6000, 0, 0, 0, NULL, ISIS_LEVEL2); - lsp_insert(lsp1, lspdb); + lsp_insert(lspdb, lsp1); struct isis_lsp *lsp2 = lsp_new(area, lsp_id2, 6000, 0, 0, 0, NULL, ISIS_LEVEL2); - lsp_insert(lsp2, lspdb); + lsp_insert(lspdb, lsp2); struct list *list = list_new(); - lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb); + lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list); assert(list->count == 1); assert(listgetdata(listhead(list)) == lsp1); list_delete_all_node(list); @@ -50,7 +51,7 @@ static void test_lsp_build_list_nonzero_ht(void) lsp_id_end[5] = 0x03; lsp_id_end[6] = 0x00; - lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb); + lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list); assert(list->count == 2); assert(listgetdata(listhead(list)) == lsp1); assert(listgetdata(listtail(list)) == lsp2); @@ -58,7 +59,7 @@ static void test_lsp_build_list_nonzero_ht(void) memcpy(lsp_id1, lsp_id2, sizeof(lsp_id1)); - lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb); + lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list); assert(list->count == 1); assert(listgetdata(listhead(list)) == lsp2); list_delete_all_node(list); @@ -66,13 +67,13 @@ static void test_lsp_build_list_nonzero_ht(void) lsp_id1[5] = 0x03; lsp_id_end[5] = 0x04; - lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb); + lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list); assert(list->count == 0); list_delete_all_node(list); lsp_id1[5] = 0x00; - lsp_build_list_nonzero_ht(lsp_id1, lsp_id_end, list, lspdb); + lsp_build_list_nonzero_ht(lspdb, lsp_id1, lsp_id_end, list); assert(list->count == 2); assert(listgetdata(listhead(list)) == lsp1); assert(listgetdata(listtail(list)) == lsp2); diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index 393b58874571..2071ae08cd7e 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -82,7 +82,7 @@ int main(int argc, char **argv) cmd_hostname_set("test"); cmd_domainname_set("test.domain"); - vty_init(master); + vty_init(master, false); memory_init(); yang_init(); nb_init(master, NULL, 0); diff --git a/tests/lib/cxxcompat.c b/tests/lib/cxxcompat.c index d10962993d21..48fa0ec8a9f8 100644 --- a/tests/lib/cxxcompat.c +++ b/tests/lib/cxxcompat.c @@ -32,7 +32,6 @@ #include "lib/debug.h" #include "lib/distribute.h" #include "lib/ferr.h" -#include "lib/fifo.h" #include "lib/filter.h" #include "lib/frr_pthread.h" #include "lib/frratomic.h" @@ -93,6 +92,8 @@ #include "lib/table.h" #include "lib/termtable.h" #include "lib/thread.h" +#include "lib/typesafe.h" +#include "lib/typerb.h" #include "lib/vector.h" #include "lib/vlan.h" #include "lib/vrf.h" diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c index 7cd622854e88..3180f9f9f3fd 100644 --- a/tests/lib/northbound/test_oper_data.c +++ b/tests/lib/northbound/test_oper_data.c @@ -411,7 +411,7 @@ int main(int argc, char **argv) /* Library inits. */ cmd_init(1); cmd_hostname_set("test"); - vty_init(master); + vty_init(master, false); memory_init(); yang_init(); nb_init(master, modules, array_size(modules)); diff --git a/tests/lib/test_atomlist.c b/tests/lib/test_atomlist.c new file mode 100644 index 000000000000..249fff8edb34 --- /dev/null +++ b/tests/lib/test_atomlist.c @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "atomlist.h" +#include "seqlock.h" +#include "monotime.h" + +/* + * maybe test: + * - alist_del_hint + * - alist_next_safe + * - asort_del_hint + * - asort_next_safe + */ + +static struct seqlock sqlo; + +PREDECL_ATOMLIST(alist) +PREDECL_ATOMSORT_UNIQ(asort) +struct item { + uint64_t val1; + struct alist_item chain; + struct asort_item sortc; + uint64_t val2; +}; +DECLARE_ATOMLIST(alist, struct item, chain) + +static int icmp(const struct item *a, const struct item *b); +DECLARE_ATOMSORT_UNIQ(asort, struct item, sortc, icmp) + +static int icmp(const struct item *a, const struct item *b) +{ + if (a->val1 > b->val1) + return 1; + if (a->val1 < b->val1) + return -1; + return 0; +} + +#define NITEM 10000 +struct item itm[NITEM]; + +static struct alist_head ahead; +static struct asort_head shead; + +#define NTHREADS 4 +static struct testthread { + pthread_t pt; + struct seqlock sqlo; + size_t counter, nullops; +} thr[NTHREADS]; + +struct testrun { + struct testrun *next; + int lineno; + const char *desc; + ssize_t prefill; + bool sorted; + void (*func)(unsigned int offset); +}; +struct testrun *runs = NULL; + +#define NOCLEAR -1 + +#define deftestrun(name, _desc, _prefill, _sorted) \ +static void trfunc_##name(unsigned int offset); \ +struct testrun tr_##name = { \ + .desc = _desc, \ + .lineno = __LINE__, \ + .prefill = _prefill, \ + .func = &trfunc_##name, \ + .sorted = _sorted }; \ +static void __attribute__((constructor)) trsetup_##name(void) \ +{ \ + struct testrun **inspos = &runs; \ + while (*inspos && (*inspos)->lineno < tr_##name.lineno) \ + inspos = &(*inspos)->next; \ + tr_##name.next = *inspos; \ + *inspos = &tr_##name; \ +} \ +static void trfunc_##name(unsigned int offset) \ +{ \ + size_t i = 0, n = 0; + +#define endtestrun \ + thr[offset].counter = i; \ + thr[offset].nullops = n; \ +} + +deftestrun(add, "add vs. add", 0, false) + for (; i < NITEM / NTHREADS; i++) + alist_add_head(&ahead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(del, "del vs. del", NOCLEAR, false) + for (; i < NITEM / NTHREADS / 10; i++) + alist_del(&ahead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(addtail, "add_tail vs. add_tail", 0, false) + for (; i < NITEM / NTHREADS; i++) + alist_add_tail(&ahead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(pop, "pop vs. pop", NOCLEAR, false) + for (; i < NITEM / NTHREADS; ) + if (alist_pop(&ahead)) + i++; + else + n++; +endtestrun + +deftestrun(headN_vs_pop1, "add_head(N) vs. pop(1)", 1, false); + if (offset == 0) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM; ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = offset; i < NITEM; i += NTHREADS) + alist_add_head(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(head1_vs_popN, "add_head(1) vs. pop(N)", 0, false); + if (offset < NTHREADS - 1) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM / NTHREADS; ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = 0; i < NITEM; i++) + alist_add_head(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(headN_vs_popN, "add_head(N) vs. pop(N)", NTHREADS / 2, false) + if (offset < NTHREADS / 2) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM * 2 / NTHREADS; ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = offset; i < NITEM; i += NTHREADS) + alist_add_head(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(tailN_vs_pop1, "add_tail(N) vs. pop(1)", 1, false) + if (offset == 0) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM - (NITEM / NTHREADS); ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = offset; i < NITEM; i += NTHREADS) + alist_add_tail(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(tail1_vs_popN, "add_tail(1) vs. pop(N)", 0, false) + if (offset < NTHREADS - 1) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM / NTHREADS; ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = 0; i < NITEM; i++) + alist_add_tail(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(sort_add, "add_sort vs. add_sort", 0, true) + for (; i < NITEM / NTHREADS / 10; i++) + asort_add(&shead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(sort_del, "del_sort vs. del_sort", NOCLEAR, true) + for (; i < NITEM / NTHREADS / 10; i++) + asort_del(&shead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(sort_add_del, "add_sort vs. del_sort", NTHREADS / 2, true) + if (offset < NTHREADS / 2) { + for (; i < NITEM / NTHREADS / 10; i++) + asort_del(&shead, &itm[i * NTHREADS + offset]); + } else { + for (; i < NITEM / NTHREADS / 10; i++) + asort_add(&shead, &itm[i * NTHREADS + offset]); + } +endtestrun + +static void *thr1func(void *arg) +{ + struct testthread *p = arg; + unsigned int offset = (unsigned int)(p - &thr[0]); + seqlock_val_t sv; + struct testrun *tr; + + for (tr = runs; tr; tr = tr->next) { + sv = seqlock_bump(&p->sqlo); + seqlock_wait(&sqlo, sv); + + tr->func(offset); + } + seqlock_bump(&p->sqlo); + + return NULL; +} + +static void clear_list(size_t prefill) +{ + size_t i; + + memset(&ahead, 0, sizeof(ahead)); + memset(&shead, 0, sizeof(shead)); + memset(itm, 0, sizeof(itm)); + for (i = 0; i < NITEM; i++) { + itm[i].val1 = itm[i].val2 = i; + if ((i % NTHREADS) < prefill) { + alist_add_tail(&ahead, &itm[i]); + asort_add(&shead, &itm[i]); + } + } +} + +static void run_tr(struct testrun *tr) +{ + const char *desc = tr->desc; + struct timeval tv; + int64_t delta; + seqlock_val_t sv; + size_t c = 0, s = 0, n = 0; + struct item *item, *prev, dummy; + + printf("[%02u] %35s %s\n", seqlock_cur(&sqlo) >> 1, "", desc); + fflush(stdout); + + if (tr->prefill != NOCLEAR) + clear_list(tr->prefill); + + monotime(&tv); + sv = seqlock_bump(&sqlo); + for (size_t i = 0; i < NTHREADS; i++) { + seqlock_wait(&thr[i].sqlo, seqlock_cur(&sqlo)); + s += thr[i].counter; + n += thr[i].nullops; + thr[i].counter = 0; + thr[i].nullops = 0; + } + + delta = monotime_since(&tv, NULL); + if (tr->sorted) { + uint64_t prevval = 0; + + frr_each(asort, &shead, item) { + assert(item->val1 >= prevval); + prevval = item->val1; + c++; + } + assert(c == asort_count(&shead)); + } else { + prev = &dummy; + frr_each(alist, &ahead, item) { + assert(item != prev); + prev = item; + c++; + assert(c <= NITEM); + } + assert(c == alist_count(&ahead)); + } + printf("\033[1A[%02u] %9"PRId64"us c=%5zu s=%5zu n=%5zu %s\n", + sv >> 1, delta, c, s, n, desc); +} + +#ifdef BASIC_TESTS +static void dump(const char *lbl) +{ + struct item *item, *safe; + size_t ctr = 0; + + printf("dumping %s:\n", lbl); + frr_each_safe(alist, &ahead, item) { + printf("%s %3zu %p %3"PRIu64" %3"PRIu64"\n", lbl, ctr++, + (void *)item, item->val1, item->val2); + } +} + +static void basic_tests(void) +{ + size_t i; + + memset(&ahead, 0, sizeof(ahead)); + memset(itm, 0, sizeof(itm)); + for (i = 0; i < NITEM; i++) + itm[i].val1 = itm[i].val2 = i; + + assert(alist_first(&ahead) == NULL); + dump(""); + alist_add_head(&ahead, &itm[0]); + dump(""); + alist_add_head(&ahead, &itm[1]); + dump(""); + alist_add_tail(&ahead, &itm[2]); + dump(""); + alist_add_tail(&ahead, &itm[3]); + dump(""); + alist_del(&ahead, &itm[1]); + dump(""); + printf("POP: %p\n", alist_pop(&ahead)); + dump(""); + printf("POP: %p\n", alist_pop(&ahead)); + printf("POP: %p\n", alist_pop(&ahead)); + printf("POP: %p\n", alist_pop(&ahead)); + printf("POP: %p\n", alist_pop(&ahead)); + dump(""); +} +#else +#define basic_tests() do { } while (0) +#endif + +int main(int argc, char **argv) +{ + size_t i; + + basic_tests(); + + seqlock_init(&sqlo); + seqlock_acquire_val(&sqlo, 1); + + for (i = 0; i < NTHREADS; i++) { + seqlock_init(&thr[i].sqlo); + seqlock_acquire(&thr[i].sqlo, &sqlo); + thr[i].counter = 0; + thr[i].nullops = 0; + + pthread_create(&thr[i].pt, NULL, thr1func, &thr[i]); + } + + struct testrun *tr; + + for (tr = runs; tr; tr = tr->next) + run_tr(tr); + + for (i = 0; i < NTHREADS; i++) + pthread_join(thr[i].pt, NULL); + + return 0; +} diff --git a/tests/lib/test_atomlist.py b/tests/lib/test_atomlist.py new file mode 100644 index 000000000000..293d47f31663 --- /dev/null +++ b/tests/lib/test_atomlist.py @@ -0,0 +1,6 @@ +import frrtest + +class TestAtomlist(frrtest.TestMultiOut): + program = './test_atomlist' + +TestAtomlist.exit_cleanly() diff --git a/tests/lib/test_ntop.c b/tests/lib/test_ntop.c new file mode 100644 index 000000000000..180605996360 --- /dev/null +++ b/tests/lib/test_ntop.c @@ -0,0 +1,87 @@ +/* + * frr_inet_ntop() unit test + * Copyright (C) 2019 David Lamparter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "tests/helpers/c/prng.h" + +/* NB: libfrr is NOT linked for this unit test! */ + +#define INET_NTOP_NO_OVERRIDE +#include "lib/ntop.c" + +int main(int argc, char **argv) +{ + size_t i, j, k, l; + struct in_addr i4; + struct in6_addr i6, i6check; + char buf1[64], buf2[64]; + const char *rv; + struct prng *prng; + + prng = prng_new(0); + /* IPv4 */ + for (i = 0; i < 1000; i++) { + i4.s_addr = prng_rand(prng); + assert(frr_inet_ntop(AF_INET, &i4, buf1, sizeof(buf1))); + assert(inet_ntop(AF_INET, &i4, buf2, sizeof(buf2))); + assert(!strcmp(buf1, buf2)); + } + + /* check size limit */ + for (i = 0; i < sizeof(buf1); i++) { + memset(buf2, 0xcc, sizeof(buf2)); + rv = frr_inet_ntop(AF_INET, &i4, buf2, i); + if (i < strlen(buf1) + 1) + assert(!rv); + else + assert(rv && !strcmp(buf1, buf2)); + } + + /* IPv6 */ + for (i = 0; i < 10000; i++) { + uint16_t *i6w = (uint16_t *)&i6; + for (j = 0; j < 8; j++) + i6w[j] = prng_rand(prng); + + /* clear some words */ + l = prng_rand(prng) & 7; + for (j = 0; j < l; j++) { + uint32_t num = __builtin_ctz(prng_rand(prng)); + uint32_t where = prng_rand(prng) & 7; + + for (k = where; k < where + num && k < 8; k++) + i6w[k] = 0; + } + + assert(frr_inet_ntop(AF_INET6, &i6, buf1, sizeof(buf1))); + assert(inet_ntop(AF_INET6, &i6, buf2, sizeof(buf2))); + if (strcmp(buf1, buf2)) + printf("%-40s (FRR) != (SYS) %-40s\n", buf1, buf2); + + assert(inet_pton(AF_INET6, buf1, &i6check)); + assert(!memcmp(&i6, &i6check, sizeof(i6))); + assert(strlen(buf1) <= strlen(buf2)); + } + return 0; +} diff --git a/tests/lib/test_ntop.py b/tests/lib/test_ntop.py new file mode 100644 index 000000000000..2526f53db549 --- /dev/null +++ b/tests/lib/test_ntop.py @@ -0,0 +1,6 @@ +import frrtest + +class TestNtop(frrtest.TestMultiOut): + program = './test_ntop' + +TestNtop.exit_cleanly() diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c new file mode 100644 index 000000000000..c8ef150b07bc --- /dev/null +++ b/tests/lib/test_printfrr.c @@ -0,0 +1,148 @@ +/* + * printfrr() unit test + * Copyright (C) 2019 David Lamparter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "zebra.h" + +#include + +#include "lib/printfrr.h" +#include "lib/memory.h" +#include "lib/prefix.h" + +static int errors; + +static void printcmp(const char *fmt, ...) +{ + va_list ap; + char buf[256], bufrr[256], *p; + int cmp; + memset(bufrr, 0xcc, sizeof(bufrr)); + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + va_start(ap, fmt); + vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap); + va_end(ap); + + cmp = strcmp(buf, bufrr); + + /* OS dependent "+nan" vs. "nan" */ + if (cmp && (p = strstr(bufrr, "+nan"))) { + p[0] = ' '; + if (!strcmp(buf, bufrr)) + cmp = 0; + p[0] = '+'; + } + printf("fmt: \"%s\"\nsys: \"%s\"\nfrr: \"%s\"\n%s\n\n", + fmt, buf, bufrr, cmp ? "ERROR" : "ok"); + + if (cmp) + errors++; +} + +static void printchk(const char *ref, const char *fmt, ...) +{ + va_list ap; + char bufrr[256]; + memset(bufrr, 0xcc, sizeof(bufrr)); + + va_start(ap, fmt); + vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap); + va_end(ap); + + printf("fmt: \"%s\"\nref: \"%s\"\nfrr: \"%s\"\n%s\n\n", + fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok"); + if (strcmp(ref, bufrr)) + errors++; +} + +int main(int argc, char **argv) +{ + size_t i; + float flts[] = { + 123.456789, + 23.456789e-30, + 3.456789e+30, + INFINITY, + NAN, + }; + uint64_t ui64 = 0xfeed1278cafef00d; + struct in_addr ip; + char *p; + char buf[256]; + + printcmp("%d %u %d %u", 123, 123, -456, -456); + printcmp("%lld %llu %lld %llu", 123LL, 123LL, -456LL, -456LL); + + printcmp("%-20s,%20s,%.20s", "test", "test", "test"); + printcmp("%-3s,%3s,%.3s", "test", "test", "test"); + printcmp("%-6.3s,%6.3s,%6.3s", "test", "test", "test"); + printcmp("%*s,%*s,%.*s", -3, "test", 3, "test", 3, "test"); + + for (i = 0; i < array_size(flts); i++) { + printcmp("%-6.3e,%6.3e,%+06.3e", flts[i], flts[i], flts[i]); + printcmp("%-6.3f,%6.3f,%+06.3f", flts[i], flts[i], flts[i]); + printcmp("%-6.3g,%6.3g,%+06.3g", flts[i], flts[i], flts[i]); + printcmp("%-6.3a,%6.3a,%+06.3a", flts[i], flts[i], flts[i]); + } + + printchk("-77385308584349683 18369358765125201933 feed1278cafef00d", + "%Ld %Lu %Lx", ui64, ui64, ui64); + + inet_aton("192.168.1.2", &ip); + printchk("192.168.1.2", "%pI4", &ip); + printchk(" 192.168.1.2", "%20pI4", &ip); + + printcmp("%p", &ip); + + snprintfrr(buf, sizeof(buf), "test%s", "#1"); + csnprintfrr(buf, sizeof(buf), "test%s", "#2"); + assert(strcmp(buf, "test#1test#2") == 0); + + p = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), "test%s", "#3"); + assert(p == buf); + assert(strcmp(buf, "test#3") == 0); + + p = asnprintfrr(MTYPE_TMP, buf, 4, "test%s", "#4"); + assert(p != buf); + assert(strcmp(p, "test#4") == 0); + XFREE(MTYPE_TMP, p); + + p = asprintfrr(MTYPE_TMP, "test%s", "#5"); + assert(strcmp(p, "test#5") == 0); + XFREE(MTYPE_TMP, p); + + struct prefix_sg sg; + sg.src.s_addr = INADDR_ANY; + sg.grp.s_addr = INADDR_ANY; + printchk("(*,*)", "%pSG4", &sg); + + inet_aton("192.168.1.2", &sg.src); + printchk("(192.168.1.2,*)", "%pSG4", &sg); + + inet_aton("224.1.2.3", &sg.grp); + printchk("(192.168.1.2,224.1.2.3)", "%pSG4", &sg); + + sg.src.s_addr = INADDR_ANY; + printchk("(*,224.1.2.3)", "%pSG4", &sg); + + return !!errors; +} diff --git a/tests/lib/test_printfrr.py b/tests/lib/test_printfrr.py new file mode 100644 index 000000000000..4fe238618ed9 --- /dev/null +++ b/tests/lib/test_printfrr.py @@ -0,0 +1,6 @@ +import frrtest + +class TestPrintfrr(frrtest.TestMultiOut): + program = './test_printfrr' + +TestPrintfrr.exit_cleanly() diff --git a/tests/lib/test_seqlock.c b/tests/lib/test_seqlock.c new file mode 100644 index 000000000000..6b2b9ed8a5bd --- /dev/null +++ b/tests/lib/test_seqlock.c @@ -0,0 +1,122 @@ +/* + * basic test for seqlock + * + * Copyright (C) 2015 David Lamparter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "monotime.h" +#include "seqlock.h" + +static struct seqlock sqlo; +static pthread_t thr1; +static struct timeval start; + +static void writestr(const char *str) +{ + struct iovec iov[2]; + char buf[32]; + int64_t usec = monotime_since(&start, NULL); + + snprintf(buf, sizeof(buf), "[%02"PRId64"] ", usec / 100000); + + iov[0].iov_base = buf; + iov[0].iov_len = strlen(buf); + iov[1].iov_base = (char *)str; + iov[1].iov_len = strlen(str); + writev(1, iov, 2); +} + +static void *thr1func(void *arg) +{ + assert(!seqlock_held(&sqlo)); + assert(seqlock_check(&sqlo, 1)); + seqlock_wait(&sqlo, 1); + writestr("thr1 (unheld)\n"); + + sleep(2); + + assert(seqlock_held(&sqlo)); + assert(seqlock_check(&sqlo, 1)); + seqlock_wait(&sqlo, 1); + writestr("thr1 @1\n"); + + seqlock_wait(&sqlo, 3); + writestr("thr1 @3\n"); + + seqlock_wait(&sqlo, 5); + writestr("thr1 @5\n"); + + seqlock_wait(&sqlo, 7); + writestr("thr1 @7\n"); + + seqlock_wait(&sqlo, 9); + writestr("thr1 @9\n"); + + seqlock_wait(&sqlo, 11); + writestr("thr1 @11\n"); + return NULL; +} + +int main(int argc, char **argv) +{ + monotime(&start); + + seqlock_init(&sqlo); + + assert(!seqlock_held(&sqlo)); + seqlock_acquire_val(&sqlo, 1); + assert(seqlock_held(&sqlo)); + + assert(seqlock_cur(&sqlo) == 1); + assert(seqlock_bump(&sqlo) == 1); + assert(seqlock_cur(&sqlo) == 3); + assert(seqlock_bump(&sqlo) == 3); + assert(seqlock_bump(&sqlo) == 5); + assert(seqlock_bump(&sqlo) == 7); + assert(seqlock_cur(&sqlo) == 9); + + assert(seqlock_held(&sqlo)); + seqlock_release(&sqlo); + assert(!seqlock_held(&sqlo)); + + pthread_create(&thr1, NULL, thr1func, NULL); + sleep(1); + + writestr("main @3\n"); + seqlock_acquire_val(&sqlo, 3); + sleep(2); + + writestr("main @5\n"); + seqlock_bump(&sqlo); + sleep(1); + + writestr("main @9\n"); + seqlock_acquire_val(&sqlo, 9); + sleep(1); + + writestr("main @release\n"); + seqlock_release(&sqlo); + sleep(1); +} diff --git a/tests/lib/test_srcdest_table.c b/tests/lib/test_srcdest_table.c index 19a40b21841f..0fca571d28b0 100644 --- a/tests/lib/test_srcdest_table.c +++ b/tests/lib/test_srcdest_table.c @@ -81,9 +81,9 @@ static char *format_srcdest(const struct prefix_ipv6 *dst_p, return rv; } -static unsigned int log_key(void *data) +static unsigned int log_key(const void *data) { - struct prefix *hash_entry = data; + const struct prefix *hash_entry = data; struct prefix_ipv6 *dst_p = (struct prefix_ipv6 *)&hash_entry[0]; struct prefix_ipv6 *src_p = (struct prefix_ipv6 *)&hash_entry[1]; unsigned int hash = 0; diff --git a/tests/lib/test_typelist.c b/tests/lib/test_typelist.c new file mode 100644 index 000000000000..2438fb5f0855 --- /dev/null +++ b/tests/lib/test_typelist.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#define WNO_ATOMLIST_UNSAFE_FIND + +#include "typesafe.h" +#include "atomlist.h" +#include "memory.h" +#include "monotime.h" +#include "jhash.h" +#include "sha256.h" + +#include "tests/helpers/c/prng.h" + +/* note: these macros are layered 2-deep because that makes the C + * preprocessor expand the "type" argument. Otherwise, you get + * "PREDECL_type" instead of "PREDECL_LIST" + */ +#define _concat(a, b) a ## b +#define concat(a, b) _concat(a, b) +#define _str(x) #x +#define str(x) _str(x) + +#define _PREDECL(type, ...) PREDECL_##type(__VA_ARGS__) +#define PREDECL(type, ...) _PREDECL(type, __VA_ARGS__) +#define _DECLARE(type, ...) DECLARE_##type(__VA_ARGS__) +#define DECLARE(type, ...) _DECLARE(type, __VA_ARGS__) + +#define T_SORTED (1 << 0) +#define T_UNIQ (1 << 1) +#define T_HASH (1 << 2) +#define T_HEAP (1 << 3) +#define T_ATOMIC (1 << 4) + +#define _T_LIST (0) +#define _T_DLIST (0) +#define _T_ATOMLIST (0 | T_ATOMIC) +#define _T_HEAP (T_SORTED | T_HEAP) +#define _T_SORTLIST_UNIQ (T_SORTED | T_UNIQ) +#define _T_SORTLIST_NONUNIQ (T_SORTED) +#define _T_HASH (T_SORTED | T_UNIQ | T_HASH) +#define _T_SKIPLIST_UNIQ (T_SORTED | T_UNIQ) +#define _T_SKIPLIST_NONUNIQ (T_SORTED) +#define _T_RBTREE_UNIQ (T_SORTED | T_UNIQ) +#define _T_RBTREE_NONUNIQ (T_SORTED) +#define _T_ATOMSORT_UNIQ (T_SORTED | T_UNIQ | T_ATOMIC) +#define _T_ATOMSORT_NONUNIQ (T_SORTED | T_ATOMIC) + +#define _T_TYPE(type) _T_##type +#define IS_SORTED(type) (_T_TYPE(type) & T_SORTED) +#define IS_UNIQ(type) (_T_TYPE(type) & T_UNIQ) +#define IS_HASH(type) (_T_TYPE(type) & T_HASH) +#define IS_HEAP(type) (_T_TYPE(type) & T_HEAP) +#define IS_ATOMIC(type) (_T_TYPE(type) & T_ATOMIC) + +static struct timeval ref, ref0; + +static void ts_start(void) +{ + monotime(&ref0); + monotime(&ref); +} +static void ts_ref(const char *text) +{ + int64_t us; + us = monotime_since(&ref, NULL); + printf("%7"PRId64"us %s\n", us, text); + monotime(&ref); +} +static void ts_end(void) +{ + int64_t us; + us = monotime_since(&ref0, NULL); + printf("%7"PRId64"us total\n", us); +} + +#define TYPE LIST +#include "test_typelist.h" + +#define TYPE DLIST +#include "test_typelist.h" + +#define TYPE ATOMLIST +#include "test_typelist.h" + +#define TYPE HEAP +#include "test_typelist.h" + +#define TYPE SORTLIST_UNIQ +#include "test_typelist.h" + +#define TYPE SORTLIST_NONUNIQ +#include "test_typelist.h" + +#define TYPE HASH +#include "test_typelist.h" + +#define TYPE HASH_collisions +#define REALTYPE HASH +#define SHITTY_HASH +#include "test_typelist.h" +#undef SHITTY_HASH + +#define TYPE SKIPLIST_UNIQ +#include "test_typelist.h" + +#define TYPE SKIPLIST_NONUNIQ +#include "test_typelist.h" + +#define TYPE RBTREE_UNIQ +#include "test_typelist.h" + +#define TYPE RBTREE_NONUNIQ +#include "test_typelist.h" + +#define TYPE ATOMSORT_UNIQ +#include "test_typelist.h" + +#define TYPE ATOMSORT_NONUNIQ +#include "test_typelist.h" + +int main(int argc, char **argv) +{ + srandom(1); + + test_LIST(); + test_DLIST(); + test_ATOMLIST(); + test_HEAP(); + test_SORTLIST_UNIQ(); + test_SORTLIST_NONUNIQ(); + test_HASH(); + test_HASH_collisions(); + test_SKIPLIST_UNIQ(); + test_SKIPLIST_NONUNIQ(); + test_RBTREE_UNIQ(); + test_RBTREE_NONUNIQ(); + test_ATOMSORT_UNIQ(); + test_ATOMSORT_NONUNIQ(); + + log_memstats_stderr("test: "); + return 0; +} diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h new file mode 100644 index 000000000000..b288f0bd8e33 --- /dev/null +++ b/tests/lib/test_typelist.h @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +/* C++ called, they want their templates back */ +#define item concat(item_, TYPE) +#define itm concat(itm_, TYPE) +#define head concat(head_, TYPE) +#define list concat(TYPE, ) +#define list_head concat(TYPE, _head) +#define list_item concat(TYPE, _item) +#define list_cmp concat(TYPE, _cmp) +#define list_hash concat(TYPE, _hash) +#define list_init concat(TYPE, _init) +#define list_fini concat(TYPE, _fini) +#define list_first concat(TYPE, _first) +#define list_next concat(TYPE, _next) +#define list_next_safe concat(TYPE, _next_safe) +#define list_count concat(TYPE, _count) +#define list_add concat(TYPE, _add) +#define list_add_head concat(TYPE, _add_head) +#define list_add_tail concat(TYPE, _add_tail) +#define list_add_after concat(TYPE, _add_after) +#define list_find concat(TYPE, _find) +#define list_find_lt concat(TYPE, _find_lt) +#define list_find_gteq concat(TYPE, _find_gteq) +#define list_del concat(TYPE, _del) +#define list_pop concat(TYPE, _pop) + +#define ts_hash concat(ts_hash_, TYPE) + +#ifndef REALTYPE +#define REALTYPE TYPE +#endif + +PREDECL(REALTYPE, list) +struct item { + uint64_t val; + struct list_item itm; + int scratchpad; +}; + +#if IS_SORTED(REALTYPE) +static int list_cmp(const struct item *a, const struct item *b); + +#if IS_HASH(REALTYPE) +static uint32_t list_hash(const struct item *a); +DECLARE(REALTYPE, list, struct item, itm, list_cmp, list_hash) + +static uint32_t list_hash(const struct item *a) +{ +#ifdef SHITTY_HASH + /* crappy hash to get some hash collisions */ + return a->val ^ (a->val << 29) ^ 0x55AA0000U; +#else + return jhash_1word(a->val, 0xdeadbeef); +#endif +} + +#else +DECLARE(REALTYPE, list, struct item, itm, list_cmp) +#endif + +static int list_cmp(const struct item *a, const struct item *b) +{ + if (a->val > b->val) + return 1; + if (a->val < b->val) + return -1; + return 0; +} + +#else /* !IS_SORTED */ +DECLARE(REALTYPE, list, struct item, itm) +#endif + +#define NITEM 10000 +struct item itm[NITEM]; +static struct list_head head = concat(INIT_, REALTYPE)(head); + +static void ts_hash(const char *text, const char *expect) +{ + int64_t us = monotime_since(&ref, NULL); + SHA256_CTX ctx; + struct item *item; + unsigned i = 0; + uint8_t hash[32]; + char hashtext[65]; + uint32_t count; + + count = htonl(list_count(&head)); + + SHA256_Init(&ctx); + SHA256_Update(&ctx, &count, sizeof(count)); + + frr_each (list, &head, item) { + struct { + uint32_t val_upper, val_lower, index; + } hashitem = { + htonl(item->val >> 32), + htonl(item->val & 0xFFFFFFFFULL), + htonl(i), + }; + SHA256_Update(&ctx, &hashitem, sizeof(hashitem)); + i++; + assert(i < count); + } + SHA256_Final(hash, &ctx); + + for (i = 0; i < sizeof(hash); i++) + sprintf(hashtext + i * 2, "%02x", hash[i]); + + printf("%7"PRId64"us %-25s %s%s\n", us, text, + expect ? " " : "*", hashtext); + if (expect && strcmp(expect, hashtext)) { + printf("%-21s %s\n", "EXPECTED:", expect); + assert(0); + } + monotime(&ref); +} +/* hashes will have different item ordering */ +#if IS_HASH(REALTYPE) || IS_HEAP(REALTYPE) +#define ts_hashx(pos, csum) ts_hash(pos, NULL) +#else +#define ts_hashx(pos, csum) ts_hash(pos, csum) +#endif + +static void concat(test_, TYPE)(void) +{ + size_t i, j, k, l; + struct prng *prng; + struct item *item, *prev __attribute__((unused)); + struct item dummy __attribute__((unused)); + + memset(itm, 0, sizeof(itm)); + for (i = 0; i < NITEM; i++) + itm[i].val = i; + + printf("%s start\n", str(TYPE)); + ts_start(); + + list_init(&head); + assert(list_first(&head) == NULL); + + ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); + +#if IS_SORTED(REALTYPE) + prng = prng_new(0); + k = 0; + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 0) { + list_add(&head, &itm[j]); + itm[j].scratchpad = 1; + k++; + } +#if !IS_HEAP(REALTYPE) + else + assert(list_add(&head, &itm[j]) == &itm[j]); +#endif + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hashx("fill", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838"); + + k = 0; + prev = NULL; + frr_each(list, &head, item) { +#if IS_HASH(REALTYPE) || IS_HEAP(REALTYPE) + /* hash table doesn't give sorting */ + (void)prev; +#else + assert(!prev || prev->val < item->val); +#endif + prev = item; + k++; + } + assert(list_count(&head) == k); + ts_ref("walk"); + +#if IS_UNIQ(REALTYPE) + prng_free(prng); + prng = prng_new(0); + + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + dummy.val = j; + assert(list_find(&head, &dummy) == &itm[j]); + } + ts_ref("find"); + + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + memset(&dummy, 0, sizeof(dummy)); + dummy.val = j; + if (itm[j].scratchpad) + assert(list_add(&head, &dummy) == &itm[j]); + else { + assert(list_add(&head, &dummy) == NULL); + list_del(&head, &dummy); + } + } + ts_hashx("add-dup", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838"); + +#elif IS_HEAP(REALTYPE) + /* heap - partially sorted. */ + prev = NULL; + l = k / 2; + for (i = 0; i < l; i++) { + item = list_pop(&head); + if (prev) + assert(prev->val < item->val); + item->scratchpad = 0; + k--; + prev = item; + } + ts_hash("pop", NULL); + +#else /* !IS_UNIQ(REALTYPE) && !IS_HEAP(REALTYPE) */ + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + memset(&dummy, 0, sizeof(dummy)); + dummy.val = j; + + list_add(&head, &dummy); + if (itm[j].scratchpad) { + struct item *lt, *gteq, dummy2; + + assert(list_next(&head, &itm[j]) == &dummy || + list_next(&head, &dummy) == &itm[j]); + + memset(&dummy2, 0, sizeof(dummy)); + dummy2.val = j; + lt = list_find_lt(&head, &dummy2); + gteq = list_find_gteq(&head, &dummy2); + + assert(gteq == &itm[j] || gteq == &dummy); + if (lt) + assert(list_next(&head, lt) == &itm[j] || + list_next(&head, lt) == &dummy); + else + assert(list_first(&head) == &itm[j] || + list_first(&head) == &dummy); + } else if (list_next(&head, &dummy)) + assert(list_next(&head, &dummy)->val > j); + list_del(&head, &dummy); + } + ts_hash("add-dup+find_{lt,gteq}", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838"); +#endif +#if !IS_HASH(REALTYPE) && !IS_HEAP(REALTYPE) + prng_free(prng); + prng = prng_new(123456); + + l = 0; + for (i = 0; i < NITEM; i++) { + struct item *lt, *gteq, *tmp; + + j = prng_rand(prng) % NITEM; + dummy.val = j; + + lt = list_find_lt(&head, &dummy); + gteq = list_find_gteq(&head, &dummy); + + if (lt) { + assert(lt->val < j); + tmp = list_next(&head, lt); + assert(tmp == gteq); + assert(!tmp || tmp->val >= j); + } else + assert(gteq == list_first(&head)); + + if (gteq) + assert(gteq->val >= j); + } + ts_ref("find_{lt,gteq}"); +#endif /* !IS_HASH */ + + prng_free(prng); + prng = prng_new(0); + + l = 0; + for (i = 0; i < NITEM; i++) { + (void)prng_rand(prng); + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 1) { + list_del(&head, &itm[j]); + itm[j].scratchpad = 0; + l++; + } + } + assert(l + list_count(&head) == k); + ts_hashx("del", "cb2e5d80f08a803ef7b56c15e981b681adcea214bebc2f55e12e0bfb242b07ca"); + + frr_each_safe(list, &head, item) { + assert(item->scratchpad != 0); + + if (item->val & 1) { + list_del(&head, item); + item->scratchpad = 0; + l++; + } + } + assert(l + list_count(&head) == k); + ts_hashx("frr_each_safe+del", "e0beb71dd963a75af05b722b8e71b61b304587d860c8accdc4349067542b86bb"); + +#else /* !IS_SORTED */ + prng = prng_new(0); + k = 0; + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 0) { + list_add_tail(&head, &itm[j]); + itm[j].scratchpad = 1; + k++; + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("fill / add_tail", "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19"); + + for (i = 0; i < NITEM / 2; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 1) { + list_del(&head, &itm[j]); + itm[j].scratchpad = 0; + k--; + } + } + ts_hash("del-prng", "86d568a95eb429dab3162976c5a5f3f75aabc835932cd682aa280b6923549564"); + + l = 0; + while ((item = list_pop(&head))) { + assert(item->scratchpad != 0); + + item->scratchpad = 0; + l++; + } + assert(l == k); + assert(list_count(&head) == 0); + assert(list_first(&head) == NULL); + ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); + + prng_free(prng); + prng = prng_new(0x1e5a2d69); + + k = 0; + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 0) { + list_add_head(&head, &itm[j]); + itm[j].scratchpad = 1; + k++; + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("fill / add_head", "3084d8f8a28b8c756ccc0a92d60d86f6d776273734ddc3f9e1d89526f5ca2795"); + + for (i = 0; i < NITEM / 2; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 1) { + list_del(&head, &itm[j]); + itm[j].scratchpad = 0; + k--; + } + } + ts_hash("del-prng", "dc916fa7ea4418792c7c8232d74df2887f9975ead4222f4b977be6bc0b52285e"); + + l = 0; + while ((item = list_pop(&head))) { + assert(item->scratchpad != 0); + + item->scratchpad = 0; + l++; + } + assert(l == k); + assert(list_count(&head) == 0); + assert(list_first(&head) == NULL); + ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); + + prng_free(prng); + prng = prng_new(0x692d1e5a); + + k = 0; + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 0) { + if (prng_rand(prng) & 1) { + list_add_tail(&head, &itm[j]); + } else { + list_add_head(&head, &itm[j]); + } + itm[j].scratchpad = 1; + k++; + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("fill / add_{head,tail}", "93fa180a575c96e4b6c3775c2de7843ee3254dd6ed5af699bbe155f994114b06"); + + for (i = 0; i < NITEM * 3; i++) { + int op = prng_rand(prng); + j = prng_rand(prng) % NITEM; + + if (op & 1) { + /* delete or pop */ + if (op & 2) { + item = list_pop(&head); + if (!item) + continue; + } else { + item = &itm[j]; + if (item->scratchpad == 0) + continue; + list_del(&head, item); + } + item->scratchpad = 0; + k--; + } else { + item = &itm[j]; + if (item->scratchpad != 0) + continue; + + item->scratchpad = 1; + k++; + + switch ((op >> 1) & 1) { + case 0: + list_add_head(&head, item); + break; + case 1: + list_add_tail(&head, item); + break; + default: + assert(0); + } + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("prng add/del", "4909f31d06bb006efca4dfeebddb8de071733ddf502f89b6d532155208bbc6df"); + +#if !IS_ATOMIC(REALTYPE) + /* variant with add_after */ + + for (i = 0; i < NITEM * 3; i++) { + int op = prng_rand(prng); + j = prng_rand(prng) % NITEM; + + if (op & 1) { + /* delete or pop */ + if (op & 2) { + item = list_pop(&head); + if (!item) + continue; + } else { + item = &itm[j]; + if (item->scratchpad == 0) + continue; + list_del(&head, item); + } + item->scratchpad = 0; + k--; + } else { + item = &itm[j]; + if (item->scratchpad != 0) + continue; + + item->scratchpad = 1; + k++; + + switch ((op >> 1) & 3) { + case 0: + list_add_head(&head, item); + break; + case 1: + list_add_tail(&head, item); + break; + case 2: + case 3: + prev = NULL; + l = 0; + do { + j = prng_rand(prng) % NITEM; + prev = &itm[j]; + if (prev->scratchpad == 0 + || prev == item) + prev = NULL; + l++; + } while (!prev && l < 10); + list_add_after(&head, prev, item); + break; + default: + assert(0); + } + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("prng add/after/del", "84c5fc83294eabebb9808ccbba32a303c4fca084db87ed1277d2bae1f8c5bee4"); +#endif + + l = 0; +#endif + + while ((item = list_pop(&head))) { + assert(item->scratchpad != 0); + + item->scratchpad = 0; + l++; + } + assert(l == k); + assert(list_count(&head) == 0); + assert(list_first(&head) == NULL); + ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); + + list_fini(&head); + ts_ref("fini"); + ts_end(); + printf("%s end\n", str(TYPE)); +} + +#undef ts_hashx + +#undef item +#undef itm +#undef head +#undef list +#undef list_head +#undef list_item +#undef list_cmp +#undef list_hash +#undef list_init +#undef list_fini +#undef list_first +#undef list_next +#undef list_next_safe +#undef list_count +#undef list_add +#undef list_add_head +#undef list_add_tail +#undef list_add_after +#undef list_find +#undef list_find_lt +#undef list_find_gteq +#undef list_del +#undef list_pop + +#undef REALTYPE +#undef TYPE diff --git a/tests/lib/test_typelist.py b/tests/lib/test_typelist.py new file mode 100644 index 000000000000..0b3c74397195 --- /dev/null +++ b/tests/lib/test_typelist.py @@ -0,0 +1,19 @@ +import frrtest + +class TestTypelist(frrtest.TestMultiOut): + program = './test_typelist' + +TestTypelist.onesimple('LIST end') +TestTypelist.onesimple('DLIST end') +TestTypelist.onesimple('ATOMLIST end') +TestTypelist.onesimple('HEAP end') +TestTypelist.onesimple('SORTLIST_UNIQ end') +TestTypelist.onesimple('SORTLIST_NONUNIQ end') +TestTypelist.onesimple('HASH end') +TestTypelist.onesimple('HASH_collisions end') +TestTypelist.onesimple('SKIPLIST_UNIQ end') +TestTypelist.onesimple('SKIPLIST_NONUNIQ end') +TestTypelist.onesimple('RBTREE_UNIQ end') +TestTypelist.onesimple('RBTREE_NONUNIQ end') +TestTypelist.onesimple('ATOMSORT_UNIQ end') +TestTypelist.onesimple('ATOMSORT_NONUNIQ end') diff --git a/tests/subdir.am b/tests/subdir.am index 365fe00cc6af..41f1a4873bb4 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -2,8 +2,6 @@ # tests # -PYTHON ?= python - if BGPD TESTS_BGPD = \ tests/bgpd/test_aspath \ @@ -47,6 +45,7 @@ tests/ospf6d/test_lsdb-test_lsdb.$(OBJEXT): tests/ospf6d/test_lsdb_clippy.c check_PROGRAMS = \ tests/lib/cxxcompat \ + tests/lib/test_atomlist \ tests/lib/test_buffer \ tests/lib/test_checksum \ tests/lib/test_heavy_thread \ @@ -55,16 +54,20 @@ check_PROGRAMS = \ tests/lib/test_idalloc \ tests/lib/test_memory \ tests/lib/test_nexthop_iter \ + tests/lib/test_ntop \ + tests/lib/test_printfrr \ tests/lib/test_privs \ tests/lib/test_ringbuf \ tests/lib/test_srcdest_table \ tests/lib/test_segv \ + tests/lib/test_seqlock \ tests/lib/test_sig \ tests/lib/test_stream \ tests/lib/test_table \ tests/lib/test_timer_correctness \ tests/lib/test_timer_performance \ tests/lib/test_ttable \ + tests/lib/test_typelist \ tests/lib/test_zlog \ tests/lib/test_graph \ tests/lib/cli/test_cli \ @@ -103,6 +106,7 @@ noinst_HEADERS += \ tests/helpers/c/prng.h \ tests/helpers/c/tests.h \ tests/lib/cli/common_cli.h \ + tests/lib/test_typelist.h \ # end # @@ -189,6 +193,10 @@ tests_lib_northbound_test_oper_data_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_northbound_test_oper_data_LDADD = $(ALL_TESTS_LDADD) tests_lib_northbound_test_oper_data_SOURCES = tests/lib/northbound/test_oper_data.c nodist_tests_lib_northbound_test_oper_data_SOURCES = yang/frr-test-module.yang.c +tests_lib_test_atomlist_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_atomlist_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_atomlist_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_atomlist_SOURCES = tests/lib/test_atomlist.c tests_lib_test_buffer_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_buffer_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_buffer_LDADD = $(ALL_TESTS_LDADD) @@ -224,6 +232,14 @@ tests_lib_test_nexthop_iter_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_nexthop_iter_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_nexthop_iter_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_nexthop_iter_SOURCES = tests/lib/test_nexthop_iter.c tests/helpers/c/prng.c +tests_lib_test_ntop_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_ntop_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_ntop_LDADD = # none +tests_lib_test_ntop_SOURCES = tests/lib/test_ntop.c tests/helpers/c/prng.c +tests_lib_test_printfrr_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_printfrr_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_printfrr_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_printfrr_SOURCES = tests/lib/test_printfrr.c tests_lib_test_privs_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_privs_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_privs_LDADD = $(ALL_TESTS_LDADD) @@ -236,6 +252,10 @@ tests_lib_test_segv_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_segv_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_segv_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_segv_SOURCES = tests/lib/test_segv.c +tests_lib_test_seqlock_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_seqlock_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_seqlock_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_seqlock_SOURCES = tests/lib/test_seqlock.c tests_lib_test_sig_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_sig_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_sig_LDADD = $(ALL_TESTS_LDADD) @@ -264,6 +284,10 @@ tests_lib_test_ttable_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_ttable_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_ttable_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_ttable_SOURCES = tests/lib/test_ttable.c +tests_lib_test_typelist_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_typelist_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_typelist_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_typelist_SOURCES = tests/lib/test_typelist.c tests/helpers/c/prng.c tests_lib_test_zlog_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_zlog_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_zlog_LDADD = $(ALL_TESTS_LDADD) @@ -301,7 +325,10 @@ EXTRA_DIST += \ tests/lib/northbound/test_oper_data.in \ tests/lib/northbound/test_oper_data.py \ tests/lib/northbound/test_oper_data.refout \ + tests/lib/test_atomlist.py \ tests/lib/test_nexthop_iter.py \ + tests/lib/test_ntop.py \ + tests/lib/test_printfrr.py \ tests/lib/test_ringbuf.py \ tests/lib/test_srcdest_table.py \ tests/lib/test_stream.py \ @@ -310,6 +337,7 @@ EXTRA_DIST += \ tests/lib/test_timer_correctness.py \ tests/lib/test_ttable.py \ tests/lib/test_ttable.refout \ + tests/lib/test_typelist.py \ tests/lib/test_zlog.py \ tests/lib/test_graph.py \ tests/lib/test_graph.refout \ diff --git a/tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref b/tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref index c29ed3db6195..1e8f67f3f9a6 100644 --- a/tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref +++ b/tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref @@ -1,5 +1,5 @@ r1-eth0 is up - ifindex 2, MTU 1500 bytes, BW XX Mbit + ifindex X, MTU 1500 bytes, BW XX Mbit Internet Address 192.168.0.1/24, Broadcast 192.168.0.255, Area 0.0.0.0 MTU mismatch detection: enabled Router ID 192.168.0.1, Network Type BROADCAST, Cost: 10 @@ -10,7 +10,7 @@ r1-eth0 is up Hello due in XX.XXXs Neighbor Count is 0, Adjacent neighbor count is 0 r1-eth3 is up - ifindex 5, MTU 1500 bytes, BW XX Mbit + ifindex X, MTU 1500 bytes, BW XX Mbit Internet Address 192.168.3.1/26, Broadcast 192.168.3.63, Area 0.0.0.0 MTU mismatch detection: enabled Router ID 192.168.0.1, Network Type BROADCAST, Cost: 10 diff --git a/tests/topotests/all-protocol-startup/test_all_protocol_startup.py b/tests/topotests/all-protocol-startup/test_all_protocol_startup.py index 239de55bd6f2..9658c080c0c3 100755 --- a/tests/topotests/all-protocol-startup/test_all_protocol_startup.py +++ b/tests/topotests/all-protocol-startup/test_all_protocol_startup.py @@ -480,6 +480,8 @@ def test_ospfv2_interfaces(): actual = net['r%s' % i].cmd('vtysh -c "show ip ospf interface" 2> /dev/null').rstrip() # Mask out Bandwidth portion. They may change.. actual = re.sub(r"BW [0-9]+ Mbit", "BW XX Mbit", actual) + actual = re.sub(r"ifindex [0-9]", "ifindex X", actual) + # Drop time in next due actual = re.sub(r"Hello due in [0-9\.]+s", "Hello due in XX.XXXs", actual) # Fix 'MTU mismatch detection: enabled' vs 'MTU mismatch detection:enabled' - accept both diff --git a/tests/topotests/bfd-bgp-cbit-topo3/__init__.py b/tests/topotests/bfd-bgp-cbit-topo3/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/bgp_ipv6_routes_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgp_ipv6_routes_down.json new file mode 100644 index 000000000000..54ae57f7be58 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgp_ipv6_routes_down.json @@ -0,0 +1,103 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "10.254.254.1", + "localAS": 101, + "routes": + { + "2001:db8:6::/64": [ + { + "stale": true, + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:6::", + "prefixLen": 64, + "network": "2001:db8:6::\/64", + "med": 0, + "metric": 0, + "weight": 0, + "peerId": "2001:db8:4::1", + "origin": "IGP", + "nexthops": [ + { "ip": "2001:db8:4::1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:7::/64": [ + { + "stale": true, + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:7::", + "prefixLen": 64, "network": + "2001:db8:7::\/64", + "med": 0, + "metric": 0, + "weight": 0, + "peerId": "2001:db8:4::1", + "origin": "IGP", + "nexthops": [ + { + "ip": "2001:db8:4::1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:8::/64": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:8::", + "prefixLen": 64, + "network": "2001:db8:8::\/64", + "med": 0, + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "origin": "IGP", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:9::/64": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:9::", + "prefixLen": 64, + "network": "2001:db8:9::\/64", + "med": 0, + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "origin": "IGP", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} + diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf new file mode 100644 index 000000000000..fa6d60a8fcea --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf @@ -0,0 +1,20 @@ +debug bgp neighbor-events +router bgp 101 + bgp router-id 10.254.254.1 + timers bgp 8 24 + bgp graceful-restart + neighbor 2001:db8:4::1 remote-as 102 + neighbor 2001:db8:4::1 remote-as external + neighbor 2001:db8:4::1 bfd + neighbor 2001:db8:4::1 bfd check-control-plane-failure + neighbor 2001:db8:4::1 update-source 2001:db8:1::1 + neighbor 2001:db8:4::1 ebgp-multihop 5 + address-family ipv4 unicast + no neighbor 2001:db8:4::1 activate + exit-address-family + address-family ipv6 unicast + network 2001:db8:8::/64 + network 2001:db8:9::/64 + neighbor 2001:db8:4::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/ipv6_routes.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/ipv6_routes.json new file mode 100644 index 000000000000..8eea183285f1 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/ipv6_routes.json @@ -0,0 +1,80 @@ +{ + "2001:db8:1::/64": [{ + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [{ + "directlyConnected": true, + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "2001:db8:4::/64": [{ + "distance": 1, + "protocol": "static", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:4::/64", + "nexthops": [{ + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "2001:db8:6::/64": [{ + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:6::/64", + "nexthops": [{ + "ip":"2001:db8:4::1", + "active": true, + "afi": "ipv6", + "recursive":true + }, + { + "fib":true, + "ip":"2001:db8:1::2", + "afi": "ipv6", + "interfaceName": "r1-eth0" + } + ] + } + ], + "2001:db8:7::/64": [{ + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:7::/64", + "nexthops": [{ + "ip":"2001:db8:4::1", + "active": true, + "afi": "ipv6", + "recursive": true + }, + { + "fib":true, + "ip":"2001:db8:1::2", + "afi": "ipv6", + "interfaceName":"r1-eth0" + } + ] + } + ] +} diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json new file mode 100644 index 000000000000..d1927ae49a14 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json @@ -0,0 +1,16 @@ +[ + { + "multihop":true, + "peer":"2001:db8:4::1", + "local":"2001:db8:1::1", + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":300, + "transmit-interval":300, + "echo-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-interval":50 + } +] diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json new file mode 100644 index 000000000000..25b47f18ecf4 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json @@ -0,0 +1,14 @@ +[ + { + "multihop":true, + "peer":"2001:db8:4::1", + "local":"2001:db8:1::1", + "status":"up", + "receive-interval":300, + "transmit-interval":300, + "echo-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-interval":50 + } +] diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/zebra.conf b/tests/topotests/bfd-bgp-cbit-topo3/r1/zebra.conf new file mode 100644 index 000000000000..3a30cd42fb4a --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/zebra.conf @@ -0,0 +1,8 @@ +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ipv6 address 2001:db8:1::1/64 +! +ipv6 route 2001:db8:4::/64 2001:db8:1::2 + diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r2/zebra.conf b/tests/topotests/bfd-bgp-cbit-topo3/r2/zebra.conf new file mode 100644 index 000000000000..0f70be1bda49 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r2/zebra.conf @@ -0,0 +1,9 @@ +ip forwarding +ipv6 forwarding +! +interface r2-eth0 + ipv6 address 2001:db8:1::2/64 +! +interface r2-eth1 + ipv6 address 2001:db8:4::2/64 +! diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/bgp_ipv6_routes_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgp_ipv6_routes_down.json new file mode 100644 index 000000000000..a3bb222504f2 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgp_ipv6_routes_down.json @@ -0,0 +1,55 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "10.254.254.3", + "localAS": 102, + "routes": + { + "2001:db8:6::/64": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:6::", + "prefixLen": 64, + "network": "2001:db8:6::\/64", + "med": 0, + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "origin": "IGP", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:7::/64": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:7::", + "prefixLen": 64, + "network": "2001:db8:7::\/64", + "med": 0, + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "origin": "IGP", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf new file mode 100644 index 000000000000..ea5334029c84 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf @@ -0,0 +1,25 @@ +debug bgp neighbor-events +router bgp 102 + bgp router-id 10.254.254.3 + timers bgp 20 60 + bgp graceful-restart + ! simulate NSF machine + bgp graceful-restart preserve-fw-state + bgp graceful-restart stalepath-time 900 + bgp graceful-restart restart-time 900 + neighbor 2001:db8:1::1 remote-as 101 + neighbor 2001:db8:1::1 remote-as external + neighbor 2001:db8:1::1 update-source 2001:db8:4::1 + neighbor 2001:db8:1::1 bfd + neighbor 2001:db8:1::1 ebgp-multihop 5 + ! + address-family ipv4 unicast + no neighbor 2001:db8:1::1 activate + exit-address-family + ! + address-family ipv6 unicast + neighbor 2001:db8:1::1 activate + network 2001:db8:6::/64 + network 2001:db8:7::/64 + exit-address-family +! diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/ipv6_routes.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/ipv6_routes.json new file mode 100644 index 000000000000..09808cc09a6c --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/ipv6_routes.json @@ -0,0 +1,80 @@ +{ + "2001:db8:1::/64": [{ + "distance": 1, + "protocol": "static", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [{ + "interfaceName": "r3-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "2001:db8:4::/64": [{ + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:4::/64", + "nexthops": [{ + "directlyConnected": true, + "interfaceName": "r3-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "2001:db8:8::/64": [{ + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:8::/64", + "nexthops": [{ + "ip":"2001:db8:1::1", + "active": true, + "afi": "ipv6", + "recursive":true + }, + { + "fib":true, + "ip":"2001:db8:4::2", + "afi": "ipv6", + "interfaceName":"r3-eth0" + } + ] + } + ], + "2001:db8:9::/64": [{ + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:9::/64", + "nexthops": [{ + "ip":"2001:db8:1::1", + "active": true, + "afi": "ipv6", + "recursive":true + }, + { + "fib":true, + "ip":"2001:db8:4::2", + "afi": "ipv6", + "interfaceName":"r3-eth0" + } + ] + } + ] +} diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json new file mode 100644 index 000000000000..5193f2a6e25f --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json @@ -0,0 +1,16 @@ +[ + { + "multihop":true, + "peer":"2001:db8:1::1", + "local":"2001:db8:4::1", + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":300, + "transmit-interval":300, + "echo-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-interval":50 + } +] diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json new file mode 100644 index 000000000000..9e4bd2633f47 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json @@ -0,0 +1,14 @@ +[ + { + "multihop":true, + "peer":"2001:db8:1::1", + "local":"2001:db8:4::1", + "status":"down", + "receive-interval":300, + "transmit-interval":300, + "echo-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-interval":50 + } +] diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/zebra.conf b/tests/topotests/bfd-bgp-cbit-topo3/r3/zebra.conf new file mode 100644 index 000000000000..7759251dc5b2 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/zebra.conf @@ -0,0 +1,7 @@ +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ipv6 address 2001:db8:4::1/64 +! +ipv6 route 2001:db8:1::/64 2001:db8:4::2 diff --git a/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.dot b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.dot new file mode 100644 index 000000000000..270de829c3c5 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.dot @@ -0,0 +1,58 @@ +## 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="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n10.0.3.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + + r2 -- sw2 [label="eth1"]; + r3 -- sw2 [label="eth0"]; +} diff --git a/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py new file mode 100755 index 000000000000..59858d6fd356 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python + +# +# test_bfd_bgp_cbit_topo3.py +# +# Copyright (c) 2019 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bfd_bgp_cbit_topo3.py: Test the FRR/Quagga BFD daemon with multihop and BGP +unnumbered. +""" + +import os +import sys +import json +from functools import partial +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 +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +class BFDTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 4 routers. + 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['r2']) + switch.add_link(tgen.gears['r3']) + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BFDTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)), + ) + router.load_config( + TopoRouter.RD_BFD, + os.path.join(CWD, '{}/bfdd.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + # Initialize all routers. + 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() + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged before checking for the BFD + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + if router.name == 'r2': + continue + json_file = '{}/{}/ipv6_routes.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ipv6 route json', expected) + _, result = topotest.run_and_expect(test_func, None, count=40, + wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_bfd_connection(): + "Assert that the BFD peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('waiting for bfd peers to go up') + for router in tgen.routers().values(): + if router.name == 'r2': + continue + json_file = '{}/{}/peers.json'.format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(topotest.router_json_cmp, + router, 'show bfd peers json', expected) + _, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + +def test_bfd_loss_intermediate(): + """ + Assert that BFD notices the bfd link down failure. + but BGP entries should still be present + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('removing IPv6 address from r2 to simulate loss of connectivity') + # Disable r2-eth0 ipv6 address + cmd = 'vtysh -c \"configure terminal\" -c \"interface r2-eth1\" -c "no ipv6 address 2001:db8:4::2/64\"' + tgen.net['r2'].cmd(cmd) + + # Wait the minimum time we can before checking that BGP/BFD + # converged. + logger.info('waiting for BFD converge down') + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + if router.name == 'r2': + continue + json_file = '{}/{}/peers_down.json'.format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(topotest.router_json_cmp, + router, 'show bfd peers json', expected) + _, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + logger.info('waiting for BGP entries to become stale') + for router in tgen.routers().values(): + if router.name == 'r2': + continue + json_file = '{}/{}/bgp_ipv6_routes_down.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=50, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + logger.info("Checking IPv6 routes on r1 should still be present") + for router in tgen.routers().values(): + if router.name == 'r2': + continue + if router.name == 'r3': + continue + json_file = '{}/r1/ipv6_routes.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ipv6 route json', expected) + _, result = topotest.run_and_expect(test_func, None, count=30, + wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + +def test_bfd_comes_back_again(): + """ + Assert that BFD notices the bfd link up + and that ipv6 entries appear back + """ + tgen = get_topogen() + logger.info('re-adding IPv6 address from r2 to simulate connectivity is back') + # adds back r2-eth0 ipv6 address + cmd = 'vtysh -c \"configure terminal\" -c \"interface r2-eth1\" -c "ipv6 address 2001:db8:4::2/64\"' + tgen.net['r2'].cmd(cmd) + + # Wait the minimum time we can before checking that BGP/BFD + # converged. + logger.info('waiting for BFD to converge up') + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + if router.name == 'r2': + continue + json_file = '{}/{}/peers.json'.format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(topotest.router_json_cmp, + router, 'show bfd peers json', expected) + _, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + 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/bfd-vrf-topo1/__init__.py b/tests/topotests/bfd-vrf-topo1/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bfd-vrf-topo1/r1/bfdd.conf b/tests/topotests/bfd-vrf-topo1/r1/bfdd.conf new file mode 100644 index 000000000000..3466e6a3caa0 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r1/bfdd.conf @@ -0,0 +1,6 @@ +bfd + peer 192.168.0.2 vrf r1-cust1 + echo-mode + no shutdown + ! +! diff --git a/tests/topotests/bfd-vrf-topo1/r1/bgp_prefixes.json b/tests/topotests/bfd-vrf-topo1/r1/bgp_prefixes.json new file mode 100644 index 000000000000..4b2cc1ad621f --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r1/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.2/32": [ + { + "aspath": "102", + "prefix": "10.254.254.2", + "valid": true, + "peerId": "192.168.0.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.3/32": [ + { + "aspath": "102 103", + "prefix": "10.254.254.3", + "valid": true, + "peerId": "192.168.0.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.4/32": [ + { + "aspath": "102 104", + "prefix": "10.254.254.4", + "valid": true, + "peerId": "192.168.0.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.2", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r1/bgp_summary.json b/tests/topotests/bfd-vrf-topo1/r1/bgp_summary.json new file mode 100644 index 000000000000..fa07d60df9af --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r1/bgp_summary.json @@ -0,0 +1,11 @@ +{ + "ipv4Unicast": { + "as": 101, + "peers": { + "192.168.0.2": { + "remoteAs": 102, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r1/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r1/bgpd.conf new file mode 100644 index 000000000000..7ad4e2bd7459 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r1/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 101 vrf r1-cust1 + neighbor 192.168.0.2 remote-as 102 +! neighbor 192.168.0.2 ebgp-multihop 10 + neighbor 192.168.0.2 bfd + address-family ipv4 unicast + network 10.254.254.1/32 + exit-address-family +! diff --git a/tests/topotests/bfd-vrf-topo1/r1/peers.json b/tests/topotests/bfd-vrf-topo1/r1/peers.json new file mode 100644 index 000000000000..f49768ff759b --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r1/peers.json @@ -0,0 +1,8 @@ +[ + { + "remote-receive-interval": 1000, + "remote-transmit-interval": 500, + "peer": "192.168.0.2", + "status": "up" + } +] diff --git a/tests/topotests/bfd-vrf-topo1/r1/zebra.conf b/tests/topotests/bfd-vrf-topo1/r1/zebra.conf new file mode 100644 index 000000000000..fcd1e7db1776 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r1/zebra.conf @@ -0,0 +1,3 @@ +interface r1-eth0 vrf r1-cust1 + ip address 192.168.0.1/24 +! diff --git a/tests/topotests/bfd-vrf-topo1/r2/bfdd.conf b/tests/topotests/bfd-vrf-topo1/r2/bfdd.conf new file mode 100644 index 000000000000..3481ea8c8707 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r2/bfdd.conf @@ -0,0 +1,12 @@ +bfd + peer 192.168.0.1 vrf r2-cust1 + receive-interval 1000 + transmit-interval 500 + echo-mode + no shutdown + ! + peer 192.168.1.1 vrf r2-cust1 + echo-mode + no shutdown + ! +! diff --git a/tests/topotests/bfd-vrf-topo1/r2/bgp_prefixes.json b/tests/topotests/bfd-vrf-topo1/r2/bgp_prefixes.json new file mode 100644 index 000000000000..39f3c0a83590 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r2/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.1/32": [ + { + "aspath": "101", + "prefix": "10.254.254.1", + "valid": true, + "peerId": "192.168.0.1", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.1", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.3/32": [ + { + "aspath": "103", + "prefix": "10.254.254.3", + "valid": true, + "peerId": "192.168.1.1", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.1", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.4/32": [ + { + "aspath": "104", + "prefix": "10.254.254.4", + "valid": true, + "peerId": "192.168.2.1", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.1", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r2/bgp_summary.json b/tests/topotests/bfd-vrf-topo1/r2/bgp_summary.json new file mode 100644 index 000000000000..c0ef11ac5f78 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r2/bgp_summary.json @@ -0,0 +1,19 @@ +{ + "ipv4Unicast": { + "as": 102, + "peers": { + "192.168.0.1": { + "remoteAs": 101, + "state": "Established" + }, + "192.168.1.1": { + "remoteAs": 103, + "state": "Established" + }, + "192.168.2.1": { + "remoteAs": 104, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r2/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r2/bgpd.conf new file mode 100644 index 000000000000..0715ea32a5e3 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r2/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 102 vrf r2-cust1 + neighbor 192.168.0.1 remote-as 101 + neighbor 192.168.0.1 bfd + neighbor 192.168.1.1 remote-as 103 + neighbor 192.168.1.1 bfd + neighbor 192.168.2.1 remote-as 104 + neighbor 192.168.2.1 bfd + address-family ipv4 unicast + network 10.254.254.2/32 + exit-address-family +! diff --git a/tests/topotests/bfd-vrf-topo1/r2/peers.json b/tests/topotests/bfd-vrf-topo1/r2/peers.json new file mode 100644 index 000000000000..5035d643c514 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r2/peers.json @@ -0,0 +1,17 @@ +[ + { + "peer": "192.168.0.1", + "status": "up" + }, + { + "remote-echo-interval": 100, + "peer": "192.168.1.1", + "status": "up" + }, + { + "remote-transmit-interval": 2000, + "remote-receive-interval": 2000, + "peer": "192.168.2.1", + "status": "up" + } +] diff --git a/tests/topotests/bfd-vrf-topo1/r2/zebra.conf b/tests/topotests/bfd-vrf-topo1/r2/zebra.conf new file mode 100644 index 000000000000..daffd1912e82 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r2/zebra.conf @@ -0,0 +1,9 @@ +interface r2-eth0 vrf r2-cust1 + ip address 192.168.0.2/24 +! +interface r2-eth1 vrf r2-cust1 + ip address 192.168.1.2/24 +! +interface r2-eth2 vrf r2-cust1 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bfd-vrf-topo1/r3/bfdd.conf b/tests/topotests/bfd-vrf-topo1/r3/bfdd.conf new file mode 100644 index 000000000000..f6921b781890 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r3/bfdd.conf @@ -0,0 +1,7 @@ +bfd + peer 192.168.1.2 vrf r3-cust1 + echo-interval 100 + echo-mode + no shutdown + ! +! diff --git a/tests/topotests/bfd-vrf-topo1/r3/bgp_prefixes.json b/tests/topotests/bfd-vrf-topo1/r3/bgp_prefixes.json new file mode 100644 index 000000000000..c92d4e052a33 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r3/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.1/32": [ + { + "aspath": "102 101", + "prefix": "10.254.254.1", + "valid": true, + "peerId": "192.168.1.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.2/32": [ + { + "aspath": "102", + "prefix": "10.254.254.2", + "valid": true, + "peerId": "192.168.1.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.4/32": [ + { + "aspath": "102 104", + "prefix": "10.254.254.4", + "valid": true, + "peerId": "192.168.1.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.2", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r3/bgp_summary.json b/tests/topotests/bfd-vrf-topo1/r3/bgp_summary.json new file mode 100644 index 000000000000..d47833377bfd --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r3/bgp_summary.json @@ -0,0 +1,11 @@ +{ + "ipv4Unicast": { + "as": 103, + "peers": { + "192.168.1.2": { + "remoteAs": 102, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r3/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r3/bgpd.conf new file mode 100644 index 000000000000..277f027d5b0b --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r3/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 103 vrf r3-cust1 + neighbor 192.168.1.2 remote-as 102 + neighbor 192.168.1.2 bfd + address-family ipv4 unicast + network 10.254.254.3/32 + exit-address-family +! diff --git a/tests/topotests/bfd-vrf-topo1/r3/peers.json b/tests/topotests/bfd-vrf-topo1/r3/peers.json new file mode 100644 index 000000000000..ef3800864399 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r3/peers.json @@ -0,0 +1,6 @@ +[ + { + "peer": "192.168.1.2", + "status": "up" + } +] diff --git a/tests/topotests/bfd-vrf-topo1/r3/zebra.conf b/tests/topotests/bfd-vrf-topo1/r3/zebra.conf new file mode 100644 index 000000000000..f727c2d633c1 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r3/zebra.conf @@ -0,0 +1,3 @@ +interface r3-eth0 vrf r3-cust1 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bfd-vrf-topo1/r4/bfdd.conf b/tests/topotests/bfd-vrf-topo1/r4/bfdd.conf new file mode 100644 index 000000000000..a56a3a0d37ee --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r4/bfdd.conf @@ -0,0 +1,7 @@ +bfd + peer 192.168.2.2 vrf r4-cust1 + transmit-interval 2000 + receive-interval 2000 + no shutdown + ! +! diff --git a/tests/topotests/bfd-vrf-topo1/r4/bgp_prefixes.json b/tests/topotests/bfd-vrf-topo1/r4/bgp_prefixes.json new file mode 100644 index 000000000000..cc8510dd6137 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r4/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.1/32": [ + { + "aspath": "102 101", + "prefix": "10.254.254.1", + "valid": true, + "peerId": "192.168.2.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.2/32": [ + { + "aspath": "102", + "prefix": "10.254.254.2", + "valid": true, + "peerId": "192.168.2.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.3/32": [ + { + "aspath": "102 103", + "prefix": "10.254.254.3", + "valid": true, + "peerId": "192.168.2.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.2", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r4/bgp_summary.json b/tests/topotests/bfd-vrf-topo1/r4/bgp_summary.json new file mode 100644 index 000000000000..7d81784b567d --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r4/bgp_summary.json @@ -0,0 +1,11 @@ +{ + "ipv4Unicast": { + "as": 104, + "peers": { + "192.168.2.2": { + "remoteAs": 102, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r4/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r4/bgpd.conf new file mode 100644 index 000000000000..66bf28587d96 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r4/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 104 vrf r4-cust1 + neighbor 192.168.2.2 remote-as 102 + neighbor 192.168.2.2 bfd + address-family ipv4 unicast + network 10.254.254.4/32 + exit-address-family +! diff --git a/tests/topotests/bfd-vrf-topo1/r4/peers.json b/tests/topotests/bfd-vrf-topo1/r4/peers.json new file mode 100644 index 000000000000..37140089e14a --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r4/peers.json @@ -0,0 +1,6 @@ +[ + { + "peer": "192.168.2.2", + "status": "up" + } +] diff --git a/tests/topotests/bfd-vrf-topo1/r4/zebra.conf b/tests/topotests/bfd-vrf-topo1/r4/zebra.conf new file mode 100644 index 000000000000..69770dd2bf79 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r4/zebra.conf @@ -0,0 +1,3 @@ +interface r4-eth0 vrf r4-cust1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.dot b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.dot new file mode 100644 index 000000000000..c84ace27806d --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.dot @@ -0,0 +1,73 @@ +## 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="bfd-topo1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n192.168.0.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n192.168.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n192.168.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0\n.1"]; + r2 -- sw1 [label="eth0\n.2"]; + + r3 -- sw2 [label="eth0\n.1"]; + r2 -- sw2 [label="eth1\n.2"]; + + r4 -- sw3 [label="eth0\n.1"]; + r2 -- sw3 [label="eth2\n.2"]; +} diff --git a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.jpg b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.jpg new file mode 100644 index 000000000000..4d6d56e07281 Binary files /dev/null and b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.jpg differ diff --git a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py new file mode 100755 index 000000000000..e2933820bda7 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py @@ -0,0 +1,292 @@ +#!/usr/bin/env python + +# +# test_bfd_vrf_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018 by +# Network Device Education Foundation, Inc. ("NetDEF") +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bfd_vrf_topo1.py: Test the FRR/Quagga BFD daemon. +""" + +import os +import sys +import json +from functools import partial +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 +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +class BFDTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 4 routers + for routern in range(1, 5): + 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['r2']) + switch.add_link(tgen.gears['r3']) + + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['r2']) + switch.add_link(tgen.gears['r4']) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BFDTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # check for zebra capability + for rname, router in router_list.iteritems(): + if router.check_capability( + TopoRouter.RD_ZEBRA, + '--vrfwnetns' + ) == False: + return pytest.skip('Skipping BFD Topo1 VRF NETNS feature. VRF NETNS backend not available on FRR') + + if os.system('ip netns list') != 0: + return pytest.skip('Skipping BFD Topo1 VRF NETNS Test. NETNS not available on System') + + logger.info('Testing with VRF Namespace support') + + cmds = ['if [ -e /var/run/netns/{0}-cust1 ] ; then ip netns del {0}-cust1 ; fi', + 'ip netns add {0}-cust1', + 'ip link set dev {0}-eth0 netns {0}-cust1', + 'ip netns exec {0}-cust1 ifconfig {0}-eth0 up'] + cmds2 = ['ip link set dev {0}-eth1 netns {0}-cust1', + 'ip netns exec {0}-cust1 ifconfig {0}-eth1 up', + 'ip link set dev {0}-eth2 netns {0}-cust1', + 'ip netns exec {0}-cust1 ifconfig {0}-eth2 up'] + + for rname, router in router_list.iteritems(): + # create VRF rx-cust1 and link rx-eth0 to rx-cust1 + for cmd in cmds: + output = tgen.net[rname].cmd(cmd.format(rname)) + if rname == 'r2': + for cmd in cmds2: + output = tgen.net[rname].cmd(cmd.format(rname)) + + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)), + '--vrfwnetns' + ) + router.load_config( + TopoRouter.RD_BFD, + os.path.join(CWD, '{}/bfdd.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + # Initialize all routers. + 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() + # move back rx-eth0 to default VRF + # delete rx-vrf + cmds = ['ip netns exec {0}-cust1 ip link set {0}-eth0 netns 1', + 'ip netns delete {0}-cust1'] + cmds2 = ['ip netns exec {0}-cust1 ip link set {0}-eth1 netns 1', + 'ip netns exec {0}-cust2 ip link set {0}-eth1 netns 1'] + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + if rname == 'r2': + for cmd in cmds2: + tgen.net[rname].cmd(cmd.format(rname)) + for cmd in cmds: + tgen.net[rname].cmd(cmd.format(rname)) + tgen.stop_topology() + +def test_bfd_connection(): + "Assert that the BFD peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('waiting for bfd peers to go up') + for router in tgen.routers().values(): + json_file = '{}/{}/peers.json'.format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(topotest.router_json_cmp, + router, 'show bfd peers json', expected) + _, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_bgp_convergence(): + "Assert that BGP is converging." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('waiting for bgp peers to go up') + + for router in tgen.routers().values(): + ref_file = '{}/{}/bgp_summary.json'.format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ip bgp vrf {}-cust1 summary json'.format(router.name), expected) + _, res = topotest.run_and_expect(test_func, None, count=125, wait=1.0) + assertmsg = '{}: bgp did not converge'.format(router.name) + assert res is None, assertmsg + + +def test_bgp_fast_convergence(): + "Assert that BGP is converging before setting a link down." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('waiting for bgp peers converge') + + for router in tgen.routers().values(): + ref_file = '{}/{}/bgp_prefixes.json'.format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ip bgp vrf {}-cust1 json'.format(router.name), expected) + _, res = topotest.run_and_expect(test_func, None, count=40, wait=0.5) + assertmsg = '{}: bgp did not converge'.format(router.name) + assert res is None, assertmsg + + +def test_bfd_fast_convergence(): + """ + Assert that BFD notices the link down after simulating network + failure. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Disable r2-eth0 link + router2 = tgen.gears['r2'] + topotest.interface_set_status(router2, 'r2-eth0', ifaceaction=False, vrf_name='r2-cust1') + + # Wait the minimum time we can before checking that BGP/BFD + # converged. + logger.info('waiting for BFD converge') + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + json_file = '{}/{}/peers.json'.format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + # Load the same file as previous test, but expect R1 to be down. + if router.name == 'r1': + for peer in expected: + if peer['peer'] == '192.168.0.2': + peer['status'] = 'down' + else: + for peer in expected: + if peer['peer'] == '192.168.0.1': + peer['status'] = 'down' + + test_func = partial(topotest.router_json_cmp, + router, 'show bfd peers json', expected) + _, res = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert res is None, assertmsg + + +def test_bgp_fast_reconvergence(): + "Assert that BGP is converging after setting a link down." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('waiting for BGP re convergence') + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + ref_file = '{}/{}/bgp_prefixes.json'.format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + + # Load the same file as previous test, but set networks to None + # to test absence. + if router.name == 'r1': + expected['routes']['10.254.254.2/32'] = None + expected['routes']['10.254.254.3/32'] = None + expected['routes']['10.254.254.4/32'] = None + else: + expected['routes']['10.254.254.1/32'] = None + + test_func = partial(topotest.router_json_cmp, + router, 'show ip bgp vrf {}-cust1 json'.format(router.name), expected) + _, res = topotest.run_and_expect( + test_func, + None, + count=3, + wait=1 + ) + assertmsg = '{}: bgp did not converge'.format(router.name) + assert res 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/bgp-vrf-route-leak-basic/test_bgp.py b/tests/topotests/bgp-vrf-route-leak-basic/test_bgp.py old mode 100644 new mode 100755 diff --git a/tests/topotests/bgp_ebgp_requires_policy/__init__.py b/tests/topotests/bgp_ebgp_requires_policy/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf new file mode 100644 index 000000000000..e06fa08b5777 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65000 + bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 local-as 500 + address-family ipv4 unicast + redistribute connected + neighbor 192.168.255.2 route-map outgoing out +! +ip prefix-list peer-out permit 172.16.255.254/32 +route-map outgoing permit 10 + match ip address prefix-list peer-out +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf new file mode 100644 index 000000000000..0a283c06d57c --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf new file mode 100644 index 000000000000..0549697ff068 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf @@ -0,0 +1,2 @@ +router bgp 1000 + neighbor 192.168.255.1 remote-as 500 diff --git a/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf new file mode 100644 index 000000000000..606c17bec9c1 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf new file mode 100644 index 000000000000..b4e304d82a47 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65000 + bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 local-as 500 + address-family ipv4 unicast + redistribute connected diff --git a/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf new file mode 100644 index 000000000000..39499a198d42 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r3-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf new file mode 100644 index 000000000000..0549697ff068 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf @@ -0,0 +1,2 @@ +router bgp 1000 + neighbor 192.168.255.1 remote-as 500 diff --git a/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf new file mode 100644 index 000000000000..b85911504e25 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf @@ -0,0 +1,6 @@ +! +interface r4-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py new file mode 100644 index 000000000000..eecacfd00cde --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python + +# +# bgp_ebgp_requires_policy.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# 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. +# + +""" +bgp_ebgp_requires_policy.py: + +Test if eBGP sender without a filter applied to the peer is allowed +to send advertisements. +""" + +import os +import sys +import json +import time +import pytest + +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 +from mininet.topo import Topo + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 5): + 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['r3']) + switch.add_link(tgen.gears['r4']) + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + tgen.start_router() + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + +def test_bgp_remove_private_as(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router): + while True: + cmd = "show ip bgp neighbor 192.168.255.1 json" + output = json.loads(tgen.gears[router].vtysh_cmd(cmd)) + if output['192.168.255.1']['bgpState'] == 'Established': + time.sleep(3) + return True + + def _bgp_ebgp_requires_policy(router): + cmd = "show ip bgp 172.16.255.254/32 json" + output = json.loads(tgen.gears[router].vtysh_cmd(cmd)) + if 'prefix' in output: + return True + return False + + if _bgp_converge('r2'): + assert _bgp_ebgp_requires_policy('r2') == True + + if _bgp_converge('r4'): + assert _bgp_ebgp_requires_policy('r4') == False + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv6_rtadv/__init__.py b/tests/topotests/bgp_ipv6_rtadv/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf new file mode 100644 index 000000000000..1623b4578b86 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 101 + bgp router-id 10.254.254.1 + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r1-eth0 interface peer-group r2g + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json new file mode 100644 index 000000000000..8718e49778a7 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json @@ -0,0 +1,44 @@ +{ + "10.254.254.2/32": [ + { + "distance": 20, + "protocol": "bgp", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.2/32", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "interfaceIndex": 2, + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "10.254.254.1/32": [ + { + "distance": 0, + "protocol": "connected", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.1/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "interfaceIndex": 1, + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json new file mode 100644 index 000000000000..d0378b56493f --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json @@ -0,0 +1,39 @@ +{ + "2001:db8:1::/64": [ + { + "distance": 20, + "protocol": "bgp", + "internalFlags": 0, + "metric": 0, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "interfaceIndex": 2, + "flags": 1, + "active": true, + "afi": "ipv6" + } + ] + }, + { + "distance": 0, + "protocol": "connected", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r1-eth0", + "interfaceIndex": 2, + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf b/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf new file mode 100644 index 000000000000..f95c3b07a7ba --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf @@ -0,0 +1,9 @@ +debug zebra packet recv +debug zebra packet send +log stdout +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf new file mode 100644 index 000000000000..bf42d218123a --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf @@ -0,0 +1,16 @@ +router bgp 102 + bgp router-id 10.254.254.2 + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r2-eth0 interface peer-group r2g + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json b/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json new file mode 100644 index 000000000000..fe26937e80fd --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json @@ -0,0 +1,44 @@ +{ + "10.254.254.2/32": [ + { + "distance": 0, + "protocol": "connected", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.2/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "interfaceIndex": 1, + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "distance": 20, + "protocol": "bgp", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.1/32", + "nexthops": [ + { + "interfaceName": "r2-eth0", + "interfaceIndex": 2, + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json new file mode 100644 index 000000000000..d5ad1a2c5ba9 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json @@ -0,0 +1,23 @@ +{ + "2001:db8:1::/64": [ + { + "distance": 0, + "protocol": "connected", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r2-eth0", + "interfaceIndex": 2, + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf b/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf new file mode 100644 index 000000000000..0131a11be0e3 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf @@ -0,0 +1,9 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot new file mode 100644 index 000000000000..da67c29a0954 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot @@ -0,0 +1,44 @@ +## 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="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + +} diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py new file mode 100644 index 000000000000..6cf223af4280 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python + +# +# test_bgp_ipv6_rtadv.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" + test_bgp_ipv6_rtadv.py: Test the FRR/Quagga BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +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 +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BGPIPV6RTADVTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 2 routers. + tgen.add_router('r1') + tgen.add_router('r2') + + 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" + tgen = Topogen(BGPIPV6RTADVTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rname, router in router_list.iteritems(): + 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)) + ) + + # Initialize all routers. + tgen.start_router() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv4 routing tables. + logger.info("Checking IPv4 routes for convergence") + for router in tgen.routers().values(): + json_file = '{}/{}/ipv4_routes.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ip route json'.format(router.name), 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 IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + json_file = '{}/{}/ipv6_routes.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ipv6 route json'.format(router.name), 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_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_l3vpn_to_bgp_vrf/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf index 4bd0f95f2c81..a38fb1e9a176 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf @@ -15,6 +15,7 @@ router bgp 5227 network 99.0.0.1/32 network 5.1.0.0/24 route-map rm-nh network 5.1.1.0/24 route-map rm-nh + redistribute sharp route-map sharp-nh neighbor 192.168.1.1 activate exit-address-family ! @@ -29,6 +30,15 @@ route-map rm-nh permit 10 set extcommunity rt 89:123 set community 0:67 ! +route-map sharp-nh permit 10 + match ip address al-any + set ip next-hop 99.0.0.1 + set local-preference 200 + set metric 200 + set large-community 90:12:34 + set extcommunity rt 80:987 + set community 0:65 +! end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf index 2115f0874139..3aeb9f9c9f59 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf @@ -15,6 +15,7 @@ router bgp 5227 network 99.0.0.2/32 network 5.1.0.0/24 route-map rm-nh network 5.1.1.0/24 route-map rm-nh + redistribute sharp route-map sharp-nh neighbor 192.168.1.1 activate exit-address-family ! @@ -29,6 +30,15 @@ route-map rm-nh permit 10 set extcommunity rt 89:123 set community 0:67 ! +route-map sharp-nh permit 10 + match ip address al-any + set ip next-hop 99.0.0.2 + set local-preference 200 + set metric 200 + set large-community 78:90:12 + set extcommunity rt 70:456 + set community 0:66 +! end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf index 200461255710..c4b6ac9bb457 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf @@ -7,7 +7,7 @@ log monitor notifications log commands log file bgpd.log -debug bgp vpn label +#debug bgp vpn label router bgp 5226 bgp router-id 3.3.3.3 bgp cluster-id 3.3.3.3 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf index b2df5990ce31..6295406e69e3 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf @@ -7,9 +7,9 @@ log monitor notifications log commands log file bgpd.log debug -debug bgp vpn label -debug bgp nht -debug bgp zebra +#debug bgp vpn label +#debug bgp nht +#debug bgp zebra router bgp 5226 bgp router-id 4.4.4.4 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py index f5d73a8c490e..149a420a320a 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py @@ -1,5 +1,6 @@ from lutil import luCommand from customize import l3mdev_accept + l3mdev_rtrs = ['r1', 'r3', 'r4', 'ce4'] for rtr in l3mdev_rtrs: luCommand(rtr,'sysctl net.ipv4.tcp_l3mdev_accept',' = \d*','none','') diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py new file mode 100644 index 000000000000..897fc484360a --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py @@ -0,0 +1,20 @@ +from lutil import luCommand +ret = luCommand('ce1', 'vtysh -c "show ip route" | grep -c \\ 10\\.\\*/32','(.*)','pass', 'Looking for sharp routes') +found = luLast() +if ret != False and found != None: + num = int(found.group()) + luCommand('ce3', 'vtysh -c "show bgp sum"', + '.', 'pass', 'See %s sharp routes' % num) + if num > 0: + wait = num/500 + luCommand('ce1', 'vtysh -c "sharp remove routes 10.0.0.0 {}"'.format(num),'.','none','Removing {} routes'.format(num)) + luCommand('ce2', 'vtysh -c "sharp remove routes 10.0.0.0 {}"'.format(num),'.','none','Removing {} routes'.format(num)) + rtrs = ['ce1', 'ce2', 'ce3'] + for rtr in rtrs: + luCommand(rtr, 'vtysh -c "show bgp ipv4 uni" | grep -c 10\\.\\*/32','^0$', 'wait', 'BGP routes removed', wait) + for rtr in rtrs: + luCommand(rtr, 'ip route show | grep -c \\^10\\.','^0$', 'wait', 'Linux routes removed', wait) + rtrs = ['r1', 'r3', 'r4'] + for rtr in rtrs: + luCommand(rtr, 'ip route show vrf {}-cust1 | grep -c \\^10\\.'.format(rtr),'^0$','wait','VRF route removed',wait) +#done diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py new file mode 100644 index 000000000000..3b3aac5fbe2e --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py @@ -0,0 +1,66 @@ +from lutil import luCommand +num = 50000 +b = int(num/(256*256)) +if b > 0: + r = num - b * (256*256) +else: + r = num +c = int(r/256) +if c > 0: + d = r - c * 256 - 1 +else: + d = r +wait = num/1000 +mem = {} +rtrs = ['ce1', 'ce2', 'ce3', 'r1', 'r2', 'r3', 'r4'] +for rtr in rtrs: + mem[rtr] = {'value': 0, 'units': 'unknown'} + ret = luCommand(rtr, 'vtysh -c "show memory"', 'bgpd: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*)', 'none', 'collect bgpd memory stats') + found = luLast() + if ret != False and found != None: + mem[rtr] = {'value': int(found.group(1)), 'units': found.group(2)} + +luCommand('ce1', 'vtysh -c "sharp data nexthop"', 'sharpd is not running', 'none','check if sharpd running') +doSharp = True +found = luLast() +if ret != False and found != None: + if len(found.group()): + luCommand('ce1', 'vtysh -c "sharp data nexthop"', 'sharpd is not running', 'pass','sharpd NOT running, skipping test') + doSharp = False + +if doSharp == True: + luCommand('ce1', 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.1 {}"'.format(num),'','pass','Adding {} routes'.format(num)) + luCommand('ce2', 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.2 {}"'.format(num),'','pass','Adding {} routes'.format(num)) + rtrs = ['ce1', 'ce2', 'ce3'] + for rtr in rtrs: + luCommand(rtr, 'vtysh -c "show bgp ipv4 uni 10.{}.{}.{}"'.format(b,c,d), 'Last update:', 'wait', 'RXed last route, 10.{}.{}.{}'.format(b,c,d), wait) + luCommand(rtr, 'vtysh -c "show bgp ipv4 uni" | grep -c 10\\.\\*/32', str(num), 'wait', 'See all sharp routes in BGP', wait) + luCommand('r1', 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 10.{}.{}.{}"'.format(b,c,d),'99.0.0.1','wait','RXed -> 10.{}.{}.{} from CE1'.format(b,c,d), wait) + luCommand('r3', 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 10.{}.{}.{}"'.format(b,c,d),'99.0.0.2','wait','RXed -> 10.{}.{}.{} from CE2'.format(b,c,d), wait) + luCommand('r1', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'99.0.0.1','wait','see VPN safi -> 10.{}.{}.{} from CE1'.format(b,c,d)) + luCommand('r3', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'99.0.0.2','wait','see VPN safi -> 10.{}.{}.{} from CE2'.format(b,c,d)) + luCommand('r3', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'1.1.1.1','wait','see VPN safi -> 10.{}.{}.{} from CE1'.format(b,c,d)) + luCommand('r1', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'3.3.3.3','wait','see VPN safi -> 10.{}.{}.{} from CE2'.format(b,c,d)) + luCommand('r4', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'1.1.1.1','wait','see VPN safi -> 10.{}.{}.{} from CE1'.format(b,c,d)) + luCommand('r4', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'3.3.3.3','wait','see VPN safi -> 10.{}.{}.{} from CE2'.format(b,c,d)) + rtrs = ['ce1', 'ce2', 'ce3'] + for rtr in rtrs: + luCommand(rtr, 'ip route get 10.{}.{}.{}'.format(b,c,d),'dev','wait','Route to 10.{}.{}.{} available'.format(b,c,d), wait) + luCommand(rtr, 'ip route show | grep -c \\^10\\.', str(num), 'wait', 'See {} linux routes'.format(num), wait) + + rtrs = ['r1', 'r3', 'r4'] + for rtr in rtrs: + luCommand(rtr, 'ip route get vrf {}-cust1 10.{}.{}.{}'.format(rtr,b,c,d),'dev','wait','VRF route available',wait) + luCommand(rtr, 'ip route show vrf {}-cust1 | grep -c \\^10\\.'.format(rtr), str(num), 'wait','See {} linux routes'.format(num), wait) + rtrs = ['ce1', 'ce2', 'ce3', 'r1', 'r2', 'r3', 'r4'] + for rtr in rtrs: + ret = luCommand(rtr, 'vtysh -c "show memory"', 'bgpd: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*)', 'none', 'collect bgpd memory stats') + found = luLast() + if ret != False and found != None: + val = int(found.group(1)) + if mem[rtr]['units'] != found.group(2): + val *= 1000 + delta = val - int(mem[rtr]['value']) + ave = float(delta)/float(num) + luCommand(rtr, 'vtysh -c "show thread cpu"', '.', 'pass', 'BGPd heap: {0} {1} --> {2} {3} ({4} {1}/route)'.format(mem[rtr]['value'], mem[rtr]['units'], found.group(1), found.group(2), round(ave,4))) +#done diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py index 1da1066f0e1c..2dad5e7687a0 100755 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py @@ -75,6 +75,24 @@ def test_check_linux_mpls(): #CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' ltemplateTest('scripts/check_linux_mpls.py', False, CliOnFail, CheckFunc) +def test_check_scale_up(): + CliOnFail = None + # For debugging, uncomment the next line + #CliOnFail = 'tgen.mininet_cli' + CheckFunc = 'ltemplateVersionCheck(\'4.1\', iproute2=\'4.9\')' + #uncomment next line to start cli *before* script is run + #CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest('scripts/scale_up.py', False, CliOnFail, CheckFunc) + +def test_check_scale_down(): + CliOnFail = None + # For debugging, uncomment the next line + #CliOnFail = 'tgen.mininet_cli' + CheckFunc = 'ltemplateVersionCheck(\'4.1\', iproute2=\'4.9\')' + #uncomment next line to start cli *before* script is run + #CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest('scripts/scale_down.py', False, CliOnFail, CheckFunc) + def SKIP_test_cleanup_all(): CliOnFail = None # For debugging, uncomment the next line diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py b/tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf new file mode 100644 index 000000000000..235b42b3d56a --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65000 + neighbor 192.168.255.2 remote-as 65001 + address-family ipv4 unicast + redistribute connected diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf new file mode 100644 index 000000000000..0a283c06d57c --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf new file mode 100644 index 000000000000..e0162841597f --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65001 + neighbor 192.168.255.1 remote-as 65000 + address-family ipv4 + neighbor 192.168.255.1 maximum-prefix 1 diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf new file mode 100644 index 000000000000..606c17bec9c1 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py new file mode 100644 index 000000000000..69b8c7ca5ed5 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python + +# +# bgp_local_as_private_remove.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# 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. +# + +""" +bgp_maximum_prefix_invalid_update.py: +Test if unnecesarry UPDATE message like below: + +[Error] Error parsing NLRI +%NOTIFICATION: sent to neighbor X.X.X.X 3/10 (UPDATE Message Error/Invalid Network Field) 0 bytes + +is not sent if maximum-prefix count is overflow. +""" + +import os +import sys +import json +import time +import pytest + +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 +from mininet.topo import Topo + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router('r{}'.format(routern)) + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + tgen.start_router() + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + +def test_bgp_maximum_prefix_invalid(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router): + while True: + output = json.loads(tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + if output['192.168.255.1']['connectionsEstablished'] > 3: + return True + time.sleep(1) + + def _bgp_parsing_nlri(router): + cmd_max_exceeded = 'grep "%MAXPFXEXCEED: No. of IPv4 Unicast prefix received" bgpd.log' + cmdt_error_parsing_nlri = 'grep "Error parsing NLRI" bgpd.log' + output_max_exceeded = tgen.gears[router].run(cmd_max_exceeded) + output_error_parsing_nlri = tgen.gears[router].run(cmdt_error_parsing_nlri) + + if len(output_max_exceeded) > 0: + if len(output_error_parsing_nlri) > 0: + return False + return True + + + if _bgp_converge('r2'): + assert _bgp_parsing_nlri('r2') == True + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py index 096e97fa9449..e9c1916f7581 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py @@ -10,7 +10,8 @@ luCommand('r4','vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 33.33.33.0/24"','', 'none', 'Prefix removed') luCommand('r4','vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 11.11.11.0/24"','', 'none', 'MP prefix removed') luCommand('r4','vtysh -c "show vnc registrations"','Locally: *Active: 0 ','wait','Local registration removed') -luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI') +#luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI') +luCommand('r4','vtysh -c "clear vnc nve *"','.', 'pass', 'Cleared NVEs') luCommand('r1','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0','wait','All registrations cleared') luCommand('r3','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0','wait','All registrations cleared') diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/__init__.py b/tests/topotests/bgp_show_ip_bgp_fqdn/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r1/bgpd.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r1/bgpd.conf new file mode 100644 index 000000000000..235b42b3d56a --- /dev/null +++ b/tests/topotests/bgp_show_ip_bgp_fqdn/r1/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65000 + neighbor 192.168.255.2 remote-as 65001 + address-family ipv4 unicast + redistribute connected diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r1/zebra.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r1/zebra.conf new file mode 100644 index 000000000000..0a283c06d57c --- /dev/null +++ b/tests/topotests/bgp_show_ip_bgp_fqdn/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r2/bgpd.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r2/bgpd.conf new file mode 100644 index 000000000000..c05bfd5a6b1e --- /dev/null +++ b/tests/topotests/bgp_show_ip_bgp_fqdn/r2/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 65001 + bgp default show-hostname + neighbor 192.168.255.1 remote-as 65000 + address-family ipv4 unicast + redistribute connected diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/r2/zebra.conf b/tests/topotests/bgp_show_ip_bgp_fqdn/r2/zebra.conf new file mode 100644 index 000000000000..5abba71ce8d2 --- /dev/null +++ b/tests/topotests/bgp_show_ip_bgp_fqdn/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.253/32 +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py b/tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py new file mode 100644 index 000000000000..59ffd36ef396 --- /dev/null +++ b/tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python + +# +# test_bgp_show_ip_bgp_fqdn.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_show_ip_bgp_fqdn.py: +Test if FQND is visible in `show [ip] bgp` output if +`bgp default show-hostname` is toggled. +""" + +import os +import sys +import json +import time +import pytest + +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 +from mininet.topo import Topo + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router('r{}'.format(routern)) + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + tgen.start_router() + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + +def test_bgp_maximum_prefix_invalid(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router, neighbor): + cmd = "show ip bgp neighbor {0} json".format(neighbor) + while True: + output = json.loads(tgen.gears[router].vtysh_cmd(cmd)) + if output[neighbor]['bgpState'] == 'Established': + time.sleep(3) + return True + + def _bgp_show_nexthop(router, prefix): + cmd = "show ip bgp json" + output = json.loads(tgen.gears[router].vtysh_cmd(cmd)) + for nh in output['routes'][prefix][0]['nexthops']: + if 'fqdn' in nh: + return 'fqdn' + return 'ip' + + if _bgp_converge('r2', '192.168.255.1'): + assert _bgp_show_nexthop('r2', '172.16.255.254/32') == 'fqdn' + + if _bgp_converge('r1', '192.168.255.2'): + assert _bgp_show_nexthop('r1', '172.16.255.253/32') == 'ip' + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf new file mode 100644 index 000000000000..3c974c767fd4 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 101 vrf r1-cust1 + bgp router-id 10.254.254.1 + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r1-eth0 interface peer-group r2g + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json new file mode 100644 index 000000000000..e32c84b7d57a --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json @@ -0,0 +1,50 @@ +{ + "10.254.254.2/32": [ + { + "prefix": "10.254.254.2/32", + "protocol": "bgp", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "internalStatus": 34, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceIndex": 2, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "prefix": "10.254.254.1/32", + "protocol": "connected", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "internalStatus": 32, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceIndex": 4, + "interfaceName": "loop1", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json new file mode 100644 index 000000000000..88e8c5cd8306 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json @@ -0,0 +1,44 @@ +{ + "2001:db8:1::/64": [ + { + "prefix": "2001:db8:1::/64", + "protocol": "bgp", + "vrfId":3, + "distance": 20, + "metric": 0, + "internalStatus": 2, + "internalFlags": 0, + "nexthops": [ + { + "flags": 1, + "afi": "ipv6", + "interfaceIndex": 2, + "interfaceName": "r1-eth0", + "active": true + } + ] + }, + { + "prefix": "2001:db8:1::/64", + "protocol": "connected", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "internalStatus": 32, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceIndex": 2, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf new file mode 100644 index 000000000000..f19c49720897 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf @@ -0,0 +1,9 @@ +debug zebra packet recv +debug zebra packet send +log stdout +interface loop1 vrf r1-cust1 + ip address 10.254.254.1/32 +! +interface r1-eth0 vrf r1-cust1 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf new file mode 100644 index 000000000000..39362abd469b --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf @@ -0,0 +1,16 @@ +router bgp 102 vrf r2-cust1 + bgp router-id 10.254.254.2 + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r2-eth0 interface peer-group r2g + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json new file mode 100644 index 000000000000..9d7c0e6e4f11 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json @@ -0,0 +1,50 @@ +{ + "10.254.254.2/32": [ + { + "prefix": "10.254.254.2/32", + "protocol": "connected", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "internalStatus": 32, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceIndex": 4, + "interfaceName": "loop1", + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "prefix": "10.254.254.1/32", + "protocol": "bgp", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "internalStatus": 34, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceIndex": 2, + "interfaceName": "r2-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json new file mode 100644 index 000000000000..230fe38748de --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json @@ -0,0 +1,26 @@ +{ + "2001:db8:1::/64": [ + { + "prefix": "2001:db8:1::/64", + "protocol": "connected", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "internalStatus": 32, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceIndex": 2, + "interfaceName": "r2-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf new file mode 100644 index 000000000000..c3795ab95938 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf @@ -0,0 +1,9 @@ +ip forwarding +ipv6 forwarding +! +interface loop1 vrf r2-cust1 + ip address 10.254.254.2/32 +! +interface r2-eth0 vrf r2-cust1 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot new file mode 100644 index 000000000000..da67c29a0954 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot @@ -0,0 +1,44 @@ +## 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="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py new file mode 100644 index 000000000000..6b4df78c6908 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python + +# +# test_bgp_ipv6_rtadv.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" + test_bgp_ipv6_rtadv.py: Test the FRR/Quagga BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +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 +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BGPIPV6RTADVVRFTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 2 routers. + tgen.add_router('r1') + tgen.add_router('r2') + + 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" + tgen = Topogen(BGPIPV6RTADVVRFTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + logger.info('Testing with VRF Lite support') + + cmds = ['ip link add {0}-cust1 type vrf table 1001', + 'ip link add loop1 type dummy', + 'ip link set loop1 master {0}-cust1', + 'ip link set {0}-eth0 master {0}-cust1'] + + for rname, router in router_list.iteritems(): + for cmd in cmds: + output = tgen.net[rname].cmd(cmd.format(rname)) + + for rname, router in router_list.iteritems(): + 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)) + ) + + # Initialize all routers. + tgen.start_router() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv4 routing tables. + logger.info("Checking IPv4 routes for convergence") + + for router in tgen.routers().values(): + json_file = '{}/{}/ipv4_routes.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ip route vrf {}-cust1 json'.format(router.name), 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 IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + json_file = '{}/{}/ipv6_routes.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ipv6 route vrf {}-cust1 json'.format(router.name), 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_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/conftest.py b/tests/topotests/conftest.py index 327e4625f246..49e48ba927c2 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -7,6 +7,8 @@ from lib.topolog import logger import pytest +topology_only = False + def pytest_addoption(parser): """ Add topology-only option to the topology tester. This option makes pytest @@ -20,9 +22,9 @@ def pytest_runtest_call(): This function must be run after setup_module(), it does standarized post setup routines. It is only being used for the 'topology-only' option. """ - # pylint: disable=E1101 - # Trust me, 'config' exists. - if pytest.config.getoption('--topology-only'): + global topology_only + + if topology_only: tgen = get_topogen() if tgen is not None: # Allow user to play with the setup. @@ -44,9 +46,15 @@ def pytest_assertrepr_compare(op, left, right): def pytest_configure(config): "Assert that the environment is correctly configured." + + global topology_only + if not diagnose_env(): pytest.exit('enviroment has errors, please read the logs') + if config.getoption('--topology-only'): + topology_only = True + def pytest_runtest_makereport(item, call): "Log all assert messages to default logger with error level" # Nothing happened diff --git a/tests/topotests/lib/ltemplate.py b/tests/topotests/lib/ltemplate.py index 31eaec700978..1d12d11a2670 100644 --- a/tests/topotests/lib/ltemplate.py +++ b/tests/topotests/lib/ltemplate.py @@ -83,22 +83,15 @@ def setup_module(self, mod): # For all registred routers, load the zebra configuration file for rname, router in router_list.iteritems(): - print("Setting up %s" % rname) - config = os.path.join(self.testdir, '{}/zebra.conf'.format(rname)) - if os.path.exists(config): - router.load_config(TopoRouter.RD_ZEBRA, config) - config = os.path.join(self.testdir, '{}/ospfd.conf'.format(rname)) - if os.path.exists(config): - router.load_config(TopoRouter.RD_OSPF, config) - config = os.path.join(self.testdir, '{}/ldpd.conf'.format(rname)) - if os.path.exists(config): - router.load_config(TopoRouter.RD_LDP, config) - config = os.path.join(self.testdir, '{}/bgpd.conf'.format(rname)) - if os.path.exists(config): - router.load_config(TopoRouter.RD_BGP, config) - config = os.path.join(self.testdir, '{}/isisd.conf'.format(rname)) - if os.path.exists(config): - router.load_config(TopoRouter.RD_ISIS, config) + logger.info("Setting up %s" % rname) + for rd_val in TopoRouter.RD: + config = os.path.join(self.testdir, '{}/{}.conf'.format(rname,TopoRouter.RD[rd_val])) + prog = os.path.join(tgen.net[rname].daemondir, TopoRouter.RD[rd_val]) + if os.path.exists(config): + if os.path.exists(prog): + router.load_config(rd_val, config) + else: + logger.warning("{} not found, but have {}.conf file".format(prog, TopoRouter.RD[rd_val])) # After loading the configurations, this function loads configured daemons. logger.info('Starting routers') diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index d989240a162b..d7145c3be0ff 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -42,7 +42,12 @@ import sys import logging import json -import ConfigParser + +if sys.version_info[0] > 2: + import configparser +else: + import ConfigParser as configparser + import glob import grp import platform @@ -153,7 +158,7 @@ def _load_config(self): Loads the configuration file `pytest.ini` located at the root dir of topotests. """ - self.config = ConfigParser.ConfigParser(tgen_defaults) + self.config = configparser.ConfigParser(tgen_defaults) pytestini_path = os.path.join(CWD, '../pytest.ini') self.config.read(pytestini_path) @@ -535,6 +540,7 @@ class TopoRouter(TopoGear): RD_NHRP = 11 RD_STATIC = 12 RD_BFD = 13 + RD_SHARP = 14 RD = { RD_ZEBRA: 'zebra', RD_RIP: 'ripd', @@ -549,6 +555,7 @@ class TopoRouter(TopoGear): RD_NHRP: 'nhrpd', RD_STATIC: 'staticd', RD_BFD: 'bfdd', + RD_SHARP: 'sharpd', } def __init__(self, tgen, cls, name, **params): @@ -599,7 +606,7 @@ def __str__(self): def _prepare_tmpfiles(self): # Create directories if they don't exist try: - os.makedirs(self.logdir, 0755) + os.makedirs(self.logdir, 0o755) except OSError: pass @@ -608,10 +615,10 @@ def _prepare_tmpfiles(self): # Only allow group, if it exist. gid = grp.getgrnam(self.routertype)[2] os.chown(self.logdir, 0, gid) - os.chmod(self.logdir, 0775) + os.chmod(self.logdir, 0o775) except KeyError: # Allow anyone, but set the sticky bit to avoid file deletions - os.chmod(self.logdir, 01777) + os.chmod(self.logdir, 0o1777) # Try to find relevant old logfiles in /tmp and delete them map(os.remove, glob.glob('{}/{}/*.log'.format(self.logdir, self.name))) @@ -931,7 +938,7 @@ def diagnose_env_linux(): logger.info('Running environment diagnostics') # Load configuration - config = ConfigParser.ConfigParser(tgen_defaults) + config = configparser.ConfigParser(tgen_defaults) pytestini_path = os.path.join(CWD, '../pytest.ini') config.read(pytestini_path) diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index e0da20e07f50..2acb04fb0ed8 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -429,6 +429,33 @@ def ip4_route_zebra(node, vrf_name=None): lines = lines[1:] return '\n'.join(lines) +def ip6_route_zebra(node, vrf_name=None): + """ + Retrieves the output of 'show ipv6 route [vrf vrf_name]', then + canonicalizes it by eliding link-locals. + """ + + if vrf_name == None: + tmp = node.vtysh_cmd('show ipv6 route') + else: + tmp = node.vtysh_cmd('show ipv6 route vrf {0}'.format(vrf_name)) + + # Mask out timestamp + output = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", tmp) + + # Mask out the link-local addresses + output = re.sub(r'fe80::[^ ]+,', 'fe80::XXXX:XXXX:XXXX:XXXX,', output) + + lines = output.splitlines() + header_found = False + while lines and (not lines[0].strip() or not header_found): + if '> - selected route' in lines[0]: + header_found = True + lines = lines[1:] + + return '\n'.join(lines) + + def proto_name_to_number(protocol): return { 'bgp': '186', diff --git a/tests/topotests/ospf6-topo1/r1/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r1/ospf6d.conf-pre-v4 deleted file mode 100644 index c4b382171d61..000000000000 --- a/tests/topotests/ospf6-topo1/r1/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,27 +0,0 @@ -hostname r1 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r1-stubnet - ipv6 ospf6 network broadcast -! -interface r1-sw5 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.1 - log-adjacency-changes detail - redistribute static - interface r1-stubnet area 0.0.0.0 - interface r1-sw5 area 0.0.0.0 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref index c961512bd9f1..2db6f620f974 100644 --- a/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref @@ -1,9 +1,9 @@ -O fc00:1:1:1::/64 [110/10] is directly connected, r1-stubnet -O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5 -O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 +O fc00:1:1:1::/64 [110/10] is directly connected, r1-stubnet, XX:XX:XX +O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5, XX:XX:XX +O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r1/zebra.conf b/tests/topotests/ospf6-topo1/r1/zebra.conf index de298f40e159..dfbcea8d21fd 100644 --- a/tests/topotests/ospf6-topo1/r1/zebra.conf +++ b/tests/topotests/ospf6-topo1/r1/zebra.conf @@ -2,6 +2,9 @@ hostname r1 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r1-stubnet ipv6 address fc00:1:1:1::1/64 ! diff --git a/tests/topotests/ospf6-topo1/r2/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r2/ospf6d.conf-pre-v4 deleted file mode 100644 index bb9958d17387..000000000000 --- a/tests/topotests/ospf6-topo1/r2/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,27 +0,0 @@ -hostname r2 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r2-stubnet - ipv6 ospf6 network broadcast -! -interface r2-sw5 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.2 - log-adjacency-changes detail - redistribute static - interface r2-stubnet area 0.0.0.0 - interface r2-sw5 area 0.0.0.0 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref index 014eddbcb69e..9060b0739f0d 100644 --- a/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref @@ -1,10 +1,10 @@ -O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O fc00:2:2:2::/64 [110/10] is directly connected, r2-stubnet -O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5 -O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 +O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O fc00:2:2:2::/64 [110/10] is directly connected, r2-stubnet, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5, XX:XX:XX +O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r2/zebra.conf b/tests/topotests/ospf6-topo1/r2/zebra.conf index d5345fede679..f05d1a60ff40 100644 --- a/tests/topotests/ospf6-topo1/r2/zebra.conf +++ b/tests/topotests/ospf6-topo1/r2/zebra.conf @@ -2,6 +2,9 @@ hostname r2 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r2-stubnet ipv6 address fc00:2:2:2::2/64 ! diff --git a/tests/topotests/ospf6-topo1/r3/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r3/ospf6d.conf-pre-v4 deleted file mode 100644 index d2dbc4a41c43..000000000000 --- a/tests/topotests/ospf6-topo1/r3/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,31 +0,0 @@ -hostname r3 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r3-stubnet - ipv6 ospf6 network broadcast -! -interface r3-sw5 - ipv6 ospf6 network broadcast -! -interface r3-sw6 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.3 - log-adjacency-changes detail - redistribute static - interface r3-stubnet area 0.0.0.0 - interface r3-sw5 area 0.0.0.0 - interface r3-sw6 area 0.0.0.1 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref index 1ac7cbd6b4a2..9406f41e9433 100644 --- a/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref @@ -1,10 +1,10 @@ -O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet -O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6 -O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5 -O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6 -O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O>* fc00:4444:4444:4444::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6 +O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, XX:XX:XX +O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, XX:XX:XX +O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet, XX:XX:XX +O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5, XX:XX:XX +O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r3/zebra.conf b/tests/topotests/ospf6-topo1/r3/zebra.conf index 11f1ff59f242..d8051c350df2 100644 --- a/tests/topotests/ospf6-topo1/r3/zebra.conf +++ b/tests/topotests/ospf6-topo1/r3/zebra.conf @@ -2,6 +2,9 @@ hostname r3 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r3-stubnet ipv6 address fc00:3:3:3::3/64 ! diff --git a/tests/topotests/ospf6-topo1/r4/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r4/ospf6d.conf-pre-v4 deleted file mode 100644 index 6f9c30d75a73..000000000000 --- a/tests/topotests/ospf6-topo1/r4/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,27 +0,0 @@ -hostname r4 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r4-stubnet - ipv6 ospf6 network broadcast -! -interface r4-sw6 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.4 - log-adjacency-changes detail - redistribute static - interface r4-stubnet area 0.0.0.1 - interface r4-sw6 area 0.0.0.1 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref index 698dea6c7be3..9bf032b5e78d 100644 --- a/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref @@ -1,9 +1,9 @@ -O>* fc00:1:1:1::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:2:2:2::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O fc00:4:4:4::/64 [110/10] is directly connected, r4-stubnet -O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6 -O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 +O>* fc00:1:1:1::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O>* fc00:2:2:2::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O fc00:4:4:4::/64 [110/10] is directly connected, r4-stubnet, XX:XX:XX +O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r4/zebra.conf b/tests/topotests/ospf6-topo1/r4/zebra.conf index 4b0a8a1f9201..cada58bd01ff 100644 --- a/tests/topotests/ospf6-topo1/r4/zebra.conf +++ b/tests/topotests/ospf6-topo1/r4/zebra.conf @@ -2,6 +2,9 @@ hostname r4 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r4-stubnet ipv6 address fc00:4:4:4::4/64 ! diff --git a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py index 5da04b64496d..b70ae02266f4 100755 --- a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py +++ b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py @@ -76,20 +76,19 @@ import pytest from time import sleep -from mininet.topo import Topo -from mininet.net import Mininet -from mininet.node import Node, OVSSwitch, Host -from mininet.log import setLogLevel, info -from mininet.cli import CLI -from mininet.link import Intf - from functools import partial -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from lib import topotest +from mininet.topo import Topo +# Save the Current Working Directory to find configuration files later. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) -fatal_error = "" +# 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 ##################################################### @@ -102,34 +101,45 @@ class NetworkTopo(Topo): "OSPFv3 (IPv6) Test Topology 1" def build(self, **_opts): - # - # Define Switches first - # - switch = {} - for i in range(1, 7): - switch[i] = self.addSwitch('SW%s' % i, - dpid=topotest.int2dpid(i), - cls=topotest.LegacySwitch) - # - # Define FRR/Quagga Routers - # - router = {} - for i in range(1, 5): - router[i] = topotest.addRouter(self, 'r%s' % i) + "Build function" + + tgen = get_topogen(self) + + # Create 4 routers + for routern in range(1, 5): + tgen.add_router('r{}'.format(routern)) # # Wire up the switches and routers + # Note that we specify the link names so we match the config files # - # Stub nets - for i in range(1, 5): - self.addLink(switch[i], router[i], intfName2='r%s-stubnet' % i) - # Switch 5 - self.addLink(switch[5], router[1], intfName2='r1-sw5') - self.addLink(switch[5], router[2], intfName2='r2-sw5') - self.addLink(switch[5], router[3], intfName2='r3-sw5') - # Switch 6 - self.addLink(switch[6], router[3], intfName2='r3-sw6') - self.addLink(switch[6], router[4], intfName2='r4-sw6') + + # Create a empty network for router 1 + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1'], nodeif='r1-stubnet') + + # Create a empty network for router 2 + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['r2'], nodeif='r2-stubnet') + + # Create a empty network for router 3 + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['r3'], nodeif='r3-stubnet') + + # Create a empty network for router 4 + switch = tgen.add_switch('s4') + switch.add_link(tgen.gears['r4'], nodeif='r4-stubnet') + + # Interconnect routers 1, 2, and 3 + switch = tgen.add_switch('s5') + switch.add_link(tgen.gears['r1'], nodeif='r1-sw5') + switch.add_link(tgen.gears['r2'], nodeif='r2-sw5') + switch.add_link(tgen.gears['r3'], nodeif='r3-sw5') + + # Interconnect routers 3 and 4 + switch = tgen.add_switch('s6') + switch.add_link(tgen.gears['r3'], nodeif='r3-sw6') + switch.add_link(tgen.gears['r4'], nodeif='r4-sw6') ##################################################### @@ -138,192 +148,178 @@ def build(self, **_opts): ## ##################################################### -def setup_module(module): - global topo, net +def setup_module(mod): + "Sets up the pytest environment" - print("\n\n** %s: Setup Topology" % module.__name__) - print("******************************************\n") + tgen = Topogen(NetworkTopo, mod.__name__) + tgen.start_topology() - print("Cleanup old Mininet runs") - os.system('sudo mn -c > /dev/null 2>&1') + logger.info("** %s: Setup Topology" % mod.__name__) + logger.info("******************************************") - thisDir = os.path.dirname(os.path.realpath(__file__)) - topo = NetworkTopo() + # For debugging after starting net, but before starting FRR, + # uncomment the next line + # tgen.mininet_cli() - net = Mininet(controller=None, topo=topo) - net.start() + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF6, + os.path.join(CWD, '{}/ospf6d.conf'.format(rname)) + ) - # For debugging after starting net, but before starting FRR/Quagga, uncomment the next line - # CLI(net) + # Initialize all routers. + tgen.start_router() - ospf_config = 'ospf6d.conf' - if net['r1'].checkRouterVersion('<', '4.0'): - ospf_config = 'ospf6d.conf-pre-v4' + # For debugging after starting FRR daemons, uncomment the next line + # tgen.mininet_cli() - # Starting Routers - for i in range(1, 5): - net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i)) - net['r%s' % i].loadConf('ospf6d', '%s/r%s/%s' % (thisDir, i, ospf_config)) - net['r%s' % i].startRouter() - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() -def teardown_module(module): - global net +def test_ospf6_converged(): - print("\n\n** %s: Shutdown Topology" % module.__name__) - print("******************************************\n") + tgen = get_topogen() - # End - Shutdown network - net.stop() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # For debugging, uncomment the next line + #tgen.mininet_cli() -def test_router_running(): - global fatal_error - global net + # Wait for OSPF6 to converge (All Neighbors in either Full or TwoWay State) + logger.info("Waiting for OSPF6 convergence") - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + # Set up for regex + pat1 = re.compile('^[0-9]') + pat2 = re.compile('Full') - print("\n\n** Check if FRR/Quagga is running on each Router node") - print("******************************************\n") - sleep(5) + timeout = 60 + while timeout > 0: + logger.info("Timeout in %s: " % timeout), + sys.stdout.flush() - # Make sure that all daemons are running - for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() - assert fatal_error == "", fatal_error + # Look for any node not yet converged + for router, rnode in tgen.routers().iteritems(): + resStr = rnode.vtysh_cmd('show ipv6 ospf neigh') - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) + isConverged = False -def test_ospf6_converged(): - global fatal_error - global net + for line in resStr.splitlines(): + res1 = pat1.match(line) + if res1: + isConverged = True + res2 = pat2.search(line) - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + if res2 == None: + isConverged = False + break - # Wait for OSPF6 to converge (All Neighbors in either Full or TwoWay State) - print("\n\n** Verify OSPF6 daemons to converge") - print("******************************************\n") - timeout = 60 - while timeout > 0: - print("Timeout in %s: " % timeout), - sys.stdout.flush() - # Look for any node not yet converged - for i in range(1, 5): - notConverged = net['r%s' % i].cmd('vtysh -c "show ipv6 ospf neigh" 2> /dev/null | grep ^[0-9] | grep -v Full') - if notConverged: - print('Waiting for r%s' %i) + if isConverged == False: + logger.info('Waiting for {}'.format(router)) sys.stdout.flush() break - if notConverged: + + if isConverged: + logger.info('Done') + break + else: sleep(5) timeout -= 5 - else: - print('Done') - print(notConverged) - break - else: + + if timeout == 0: # Bail out with error if a router fails to converge - ospfStatus = net['r%s' % i].cmd('vtysh -c "show ipv6 ospf neigh"') - fatal_error = "OSPFv6 did not converge" - assert False, "OSPFv6 did not converge:\n%s" % ospfStatus + ospfStatus = rnode.vtysh_cmd('show ipv6 ospf neigh') + assert False, "OSPFv6 did not converge:\n{}".format(ospfStatus) - print("OSPFv3 converged.") + logger.info("OSPFv3 converged.") - if timeout < 60: - # Only wait if we actually went through a convergence - print("\nwaiting 15s for routes to populate") - sleep(15) + # For debugging, uncomment the next line + # tgen.mininet_cli() # Make sure that all daemons are still running - for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() - assert fatal_error == "", fatal_error + if tgen.routers_have_failure(): + assert tgen.errors == "", tgen.errors -def test_ospfv3_routingTable(): - global fatal_error - global net +def compare_show_ipv6(rname, expected): + """ + Calls 'show ipv6 route' for router `rname` and compare the obtained + result with the expected output. + """ + tgen = get_topogen() - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + # Use the vtysh output, with some masking to make comparison easy + current = topotest.ip6_route_zebra(tgen.gears[rname]) - thisDir = os.path.dirname(os.path.realpath(__file__)) + # Use just the 'O'spf lines of the output + linearr = [] + for line in current.splitlines(): + if re.match('^O', line): + linearr.append(line) - # Verify OSPFv3 Routing Table - print("\n\n** Verifying OSPFv3 Routing Table") - print("******************************************\n") - failures = 0 - for i in range(1, 5): - refTableFile = '%s/r%s/show_ipv6_route.ref' % (thisDir, i) - if os.path.isfile(refTableFile): - # Read expected result from file - expected = open(refTableFile).read().rstrip() - # Fix newlines (make them all the same) - expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) + current = '\n'.join(linearr) - # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show ipv6 route" 2> /dev/null | grep "^O"').rstrip() - # Mask out Link-Local mac address portion. They are random... - actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual) - # Drop timers on end of line (older Quagga Versions) - actual = re.sub(r", [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", "", actual) - # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + return topotest.difflines(topotest.normalize_text(current), + topotest.normalize_text(expected), + title1="Current output", + title2="Expected output") - # Generate Diff - diff = topotest.get_textdiff(actual, expected, - title1="actual OSPFv3 IPv6 routing table", - title2="expected OSPFv3 IPv6 routing table") +def test_ospfv3_routingTable(): - # Empty string if it matches, otherwise diff contains unified diff - if diff: - sys.stderr.write('r%s failed OSPFv3 (IPv6) Routing Table Check:\n%s\n' % (i, diff)) - failures += 1 - else: - print("r%s ok" % i) + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') - assert failures == 0, "OSPFv3 (IPv6) Routing Table verification failed for router r%s:\n%s" % (i, diff) + # For debugging, uncomment the next line + # tgen.mininet_cli() - # Make sure that all daemons are still running - for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() - assert fatal_error == "", fatal_error + # Verify OSPFv3 Routing Table + for router, rnode in tgen.routers().iteritems(): + logger.info('Waiting for router "%s" convergence', router) + + # Load expected results from the command + reffile = os.path.join(CWD, '{}/show_ipv6_route.ref'.format(router)) + expected = open(reffile).read() + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial( + compare_show_ipv6, router, expected) + result, diff = topotest.run_and_expect(test_func, '', + count=120, wait=0.5) + assert result, 'OSPFv3 did not converge on {}:\n{}'.format(router, diff) - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) def test_linux_ipv6_kernel_routingTable(): - global fatal_error - global net - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + tgen = get_topogen() - thisDir = os.path.dirname(os.path.realpath(__file__)) + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') # Verify Linux Kernel Routing Table - print("\n\n** Verifying Linux IPv6 Kernel Routing Table") - print("******************************************\n") + logger.info("Verifying Linux IPv6 Kernel Routing Table") + failures = 0 # Get a list of all current link-local addresses first as they change for # each run and we need to translate them linklocals = [] for i in range(1, 5): - linklocals += net['r%s' % i].get_ipv6_linklocal() + linklocals += tgen.net['r{}'.format(i)].get_ipv6_linklocal() + + # Now compare the routing tables (after substituting link-local addresses) - # Now compare the routing tables (after substituting link-local addresses) for i in range(1, 5): - refTableFile = '%s/r%s/ip_6_address.ref' % (thisDir, i) + refTableFile = os.path.join(CWD, 'r{}/ip_6_address.ref'.format(i)) if os.path.isfile(refTableFile): expected = open(refTableFile).read().rstrip() @@ -331,7 +327,7 @@ def test_linux_ipv6_kernel_routingTable(): expected = ('\n'.join(expected.splitlines())).splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('ip -6 route').rstrip() + actual = tgen.gears['r{}'.format(i)].run('ip -6 route').rstrip() # Mask out Link-Local mac addresses for ll in linklocals: actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0]) @@ -354,9 +350,9 @@ def test_linux_ipv6_kernel_routingTable(): actual = '\n'.join(filtered_lines).splitlines(1) # Print Actual table - # print("Router r%s table" % i) + # logger.info("Router r%s table" % i) # for line in actual: - # print(line.rstrip()) + # logger.info(line.rstrip()) # Generate Diff diff = topotest.get_textdiff(actual, expected, @@ -368,64 +364,60 @@ def test_linux_ipv6_kernel_routingTable(): sys.stderr.write('r%s failed Linux IPv6 Kernel Routing Table Check:\n%s\n' % (i, diff)) failures += 1 else: - print("r%s ok" % i) + logger.info("r%s ok" % i) assert failures == 0, "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s" % (i, diff) - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) - def test_shutdown_check_stderr(): - global fatal_error - global net - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: - print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n") + logger.info("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n") pytest.skip('Skipping test for Stderr output') - thisDir = os.path.dirname(os.path.realpath(__file__)) + net = tgen.net - print("\n\n** Verifying unexpected STDERR output from daemons") - print("******************************************\n") + logger.info("\n\n** Verifying unexpected STDERR output from daemons") + logger.info("******************************************") for i in range(1, 5): net['r%s' % i].stopRouter() log = net['r%s' % i].getStdErr('ospf6d') if log: - print("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log)) + logger.info("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log)) log = net['r%s' % i].getStdErr('zebra') if log: - print("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log)) + logger.info("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log)) def test_shutdown_check_memleak(): - global fatal_error - global net - - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + "Run the memory leak test and report results." if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None: - print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n") + logger.info("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)") pytest.skip('Skipping test for memory leaks') - - thisDir = os.path.dirname(os.path.realpath(__file__)) + + tgen = get_topogen() + + net = tgen.net for i in range(1, 5): net['r%s' % i].stopRouter() - net['r%s' % i].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__)) + net['r%s' % i].report_memory_leaks( + os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), + os.path.basename(__file__)) if __name__ == '__main__': - setLogLevel('info') - # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli + # To suppress tracebacks, either use the following pytest call or + # add "--tb=no" to cli # retval = pytest.main(["-s", "--tb=no"]) + retval = pytest.main(["-s"]) sys.exit(retval) diff --git a/tools/checkpatch.pl b/tools/checkpatch.pl index 77e6e9a3301f..1b48d10a09a0 100755 --- a/tools/checkpatch.pl +++ b/tools/checkpatch.pl @@ -6347,6 +6347,35 @@ sub process { "Please, only use 32 bit atomics.\n" . $herecurr); } +# check for use of strcpy() + if ($line =~ /\bstrcpy\s*\(.*\)/) { + ERROR("STRCPY", + "strcpy() is error-prone; please use strlcpy()" . $herecurr); + } + +# check for use of strncpy() + if ($line =~ /\bstrncpy\s*\(.*\)/) { + WARN("STRNCPY", + "strncpy() is error-prone; please use strlcpy() if possible, or memcpy()" . $herecurr); + } + +# check for use of strcat() + if ($line =~ /\bstrcat\s*\(.*\)/) { + ERROR("STRCAT", + "strcat() is error-prone; please use strlcat() if possible" . $herecurr); + } + +# check for use of strncat() + if ($line =~ /\bstrncat\s*\(.*\)/) { + WARN("STRNCAT", + "strncat() is error-prone; please use strlcat() if possible" . $herecurr); + } + +# check for use of bzero() + if ($line =~ /\bbzero\s*\(.*\)/) { + ERROR("BZERO", + "bzero() is deprecated; use memset()" . $herecurr); + } } # If we have no input at all, then there is nothing to report on diff --git a/tools/coccinelle/hash_const.cocci b/tools/coccinelle/hash_const.cocci new file mode 100644 index 000000000000..9c53cb01fbfb --- /dev/null +++ b/tools/coccinelle/hash_const.cocci @@ -0,0 +1,76 @@ +// +// Transition hash key signatures to take their argument as const. +// Does not handle headers or weirdly named hash functions. +// +@noconst disable optional_qualifier@ +identifier A; +identifier func =~ ".*key$|.*key_make$|.*hash_make$|.*hash_keymake$|.*hash_key$|.*hash_key.*"; +@@ + +- func (void *A) ++ func (const void *A) + { ... } + +@ depends on noconst disable optional_qualifier @ +identifier noconst.A; +identifier noconst.func; +identifier b; +type T; +@@ + +func( ... ) { +<... +- T b = A; ++ const T b = A; +...> + } + +@ depends on noconst disable optional_qualifier @ +identifier noconst.A; +identifier noconst.func; +identifier b; +type T; +@@ + +func(...) + { +<... +- T b = (T) A; ++ const T b = A; +...> + } + +@ depends on noconst disable optional_qualifier @ +identifier noconst.A; +identifier noconst.func; +identifier b; +type T; +@@ + +func(...) + { +<... +- T b; ++ const T b; +... + b = A; +...> + } + +@ depends on noconst disable optional_qualifier @ +identifier noconst.A; +identifier noconst.func; +identifier b; +type T; +@@ + +func(...) + { +<... +- T b; ++ const T b; +... +- b = (T) A; ++ b = A; +...> + } diff --git a/tools/etc/frr/daemons b/tools/etc/frr/daemons index 2abff422c99b..79c52d30d103 100644 --- a/tools/etc/frr/daemons +++ b/tools/etc/frr/daemons @@ -29,6 +29,7 @@ sharpd=no pbrd=no bfdd=no fabricd=no +vrrpd=no # # If this option is set the /etc/init.d/frr script automatically loads @@ -53,6 +54,15 @@ pbrd_options=" -A 127.0.0.1" staticd_options="-A 127.0.0.1" bfdd_options=" -A 127.0.0.1" fabricd_options="-A 127.0.0.1" +vrrpd_options=" -A 127.0.0.1" + +# +# This is the maximum number of FD's that will be available. +# Upon startup this is read by the control files and ulimit +# is called. Uncomment and use a reasonable value for your +# setup if you are expecting a large number of peers in +# say BGP. +#MAX_FDS=1024 # The list of daemons to watch is automatically generated by the init script. #watchfrr_options="" diff --git a/tools/etc/rsyslog.d/45-frr.conf b/tools/etc/rsyslog.d/45-frr.conf index 4612e8beaf7a..feeeb13f13fc 100644 --- a/tools/etc/rsyslog.d/45-frr.conf +++ b/tools/etc/rsyslog.d/45-frr.conf @@ -16,6 +16,7 @@ if $programname == 'babeld' or $programname == 'pimd' or $programname == 'ripd' or $programname == 'ripngd' or + $programname == 'vrrpd' or $programname == 'watchfrr' or $programname == 'zebra' then :omfile:$frr_log @@ -33,6 +34,7 @@ if $programname == 'babeld' or $programname == 'pimd' or $programname == 'ripd' or $programname == 'ripngd' or + $programname == 'vrrpd' or $programname == 'watchfrr' or $programname == 'zebra' then stop diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 59f1bcf52b01..23e8f3000d94 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -410,7 +410,8 @@ def load_contexts(self): "service ", "table ", "username ", - "zebra ") + "zebra ", + "vrrp autoconfigure") for line in self.lines: @@ -741,6 +742,27 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): lines_to_add_to_del.append((ctx_keys, swpx_interface)) lines_to_add_to_del.append((tmp_ctx_keys, swpx_peergroup)) + ''' + Changing the bfd timers on neighbors is allowed without doing + a delete/add process. Since doing a "no neighbor blah bfd ..." + will cause the peer to bounce unnecessarily, just skip the delete + and just do the add. + ''' + re_nbr_bfd_timers = re.search(r'neighbor (\S+) bfd (\S+) (\S+) (\S+)', line) + + if re_nbr_bfd_timers: + nbr = re_nbr_bfd_timers.group(1) + bfd_nbr = "neighbor %s" % nbr + + for (ctx_keys, add_line) in lines_to_add: + re_add_nbr_bfd_timers = re.search(r'neighbor (\S+) bfd (\S+) (\S+) (\S+)', add_line) + + if re_add_nbr_bfd_timers: + found_add_bfd_nbr = line_exist(lines_to_add, ctx_keys, bfd_nbr, False) + + if found_add_bfd_nbr: + lines_to_del_to_del.append((ctx_keys, line)) + ''' We changed how we display the neighbor interface command. Older versions of frr would display the following: diff --git a/tools/frr.in b/tools/frr.in index 2e3a094589cb..50f7ecaa9f61 100755 --- a/tools/frr.in +++ b/tools/frr.in @@ -25,7 +25,7 @@ FRR_VTY_GROUP="@enable_vty_group@" # frrvty # Local Daemon selection may be done by using /etc/frr/daemons. # See /usr/share/doc/frr/README.Debian.gz for further information. # Keep zebra first and do not list watchfrr! -DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd" +DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd" MAX_INSTANCES=5 RELOAD_SCRIPT="$D_PATH/frr-reload.py" @@ -561,30 +561,7 @@ case "$1" in stop_prio 0 $dmn fi - if [ -z "$dmn" -o "$dmn" = "zebra" ]; then - echo "Removing all routes made by FRR." - # Specific values for each proto can be found - # in /etc/iproute2/rt_protos as well as FRR - # specific ones in /etc/iproute2/rt_protos.d - # Additionally if a new protocol is added - # we need to add it here as well as - # in rt_netlink.h( follow the directions! ) - ip route flush proto 4 - ip route flush proto 11 - ip route flush proto 42 - ip route flush proto 186 - ip route flush proto 187 - ip route flush proto 188 - ip route flush proto 189 - ip route flush proto 190 - ip route flush proto 191 - ip route flush proto 192 - ip route flush proto 193 - ip route flush proto 194 - ip route flush proto 195 - ip route flush proto 196 - ip route flush proto 197 - else + if [ [ -n "$dmn" ] && [ "$dmn" != "zebra" ] ]; then [ -n "$dmn" ] && eval "${dmn/-/_}=0" start_watchfrr fi diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in index 897e6d65587d..3fc38d4bed80 100644 --- a/tools/frrcommon.sh.in +++ b/tools/frrcommon.sh.in @@ -29,7 +29,7 @@ FRR_VTY_GROUP="@enable_vty_group@" # frrvty # - keep zebra first # - watchfrr does NOT belong in this list -DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd" +DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd" RELOAD_SCRIPT="$D_PATH/frr-reload.py" # diff --git a/tools/frrinit.sh.in b/tools/frrinit.sh.in index 0f5ed85864e7..423d6b9b1de6 100644 --- a/tools/frrinit.sh.in +++ b/tools/frrinit.sh.in @@ -71,6 +71,16 @@ reload) exit 1 fi + # systemd doesn't set WATCHDOG_USEC for reload commands. + watchfrr_pidfile="$V_PATH/watchfrr.pid" + watchfrr_pid="`cat \"$watchfrr_pidfile\"`" + if [ -d "/proc/$watchfrr_pid" ]; then + wdt="`tr '\0' '\n' < /proc/$watchfrr_pid/environ | grep '^WATCHDOG_USEC='`" + wdt="${wdt#WATCHDOG_USEC=}" + [ -n "$wdt" ] && : ${WATCHDOG_USEC:=$wdt} + [ -n "$WATCHDOG_USEC" ] && export WATCHDOG_USEC + fi + # restart watchfrr to pick up added daemons. # NB: This will NOT cause the other daemons to be restarted. daemon_list daemons @@ -78,6 +88,21 @@ reload) daemon_stop watchfrr && \ daemon_start watchfrr + # make systemd not kill watchfrr after ExecReload completes + # 3 goats were sacrificed to restore sanity after coding this + watchfrr_pid="`cat \"$watchfrr_pidfile\"`" + if [ -f "/proc/$watchfrr_pid/cgroup" -a -d "/sys/fs/cgroup/systemd" ]; then + cg="`egrep '^[0-9]+:name=systemd:' \"/proc/$watchfrr_pid/cgroup\"`" + cg="${cg#*:*:}" + + cgmain="$cg" + cgmain="${cgmain%/.control}" + cgmain="${cgmain%/control}" + + [ -n "$cg" -a "$cg" != "$cgmain" ] && \ + echo "$watchfrr_pid" > "/sys/fs/cgroup/systemd/$cgmain/tasks" + fi + NEW_CONFIG_FILE="${2:-$C_PATH/frr.conf}" [ ! -r $NEW_CONFIG_FILE ] && log_failure_msg "Unable to read new configuration file $NEW_CONFIG_FILE" && exit 1 "$RELOAD_SCRIPT" --reload "$NEW_CONFIG_FILE" diff --git a/vrrpd/.gitignore b/vrrpd/.gitignore new file mode 100644 index 000000000000..e1751b28ac81 --- /dev/null +++ b/vrrpd/.gitignore @@ -0,0 +1,2 @@ +libvrrp.a +vrrpd diff --git a/vrrpd/Makefile b/vrrpd/Makefile new file mode 100644 index 000000000000..027c6ee1f809 --- /dev/null +++ b/vrrpd/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. vrrp/vrrp +%: ALWAYS + @$(MAKE) -s -C .. vrrp/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/vrrpd/subdir.am b/vrrpd/subdir.am new file mode 100644 index 000000000000..a328f969d6e4 --- /dev/null +++ b/vrrpd/subdir.am @@ -0,0 +1,39 @@ +# +# vrrpd +# + +if VRRPD +noinst_LIBRARIES += vrrpd/libvrrp.a +sbin_PROGRAMS += vrrpd/vrrpd +# dist_examples_DATA += staticd/staticd.conf.sample +vtysh_scan += $(top_srcdir)/vrrpd/vrrp_vty.c +man8 += $(MANBUILD)/vrrpd.8 +endif + +vrrpd_libvrrp_a_SOURCES = \ + vrrpd/vrrp.c \ + vrrpd/vrrp_arp.c \ + vrrpd/vrrp_debug.c \ + vrrpd/vrrp_memory.c \ + vrrpd/vrrp_ndisc.c \ + vrrpd/vrrp_packet.c \ + vrrpd/vrrp_vty.c \ + vrrpd/vrrp_zebra.c \ + # end + +noinst_HEADERS += \ + vrrpd/vrrp.h \ + vrrpd/vrrp_arp.h \ + vrrpd/vrrp_debug.h \ + vrrpd/vrrp_memory.h \ + vrrpd/vrrp_ndisc.h \ + vrrpd/vrrp_packet.h \ + vrrpd/vrrp_vty.h \ + vrrpd/vrrp_zebra.h \ + # end + +vrrpd/vrrp_vty_clippy.c: $(CLIPPY_DEPS) +vrrpd/vrrp_vty.$(OBJEXT): vrrpd/vrrp_vty_clippy.c + +vrrpd_vrrpd_SOURCES = vrrpd/vrrp_main.c +vrrpd_vrrpd_LDADD = vrrpd/libvrrp.a lib/libfrr.la @LIBCAP@ diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c new file mode 100644 index 000000000000..3d535cbfba25 --- /dev/null +++ b/vrrpd/vrrp.c @@ -0,0 +1,2378 @@ +/* + * VRRP global definitions and state machine. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "lib/hash.h" +#include "lib/hook.h" +#include "lib/if.h" +#include "lib/linklist.h" +#include "lib/memory.h" +#include "lib/network.h" +#include "lib/prefix.h" +#include "lib/sockopt.h" +#include "lib/sockunion.h" +#include "lib/vrf.h" +#include "lib/vty.h" + +#include "vrrp.h" +#include "vrrp_arp.h" +#include "vrrp_debug.h" +#include "vrrp_memory.h" +#include "vrrp_ndisc.h" +#include "vrrp_packet.h" +#include "vrrp_zebra.h" + +#define VRRP_LOGPFX "[CORE] " + +/* statics */ +struct hash *vrrp_vrouters_hash; +bool vrrp_autoconfig_is_on; +int vrrp_autoconfig_version; + +struct vrrp_defaults vd; + +const char *vrrp_state_names[3] = { + [VRRP_STATE_INITIALIZE] = "Initialize", + [VRRP_STATE_MASTER] = "Master", + [VRRP_STATE_BACKUP] = "Backup", +}; + +const char *vrrp_event_names[2] = { + [VRRP_EVENT_STARTUP] = "Startup", + [VRRP_EVENT_SHUTDOWN] = "Shutdown", +}; + + +/* Utility functions ------------------------------------------------------- */ + +/* + * Sets an ethaddr to RFC-defined Virtual Router MAC address. + * + * mac + * ethaddr to set + * + * v6 + * Whether this is a V6 or V4 Virtual Router MAC + * + * vrid + * Virtual Router Identifier + */ +static void vrrp_mac_set(struct ethaddr *mac, bool v6, uint8_t vrid) +{ + /* + * V4: 00-00-5E-00-01-{VRID} + * V6: 00-00-5E-00-02-{VRID} + */ + mac->octet[0] = 0x00; + mac->octet[1] = 0x00; + mac->octet[2] = 0x5E; + mac->octet[3] = 0x00; + mac->octet[4] = v6 ? 0x02 : 0x01; + mac->octet[5] = vrid; +} + +/* + * Recalculates and sets skew_time and master_down_interval based + * values. + * + * r + * VRRP Router to operate on + */ +static void vrrp_recalculate_timers(struct vrrp_router *r) +{ + uint16_t mdiadv = r->vr->version == 3 ? r->master_adver_interval + : r->vr->advertisement_interval; + uint16_t skm = (r->vr->version == 3) ? r->master_adver_interval : 100; + + r->skew_time = ((256 - r->vr->priority) * skm) / 256; + r->master_down_interval = 3 * mdiadv; + r->master_down_interval += r->skew_time; +} + +/* + * Determines if a VRRP router is the owner of the specified address. + * + * The determining factor for whether an interface is the address owner is + * simply whether the address is assigned to the VRRP base interface by someone + * other than vrrpd. + * + * This function should always return the correct answer regardless of + * master/backup status. + * + * ifp + * The interface to check owernship of. This should be the base interface of + * a VRRP router. + * + * vr + * Virtual Router + * + * Returns: + * whether or not vr owns the specified address + */ +static bool vrrp_is_owner(struct interface *ifp, struct ipaddr *addr) +{ + /* + * This code sanity checks implicit ownership configuration. Ideally, + * the way we determine address ownership status for this VRRP router + * is by looking at whether our VIPs are also assigned to the base + * interface, and therefore count as "real" addresses. This frees the + * user from having to manually configure priority 255 to indicate + * address ownership. However, this means one of the VIPs will be used + * as the source address for VRRP advertisements, which in turn means + * that other VRRP routers will be receiving packets with a source + * address they themselves have. This causes lots of different issues + * so for now we're disabling this and forcing the user to configure + * priority 255 to indicate ownership. + */ + + return false; + +#if 0 + struct prefix p; + + p.family = IS_IPADDR_V4(addr) ? AF_INET : AF_INET6; + p.prefixlen = IS_IPADDR_V4(addr) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN; + memcpy(&p.u, &addr->ip, sizeof(addr->ip)); + + return !!connected_lookup_prefix_exact(ifp, &p); +#endif +} + +/* + * Whether an interface has a MAC address that matches the VRRP RFC. + * + * ifp + * Interface to check + * + * Returns: + * Whether the interface has a VRRP mac or not + */ +static bool vrrp_ifp_has_vrrp_mac(struct interface *ifp) +{ + struct ethaddr vmac4; + struct ethaddr vmac6; + + vrrp_mac_set(&vmac4, 0, 0x00); + vrrp_mac_set(&vmac6, 1, 0x00); + + return !memcmp(ifp->hw_addr, vmac4.octet, sizeof(vmac4.octet) - 1) + || !memcmp(ifp->hw_addr, vmac6.octet, sizeof(vmac6.octet) - 1); +} + +/* + * Lookup a Virtual Router instance given a macvlan subinterface. + * + * The VRID is extracted from the interface MAC and the 2-tuple (iface, vrid) + * is used to look up any existing instances that match the interface. It does + * not matter whether the instance is already bound to the interface or not. + * + * mvl_ifp + * Interface pointer to use to lookup. Should be a macvlan device. + * + * Returns: + * Virtual Router, if found + * NULL otherwise + */ +static struct vrrp_vrouter *vrrp_lookup_by_if_mvl(struct interface *mvl_ifp) +{ + struct interface *p; + + if (!mvl_ifp || !mvl_ifp->link_ifindex + || !vrrp_ifp_has_vrrp_mac(mvl_ifp)) + return NULL; + + p = if_lookup_by_index(mvl_ifp->link_ifindex, VRF_DEFAULT); + uint8_t vrid = mvl_ifp->hw_addr[5]; + + return vrrp_lookup(p, vrid); +} + +/* + * Lookup the Virtual Router instances configured on a particular interface. + * + * ifp + * Interface pointer to use to lookup. Should not be a macvlan device. + * + * Returns: + * List of virtual routers found + */ +static struct list *vrrp_lookup_by_if(struct interface *ifp) +{ + struct list *l = hash_to_list(vrrp_vrouters_hash); + struct listnode *ln, *nn; + struct vrrp_vrouter *vr; + + for (ALL_LIST_ELEMENTS(l, ln, nn, vr)) + if (vr->ifp != ifp) + list_delete_node(l, ln); + + return l; +} + +/* + * Lookup any Virtual Router instances associated with a particular interface. + * This is a combination of the results from vrrp_lookup_by_if_mvl and + * vrrp_lookup_by_if. + * + * Suppose the system interface list looks like the following: + * + * eth0 + * \- eth0-v0 00:00:5e:00:01:01 + * \- eth0-v1 00:00:5e:00:02:01 + * \- eth0-v2 00:00:5e:00:01:0a + * + * Passing eth0-v2 to this function will give you the VRRP instance configured + * on eth0 with VRID 10. Passing eth0-v0 or eth0-v1 will give you the VRRP + * instance configured on eth0 with VRID 1. Passing eth0 will give you both. + * + * ifp + * Interface pointer to use to lookup. Can be any interface. + * + * Returns: + * List of virtual routers found + */ +static struct list *vrrp_lookup_by_if_any(struct interface *ifp) +{ + struct vrrp_vrouter *vr; + struct list *vrs; + + vr = vrrp_lookup_by_if_mvl(ifp); + vrs = vr ? list_new() : vrrp_lookup_by_if(ifp); + + if (vr) + listnode_add(vrs, vr); + + return vrs; +} + +/* Configuration controllers ----------------------------------------------- */ + +void vrrp_check_start(struct vrrp_vrouter *vr) +{ + struct vrrp_router *r; + bool start; + const char *whynot = NULL; + + if (vr->shutdown || vr->ifp == NULL) + return; + + r = vr->v4; + /* Must not already be started */ + start = r->fsm.state == VRRP_STATE_INITIALIZE; + /* Must have a parent interface */ + start = start && (vr->ifp != NULL); + whynot = (!start && !whynot) ? "No base interface" : NULL; +#if 0 + /* Parent interface must be up */ + start = start && if_is_operative(vr->ifp); +#endif + /* Parent interface must have at least one v4 */ + start = start && vr->ifp->connected->count > 1; + whynot = (!start && !whynot) ? "No primary IPv4 address" : NULL; + /* Must have a macvlan interface */ + start = start && (r->mvl_ifp != NULL); + whynot = (!start && !whynot) ? "No VRRP interface" : NULL; +#if 0 + /* Macvlan interface must be admin up */ + start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP); +#endif + /* Must have at least one VIP configured */ + start = start && r->addrs->count > 0; + whynot = + (!start && !whynot) ? "No Virtual IP address configured" : NULL; + if (start) + vrrp_event(r, VRRP_EVENT_STARTUP); + else if (whynot) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Refusing to start Virtual Router: %s", + vr->vrid, family2str(r->family), whynot); + + r = vr->v6; + /* Must not already be started */ + start = r->fsm.state == VRRP_STATE_INITIALIZE; + /* Must not be v2 */ + start = start && vr->version != 2; + whynot = (!start && !whynot) ? "VRRPv2 does not support v6" : NULL; + /* Must have a parent interface */ + start = start && (vr->ifp != NULL); + whynot = (!start && !whynot) ? "No base interface" : NULL; +#if 0 + /* Parent interface must be up */ + start = start && if_is_operative(vr->ifp); +#endif + /* Must have a macvlan interface */ + start = start && (r->mvl_ifp != NULL); + whynot = (!start && !whynot) ? "No VRRP interface" : NULL; +#if 0 + /* Macvlan interface must be admin up */ + start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP); + /* Macvlan interface must have a link local */ + start = start && connected_get_linklocal(r->mvl_ifp); + whynot = + (!start && !whynot) ? "No link local address configured" : NULL; + /* Macvlan interface must have a v6 IP besides the link local */ + start = start && (r->mvl_ifp->connected->count >= 2); + whynot = (!start && !whynot) + ? "No Virtual IP configured on macvlan device" + : NULL; +#endif + /* Must have at least one VIP configured */ + start = start && r->addrs->count > 0; + whynot = + (!start && !whynot) ? "No Virtual IP address configured" : NULL; + if (start) + vrrp_event(r, VRRP_EVENT_STARTUP); + else if (whynot) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Refusing to start Virtual Router: %s", + vr->vrid, family2str(r->family), whynot); +} + +void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority) +{ + vr->priority = priority; + vr->v4->priority = priority; + vr->v6->priority = priority; +} + +void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr, + uint16_t advertisement_interval) +{ + if (vr->advertisement_interval == advertisement_interval) + return; + + vr->advertisement_interval = advertisement_interval; + vrrp_recalculate_timers(vr->v4); + vrrp_recalculate_timers(vr->v6); +} + +static bool vrrp_has_ip(struct vrrp_vrouter *vr, struct ipaddr *ip) +{ + struct vrrp_router *r = ip->ipa_type == IPADDR_V4 ? vr->v4 : vr->v6; + struct listnode *ln; + struct ipaddr *iter; + + for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, iter)) + if (!memcmp(&iter->ip, &ip->ip, IPADDRSZ(ip))) + return true; + + return false; +} + +int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip) +{ + int af = (ip->ipa_type == IPADDR_V6) ? AF_INET6 : AF_INET; + + assert(r->family == af); + assert(!(r->vr->version == 2 && ip->ipa_type == IPADDR_V6)); + + if (vrrp_has_ip(r->vr, ip)) + return 0; + + if (!vrrp_is_owner(r->vr->ifp, ip) && r->is_owner) { + char ipbuf[INET6_ADDRSTRLEN]; + + inet_ntop(r->family, &ip->ip, ipbuf, sizeof(ipbuf)); + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "This VRRP router is not the address owner of %s, but is the address owner of other addresses; this config is unsupported.", + r->vr->vrid, family2str(r->family), ipbuf); + return -1; + } + + struct ipaddr *new = XCALLOC(MTYPE_VRRP_IP, sizeof(struct ipaddr)); + + *new = *ip; + listnode_add(r->addrs, new); + + if (r->fsm.state == VRRP_STATE_MASTER) { + switch (r->family) { + case AF_INET: + vrrp_garp_send(r, &new->ipaddr_v4); + break; + case AF_INET6: + vrrp_ndisc_una_send(r, new); + break; + } + } + + return 0; +} + +int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) +{ + struct ipaddr ip; + + ip.ipa_type = IPADDR_V4; + ip.ipaddr_v4 = v4; + return vrrp_add_ip(vr->v4, &ip); +} + +int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) +{ + assert(vr->version != 2); + + struct ipaddr ip; + + ip.ipa_type = IPADDR_V6; + ip.ipaddr_v6 = v6; + return vrrp_add_ip(vr->v6, &ip); +} + +int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip) +{ + struct listnode *ln, *nn; + struct ipaddr *iter; + int ret = 0; + + if (!vrrp_has_ip(r->vr, ip)) + return 0; + + for (ALL_LIST_ELEMENTS(r->addrs, ln, nn, iter)) + if (!memcmp(&iter->ip, &ip->ip, IPADDRSZ(ip))) + list_delete_node(r->addrs, ln); + + /* + * NB: Deleting the last address and then issuing a shutdown will cause + * transmission of a priority 0 VRRP Advertisement - as per the RFC - + * but it will have no addresses. This is not forbidden in the RFC but + * might confuse other implementations. + */ + if (r->addrs->count == 0 && r->fsm.state != VRRP_STATE_INITIALIZE) + ret = vrrp_event(r, VRRP_EVENT_SHUTDOWN); + + return ret; +} + +int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) +{ + struct ipaddr ip; + + ip.ipa_type = IPADDR_V6; + ip.ipaddr_v6 = v6; + return vrrp_del_ip(vr->v6, &ip); +} + +int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) +{ + struct ipaddr ip; + + ip.ipa_type = IPADDR_V4; + ip.ipaddr_v4 = v4; + return vrrp_del_ip(vr->v4, &ip); +} + + +/* Creation and destruction ------------------------------------------------ */ + +static void vrrp_router_addr_list_del_cb(void *val) +{ + struct ipaddr *ip = val; + + XFREE(MTYPE_VRRP_IP, ip); +} + +/* + * Search for a suitable macvlan subinterface we can attach to, and if found, + * attach to it. + * + * r + * Router to attach to interface + * + * Returns: + * Whether an interface was successfully attached + */ +static bool vrrp_attach_interface(struct vrrp_router *r) +{ + /* Search for existing interface with computed MAC address */ + struct interface **ifps; + + size_t ifps_cnt = if_lookup_by_hwaddr( + r->vmac.octet, sizeof(r->vmac.octet), &ifps, VRF_DEFAULT); + + /* + * Filter to only those macvlan interfaces whose parent is the base + * interface this VRRP router is configured on. + * + * If there are still multiple interfaces we just select the first one, + * as it should be functionally identical to the others. + */ + unsigned int candidates = 0; + struct interface *selection = NULL; + + for (unsigned int i = 0; i < ifps_cnt; i++) { + if (ifps[i]->link_ifindex != r->vr->ifp->ifindex) + ifps[i] = NULL; + else { + selection = selection ? selection : ifps[i]; + candidates++; + } + } + + if (ifps_cnt) + XFREE(MTYPE_TMP, ifps); + + char ethstr[ETHER_ADDR_STRLEN]; + + prefix_mac2str(&r->vmac, ethstr, sizeof(ethstr)); + + assert(!!selection == !!candidates); + + if (candidates == 0) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Interface: None (no interface found w/ MAC %s)", + r->vr->vrid, family2str(r->family), ethstr); + else if (candidates > 1) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Interface: Multiple interfaces found; using %s", + r->vr->vrid, family2str(r->family), selection->name); + else + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Interface: %s", + r->vr->vrid, family2str(r->family), selection->name); + + r->mvl_ifp = selection; + + return !!r->mvl_ifp; +} + +static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, + int family) +{ + struct vrrp_router *r = + XCALLOC(MTYPE_VRRP_RTR, sizeof(struct vrrp_router)); + + r->family = family; + r->sock_rx = -1; + r->sock_tx = -1; + r->vr = vr; + r->addrs = list_new(); + r->addrs->del = vrrp_router_addr_list_del_cb; + r->priority = vr->priority; + r->fsm.state = VRRP_STATE_INITIALIZE; + vrrp_mac_set(&r->vmac, family == AF_INET6, vr->vrid); + + vrrp_attach_interface(r); + + return r; +} + +static void vrrp_router_destroy(struct vrrp_router *r) +{ + if (r->is_active) + vrrp_event(r, VRRP_EVENT_SHUTDOWN); + + if (r->sock_rx >= 0) + close(r->sock_rx); + if (r->sock_tx >= 0) + close(r->sock_tx); + + /* FIXME: also delete list elements */ + list_delete(&r->addrs); + XFREE(MTYPE_VRRP_RTR, r); +} + +struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid, + uint8_t version) +{ + struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid); + + if (vr) + return vr; + + if (version != 2 && version != 3) + return NULL; + + vr = XCALLOC(MTYPE_VRRP_RTR, sizeof(struct vrrp_vrouter)); + + vr->ifp = ifp; + vr->version = version; + vr->vrid = vrid; + vr->priority = vd.priority; + vr->preempt_mode = vd.preempt_mode; + vr->accept_mode = vd.accept_mode; + vr->shutdown = vd.shutdown; + + vr->v4 = vrrp_router_create(vr, AF_INET); + vr->v6 = vrrp_router_create(vr, AF_INET6); + + vrrp_set_advertisement_interval(vr, vd.advertisement_interval); + + hash_get(vrrp_vrouters_hash, vr, hash_alloc_intern); + + return vr; +} + +void vrrp_vrouter_destroy(struct vrrp_vrouter *vr) +{ + vrrp_router_destroy(vr->v4); + vrrp_router_destroy(vr->v6); + hash_release(vrrp_vrouters_hash, vr); + XFREE(MTYPE_VRRP_RTR, vr); +} + +struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid) +{ + struct vrrp_vrouter vr; + + vr.vrid = vrid; + vr.ifp = ifp; + + return hash_lookup(vrrp_vrouters_hash, &vr); +} + +/* Network ----------------------------------------------------------------- */ + +/* Forward decls */ +static void vrrp_change_state(struct vrrp_router *r, int to); +static int vrrp_adver_timer_expire(struct thread *thread); +static int vrrp_master_down_timer_expire(struct thread *thread); + +/* + * Finds the first connected address of the appropriate family on a VRRP + * router's interface and binds the Tx socket of the VRRP router to that + * address. + * + * Also sets src field of vrrp_router. + * + * r + * VRRP router to operate on + * + * Returns: + * 0 on success + * -1 on failure + */ +static int vrrp_bind_to_primary_connected(struct vrrp_router *r) +{ + char ipstr[INET6_ADDRSTRLEN]; + struct interface *ifp; + + /* + * A slight quirk: the RFC specifies that advertisements under IPv6 must + * be transmitted using the link local address of the source interface + */ + ifp = r->family == AF_INET ? r->vr->ifp : r->mvl_ifp; + + struct listnode *ln; + struct connected *c = NULL; + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, ln, c)) + if (c->address->family == r->family) { + if (r->family == AF_INET6 + && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + break; + else if (r->family == AF_INET) + break; + } + + if (c == NULL) { + zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to find address to bind on %s", + r->vr->vrid, family2str(r->family), ifp->name); + return -1; + } + + union sockunion su; + + memset(&su, 0x00, sizeof(su)); + + switch (r->family) { + case AF_INET: + r->src.ipa_type = IPADDR_V4; + r->src.ipaddr_v4 = c->address->u.prefix4; + su.sin.sin_family = AF_INET; + su.sin.sin_addr = c->address->u.prefix4; + break; + case AF_INET6: + r->src.ipa_type = IPADDR_V6; + r->src.ipaddr_v6 = c->address->u.prefix6; + su.sin6.sin6_family = AF_INET6; + su.sin6.sin6_scope_id = ifp->ifindex; + su.sin6.sin6_addr = c->address->u.prefix6; + break; + } + + int ret = 0; + + sockopt_reuseaddr(r->sock_tx); + if (bind(r->sock_tx, (const struct sockaddr *)&su, sizeof(su)) < 0) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Tx socket to primary IP address %s: %s", + r->vr->vrid, family2str(r->family), + inet_ntop(r->family, + (const void *)&c->address->u.prefix, ipstr, + sizeof(ipstr)), + safe_strerror(errno)); + ret = -1; + } else { + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Tx socket to primary IP address %s", + r->vr->vrid, family2str(r->family), + inet_ntop(r->family, (const void *)&c->address->u.prefix, + ipstr, sizeof(ipstr))); + } + + return ret; +} + + +/* + * Create and multicast a VRRP ADVERTISEMENT message. + * + * r + * VRRP Router for which to send ADVERTISEMENT + */ +static void vrrp_send_advertisement(struct vrrp_router *r) +{ + struct vrrp_pkt *pkt; + ssize_t pktsz; + struct ipaddr *addrs[r->addrs->count]; + union sockunion dest; + + if (r->src.ipa_type == IPADDR_NONE + && vrrp_bind_to_primary_connected(r) < 0) + return; + + list_to_array(r->addrs, (void **)addrs, r->addrs->count); + + pktsz = vrrp_pkt_adver_build(&pkt, &r->src, r->vr->version, r->vr->vrid, + r->priority, r->vr->advertisement_interval, + r->addrs->count, (struct ipaddr **)&addrs); + + if (DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) + zlog_hexdump(pkt, (size_t)pktsz); + + const char *group = r->family == AF_INET ? VRRP_MCASTV4_GROUP_STR + : VRRP_MCASTV6_GROUP_STR; + (void)str2sockunion(group, &dest); + + ssize_t sent = sendto(r->sock_tx, pkt, (size_t)pktsz, 0, &dest.sa, + sockunion_sizeof(&dest)); + + XFREE(MTYPE_VRRP_PKT, pkt); + + if (sent < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to send VRRP Advertisement: %s", + r->vr->vrid, family2str(r->family), + safe_strerror(errno)); + } else { + ++r->stats.adver_tx_cnt; + } +} + +/* + * Receive and parse VRRP advertisement. + * + * By the time we get here all fields have been validated for basic correctness + * and the packet is a valid VRRP packet. + * + * However, we have not validated whether the VRID is correct for this virtual + * router, nor whether the priority is correct (i.e. is not 255 when we are the + * address owner), nor whether the advertisement interval equals our own + * configured value (this check is only performed in VRRPv2). + * + * r + * VRRP Router associated with the socket this advertisement was received on + * + * src + * Source address of sender + * + * pkt + * The advertisement they sent + * + * pktsize + * Size of advertisement + * + * Returns: + * -1 if advertisement is invalid + * 0 otherwise + */ +static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, + struct vrrp_pkt *pkt, size_t pktsize) +{ + char sipstr[INET6_ADDRSTRLEN]; + char dipstr[INET6_ADDRSTRLEN]; + + ipaddr2str(src, sipstr, sizeof(sipstr)); + ipaddr2str(&r->src, dipstr, sizeof(dipstr)); + + char dumpbuf[BUFSIZ]; + + vrrp_pkt_adver_dump(dumpbuf, sizeof(dumpbuf), pkt); + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Received VRRP Advertisement from %s:\n%s", + r->vr->vrid, family2str(r->family), sipstr, dumpbuf); + + /* Check that VRID matches our configured VRID */ + if (pkt->hdr.vrid != r->vr->vrid) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram invalid: Advertisement contains VRID %" PRIu8 + " which does not match our instance", + r->vr->vrid, family2str(r->family), pkt->hdr.vrid); + return -1; + } + + /* Verify that we are not the IPvX address owner */ + if (r->is_owner) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram invalid: Received advertisement but we are the address owner", + r->vr->vrid, family2str(r->family)); + return -1; + } + + /* If v2, verify that adver time matches ours */ + bool adveq = (pkt->hdr.v2.adver_int + == MAX(r->vr->advertisement_interval / 100, 1)); + if (r->vr->version == 2 && !adveq) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram invalid: Received advertisement with advertisement interval %" PRIu8 + " unequal to our configured value %u", + r->vr->vrid, family2str(r->family), + pkt->hdr.v2.adver_int, + MAX(r->vr->advertisement_interval / 100, 1)); + return -1; + } + + + /* Check that # IPs received matches our # configured IPs */ + if (pkt->hdr.naddr != r->addrs->count) + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram has %" PRIu8 + " addresses, but this VRRP instance has %u", + r->vr->vrid, family2str(r->family), pkt->hdr.naddr, + r->addrs->count); + + ++r->stats.adver_rx_cnt; + + int addrcmp; + + switch (r->fsm.state) { + case VRRP_STATE_MASTER: + addrcmp = memcmp(&src->ip, &r->src.ip, IPADDRSZ(src)); + + if (pkt->hdr.priority == 0) { + vrrp_send_advertisement(r); + THREAD_OFF(r->t_adver_timer); + thread_add_timer_msec( + master, vrrp_adver_timer_expire, r, + r->vr->advertisement_interval * 10, + &r->t_adver_timer); + } else if (pkt->hdr.priority > r->priority + || ((pkt->hdr.priority == r->priority) + && addrcmp > 0)) { + zlog_info( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Received advertisement from %s w/ priority %" PRIu8 + "; switching to Backup", + r->vr->vrid, family2str(r->family), sipstr, + pkt->hdr.priority); + THREAD_OFF(r->t_adver_timer); + if (r->vr->version == 3) { + r->master_adver_interval = + htons(pkt->hdr.v3.adver_int); + } + vrrp_recalculate_timers(r); + THREAD_OFF(r->t_master_down_timer); + thread_add_timer_msec(master, + vrrp_master_down_timer_expire, r, + r->master_down_interval * 10, + &r->t_master_down_timer); + vrrp_change_state(r, VRRP_STATE_BACKUP); + } else { + /* Discard advertisement */ + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Discarding advertisement from %s (%" PRIu8 + " <= %" PRIu8 " & %s <= %s)", + r->vr->vrid, family2str(r->family), sipstr, + pkt->hdr.priority, r->priority, sipstr, dipstr); + } + break; + case VRRP_STATE_BACKUP: + if (pkt->hdr.priority == 0) { + THREAD_OFF(r->t_master_down_timer); + thread_add_timer_msec( + master, vrrp_master_down_timer_expire, r, + r->skew_time * 10, &r->t_master_down_timer); + } else if (r->vr->preempt_mode == false + || pkt->hdr.priority >= r->priority) { + if (r->vr->version == 3) { + r->master_adver_interval = + ntohs(pkt->hdr.v3.adver_int); + } + vrrp_recalculate_timers(r); + THREAD_OFF(r->t_master_down_timer); + thread_add_timer_msec(master, + vrrp_master_down_timer_expire, r, + r->master_down_interval * 10, + &r->t_master_down_timer); + } else if (r->vr->preempt_mode == true + && pkt->hdr.priority < r->priority) { + /* Discard advertisement */ + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Discarding advertisement from %s (%" PRIu8 + " < %" PRIu8 " & preempt = true)", + r->vr->vrid, family2str(r->family), sipstr, + pkt->hdr.priority, r->priority); + } + break; + case VRRP_STATE_INITIALIZE: + zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Received ADVERTISEMENT in state %s; this is a bug", + r->vr->vrid, family2str(r->family), + vrrp_state_names[r->fsm.state]); + break; + } + + return 0; +} + +/* + * Read and process next IPvX datagram. + */ +static int vrrp_read(struct thread *thread) +{ + struct vrrp_router *r = thread->arg; + + struct vrrp_pkt *pkt; + ssize_t pktsize; + ssize_t nbytes; + bool resched; + char errbuf[BUFSIZ]; + struct sockaddr_storage sa; + uint8_t control[64]; + struct ipaddr src = {}; + + struct msghdr m = {}; + struct iovec iov; + + iov.iov_base = r->ibuf; + iov.iov_len = sizeof(r->ibuf); + m.msg_name = &sa; + m.msg_namelen = sizeof(sa); + m.msg_iov = &iov; + m.msg_iovlen = 1; + m.msg_control = control; + m.msg_controllen = sizeof(control); + + nbytes = recvmsg(r->sock_rx, &m, MSG_DONTWAIT); + + if ((nbytes < 0 && ERRNO_IO_RETRY(errno))) { + resched = true; + goto done; + } else if (nbytes <= 0) { + vrrp_event(r, VRRP_EVENT_SHUTDOWN); + resched = false; + goto done; + } + + if (DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) { + DEBUGD(&vrrp_dbg_pkt, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram rx: ", + r->vr->vrid, family2str(r->family)); + zlog_hexdump(r->ibuf, nbytes); + } + + pktsize = vrrp_pkt_parse_datagram(r->family, r->vr->version, &m, nbytes, + &src, &pkt, errbuf, sizeof(errbuf)); + + if (pktsize < 0) + DEBUGD(&vrrp_dbg_pkt, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram invalid: %s", + r->vr->vrid, family2str(r->family), errbuf); + else + vrrp_recv_advertisement(r, &src, pkt, pktsize); + + resched = true; + +done: + memset(r->ibuf, 0x00, sizeof(r->ibuf)); + + if (resched) + thread_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read); + + return 0; +} + +/* + * Creates and configures VRRP router sockets. + * + * This function: + * - Creates two sockets, one for Tx, one for Rx + * - Joins the Rx socket to the appropriate VRRP multicast group + * - Sets the Tx socket to set the TTL (v4) or Hop Limit (v6) field to 255 for + * all transmitted IPvX packets + * - Requests the kernel to deliver IPv6 header values needed to validate VRRP + * packets + * + * If any of the above fail, the sockets are closed. The only exception is if + * the TTL / Hop Limit settings fail; these are logged, but configuration + * proceeds. + * + * The first connected address on the Virtual Router's interface is used as the + * interface address. + * + * r + * VRRP Router for which to create listen socket + * + * Returns: + * 0 on success + * -1 on failure + */ +static int vrrp_socket(struct vrrp_router *r) +{ + int ret; + bool failed = false; + + frr_elevate_privs(&vrrp_privs) + { + r->sock_rx = socket(r->family, SOCK_RAW, IPPROTO_VRRP); + r->sock_tx = socket(r->family, SOCK_RAW, IPPROTO_VRRP); + } + + if (r->sock_rx < 0 || r->sock_tx < 0) { + const char *rxtx = r->sock_rx < 0 ? "Rx" : "Tx"; + + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Can't create VRRP %s socket", + r->vr->vrid, family2str(r->family), rxtx); + failed = true; + goto done; + } + + /* Configure sockets */ + if (r->family == AF_INET) { + /* Set Tx socket to always Tx with TTL set to 255 */ + int ttl = 255; + + ret = setsockopt(r->sock_tx, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, + sizeof(ttl)); + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to set outgoing multicast TTL count to 255; RFC 5798 compliant implementations will drop our packets", + r->vr->vrid, family2str(r->family)); + } + + /* Set Tx socket DSCP byte */ + setsockopt_ipv4_tos(r->sock_tx, IPTOS_PREC_INTERNETCONTROL); + + /* Turn off multicast loop on Tx */ + setsockopt_ipv4_multicast_loop(r->sock_tx, 0); + + /* Bind Rx socket to exact interface */ + frr_elevate_privs(&vrrp_privs) + { + ret = setsockopt(r->sock_rx, SOL_SOCKET, + SO_BINDTODEVICE, r->vr->ifp->name, + strlen(r->vr->ifp->name)); + } + if (ret) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Rx socket to %s: %s", + r->vr->vrid, family2str(r->family), + r->vr->ifp->name, safe_strerror(errno)); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Rx socket to %s", + r->vr->vrid, family2str(r->family), r->vr->ifp->name); + + /* Bind Rx socket to v4 multicast address */ + struct sockaddr_in sa = {0}; + + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(VRRP_MCASTV4_GROUP); + if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Rx socket to VRRP multicast group: %s", + r->vr->vrid, family2str(r->family), + safe_strerror(errno)); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Rx socket to VRRP multicast group", + r->vr->vrid, family2str(r->family)); + + /* Join Rx socket to VRRP IPv4 multicast group */ + assert(listhead(r->vr->ifp->connected)); + struct connected *c = listhead(r->vr->ifp->connected)->data; + struct in_addr v4 = c->address->u.prefix4; + + ret = setsockopt_ipv4_multicast(r->sock_rx, IP_ADD_MEMBERSHIP, + v4, htonl(VRRP_MCASTV4_GROUP), + r->vr->ifp->ifindex); + if (ret < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to join VRRP %s multicast group", + r->vr->vrid, family2str(r->family)); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Joined VRRP multicast group", + r->vr->vrid, family2str(r->family)); + + /* Set outgoing interface for advertisements */ + struct ip_mreqn mreqn = {}; + + mreqn.imr_ifindex = r->mvl_ifp->ifindex; + ret = setsockopt(r->sock_tx, IPPROTO_IP, IP_MULTICAST_IF, + (void *)&mreqn, sizeof(mreqn)); + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Could not set %s as outgoing multicast interface", + r->vr->vrid, family2str(r->family), + r->mvl_ifp->name); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Set %s as outgoing multicast interface", + r->vr->vrid, family2str(r->family), r->mvl_ifp->name); + + /* Select and bind source address */ + if (vrrp_bind_to_primary_connected(r) < 0) { + failed = true; + goto done; + } + + } else if (r->family == AF_INET6) { + /* Always transmit IPv6 packets with hop limit set to 255 */ + ret = setsockopt_ipv6_multicast_hops(r->sock_tx, 255); + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to set outgoing multicast hop count to 255; RFC 5798 compliant implementations will drop our packets", + r->vr->vrid, family2str(r->family)); + } + + /* Set Tx socket DSCP byte */ + setsockopt_ipv6_tclass(r->sock_tx, IPTOS_PREC_INTERNETCONTROL); + + /* Request hop limit delivery */ + setsockopt_ipv6_hoplimit(r->sock_rx, 1); + if (ret < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to request IPv6 Hop Limit delivery", + r->vr->vrid, family2str(r->family)); + failed = true; + goto done; + } + + /* Turn off multicast loop on Tx */ + setsockopt_ipv6_multicast_loop(r->sock_tx, 0); + + /* Bind Rx socket to exact interface */ + frr_elevate_privs(&vrrp_privs) + { + ret = setsockopt(r->sock_rx, SOL_SOCKET, + SO_BINDTODEVICE, r->vr->ifp->name, + strlen(r->vr->ifp->name)); + } + if (ret) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Rx socket to %s: %s", + r->vr->vrid, family2str(r->family), + r->vr->ifp->name, safe_strerror(errno)); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Rx socket to %s", + r->vr->vrid, family2str(r->family), r->vr->ifp->name); + + /* Bind Rx socket to v6 multicast address */ + struct sockaddr_in6 sa = {0}; + + sa.sin6_family = AF_INET6; + inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &sa.sin6_addr); + if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Rx socket to VRRP multicast group: %s", + r->vr->vrid, family2str(r->family), + safe_strerror(errno)); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Rx socket to VRRP multicast group", + r->vr->vrid, family2str(r->family)); + + /* Join VRRP IPv6 multicast group */ + struct ipv6_mreq mreq; + + inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, + &mreq.ipv6mr_multiaddr); + mreq.ipv6mr_interface = r->vr->ifp->ifindex; + ret = setsockopt(r->sock_rx, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, sizeof(mreq)); + if (ret < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to join VRRP multicast group", + r->vr->vrid, family2str(r->family)); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Joined VRRP multicast group", + r->vr->vrid, family2str(r->family)); + + /* Set outgoing interface for advertisements */ + ret = setsockopt(r->sock_tx, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &r->mvl_ifp->ifindex, sizeof(ifindex_t)); + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Could not set %s as outgoing multicast interface", + r->vr->vrid, family2str(r->family), + r->mvl_ifp->name); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Set %s as outgoing multicast interface", + r->vr->vrid, family2str(r->family), r->mvl_ifp->name); + } + +done: + ret = 0; + if (failed) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to initialize VRRP router", + r->vr->vrid, family2str(r->family)); + if (r->sock_rx >= 0) { + close(r->sock_rx); + r->sock_rx = -1; + } + if (r->sock_tx >= 0) { + close(r->sock_tx); + r->sock_tx = -1; + } + ret = -1; + } + + return ret; +} + + +/* State machine ----------------------------------------------------------- */ + +DEFINE_HOOK(vrrp_change_state_hook, (struct vrrp_router *r, int to), (r, to)); + +/* + * Handle any necessary actions during state change to MASTER state. + * + * r + * VRRP Router to operate on + */ +static void vrrp_change_state_master(struct vrrp_router *r) +{ + /* Enable ND Router Advertisements */ + if (r->family == AF_INET6) + vrrp_zebra_radv_set(r, true); + + /* Set protodown off */ + vrrp_zclient_send_interface_protodown(r->mvl_ifp, false); + + /* + * If protodown is already off, we can send our stuff, otherwise we + * have to delay until the interface is all the way up + */ + if (if_is_operative(r->mvl_ifp)) { + vrrp_send_advertisement(r); + + if (r->family == AF_INET) + vrrp_garp_send_all(r); + else if (r->family == AF_INET6) + vrrp_ndisc_una_send_all(r); + } else { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Delaying VRRP advertisement until interface is up", + r->vr->vrid, family2str(r->family)); + r->advert_pending = true; + + if (r->family == AF_INET) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Delaying VRRP gratuitous ARPs until interface is up", + r->vr->vrid, family2str(r->family)); + r->garp_pending = true; + } else if (r->family == AF_INET6) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Delaying VRRP unsolicited neighbor advertisement until interface is up", + r->vr->vrid, family2str(r->family)); + r->ndisc_pending = true; + } + } +} + +/* + * Handle any necessary actions during state change to BACKUP state. + * + * r + * Virtual Router to operate on + */ +static void vrrp_change_state_backup(struct vrrp_router *r) +{ + /* Disable ND Router Advertisements */ + if (r->family == AF_INET6) + vrrp_zebra_radv_set(r, false); + + /* Disable Adver_Timer */ + THREAD_OFF(r->t_adver_timer); + + r->advert_pending = false; + r->garp_pending = false; + r->ndisc_pending = false; + memset(&r->src, 0x00, sizeof(r->src)); + + vrrp_zclient_send_interface_protodown(r->mvl_ifp, true); +} + +/* + * Handle any necessary actions during state change to INITIALIZE state. + * + * This is not called for initial startup, only when transitioning from MASTER + * or BACKUP. + * + * r + * VRRP Router to operate on + */ +static void vrrp_change_state_initialize(struct vrrp_router *r) +{ + r->master_adver_interval = 0; + vrrp_recalculate_timers(r); + + r->advert_pending = false; + r->garp_pending = false; + r->ndisc_pending = false; + + /* Disable ND Router Advertisements */ + if (r->family == AF_INET6) + vrrp_zebra_radv_set(r, false); +} + +void (*vrrp_change_state_handlers[])(struct vrrp_router *vr) = { + [VRRP_STATE_MASTER] = vrrp_change_state_master, + [VRRP_STATE_BACKUP] = vrrp_change_state_backup, + [VRRP_STATE_INITIALIZE] = vrrp_change_state_initialize, +}; + +/* + * Change Virtual Router FSM position. Handles transitional actions and calls + * any subscribers to the state change hook. + * + * r + * Virtual Router for which to change state + * + * to + * State to change to + */ +static void vrrp_change_state(struct vrrp_router *r, int to) +{ + if (r->fsm.state == to) + return; + + /* Call our handlers, then any subscribers */ + vrrp_change_state_handlers[to](r); + hook_call(vrrp_change_state_hook, r, to); + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "%s -> %s", + r->vr->vrid, family2str(r->family), + vrrp_state_names[r->fsm.state], vrrp_state_names[to]); + r->fsm.state = to; + + ++r->stats.trans_cnt; +} + +/* + * Called when Adver_Timer expires. + */ +static int vrrp_adver_timer_expire(struct thread *thread) +{ + struct vrrp_router *r = thread->arg; + + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Adver_Timer expired", + r->vr->vrid, family2str(r->family)); + + if (r->fsm.state == VRRP_STATE_MASTER) { + /* Send an ADVERTISEMENT */ + vrrp_send_advertisement(r); + + /* Reset the Adver_Timer to Advertisement_Interval */ + thread_add_timer_msec(master, vrrp_adver_timer_expire, r, + r->vr->advertisement_interval * 10, + &r->t_adver_timer); + } else { + zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Adver_Timer expired in state '%s'; this is a bug", + r->vr->vrid, family2str(r->family), + vrrp_state_names[r->fsm.state]); + } + + return 0; +} + +/* + * Called when Master_Down_Timer expires. + */ +static int vrrp_master_down_timer_expire(struct thread *thread) +{ + struct vrrp_router *r = thread->arg; + + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Master_Down_Timer expired", + r->vr->vrid, family2str(r->family)); + + thread_add_timer_msec(master, vrrp_adver_timer_expire, r, + r->vr->advertisement_interval * 10, + &r->t_adver_timer); + vrrp_change_state(r, VRRP_STATE_MASTER); + + return 0; +} + +/* + * Event handler for Startup event. + * + * Creates sockets, sends advertisements and ARP requests, starts timers, + * and transitions the Virtual Router to either Master or Backup states. + * + * This function will also initialize the program's global ARP subsystem if it + * has not yet been initialized. + * + * r + * VRRP Router on which to apply Startup event + * + * Returns: + * < 0 if the session socket could not be created, or the state is not + * Initialize + * 0 on success + */ +static int vrrp_startup(struct vrrp_router *r) +{ + /* May only be called when the state is Initialize */ + if (r->fsm.state != VRRP_STATE_INITIALIZE) + return -1; + + /* Must have a valid macvlan interface available */ + if (r->mvl_ifp == NULL && !vrrp_attach_interface(r)) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "No appropriate interface found", + r->vr->vrid, family2str(r->family)); + return -1; + } + + /* Initialize global gratuitous ARP socket if necessary */ + if (r->family == AF_INET && !vrrp_garp_is_init()) + vrrp_garp_init(); + if (r->family == AF_INET6 && !vrrp_ndisc_is_init()) + vrrp_ndisc_init(); + + /* Create socket */ + if (r->sock_rx < 0 || r->sock_tx < 0) { + int ret = vrrp_socket(r); + + if (ret < 0 || r->sock_tx < 0 || r->sock_rx < 0) + return ret; + } + + /* Schedule listener */ + thread_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read); + + /* Configure effective priority */ + assert(listhead(r->addrs)); + struct ipaddr *primary = (struct ipaddr *)listhead(r->addrs)->data; + char ipbuf[INET6_ADDRSTRLEN]; + + inet_ntop(r->family, &primary->ip.addr, ipbuf, sizeof(ipbuf)); + + if (r->vr->priority == VRRP_PRIO_MASTER + || vrrp_is_owner(r->vr->ifp, primary)) { + r->priority = VRRP_PRIO_MASTER; + vrrp_recalculate_timers(r); + + zlog_info( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "%s has priority set to 255 or owns primary Virtual Router IP %s; electing self as Master", + r->vr->vrid, family2str(r->family), r->vr->ifp->name, + ipbuf); + } + + if (r->priority == VRRP_PRIO_MASTER) { + thread_add_timer_msec(master, vrrp_adver_timer_expire, r, + r->vr->advertisement_interval * 10, + &r->t_adver_timer); + vrrp_change_state(r, VRRP_STATE_MASTER); + } else { + r->master_adver_interval = r->vr->advertisement_interval; + vrrp_recalculate_timers(r); + thread_add_timer_msec(master, vrrp_master_down_timer_expire, r, + r->master_down_interval * 10, + &r->t_master_down_timer); + vrrp_change_state(r, VRRP_STATE_BACKUP); + } + + r->is_active = true; + + return 0; +} + +/* + * Shuts down a Virtual Router and transitions it to Initialize. + * + * This call must be idempotent; it is safe to call multiple times on the same + * VRRP Router. + */ +static int vrrp_shutdown(struct vrrp_router *r) +{ + uint8_t saved_prio; + + switch (r->fsm.state) { + case VRRP_STATE_MASTER: + /* Send an ADVERTISEMENT with Priority = 0 */ + saved_prio = r->priority; + r->priority = 0; + vrrp_send_advertisement(r); + r->priority = saved_prio; + break; + case VRRP_STATE_BACKUP: + break; + case VRRP_STATE_INITIALIZE: + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Received '%s' event in '%s' state; ignoring", + r->vr->vrid, family2str(r->family), + vrrp_event_names[VRRP_EVENT_SHUTDOWN], + vrrp_state_names[VRRP_STATE_INITIALIZE]); + break; + } + + /* Cancel all timers */ + THREAD_OFF(r->t_adver_timer); + THREAD_OFF(r->t_master_down_timer); + THREAD_OFF(r->t_read); + THREAD_OFF(r->t_write); + + /* Protodown macvlan */ + vrrp_zclient_send_interface_protodown(r->mvl_ifp, true); + + /* Throw away our source address */ + memset(&r->src, 0x00, sizeof(r->src)); + + if (r->sock_rx > 0) { + close(r->sock_rx); + r->sock_rx = -1; + } + if (r->sock_tx > 0) { + close(r->sock_tx); + r->sock_tx = -1; + } + + vrrp_change_state(r, VRRP_STATE_INITIALIZE); + + r->is_active = false; + + return 0; +} + +static int (*vrrp_event_handlers[])(struct vrrp_router *r) = { + [VRRP_EVENT_STARTUP] = vrrp_startup, + [VRRP_EVENT_SHUTDOWN] = vrrp_shutdown, +}; + +/* + * Spawn a VRRP FSM event on a VRRP Router. + * + * vr + * VRRP Router on which to spawn event + * + * event + * The event to spawn + * + * Returns: + * -1 on failure + * 0 otherwise + */ +int vrrp_event(struct vrrp_router *r, int event) +{ + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "'%s' event", + r->vr->vrid, family2str(r->family), vrrp_event_names[event]); + return vrrp_event_handlers[event](r); +} + + +/* Autoconfig -------------------------------------------------------------- */ + +/* + * Set the configured addresses for this VRRP instance to exactly the addresses + * present on its macvlan subinterface(s). + * + * vr + * VRRP router to act on + */ +static void vrrp_autoconfig_autoaddrupdate(struct vrrp_router *r) +{ + struct listnode *ln; + struct connected *c = NULL; + bool is_v6_ll; + char ipbuf[INET6_ADDRSTRLEN]; + + if (!r->mvl_ifp) + return; + + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Setting Virtual IP list to match IPv4 addresses on %s", + r->vr->vrid, family2str(r->family), r->mvl_ifp->name); + for (ALL_LIST_ELEMENTS_RO(r->mvl_ifp->connected, ln, c)) { + is_v6_ll = (c->address->family == AF_INET6 + && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)); + if (c->address->family == r->family && !is_v6_ll) { + inet_ntop(r->family, &c->address->u.prefix, ipbuf, + sizeof(ipbuf)); + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Adding %s", + r->vr->vrid, family2str(r->family), ipbuf); + if (r->family == AF_INET) + vrrp_add_ipv4(r->vr, c->address->u.prefix4); + else if (r->vr->version == 3) + vrrp_add_ipv6(r->vr, c->address->u.prefix6); + } + } + + vrrp_check_start(r->vr); + + if (r->addrs->count == 0 && r->fsm.state != VRRP_STATE_INITIALIZE) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Virtual IP list is empty; shutting down", + r->vr->vrid, family2str(r->family)); + vrrp_event(r, VRRP_EVENT_SHUTDOWN); + } +} + +static struct vrrp_vrouter * +vrrp_autoconfig_autocreate(struct interface *mvl_ifp) +{ + struct interface *p; + struct vrrp_vrouter *vr; + + p = if_lookup_by_index(mvl_ifp->link_ifindex, VRF_DEFAULT); + + if (!p) + return NULL; + + uint8_t vrid = mvl_ifp->hw_addr[5]; + uint8_t fam = mvl_ifp->hw_addr[4]; + + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Autoconfiguring VRRP on %s", + vrid, family2str(fam), p->name); + + vr = vrrp_vrouter_create(p, vrid, vrrp_autoconfig_version); + + if (!vr) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to autoconfigure VRRP on %s", + vrid, family2str(fam), p->name); + return NULL; + } + + vr->autoconf = true; + + /* + * If these interfaces are protodown on, we need to un-protodown them + * in order to get Zebra to send us their addresses so we can + * autoconfigure them. + */ + if (vr->v4->mvl_ifp) + vrrp_zclient_send_interface_protodown(vr->v4->mvl_ifp, false); + if (vr->v6->mvl_ifp) + vrrp_zclient_send_interface_protodown(vr->v6->mvl_ifp, false); + + /* If they're not, we can go ahead and add the addresses we have */ + vrrp_autoconfig_autoaddrupdate(vr->v4); + vrrp_autoconfig_autoaddrupdate(vr->v6); + + return vr; +} + +/* + * Callback to notify autoconfig of interface add. + * + * If the interface is a VRRP-compatible device, and there is no existing VRRP + * router running on it, one is created. All addresses on the interface are + * added to the router. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +static int vrrp_autoconfig_if_add(struct interface *ifp) +{ + bool created = false; + struct vrrp_vrouter *vr; + + if (!vrrp_autoconfig_is_on) + return 0; + + if (!ifp || !ifp->link_ifindex || !vrrp_ifp_has_vrrp_mac(ifp)) + return -1; + + vr = vrrp_lookup_by_if_mvl(ifp); + + if (!vr) { + vr = vrrp_autoconfig_autocreate(ifp); + created = true; + } + + if (!vr || vr->autoconf == false) + return 0; + + if (!created) { + /* + * We didn't create it, but it has already been autoconfigured. + * Try to attach this interface to the existing instance. + */ + if (!vr->v4->mvl_ifp) { + vrrp_attach_interface(vr->v4); + /* If we just attached it, make sure it's turned on */ + if (vr->v4->mvl_ifp) { + vrrp_zclient_send_interface_protodown( + vr->v4->mvl_ifp, false); + /* + * If it's already up, we can go ahead and add + * the addresses we have + */ + vrrp_autoconfig_autoaddrupdate(vr->v4); + } + } + if (!vr->v6->mvl_ifp) { + vrrp_attach_interface(vr->v6); + /* If we just attached it, make sure it's turned on */ + if (vr->v6->mvl_ifp) { + vrrp_zclient_send_interface_protodown( + vr->v6->mvl_ifp, false); + /* + * If it's already up, we can go ahead and add + * the addresses we have + */ + vrrp_autoconfig_autoaddrupdate(vr->v6); + } + } + } + + return 0; +} + +/* + * Callback to notify autoconfig of interface delete. + * + * If the interface is a VRRP-compatible device, and a VRRP router is running + * on it, and that VRRP router was automatically configured, it will be + * deleted. If that was the last router for the corresponding VRID (i.e., if + * this interface was a v4 VRRP interface and no v6 router is configured for + * the same VRID) then the entire virtual router is deleted. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +static int vrrp_autoconfig_if_del(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *vrs; + + vrs = vrrp_lookup_by_if_any(ifp); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) + if (vr->autoconf + && (!vr->ifp || (!vr->v4->mvl_ifp && !vr->v6->mvl_ifp))) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "All VRRP interfaces for instance deleted; destroying autoconfigured VRRP router", + vr->vrid); + vrrp_vrouter_destroy(vr); + } + + list_delete(&vrs); + + return 0; +} + +/* + * Callback to notify autoconfig of interface up. + * + * Creates VRRP instance on interface if it does not exist. Otherwise does + * nothing. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +static int vrrp_autoconfig_if_up(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp); + + if (vr && !vr->autoconf) + return 0; + + if (!vr) { + vrrp_autoconfig_if_add(ifp); + return 0; + } + + return 0; +} + +/* + * Callback to notify autoconfig of interface down. + * + * Does nothing. An interface down event is accompanied by address deletion + * events for all the addresses on the interface; if an autoconfigured VRRP + * router exists on this interface, then it will have all its addresses deleted + * and end up in Initialize. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +static int vrrp_autoconfig_if_down(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + return 0; +} + +/* + * Callback to notify autoconfig of a new interface address. + * + * If a VRRP router exists on this interface, its address list is updated to + * match the new address list. If no addresses remain, a Shutdown event is + * issued to the VRRP router. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + * + */ +static int vrrp_autoconfig_if_address_add(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp); + + if (vr && vr->autoconf) { + if (vr->v4->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v4); + else if (vr->v6->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v6); + } + + return 0; +} + +/* + * Callback to notify autoconfig of a removed interface address. + * + * If a VRRP router exists on this interface, its address list is updated to + * match the new address list. If no addresses remain, a Shutdown event is + * issued to the VRRP router. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + * + */ +static int vrrp_autoconfig_if_address_del(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp); + + if (vr && vr->autoconf) { + if (vr->v4->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v4); + else if (vr->v6->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v6); + } + + return 0; +} + +int vrrp_autoconfig(void) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct interface *ifp; + + FOR_ALL_INTERFACES (vrf, ifp) + vrrp_autoconfig_if_add(ifp); + + return 0; +} + +void vrrp_autoconfig_on(int version) +{ + vrrp_autoconfig_is_on = true; + vrrp_autoconfig_version = version; + + vrrp_autoconfig(); +} + +void vrrp_autoconfig_off(void) +{ + vrrp_autoconfig_is_on = false; + + struct list *ll = hash_to_list(vrrp_vrouters_hash); + + struct listnode *ln; + struct vrrp_vrouter *vr; + + for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) + if (vr->autoconf) + vrrp_vrouter_destroy(vr); + + list_delete(&ll); +} + +/* Interface tracking ------------------------------------------------------ */ + +/* + * Bind any pending interfaces. + * + * mvl_ifp + * macvlan interface that some VRRP instances might want to bind to + */ +static void vrrp_bind_pending(struct interface *mvl_ifp) +{ + struct vrrp_vrouter *vr; + + vr = vrrp_lookup_by_if_mvl(mvl_ifp); + + if (vr) { + if (mvl_ifp->hw_addr[4] == 0x01 && !vr->v4->mvl_ifp) + vrrp_attach_interface(vr->v4); + else if (mvl_ifp->hw_addr[4] == 0x02 && !vr->v6->mvl_ifp) + vrrp_attach_interface(vr->v6); + } +} + +void vrrp_if_up(struct interface *ifp) +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *vrs; + + vrrp_bind_pending(ifp); + + vrs = vrrp_lookup_by_if_any(ifp); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { + vrrp_check_start(vr); + + if (!if_is_operative(ifp)) + continue; + + /* + * Handle the situation in which we performed a state + * transition on this VRRP router but needed to wait for the + * macvlan interface to come up to perform some actions + */ + if (ifp == vr->v4->mvl_ifp) { + if (vr->v4->advert_pending) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX_FAM + "Interface up; sending pending advertisement", + vr->vrid, family2str(vr->v4->family)); + vrrp_send_advertisement(vr->v4); + vr->v4->advert_pending = false; + } + if (vr->v4->garp_pending) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX_FAM + "Interface up; sending pending gratuitous ARP", + vr->vrid, family2str(vr->v4->family)); + vrrp_garp_send_all(vr->v4); + vr->v4->garp_pending = false; + } + } + if (ifp == vr->v6->mvl_ifp) { + if (vr->v6->advert_pending) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX_FAM + "Interface up; sending pending advertisement", + vr->vrid, family2str(vr->v6->family)); + vrrp_send_advertisement(vr->v6); + vr->v6->advert_pending = false; + } + if (vr->v6->ndisc_pending) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX_FAM + "Interface up; sending pending Unsolicited Neighbor Advertisement", + vr->vrid, family2str(vr->v6->family)); + vrrp_ndisc_una_send_all(vr->v6); + vr->v6->ndisc_pending = false; + } + } + } + + list_delete(&vrs); + + vrrp_autoconfig_if_up(ifp); +} + +void vrrp_if_down(struct interface *ifp) +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *vrs; + + vrs = vrrp_lookup_by_if_any(ifp); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { + if (vr->ifp == ifp || vr->v4->mvl_ifp == ifp + || vr->v6->mvl_ifp == ifp) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID "Interface %s down", + vr->vrid, ifp->name); + } + } + + list_delete(&vrs); + + vrrp_autoconfig_if_down(ifp); +} + +void vrrp_if_add(struct interface *ifp) +{ + vrrp_bind_pending(ifp); + + /* thanks, zebra */ + if (CHECK_FLAG(ifp->flags, IFF_UP)) + vrrp_if_up(ifp); + + vrrp_autoconfig_if_add(ifp); +} + +void vrrp_if_del(struct interface *ifp) +{ + struct listnode *ln; + struct vrrp_vrouter *vr; + struct list *vrs = vrrp_lookup_by_if_any(ifp); + + vrrp_if_down(ifp); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { + if ((vr->v4->mvl_ifp == ifp || vr->ifp == ifp) + && vr->v4->fsm.state != VRRP_STATE_INITIALIZE) { + vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + vr->v4->mvl_ifp = NULL; + } else if ((vr->v6->mvl_ifp == ifp || vr->ifp == ifp) + && vr->v6->fsm.state != VRRP_STATE_INITIALIZE) { + vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); + vr->v6->mvl_ifp = NULL; + } + } + + list_delete(&vrs); + + vrrp_autoconfig_if_del(ifp); +} + +void vrrp_if_address_add(struct interface *ifp) +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *vrs; + + /* + * We have to do a wide search here, because we need to know when a v6 + * macvlan device gets a new address. This is because the macvlan link + * local is used as the source address for v6 advertisements, and hence + * "do I have a link local" constitutes an activation condition for v6 + * virtual routers. + */ + vrs = vrrp_lookup_by_if_any(ifp); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) + vrrp_check_start(vr); + + list_delete(&vrs); + + vrrp_autoconfig_if_address_add(ifp); +} + +void vrrp_if_address_del(struct interface *ifp) +{ + /* + * Zebra is stupid and sends us address deletion notifications + * when any of the following condition sets are met: + * + * - if_is_operative && address deleted + * - if_is_operative -> !if_is_operative + * + * Note that the second one is nonsense, because Zebra behaves as + * though an interface going down means all the addresses on that + * interface got deleted. Which is a problem for autoconfig because all + * the addresses on an interface going away means the VRRP session goes + * to Initialize. However interfaces go down whenever we transition to + * Backup, so this effectively means that for autoconfigured instances + * we actually end up in Initialize whenever we try to go into Backup. + * + * Also, Zebra does NOT send us notifications when: + * - !if_is_operative && address deleted + * + * Which means if we're in backup and an address is deleted out from + * under us, we won't even know. + * + * The only solution here is to only resynchronize our address list + * when: + * + * - An interfaces comes up + * - An interface address is added + * - An interface address is deleted AND the interface is up + * + * Even though this is only a problem with autoconfig at the moment I'm + * papering over Zebra's braindead semantics here. Every piece of code + * in this function should be protected by a check that the interface + * is up. + */ + if (if_is_operative(ifp)) + vrrp_autoconfig_if_address_del(ifp); +} + +/* Other ------------------------------------------------------------------- */ + +int vrrp_config_write_interface(struct vty *vty) +{ + struct list *vrs = hash_to_list(vrrp_vrouters_hash); + struct listnode *ln, *ipln; + struct vrrp_vrouter *vr; + int writes = 0; + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { + vty_frame(vty, "interface %s\n", vr->ifp->name); + ++writes; + + vty_out(vty, " vrrp %" PRIu8 "%s\n", vr->vrid, + vr->version == 2 ? " version 2" : ""); + ++writes; + + if (vr->shutdown != vd.shutdown && ++writes) + vty_out(vty, " %svrrp %" PRIu8 " shutdown\n", + vr->shutdown ? "" : "no ", vr->vrid); + + if (vr->preempt_mode != vd.preempt_mode && ++writes) + vty_out(vty, " %svrrp %" PRIu8 " preempt\n", + vr->preempt_mode ? "" : "no ", vr->vrid); + + if (vr->accept_mode != vd.accept_mode && ++writes) + vty_out(vty, " %svrrp %" PRIu8 " accept\n", + vr->accept_mode ? "" : "no ", vr->vrid); + + if (vr->advertisement_interval != vd.advertisement_interval + && ++writes) + vty_out(vty, + " vrrp %" PRIu8 + " advertisement-interval %d\n", + vr->vrid, vr->advertisement_interval * CS2MS); + + if (vr->priority != vd.priority && ++writes) + vty_out(vty, " vrrp %" PRIu8 " priority %" PRIu8 "\n", + vr->vrid, vr->priority); + + struct ipaddr *ip; + + for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ipln, ip)) { + char ipbuf[INET6_ADDRSTRLEN]; + + ipaddr2str(ip, ipbuf, sizeof(ipbuf)); + vty_out(vty, " vrrp %" PRIu8 " ip %s\n", vr->vrid, + ipbuf); + ++writes; + } + + for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ipln, ip)) { + char ipbuf[INET6_ADDRSTRLEN]; + + ipaddr2str(ip, ipbuf, sizeof(ipbuf)); + vty_out(vty, " vrrp %" PRIu8 " ipv6 %s\n", vr->vrid, + ipbuf); + ++writes; + } + vty_endframe(vty, "!\n"); + } + + list_delete(&vrs); + + return writes; +} + +int vrrp_config_write_global(struct vty *vty) +{ + unsigned int writes = 0; + + if (vrrp_autoconfig_is_on && ++writes) + vty_out(vty, "vrrp autoconfigure%s\n", + vrrp_autoconfig_version == 2 ? " version 2" : ""); + + if (vd.priority != VRRP_DEFAULT_PRIORITY && ++writes) + vty_out(vty, "vrrp default priority %" PRIu8 "\n", vd.priority); + + if (vd.advertisement_interval != VRRP_DEFAULT_ADVINT && ++writes) + vty_out(vty, + "vrrp default advertisement-interval %" PRIu16 "\n", + vd.advertisement_interval * CS2MS); + + if (vd.preempt_mode != VRRP_DEFAULT_PREEMPT && ++writes) + vty_out(vty, "%svrrp default preempt\n", + !vd.preempt_mode ? "no " : ""); + + if (vd.accept_mode != VRRP_DEFAULT_ACCEPT && ++writes) + vty_out(vty, "%svrrp default accept\n", + !vd.accept_mode ? "no " : ""); + + if (vd.shutdown != VRRP_DEFAULT_SHUTDOWN && ++writes) + vty_out(vty, "%svrrp default shutdown\n", + !vd.shutdown ? "no " : ""); + + return writes; +} + +static unsigned int vrrp_hash_key(const void *arg) +{ + const struct vrrp_vrouter *vr = arg; + char key[IFNAMSIZ + 64]; + + snprintf(key, sizeof(key), "%s@%" PRIu8, vr->ifp->name, vr->vrid); + + return string_hash_make(key); +} + +static bool vrrp_hash_cmp(const void *arg1, const void *arg2) +{ + const struct vrrp_vrouter *vr1 = arg1; + const struct vrrp_vrouter *vr2 = arg2; + + if (vr1->ifp != vr2->ifp) + return 0; + if (vr1->vrid != vr2->vrid) + return 0; + + return 1; +} + +void vrrp_init(void) +{ + /* Set default defaults */ + vd.priority = VRRP_DEFAULT_PRIORITY; + vd.advertisement_interval = VRRP_DEFAULT_ADVINT; + vd.preempt_mode = VRRP_DEFAULT_PREEMPT; + vd.accept_mode = VRRP_DEFAULT_ACCEPT; + vd.shutdown = VRRP_DEFAULT_SHUTDOWN; + + vrrp_autoconfig_version = 3; + vrrp_vrouters_hash = hash_create(&vrrp_hash_key, vrrp_hash_cmp, + "VRRP virtual router hash"); + vrf_init(NULL, NULL, NULL, NULL, NULL); +} + +void vrrp_fini(void) +{ + /* Destroy all instances */ + struct list *vrs = hash_to_list(vrrp_vrouters_hash); + + struct listnode *ln; + struct vrrp_vrouter *vr; + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) + vrrp_vrouter_destroy(vr); + + list_delete(&vrs); + + hash_clean(vrrp_vrouters_hash, NULL); + hash_free(vrrp_vrouters_hash); +} diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h new file mode 100644 index 000000000000..fd4901fe22fe --- /dev/null +++ b/vrrpd/vrrp.h @@ -0,0 +1,570 @@ +/* + * VRRP global definitions and state machine. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_H__ +#define __VRRP_H__ + +#include +#include + +#include "lib/hash.h" +#include "lib/hook.h" +#include "lib/if.h" +#include "lib/linklist.h" +#include "lib/privs.h" +#include "lib/stream.h" +#include "lib/thread.h" +#include "lib/vty.h" + +/* Global definitions */ +#define VRRP_RADV_INT 16 +#define VRRP_PRIO_MASTER 255 +#define VRRP_MCASTV4_GROUP_STR "224.0.0.18" +#define VRRP_MCASTV6_GROUP_STR "ff02:0:0:0:0:0:0:12" +#define VRRP_MCASTV4_GROUP 0xe0000012 +#define VRRP_MCASTV6_GROUP 0xff020000000000000000000000000012 +#define IPPROTO_VRRP 112 + +#define VRRP_LOGPFX_VRID "[VRID %u] " +#define VRRP_LOGPFX_FAM "[%s] " + +/* Default defaults */ +#define VRRP_DEFAULT_PRIORITY 100 +#define VRRP_DEFAULT_ADVINT 100 +#define VRRP_DEFAULT_PREEMPT true +#define VRRP_DEFAULT_ACCEPT true +#define VRRP_DEFAULT_SHUTDOWN false + +/* User compatibility constant */ +#define CS2MS 10 + +/* Configured defaults */ +struct vrrp_defaults { + uint8_t priority; + uint16_t advertisement_interval; + bool preempt_mode; + bool accept_mode; + bool shutdown; +}; + +extern struct vrrp_defaults vd; + +/* threadmaster */ +extern struct thread_master *master; + +/* privileges */ +extern struct zebra_privs_t vrrp_privs; + +/* Global hash of all Virtual Routers */ +extern struct hash *vrrp_vrouters_hash; + +/* + * VRRP Router. + * + * This struct contains all state for a particular VRRP Router operating + * in a Virtual Router for either IPv4 or IPv6. + */ +struct vrrp_router { + /* + * Whether this VRRP Router is active. + */ + bool is_active; + + /* Whether we are the address owner */ + bool is_owner; + + /* Rx socket: Rx from parent of mvl_ifp */ + int sock_rx; + /* Tx socket; Tx from mvl_ifp */ + int sock_tx; + + /* macvlan interface */ + struct interface *mvl_ifp; + + /* Source address for advertisements */ + struct ipaddr src; + + /* Socket read buffer */ + uint8_t ibuf[IP_MAXPACKET]; + + /* + * Address family of this Virtual Router. + * Either AF_INET or AF_INET6. + */ + int family; + + /* + * Virtual Router this VRRP Router is participating in. + */ + struct vrrp_vrouter *vr; + + /* + * One or more IPvX addresses associated with this Virtual + * Router. The first address must be the "primary" address this + * Virtual Router is backing up in the case of IPv4. In the case of + * IPv6 it must be the link-local address of vr->ifp. + * + * Type: struct ipaddr * + */ + struct list *addrs; + + /* + * This flag says whether we are waiting on an interface up + * notification from Zebra before we send an ADVERTISEMENT. + */ + bool advert_pending; + + /* + * If this is an IPv4 VRRP router, this flag says whether we are + * waiting on an interface up notification from Zebra before we send + * gratuitous ARP packets for all our addresses. Should never be true + * if family == AF_INET6. + */ + bool garp_pending; + /* + * If this is an IPv6 VRRP router, this flag says whether we are + * waiting on an interface up notification from Zebra before we send + * Unsolicited Neighbor Advertisement packets for all our addresses. + * Should never be true if family == AF_INET. + */ + bool ndisc_pending; + + /* + * Effective priority + * => vr->priority if we are Backup + * => 255 if we are Master + */ + uint8_t priority; + + /* + * Advertisement interval contained in ADVERTISEMENTS received from the + * Master (centiseconds) + */ + uint16_t master_adver_interval; + + /* + * Time to skew Master_Down_Interval in centiseconds. Calculated as: + * (((256 - priority) * Master_Adver_Interval) / 256) + */ + uint16_t skew_time; + + /* + * Time interval for Backup to declare Master down (centiseconds). + * Calculated as: + * (3 * Master_Adver_Interval) + Skew_time + */ + uint16_t master_down_interval; + + /* + * The MAC address used for the source MAC address in VRRP + * advertisements, advertised in ARP requests/responses, and advertised + * in ND Neighbor Advertisements. + */ + struct ethaddr vmac; + + struct { + int state; + } fsm; + + struct { + /* Total number of advertisements sent and received */ + uint32_t adver_tx_cnt; + uint32_t adver_rx_cnt; + /* Total number of gratuitous ARPs sent */ + uint32_t garp_tx_cnt; + /* Total number of unsolicited Neighbor Advertisements sent */ + uint32_t una_tx_cnt; + /* Total number of state transitions */ + uint32_t trans_cnt; + } stats; + + struct thread *t_master_down_timer; + struct thread *t_adver_timer; + struct thread *t_read; + struct thread *t_write; +}; + +/* + * VRRP Virtual Router. + * + * This struct contains all state and configuration for a given Virtual Router + * Identifier on a given interface, both v4 and v6. + * + * RFC5798 s. 1 states: + * "Within a VRRP router, the virtual routers in each of the IPv4 and IPv6 + * address families are a domain unto themselves and do not overlap." + * + * This implementation has chosen the tuple (interface, VRID) as the key for a + * particular VRRP Router, and the rest of the program is designed around this + * assumption. Additionally, base protocol configuration parameters such as the + * advertisement interval and (configured) priority are shared between v4 and + * v6 instances. This corresponds to the choice made by other industrial + * implementations. + */ +struct vrrp_vrouter { + /* Whether this instance was automatically configured */ + bool autoconf; + + /* Whether this VRRP router is in administrative shutdown */ + bool shutdown; + + /* Interface */ + struct interface *ifp; + + /* Version */ + uint8_t version; + + /* Virtual Router Identifier */ + uint32_t vrid; + + /* Configured priority */ + uint8_t priority; + + /* + * Time interval between ADVERTISEMENTS (centiseconds). Default is 100 + * centiseconds (1 second). + */ + uint16_t advertisement_interval; + + /* + * Controls whether a (starting or restarting) higher-priority Backup + * router preempts a lower-priority Master router. Values are True to + * allow preemption and False to prohibit preemption. Default is True. + */ + bool preempt_mode; + + /* + * Controls whether a virtual router in Master state will accept + * packets addressed to the address owner's IPvX address as its own if + * it is not the IPvX address owner. The default is False. + */ + bool accept_mode; + + struct vrrp_router *v4; + struct vrrp_router *v6; +}; + +/* + * Initialize VRRP global datastructures. + */ +void vrrp_init(void); + +/* + * Destroy all VRRP instances and gracefully shutdown. + * + * For instances in Master state, VRRP advertisements with 0 priority will be + * sent if possible to notify Backup routers that we are going away. + */ +void vrrp_fini(void); + + +/* Creation and destruction ------------------------------------------------ */ + +/* + * Create and register a new VRRP Virtual Router. + * + * ifp + * Base interface to configure VRRP on + * + * vrid + * Virtual Router Identifier + */ +struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid, + uint8_t version); + +/* + * Destroy a VRRP Virtual Router, freeing all its resources. + * + * If there are any running VRRP instances, these are stopped and destroyed. + */ +void vrrp_vrouter_destroy(struct vrrp_vrouter *vr); + + +/* Configuration controllers ----------------------------------------------- */ + +/* + * Check if a Virtual Router ought to be started, and if so, start it. + * + * vr + * Virtual Router to checkstart + */ +void vrrp_check_start(struct vrrp_vrouter *vr); + +/* + * Change the configured priority of a VRRP Virtual Router. + * + * Note that this only changes the configured priority of the Virtual Router. + * The currently effective priority will not be changed; to change the + * effective priority, the Virtual Router must be restarted by issuing a + * VRRP_EVENT_SHUTDOWN followed by a VRRP_EVENT_STARTUP. + * + * vr + * Virtual Router to change priority of + * + * priority + * New priority + */ +void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority); + +/* + * Set Advertisement Interval on this Virtual Router. + * + * vr + * Virtual Router to change priority of + * + * advertisement_interval + * New advertisement interval + */ +void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr, + uint16_t advertisement_interval); + +/* + * Add an IPvX address to a VRRP Virtual Router. + * + * r + * Virtual Router to add IPvx address to + * + * ip + * Address to add + * + * activate + * Whether to automatically start the VRRP router if this is the first IP + * address added. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip); + +/* + * Add an IPv4 address to a VRRP Virtual Router. + * + * vr + * Virtual Router to add IPv4 address to + * + * v4 + * Address to add + * + * activate + * Whether to automatically start the VRRP router if this is the first IP + * address added. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4); + +/* + * Add an IPv6 address to a VRRP Virtual Router. + * + * vr + * Virtual Router to add IPv6 address to + * + * v6 + * Address to add + * + * activate + * Whether to automatically start the VRRP router if this is the first IP + * address added. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6); + +/* + * Remove an IP address from a VRRP Virtual Router. + * + * r + * Virtual Router to remove IP address from + * + * ip + * Address to remove + * + * deactivate + * Whether to automatically stop the VRRP router if removing v4 would leave + * us with an empty address list. If this is not true and ip is the only IP + * address backed up by this virtual router, this function will not remove + * the address and return failure. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip); + +/* + * Remove an IPv4 address from a VRRP Virtual Router. + * + * vr + * Virtual Router to remove IPv4 address from + * + * v4 + * Address to remove + * + * deactivate + * Whether to automatically stop the VRRP router if removing v4 would leave + * us with an empty address list. If this is not true and v4 is the only + * IPv4 address backed up by this virtual router, this function will not + * remove the address and return failure. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4); + +/* + * Remove an IPv6 address from a VRRP Virtual Router. + * + * vr + * Virtual Router to remove IPv6 address from + * + * v6 + * Address to remove + * + * deactivate + * Whether to automatically stop the VRRP router if removing v5 would leave + * us with an empty address list. If this is not true and v4 is the only + * IPv6 address backed up by this virtual router, this function will not + * remove the address and return failure. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6); + +/* State machine ----------------------------------------------------------- */ + +#define VRRP_STATE_INITIALIZE 0 +#define VRRP_STATE_MASTER 1 +#define VRRP_STATE_BACKUP 2 +#define VRRP_EVENT_STARTUP 0 +#define VRRP_EVENT_SHUTDOWN 1 + +extern const char *vrrp_state_names[3]; +extern const char *vrrp_event_names[2]; + +/* + * This hook called whenever the state of a Virtual Router changes, after the + * specific internal state handlers have run. + * + * Use this if you need to react to state changes to perform non-critical + * tasks. Critical tasks should go in the internal state change handlers. + */ +DECLARE_HOOK(vrrp_change_state_hook, (struct vrrp_router *r, int to), (r, to)); + +/* + * Trigger a VRRP event on a given Virtual Router.. + * + * vr + * Virtual Router to operate on + * + * event + * Event to kick off. All event related processing will have completed upon + * return of this function. + * + * Returns: + * < 0 if the event created an error + * 0 otherwise + */ +int vrrp_event(struct vrrp_router *r, int event); + +/* Autoconfig -------------------------------------------------------------- */ + +/* + * Search for and automatically configure VRRP instances on interfaces. + * + * ifp + * Interface to autoconfig. If it is a macvlan interface and has a VRRP MAC, + * a VRRP instance corresponding to VMAC assigned to macvlan will be created + * on the parent interface and all addresses on the macvlan interface except + * the v6 link local will be configured as VRRP addresses. If NULL, this + * treatment will be applied to all existing interfaces matching the above + * criterion. + * + * Returns: + * -1 on failure + * 0 otherwise + */ +int vrrp_autoconfig(void); + +/* + * Enable autoconfiguration. + * + * Calling this function will cause vrrpd to automatically configure VRRP + * instances on existing compatible macvlan interfaces. These instances will + * react to interface up/down and address add/delete events to keep themselves + * in sync with the available interfaces. + * + * version + * VRRP version to use for autoconfigured instances. Must be 2 or 3. + */ +void vrrp_autoconfig_on(int version); + +/* + * Disable autoconfiguration. + * + * Calling this function will delete all existing autoconfigured VRRP instances. + */ +void vrrp_autoconfig_off(void); + +/* Interface Tracking ------------------------------------------------------ */ + +void vrrp_if_add(struct interface *ifp); +void vrrp_if_del(struct interface *ifp); +void vrrp_if_up(struct interface *ifp); +void vrrp_if_down(struct interface *ifp); +void vrrp_if_address_add(struct interface *ifp); +void vrrp_if_address_del(struct interface *ifp); + +/* Other ------------------------------------------------------------------- */ + +/* + * Write interface block-level configuration to vty. + * + * vty + * vty to write config to + * + * Returns: + * # of lines written + */ +int vrrp_config_write_interface(struct vty *vty); + +/* + * Write global level configuration to vty. + * + * vty + * vty to write config to + * + * Returns: + * # of lines written + */ +int vrrp_config_write_global(struct vty *vty); + +/* + * Find VRRP Virtual Router by Virtual Router ID + */ +struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid); + +#endif /* __VRRP_H__ */ diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c new file mode 100644 index 000000000000..78e153a08224 --- /dev/null +++ b/vrrpd/vrrp_arp.c @@ -0,0 +1,219 @@ +/* + * VRRP ARP handling. + * Copyright (C) 2001-2017 Alexandre Cassen + * Portions: + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include +#include +#include + +#include "lib/if.h" +#include "lib/linklist.h" +#include "lib/log.h" +#include "lib/memory.h" +#include "lib/prefix.h" + +#include "vrrp.h" +#include "vrrp_arp.h" +#include "vrrp_debug.h" + +#define VRRP_LOGPFX "[ARP] " + +/* + * The size of the garp packet buffer should be the large enough to hold the + * largest arp packet to be sent + the size of the link layer header for the + * corresponding protocol. In this case we hardcode for Ethernet. + */ +#define GARP_BUFFER_SIZE \ + sizeof(struct ether_header) + sizeof(struct arphdr) + 2 * ETH_ALEN \ + + 2 * sizeof(struct in_addr) + +/* static vars */ +static int garp_fd = -1; + +/* Send the gratuitous ARP message */ +static ssize_t vrrp_send_garp(struct interface *ifp, uint8_t *buf, + ssize_t pack_len) +{ + struct sockaddr_ll sll; + ssize_t len; + + /* Build the dst device */ + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_protocol = ETH_P_ARP; + sll.sll_ifindex = (int)ifp->ifindex; + sll.sll_halen = ifp->hw_addr_len; + memset(sll.sll_addr, 0xFF, ETH_ALEN); + + /* Send packet */ + len = sendto(garp_fd, buf, pack_len, 0, (struct sockaddr *)&sll, + sizeof(sll)); + + return len; +} + +/* Build a gratuitous ARP message over a specific interface */ +static ssize_t vrrp_build_garp(uint8_t *buf, struct interface *ifp, + struct in_addr *v4) +{ + uint8_t *arp_ptr; + + if (ifp->hw_addr_len == 0) + return -1; + + /* Build Ethernet header */ + struct ether_header *eth = (struct ether_header *)buf; + + memset(eth->ether_dhost, 0xFF, ETH_ALEN); + memcpy(eth->ether_shost, ifp->hw_addr, ETH_ALEN); + eth->ether_type = htons(ETHERTYPE_ARP); + + /* Build ARP payload */ + struct arphdr *arph = (struct arphdr *)(buf + ETHER_HDR_LEN); + + arph->ar_hrd = htons(HWTYPE_ETHER); + arph->ar_pro = htons(ETHERTYPE_IP); + arph->ar_hln = ifp->hw_addr_len; + arph->ar_pln = sizeof(struct in_addr); + arph->ar_op = htons(ARPOP_REQUEST); + arp_ptr = (uint8_t *)(arph + 1); + /* Source MAC: us */ + memcpy(arp_ptr, ifp->hw_addr, ifp->hw_addr_len); + arp_ptr += ifp->hw_addr_len; + /* Source IP: us */ + memcpy(arp_ptr, v4, sizeof(struct in_addr)); + arp_ptr += sizeof(struct in_addr); + /* Dest MAC: broadcast */ + memset(arp_ptr, 0xFF, ETH_ALEN); + arp_ptr += ifp->hw_addr_len; + /* Dest IP: us */ + memcpy(arp_ptr, v4, sizeof(struct in_addr)); + arp_ptr += sizeof(struct in_addr); + + return arp_ptr - buf; +} + +void vrrp_garp_send(struct vrrp_router *r, struct in_addr *v4) +{ + struct interface *ifp = r->mvl_ifp; + uint8_t garpbuf[GARP_BUFFER_SIZE]; + ssize_t garpbuf_len; + ssize_t sent_len; + char astr[INET_ADDRSTRLEN]; + + /* If the interface doesn't support ARP, don't try sending */ + if (ifp->flags & IFF_NOARP) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Unable to send gratuitous ARP on %s; has IFF_NOARP", + r->vr->vrid, family2str(r->family), ifp->name); + return; + } + + /* Build garp */ + garpbuf_len = vrrp_build_garp(garpbuf, ifp, v4); + + if (garpbuf_len < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Unable to send gratuitous ARP on %s; MAC address unknown", + r->vr->vrid, family2str(r->family), ifp->name); + return; + }; + + /* Send garp */ + inet_ntop(AF_INET, v4, astr, sizeof(astr)); + + DEBUGD(&vrrp_dbg_arp, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Sending gratuitous ARP on %s for %s", + r->vr->vrid, family2str(r->family), ifp->name, astr); + if (DEBUG_MODE_CHECK(&vrrp_dbg_arp, DEBUG_MODE_ALL)) + zlog_hexdump(garpbuf, garpbuf_len); + + sent_len = vrrp_send_garp(ifp, garpbuf, garpbuf_len); + + if (sent_len < 0) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Error sending gratuitous ARP on %s for %s", + r->vr->vrid, family2str(r->family), ifp->name, astr); + else + ++r->stats.garp_tx_cnt; +} + +void vrrp_garp_send_all(struct vrrp_router *r) +{ + assert(r->family == AF_INET); + + struct interface *ifp = r->mvl_ifp; + + /* If the interface doesn't support ARP, don't try sending */ + if (ifp->flags & IFF_NOARP) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Unable to send gratuitous ARP on %s; has IFF_NOARP\n", + r->vr->vrid, family2str(r->family), ifp->name); + return; + } + + struct listnode *ln; + struct ipaddr *ip; + + for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip)) + vrrp_garp_send(r, &ip->ipaddr_v4); +} + + +void vrrp_garp_init(void) +{ + /* Create the socket descriptor */ + /* FIXME: why ETH_P_RARP? */ + errno = 0; + frr_elevate_privs(&vrrp_privs) { + garp_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC, + htons(ETH_P_RARP)); + } + + if (garp_fd > 0) { + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX "Initialized gratuitous ARP socket"); + DEBUGD(&vrrp_dbg_arp, + VRRP_LOGPFX "Initialized gratuitous ARP subsystem"); + } else { + zlog_err(VRRP_LOGPFX + "Error initializing gratuitous ARP subsystem"); + } +} + +void vrrp_garp_fini(void) +{ + close(garp_fd); + garp_fd = -1; + + DEBUGD(&vrrp_dbg_arp, + VRRP_LOGPFX "Deinitialized gratuitous ARP subsystem"); +} + +bool vrrp_garp_is_init(void) +{ + return garp_fd > 0; +} diff --git a/vrrpd/vrrp_arp.h b/vrrpd/vrrp_arp.h new file mode 100644 index 000000000000..21f2c4edd136 --- /dev/null +++ b/vrrpd/vrrp_arp.h @@ -0,0 +1,36 @@ +/* + * VRRP ARP handling. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_ARP_H__ +#define __VRRP_ARP_H__ + +#include + +#include "vrrp.h" + +/* FIXME: Use the kernel define for this */ +#define HWTYPE_ETHER 1 + +extern void vrrp_garp_init(void); +extern void vrrp_garp_fini(void); +extern bool vrrp_garp_is_init(void); +extern void vrrp_garp_send(struct vrrp_router *vr, struct in_addr *v4); +extern void vrrp_garp_send_all(struct vrrp_router *vr); + +#endif /* __VRRP_ARP_H__ */ diff --git a/vrrpd/vrrp_debug.c b/vrrpd/vrrp_debug.c new file mode 100644 index 000000000000..09d5e780cfb7 --- /dev/null +++ b/vrrpd/vrrp_debug.c @@ -0,0 +1,131 @@ +/* + * VRRP debugging. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "lib/command.h" +#include "lib/debug.h" +#include "lib/vector.h" + +#include "vrrp_debug.h" + +/* clang-format off */ +struct debug vrrp_dbg_arp = {0, "VRRP ARP"}; +struct debug vrrp_dbg_auto = {0, "VRRP autoconfiguration events"}; +struct debug vrrp_dbg_ndisc = {0, "VRRP Neighbor Discovery"}; +struct debug vrrp_dbg_pkt = {0, "VRRP packets"}; +struct debug vrrp_dbg_proto = {0, "VRRP protocol events"}; +struct debug vrrp_dbg_sock = {0, "VRRP sockets"}; +struct debug vrrp_dbg_zebra = {0, "VRRP Zebra events"}; + +struct debug *vrrp_debugs[] = { + &vrrp_dbg_arp, + &vrrp_dbg_auto, + &vrrp_dbg_ndisc, + &vrrp_dbg_pkt, + &vrrp_dbg_proto, + &vrrp_dbg_sock, + &vrrp_dbg_zebra +}; + +const char *vrrp_debugs_conflines[] = { + "debug vrrp arp", + "debug vrrp autoconfigure", + "debug vrrp ndisc", + "debug vrrp packets", + "debug vrrp protocol", + "debug vrrp sockets", + "debug vrrp zebra", +}; +/* clang-format on */ + +/* + * Set or unset flags on all debugs for vrrpd. + * + * flags + * The flags to set + * + * set + * Whether to set or unset the specified flags + */ +static void vrrp_debug_set_all(uint32_t flags, bool set) +{ + for (unsigned int i = 0; i < array_size(vrrp_debugs); i++) { + DEBUG_FLAGS_SET(vrrp_debugs[i], flags, set); + + /* if all modes have been turned off, don't preserve options */ + if (!DEBUG_MODE_CHECK(vrrp_debugs[i], DEBUG_MODE_ALL)) + DEBUG_CLEAR(vrrp_debugs[i]); + } +} + +static int vrrp_debug_config_write_helper(struct vty *vty, bool config) +{ + uint32_t mode = DEBUG_MODE_ALL; + + if (config) + mode = DEBUG_MODE_CONF; + + for (unsigned int i = 0; i < array_size(vrrp_debugs); i++) + if (DEBUG_MODE_CHECK(vrrp_debugs[i], mode)) + vty_out(vty, "%s\n", vrrp_debugs_conflines[i]); + + return 0; +} + +int vrrp_config_write_debug(struct vty *vty) +{ + return vrrp_debug_config_write_helper(vty, true); +} + +int vrrp_debug_status_write(struct vty *vty) +{ + return vrrp_debug_config_write_helper(vty, false); +} + +void vrrp_debug_set(struct interface *ifp, uint8_t vrid, int vtynode, + bool onoff, bool proto, bool autoconf, bool pkt, bool sock, + bool ndisc, bool arp, bool zebra) +{ + uint32_t mode = DEBUG_NODE2MODE(vtynode); + + if (proto) + DEBUG_MODE_SET(&vrrp_dbg_proto, mode, onoff); + if (autoconf) + DEBUG_MODE_SET(&vrrp_dbg_auto, mode, onoff); + if (pkt) + DEBUG_MODE_SET(&vrrp_dbg_pkt, mode, onoff); + if (sock) + DEBUG_MODE_SET(&vrrp_dbg_sock, mode, onoff); + if (ndisc) + DEBUG_MODE_SET(&vrrp_dbg_ndisc, mode, onoff); + if (arp) + DEBUG_MODE_SET(&vrrp_dbg_arp, mode, onoff); + if (zebra) + DEBUG_MODE_SET(&vrrp_dbg_zebra, mode, onoff); +} + +/* ------------------------------------------------------------------------- */ + +struct debug_callbacks vrrp_dbg_cbs = {.debug_set_all = vrrp_debug_set_all}; + +void vrrp_debug_init(void) +{ + debug_init(&vrrp_dbg_cbs); +} diff --git a/vrrpd/vrrp_debug.h b/vrrpd/vrrp_debug.h new file mode 100644 index 000000000000..20f993095564 --- /dev/null +++ b/vrrpd/vrrp_debug.h @@ -0,0 +1,87 @@ +/* + * VRRP debugging. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_DEBUG_H__ +#define __VRRP_DEBUG_H__ + +#include + +#include "lib/debug.h" + +/* VRRP debugging records */ +struct debug vrrp_dbg_arp; +struct debug vrrp_dbg_auto; +struct debug vrrp_dbg_ndisc; +struct debug vrrp_dbg_pkt; +struct debug vrrp_dbg_proto; +struct debug vrrp_dbg_sock; +struct debug vrrp_dbg_zebra; + +/* + * Initialize VRRP debugging. + * + * Installs VTY commands and registers callbacks. + */ +void vrrp_debug_init(void); + +/* + * Print VRRP debugging configuration. + * + * vty + * VTY to print debugging configuration to. + */ +int vrrp_config_write_debug(struct vty *vty); + +/* + * Print VRRP debugging configuration, human readable form. + * + * vty + * VTY to print debugging configuration to. + */ +int vrrp_debug_status_write(struct vty *vty); + +/* + * Set debugging status. + * + * ifp + * Interface to set status on + * + * vrid + * VRID of instance to set status on + * + * vtynode + * vty->node + * + * onoff + * Whether to turn the specified debugs on or off + * + * proto + * Turn protocol debugging on or off + * + * autoconf + * Turn autoconfiguration debugging on or off + * + * pkt + * Turn packet debugging on or off + */ +void vrrp_debug_set(struct interface *ifp, uint8_t vrid, int vtynode, + bool onoff, bool proto, bool autoconf, bool pkt, bool sock, + bool ndisc, bool arp, bool zebra); + +#endif /* __VRRP_DEBUG_H__ */ diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c new file mode 100644 index 000000000000..46a92d936a32 --- /dev/null +++ b/vrrpd/vrrp_main.c @@ -0,0 +1,159 @@ +/* + * VRRP entry point. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include + +#include "lib/command.h" +#include "lib/filter.h" +#include "lib/getopt.h" +#include "lib/if.h" +#include "lib/libfrr.h" +#include "lib/log.h" +#include "lib/memory.h" +#include "lib/nexthop.h" +#include "lib/privs.h" +#include "lib/sigevent.h" +#include "lib/thread.h" +#include "lib/vrf.h" + +#include "vrrp.h" +#include "vrrp_debug.h" +#include "vrrp_vty.h" +#include "vrrp_zebra.h" + +char backup_config_file[256]; + +zebra_capabilities_t _caps_p[] = { + ZCAP_NET_RAW, +}; + +struct zebra_privs_t vrrp_privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0}; + +struct option longopts[] = { {0} }; + +/* Master of threads. */ +struct thread_master *master; + +/* SIGHUP handler. */ +static void sighup(void) +{ + zlog_info("SIGHUP received"); +} + +/* SIGINT / SIGTERM handler. */ +static void __attribute__((noreturn)) sigint(void) +{ + zlog_notice("Terminating on signal"); + + vrrp_fini(); + + exit(0); +} + +/* SIGUSR1 handler. */ +static void sigusr1(void) +{ + zlog_rotate(); +} + +struct quagga_signal_t vrrp_signals[] = { + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +static const struct frr_yang_module_info *vrrp_yang_modules[] = { + &frr_interface_info, +}; + +#define VRRP_VTY_PORT 2619 + +FRR_DAEMON_INFO(vrrpd, VRRP, .vty_port = VRRP_VTY_PORT, + .proghelp = "Virtual Router Redundancy Protocol", + .signals = vrrp_signals, + .n_signals = array_size(vrrp_signals), + .privs = &vrrp_privs, + .yang_modules = vrrp_yang_modules, + .n_yang_modules = array_size(vrrp_yang_modules), +) + +int main(int argc, char **argv, char **envp) +{ + frr_preinit(&vrrpd_di, argc, argv); + frr_opt_add("", longopts, ""); + + while (1) { + int opt; + + opt = frr_getopt(argc, argv, NULL); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + default: + frr_help_exit(1); + break; + } + } + + master = frr_init(); + + vrrp_debug_init(); + vrrp_zebra_init(); + vrrp_vty_init(); + vrrp_init(); + + snprintf(backup_config_file, sizeof(backup_config_file), + "%s/vrrpd.conf", frr_sysconfdir); + vrrpd_di.backup_config_file = backup_config_file; + + frr_config_fork(); + frr_run(master); + + /* Not reached. */ + return 0; +} diff --git a/vrrpd/vrrp_memory.c b/vrrpd/vrrp_memory.c new file mode 100644 index 000000000000..30eef523cd50 --- /dev/null +++ b/vrrpd/vrrp_memory.c @@ -0,0 +1,29 @@ +/* + * VRRP memory types. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "lib/memory.h" + +#include "vrrp_memory.h" + +DEFINE_MGROUP(VRRPD, "vrrpd"); +DEFINE_MTYPE(VRRPD, VRRP_IP, "VRRP IP address"); +DEFINE_MTYPE(VRRPD, VRRP_PKT, "VRRP packet"); +DEFINE_MTYPE(VRRPD, VRRP_RTR, "VRRP Router"); diff --git a/vrrpd/vrrp_memory.h b/vrrpd/vrrp_memory.h new file mode 100644 index 000000000000..c3025d1acb5c --- /dev/null +++ b/vrrpd/vrrp_memory.h @@ -0,0 +1,32 @@ +/* + * VRRP memory types. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_MEMORY_H__ +#define __VRRP_MEMORY_H__ + +#include + +#include "lib/memory.h" + +DECLARE_MGROUP(VRRPD); +DECLARE_MTYPE(VRRP_IP); +DECLARE_MTYPE(VRRP_PKT); +DECLARE_MTYPE(VRRP_RTR); + +#endif /* __VRRP_MEMORY_H__ */ diff --git a/vrrpd/vrrp_ndisc.c b/vrrpd/vrrp_ndisc.c new file mode 100644 index 000000000000..348958509a97 --- /dev/null +++ b/vrrpd/vrrp_ndisc.c @@ -0,0 +1,245 @@ +/* + * VRRP Neighbor Discovery. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * + * Portions: + * Copyright (C) 2001-2017 Alexandre Cassen + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include +#include +#include +#include + +#include "lib/checksum.h" +#include "lib/if.h" +#include "lib/ipaddr.h" +#include "lib/log.h" + +#include "vrrp_debug.h" +#include "vrrp_ndisc.h" + +#define VRRP_LOGPFX "[NDISC] " + +#define VRRP_NDISC_HOPLIMIT 255 +#define VRRP_NDISC_SIZE \ + ETHER_HDR_LEN + sizeof(struct ip6_hdr) \ + + sizeof(struct nd_neighbor_advert) \ + + sizeof(struct nd_opt_hdr) + ETH_ALEN + +/* static vars */ +static int ndisc_fd = -1; + +/* + * Build an unsolicited Neighbour Advertisement. + * + * ifp + * Interface to send Neighbor Advertisement on + * + * ip + * IP address to send Neighbor Advertisement for + * + * buf + * Buffer to fill with IPv6 Neighbor Advertisement message. Includes + * Ethernet header. + * + * bufsiz + * Size of buf. + * + * Returns; + * -1 if bufsiz is too small + * 0 otherwise + */ +static int vrrp_ndisc_una_build(struct interface *ifp, struct ipaddr *ip, + uint8_t *buf, size_t bufsiz) +{ + if (bufsiz < VRRP_NDISC_SIZE) + return -1; + + memset(buf, 0x00, bufsiz); + + struct ether_header *eth = (struct ether_header *)buf; + struct ip6_hdr *ip6h = (struct ip6_hdr *)((char *)eth + ETHER_HDR_LEN); + struct nd_neighbor_advert *ndh = + (struct nd_neighbor_advert *)((char *)ip6h + + sizeof(struct ip6_hdr)); + struct icmp6_hdr *icmp6h = &ndh->nd_na_hdr; + struct nd_opt_hdr *nd_opt_h = + (struct nd_opt_hdr *)((char *)ndh + + sizeof(struct nd_neighbor_advert)); + char *nd_opt_lladdr = + (char *)((char *)nd_opt_h + sizeof(struct nd_opt_hdr)); + char *lladdr = (char *)ifp->hw_addr; + + /* + * An IPv6 packet with a multicast destination address DST, consisting + * of the sixteen octets DST[1] through DST[16], is transmitted to the + * Ethernet multicast address whose first two octets are the value 3333 + * hexadecimal and whose last four octets are the last four octets of + * DST. + * - RFC2464.7 + * + * In this case we are sending to the all nodes multicast address, so + * the last four octets are 0x00 0x00 0x00 0x01. + */ + memset(eth->ether_dhost, 0, ETH_ALEN); + eth->ether_dhost[0] = 0x33; + eth->ether_dhost[1] = 0x33; + eth->ether_dhost[5] = 1; + + /* Set source Ethernet address to interface link layer address */ + memcpy(eth->ether_shost, lladdr, ETH_ALEN); + eth->ether_type = htons(ETHERTYPE_IPV6); + + /* IPv6 Header */ + ip6h->ip6_vfc = 6 << 4; + ip6h->ip6_plen = htons(sizeof(struct nd_neighbor_advert) + + sizeof(struct nd_opt_hdr) + ETH_ALEN); + ip6h->ip6_nxt = IPPROTO_ICMPV6; + ip6h->ip6_hlim = VRRP_NDISC_HOPLIMIT; + memcpy(&ip6h->ip6_src, &ip->ipaddr_v6, sizeof(struct in6_addr)); + /* All nodes multicast address */ + ip6h->ip6_dst.s6_addr[0] = 0xFF; + ip6h->ip6_dst.s6_addr[1] = 0x02; + ip6h->ip6_dst.s6_addr[15] = 0x01; + + /* ICMPv6 Header */ + ndh->nd_na_type = ND_NEIGHBOR_ADVERT; + ndh->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER; + ndh->nd_na_flags_reserved |= ND_NA_FLAG_OVERRIDE; + memcpy(&ndh->nd_na_target, &ip->ipaddr_v6, sizeof(struct in6_addr)); + + /* NDISC Option header */ + nd_opt_h->nd_opt_type = ND_OPT_TARGET_LINKADDR; + nd_opt_h->nd_opt_len = 1; + memcpy(nd_opt_lladdr, lladdr, ETH_ALEN); + + /* Compute checksum */ + uint32_t len = sizeof(struct nd_neighbor_advert) + + sizeof(struct nd_opt_hdr) + ETH_ALEN; + struct ipv6_ph ph = {}; + + ph.src = ip6h->ip6_src; + ph.dst = ip6h->ip6_dst; + ph.ulpl = htonl(len); + ph.next_hdr = IPPROTO_ICMPV6; + + /* Suppress static analysis warnings about accessing icmp6 oob */ + void *offset = icmp6h; + icmp6h->icmp6_cksum = in_cksum_with_ph6(&ph, offset, len); + + return 0; +} + +int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip) +{ + assert(r->family == AF_INET6); + + int ret = 0; + struct interface *ifp = r->mvl_ifp; + uint8_t buf[VRRP_NDISC_SIZE]; + + ret = vrrp_ndisc_una_build(ifp, ip, buf, sizeof(buf)); + + if (ret == -1) + return ret; + + struct sockaddr_ll sll; + ssize_t len; + + /* Build the dst device */ + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + memcpy(sll.sll_addr, ifp->hw_addr, ETH_ALEN); + sll.sll_halen = ETH_ALEN; + sll.sll_ifindex = (int)ifp->ifindex; + + char ipbuf[INET6_ADDRSTRLEN]; + + ipaddr2str(ip, ipbuf, sizeof(ipbuf)); + + DEBUGD(&vrrp_dbg_ndisc, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Sending unsolicited Neighbor Advertisement on %s for %s", + r->vr->vrid, family2str(r->family), ifp->name, ipbuf); + + if (DEBUG_MODE_CHECK(&vrrp_dbg_ndisc, DEBUG_MODE_ALL) + && DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) + zlog_hexdump(buf, VRRP_NDISC_SIZE); + + len = sendto(ndisc_fd, buf, VRRP_NDISC_SIZE, 0, (struct sockaddr *)&sll, + sizeof(sll)); + + if (len < 0) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Error sending unsolicited Neighbor Advertisement on %s for %s", + r->vr->vrid, family2str(r->family), ifp->name, ipbuf); + ret = -1; + } else { + ++r->stats.una_tx_cnt; + } + + return ret; +} + +int vrrp_ndisc_una_send_all(struct vrrp_router *r) +{ + assert(r->family == AF_INET6); + + struct listnode *ln; + struct ipaddr *ip; + + for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip)) + vrrp_ndisc_una_send(r, ip); + + return 0; +} + +void vrrp_ndisc_init(void) +{ + frr_elevate_privs(&vrrp_privs) + { + ndisc_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IPV6)); + } + + if (ndisc_fd > 0) { + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX "Initialized Neighbor Discovery socket"); + DEBUGD(&vrrp_dbg_ndisc, + VRRP_LOGPFX "Initialized Neighbor Discovery subsystem"); + } else { + zlog_err(VRRP_LOGPFX + "Error initializing Neighbor Discovery socket"); + } +} + +void vrrp_ndisc_fini(void) +{ + close(ndisc_fd); + ndisc_fd = -1; + + DEBUGD(&vrrp_dbg_ndisc, + VRRP_LOGPFX "Deinitialized Neighbor Discovery subsystem"); +} + +bool vrrp_ndisc_is_init(void) +{ + return ndisc_fd > 0; +} diff --git a/vrrpd/vrrp_ndisc.h b/vrrpd/vrrp_ndisc.h new file mode 100644 index 000000000000..efbef348d096 --- /dev/null +++ b/vrrpd/vrrp_ndisc.h @@ -0,0 +1,74 @@ +/* + * VRRP Neighbor Discovery. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_NDISC_H__ +#define __VRRP_NDISC_H__ + +#include +#include +#include + +#include "vrrp.h" + +/* + * Initialize VRRP neighbor discovery. + */ +extern void vrrp_ndisc_init(void); + +/* + * Check whether VRRP Neighbor Discovery is initialized. + * + * Returns: + * True if initialized, false otherwise + */ +extern bool vrrp_ndisc_is_init(void); + +/* + * Finish VRRP Neighbor Discovery. + */ +extern void vrrp_ndisc_fini(void); + +/* + * Send VRRP Neighbor Advertisement. + * + * ifp + * Interface to transmit on + * + * ip + * IPv6 address to send Neighbor Advertisement for + * + * Returns: + * -1 on failure + * 0 otherwise + */ +extern int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip); + +/* + * Send VRRP Neighbor Advertisements for all virtual IPs. + * + * r + * Virtual Router to send NA's for + * + * Returns: + * -1 on failure + * 0 otherwise + */ +extern int vrrp_ndisc_una_send_all(struct vrrp_router *r); + +#endif /* __VRRP_NDISC_H__ */ diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c new file mode 100644 index 000000000000..c3f2afba4cbf --- /dev/null +++ b/vrrpd/vrrp_packet.c @@ -0,0 +1,321 @@ +/* + * VRRP packet crafting. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include +#include + +#include "lib/checksum.h" +#include "lib/ipaddr.h" +#include "lib/memory.h" + +#include "vrrp.h" +#include "vrrp_debug.h" +#include "vrrp_memory.h" +#include "vrrp_packet.h" + +/* clang-format off */ +const char *vrrp_packet_names[16] = { + [0] = "Unknown", + [VRRP_TYPE_ADVERTISEMENT] = "ADVERTISEMENT", + [2] = "Unknown", + [3] = "Unknown", + [4] = "Unknown", + [5] = "Unknown", + [6] = "Unknown", + [7] = "Unknown", + [8] = "Unknown", + [9] = "Unknown", + [10] = "Unknown", + [11] = "Unknown", + [12] = "Unknown", + [13] = "Unknown", + [14] = "Unknown", + [15] = "Unknown", +}; +/* clang-format on */ + +/* + * Compute the VRRP checksum. + * + * Checksum is not set in the packet, just computed. + * + * pkt + * VRRP packet, fully filled out except for checksum field. + * + * pktsize + * sizeof(*pkt) + * + * src + * IP address that pkt will be transmitted from. + * + * Returns: + * VRRP checksum in network byte order. + */ +static uint16_t vrrp_pkt_checksum(struct vrrp_pkt *pkt, size_t pktsize, + struct ipaddr *src) +{ + uint16_t chksum; + bool v6 = (src->ipa_type == IPADDR_V6); + + uint16_t chksum_pre = pkt->hdr.chksum; + + pkt->hdr.chksum = 0; + + if (v6) { + struct ipv6_ph ph = {}; + + ph.src = src->ipaddr_v6; + inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &ph.dst); + ph.ulpl = htons(pktsize); + ph.next_hdr = 112; + chksum = in_cksum_with_ph6(&ph, pkt, pktsize); + } else if (!v6 && ((pkt->hdr.vertype >> 4) == 3)) { + struct ipv4_ph ph = {}; + + ph.src = src->ipaddr_v4; + inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst); + ph.proto = 112; + ph.len = htons(pktsize); + chksum = in_cksum_with_ph4(&ph, pkt, pktsize); + } else if (!v6 && ((pkt->hdr.vertype >> 4) == 2)) { + chksum = in_cksum(pkt, pktsize); + } else { + assert(!"Invalid VRRP protocol version"); + } + + pkt->hdr.chksum = chksum_pre; + + return chksum; +} + +ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src, + uint8_t version, uint8_t vrid, uint8_t prio, + uint16_t max_adver_int, uint8_t numip, + struct ipaddr **ips) +{ + bool v6 = false; + size_t addrsz = 0; + + assert(version >= 2 && version <= 3); + + if (numip > 0) { + v6 = IS_IPADDR_V6(ips[0]); + addrsz = IPADDRSZ(ips[0]); + } + + assert(!(version == 2 && v6)); + + size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, version, numip); + + *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize); + + (*pkt)->hdr.vertype |= version << 4; + (*pkt)->hdr.vertype |= VRRP_TYPE_ADVERTISEMENT; + (*pkt)->hdr.vrid = vrid; + (*pkt)->hdr.priority = prio; + (*pkt)->hdr.naddr = numip; + if (version == 3) + (*pkt)->hdr.v3.adver_int = htons(max_adver_int); + else if (version == 2) { + (*pkt)->hdr.v2.auth_type = 0; + (*pkt)->hdr.v2.adver_int = MAX(max_adver_int / 100, 1); + } + + uint8_t *aptr = (void *)(*pkt)->addrs; + + for (int i = 0; i < numip; i++) { + memcpy(aptr, &ips[i]->ip.addr, addrsz); + aptr += addrsz; + } + + (*pkt)->hdr.chksum = vrrp_pkt_checksum(*pkt, pktsize, src); + + return pktsize; +} + +size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt) +{ + if (buflen < 1) + return 0; + + char tmpbuf[BUFSIZ]; + size_t rs = 0; + struct vrrp_hdr *hdr = &pkt->hdr; + + buf[0] = 0x00; + snprintf(tmpbuf, sizeof(tmpbuf), "version %u, ", (hdr->vertype >> 4)); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "type %u (%s), ", + (hdr->vertype & 0x0F), + vrrp_packet_names[(hdr->vertype & 0x0F)]); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "vrid %u, ", hdr->vrid); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "priority %u, ", hdr->priority); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "#%u addresses, ", hdr->naddr); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "max adver int %u, ", + ntohs(hdr->v3.adver_int)); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "checksum %x", ntohs(hdr->chksum)); + rs += strlcat(buf, tmpbuf, buflen); + + return rs; +} + +ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, + size_t read, struct ipaddr *src, + struct vrrp_pkt **pkt, char *errmsg, + size_t errmsg_len) +{ + /* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */ + size_t addrsz = (family == AF_INET) ? sizeof(struct in_addr) + : sizeof(struct in6_addr); + + size_t pktsize; + uint8_t *buf = m->msg_iov->iov_base; + +#define VRRP_PKT_VCHECK(cond, _f, ...) \ + do { \ + if (!(cond)) { \ + if (errmsg) \ + snprintf(errmsg, errmsg_len, (_f), \ + ##__VA_ARGS__); \ + return -1; \ + } \ + } while (0) + + /* IPvX header check */ + + if (family == AF_INET) { + VRRP_PKT_VCHECK( + read >= sizeof(struct ip), + "Datagram not large enough to contain IP header"); + + struct ip *ip = (struct ip *)buf; + + /* IP total length check */ + VRRP_PKT_VCHECK( + ntohs(ip->ip_len) == read, + "IPv4 packet length field does not match # received bytes; %" PRIu16 + "!= %zu", + ntohs(ip->ip_len), read); + + /* TTL check */ + VRRP_PKT_VCHECK(ip->ip_ttl == 255, + "IPv4 TTL is %" PRIu8 "; should be 255", + ip->ip_ttl); + + *pkt = (struct vrrp_pkt *)(buf + (ip->ip_hl << 2)); + pktsize = read - (ip->ip_hl << 2); + + /* IP empty packet check */ + VRRP_PKT_VCHECK(pktsize > 0, "IPv4 packet has no payload"); + + /* Extract source address */ + struct sockaddr_in *sa = m->msg_name; + + src->ipa_type = IPADDR_V4; + src->ipaddr_v4 = sa->sin_addr; + } else if (family == AF_INET6) { + struct cmsghdr *c; + + for (c = CMSG_FIRSTHDR(m); c != NULL; CMSG_NXTHDR(m, c)) { + if (c->cmsg_level == IPPROTO_IPV6 + && c->cmsg_type == IPV6_HOPLIMIT) + break; + } + + VRRP_PKT_VCHECK(!!c, "IPv6 Hop Limit not received"); + + uint8_t *hoplimit = CMSG_DATA(c); + + VRRP_PKT_VCHECK(*hoplimit == 255, + "IPv6 Hop Limit is %" PRIu8 "; should be 255", + *hoplimit); + + *pkt = (struct vrrp_pkt *)buf; + pktsize = read; + + /* Extract source address */ + struct sockaddr_in6 *sa = m->msg_name; + + src->ipa_type = IPADDR_V6; + memcpy(&src->ipaddr_v6, &sa->sin6_addr, + sizeof(struct in6_addr)); + } else { + assert(!"Unknown address family"); + } + + /* Size check */ + size_t minsize = (family == AF_INET) ? VRRP_MIN_PKT_SIZE_V4 + : VRRP_MIN_PKT_SIZE_V6; + size_t maxsize = (family == AF_INET) ? VRRP_MAX_PKT_SIZE_V4 + : VRRP_MAX_PKT_SIZE_V6; + VRRP_PKT_VCHECK(pktsize >= minsize, + "VRRP packet is undersized (%zu < %zu)", pktsize, + minsize); + VRRP_PKT_VCHECK(pktsize <= maxsize, + "VRRP packet is oversized (%zu > %zu)", pktsize, + maxsize); + + /* Version check */ + uint8_t pktver = (*pkt)->hdr.vertype >> 4; + + VRRP_PKT_VCHECK(pktver == version, "Bad version %u", pktver); + + /* Checksum check */ + uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src); + + VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum, + "Bad VRRP checksum %" PRIx16 "; should be %" PRIx16 "", + (*pkt)->hdr.chksum, chksum); + + /* Type check */ + VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %" PRIu8, + (*pkt)->hdr.vertype & 0x0f); + + /* Exact size check */ + size_t ves = VRRP_PKT_SIZE(family, pktver, (*pkt)->hdr.naddr); + + VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses%s", + pktver == 2 ? " or missing auth fields" : ""); + + /* auth type check */ + if (version == 2) + VRRP_PKT_VCHECK((*pkt)->hdr.v2.auth_type == 0, + "Bad authentication type %" PRIu8, + (*pkt)->hdr.v2.auth_type); + + /* Addresses check */ + char vbuf[INET6_ADDRSTRLEN]; + uint8_t *p = (uint8_t *)(*pkt)->addrs; + + for (uint8_t i = 0; i < (*pkt)->hdr.naddr; i++) { + VRRP_PKT_VCHECK(inet_ntop(family, p, vbuf, sizeof(vbuf)), + "Bad IP address, #%" PRIu8, i); + p += addrsz; + } + + /* Everything checks out */ + return pktsize; +} diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h new file mode 100644 index 000000000000..475e4780d5d9 --- /dev/null +++ b/vrrpd/vrrp_packet.h @@ -0,0 +1,202 @@ +/* + * VRRP packet crafting. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_PACKET_H__ +#define __VRRP_PACKET_H__ + +#include + +#include "lib/ipaddr.h" +#include "lib/memory.h" +#include "lib/prefix.h" + +#define VRRP_TYPE_ADVERTISEMENT 1 + +extern const char *vrrp_packet_names[16]; + +/* + * Shared header for VRRPv2/v3 packets. + */ +struct vrrp_hdr { + /* + * H L H L + * 0000 0000 + * ver type + */ + uint8_t vertype; + uint8_t vrid; + uint8_t priority; + uint8_t naddr; + union { + struct { + uint8_t auth_type; + /* advertisement interval (in sec) */ + uint8_t adver_int; + } v2; + struct { + /* + * advertisement interval (in centiseconds) + * H L H L + * 0000 000000000000 + * rsvd adver_int + */ + uint16_t adver_int; + } v3; + }; + uint16_t chksum; +} __attribute__((packed)); + +#define VRRP_HDR_SIZE sizeof(struct vrrp_hdr) + +struct vrrp_pkt { + struct vrrp_hdr hdr; + /* + * When used, this is actually an array of one or the other, not an + * array of union. If N v4 addresses are stored then + * sizeof(addrs) == N * sizeof(struct in_addr). + * + * Under v2, the last 2 entries in this array are the authentication + * data fields. We don't support auth in v2 so these are always just 8 + * bytes of 0x00. + */ + union { + struct in_addr v4; + struct in6_addr v6; + } addrs[]; +} __attribute__((packed)); + +#define VRRP_PKT_SIZE(_f, _ver, _naddr) \ + ({ \ + size_t _asz = ((_f) == AF_INET) ? sizeof(struct in_addr) \ + : sizeof(struct in6_addr); \ + size_t _auth = 2 * sizeof(uint32_t) * (3 - (_ver)); \ + sizeof(struct vrrp_hdr) + (_asz * (_naddr)) + _auth; \ + }) + +#define VRRP_MIN_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 3, 1) +#define VRRP_MAX_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 2, 255) +#define VRRP_MIN_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 3, 1) +#define VRRP_MAX_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 3, 255) + +#define VRRP_MIN_PKT_SIZE VRRP_MIN_PKT_SIZE_V4 +#define VRRP_MAX_PKT_SIZE VRRP_MAX_PKT_SIZE_V6 + +/* + * Builds a VRRP ADVERTISEMENT packet. + * + * pkt + * Pointer to store pointer to result buffer in + * + * src + * Source address packet will be transmitted from. This is needed to compute + * the VRRP checksum. The returned packet must be sent in an IP datagram with + * the source address equal to this field, or the checksum will be invalid. + * + * version + * VRRP version; must be 2 or 3 + * + * vrid + * Virtual Router Identifier + * + * prio + * Virtual Router Priority + * + * max_adver_int + * time between ADVERTISEMENTs + * + * v6 + * whether 'ips' is an array of v4 or v6 addresses + * + * numip + * number of IPvX addresses in 'ips' + * + * ips + * array of pointer to either struct in_addr (v6 = false) or struct in6_addr + * (v6 = true) + */ +ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src, + uint8_t version, uint8_t vrid, uint8_t prio, + uint16_t max_adver_int, uint8_t numip, + struct ipaddr **ips); + +/* + * Dumps a VRRP ADVERTISEMENT packet to a string. + * + * Currently only dumps the header. + * + * buf + * Buffer to store string representation + * + * buflen + * Size of buf + * + * pkt + * Packet to dump to a string + * + * Returns: + * # bytes written to buf + */ +size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt); + + +/* + * Parses a VRRP packet, checking for illegal or invalid data. + * + * This function parses both VRRPv2 and VRRPv3 packets. Which version is + * expected is determined by the version argument. For example, if version is 3 + * and the received packet has version field 2 it will fail to parse. + * + * Note that this function only checks whether the packet itself is a valid + * VRRP packet. It is up to the caller to validate whether the VRID is correct, + * priority and timer values are correct, etc. + * + * family + * Address family of received packet + * + * version + * VRRP version to use for validation + * + * m + * msghdr containing results of recvmsg() on VRRP router socket + * + * read + * Return value of recvmsg() on VRRP router socket; must be non-negative + * + * src + * Pointer to struct ipaddr to store address of datagram sender + * + * pkt + * Pointer to pointer to set to location of VRRP packet within buf + * + * errmsg + * Buffer to store human-readable error message in case of error; may be + * NULL, in which case no message will be stored + * + * errmsg_len + * Size of errmsg + * + * Returns: + * Size of VRRP packet, or -1 upon error + */ +ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, + size_t read, struct ipaddr *src, + struct vrrp_pkt **pkt, char *errmsg, + size_t errmsg_len); + +#endif /* __VRRP_PACKET_H__ */ diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c new file mode 100644 index 000000000000..48d81b025826 --- /dev/null +++ b/vrrpd/vrrp_vty.c @@ -0,0 +1,751 @@ +/* + * VRRP CLI commands. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "lib/command.h" +#include "lib/if.h" +#include "lib/ipaddr.h" +#include "lib/json.h" +#include "lib/prefix.h" +#include "lib/termtable.h" +#include "lib/vty.h" + +#include "vrrp.h" +#include "vrrp_debug.h" +#include "vrrp_memory.h" +#include "vrrp_vty.h" +#ifndef VTYSH_EXTRACT_PL +#include "vrrpd/vrrp_vty_clippy.c" +#endif + + +#define VRRP_STR "Virtual Router Redundancy Protocol\n" +#define VRRP_VRID_STR "Virtual Router ID\n" +#define VRRP_PRIORITY_STR "Virtual Router Priority\n" +#define VRRP_ADVINT_STR "Virtual Router Advertisement Interval\n" +#define VRRP_IP_STR "Virtual Router IPv4 address\n" +#define VRRP_VERSION_STR "VRRP protocol version\n" + +#define VROUTER_GET_VTY(_vty, _ifp, _vrid, _vr) \ + do { \ + _vr = vrrp_lookup(_ifp, _vrid); \ + if (!_vr) { \ + vty_out(_vty, \ + "%% Please configure VRRP instance %u\n", \ + (unsigned int)_vrid); \ + return CMD_WARNING_CONFIG_FAILED; \ + } \ + } while (0) + +/* clang-format off */ + +DEFPY(vrrp_vrid, + vrrp_vrid_cmd, + "[no] vrrp (1-255)$vrid [version (2-3)]", + NO_STR + VRRP_STR + VRRP_VRID_STR + VRRP_VERSION_STR + VRRP_VERSION_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid); + + if (version == 0) + version = 3; + + if (no && vr) + vrrp_vrouter_destroy(vr); + else if (no && !vr) + vty_out(vty, "%% VRRP instance %ld does not exist on %s\n", + vrid, ifp->name); + else if (!vr) + vrrp_vrouter_create(ifp, vrid, version); + else if (vr) + vty_out(vty, "%% VRRP instance %ld already exists on %s\n", + vrid, ifp->name); + + return CMD_SUCCESS; +} + +DEFPY(vrrp_shutdown, + vrrp_shutdown_cmd, + "[no] vrrp (1-255)$vrid shutdown", + NO_STR + VRRP_STR + VRRP_VRID_STR + "Force VRRP router into administrative shutdown\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + struct vrrp_vrouter *vr; + + VROUTER_GET_VTY(vty, ifp, vrid, vr); + + if (!no) { + if (vr->v4->fsm.state != VRRP_STATE_INITIALIZE) + vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + if (vr->v6->fsm.state != VRRP_STATE_INITIALIZE) + vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); + vr->shutdown = true; + } else { + vr->shutdown = false; + vrrp_check_start(vr); + } + + return CMD_SUCCESS; +} + +DEFPY(vrrp_priority, + vrrp_priority_cmd, + "[no] vrrp (1-255)$vrid priority (1-254)", + NO_STR + VRRP_STR + VRRP_VRID_STR + VRRP_PRIORITY_STR + "Priority value") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + struct vrrp_vrouter *vr; + uint8_t newprio = no ? vd.priority : priority; + + VROUTER_GET_VTY(vty, ifp, vrid, vr); + + vrrp_set_priority(vr, newprio); + + return CMD_SUCCESS; +} + +DEFPY(vrrp_advertisement_interval, + vrrp_advertisement_interval_cmd, + "[no] vrrp (1-255)$vrid advertisement-interval (10-40950)", + NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR + "Advertisement interval in milliseconds; must be multiple of 10") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + struct vrrp_vrouter *vr; + uint16_t newadvint = + no ? vd.advertisement_interval * 10 : advertisement_interval; + + if (newadvint % 10 != 0) { + vty_out(vty, "%% Value must be a multiple of 10\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* all internal computations are in centiseconds */ + newadvint /= CS2MS; + + VROUTER_GET_VTY(vty, ifp, vrid, vr); + vrrp_set_advertisement_interval(vr, newadvint); + + return CMD_SUCCESS; +} + +DEFPY(vrrp_ip, + vrrp_ip_cmd, + "[no] vrrp (1-255)$vrid ip A.B.C.D", + NO_STR + VRRP_STR + VRRP_VRID_STR + "Add IPv4 address\n" + VRRP_IP_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + struct vrrp_vrouter *vr; + bool deactivated = false; + bool activated = false; + bool failed = false; + int ret = CMD_SUCCESS; + int oldstate; + + VROUTER_GET_VTY(vty, ifp, vrid, vr); + + bool will_activate = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE); + + if (no) { + oldstate = vr->v4->fsm.state; + failed = vrrp_del_ipv4(vr, ip); + vrrp_check_start(vr); + deactivated = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE + && oldstate != VRRP_STATE_INITIALIZE); + } else { + oldstate = vr->v4->fsm.state; + failed = vrrp_add_ipv4(vr, ip); + vrrp_check_start(vr); + activated = (vr->v4->fsm.state != VRRP_STATE_INITIALIZE + && oldstate == VRRP_STATE_INITIALIZE); + } + + if (activated) + vty_out(vty, "%% Activated IPv4 Virtual Router %ld\n", vrid); + if (deactivated) + vty_out(vty, "%% Deactivated IPv4 Virtual Router %ld\n", vrid); + if (failed) { + vty_out(vty, "%% Failed to %s virtual IP\n", + no ? "remove" : "add"); + ret = CMD_WARNING_CONFIG_FAILED; + if (will_activate && !activated) { + vty_out(vty, + "%% Failed to activate IPv4 Virtual Router %ld\n", + vrid); + } + } + + return ret; +} + +DEFPY(vrrp_ip6, + vrrp_ip6_cmd, + "[no] vrrp (1-255)$vrid ipv6 X:X::X:X", + NO_STR + VRRP_STR + VRRP_VRID_STR + "Add IPv6 address\n" + VRRP_IP_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + struct vrrp_vrouter *vr; + bool deactivated = false; + bool activated = false; + bool failed = false; + int ret = CMD_SUCCESS; + int oldstate; + + VROUTER_GET_VTY(vty, ifp, vrid, vr); + + if (vr->version != 3) { + vty_out(vty, + "%% Cannot add IPv6 address to VRRPv2 virtual router\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + bool will_activate = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE); + + if (no) { + oldstate = vr->v6->fsm.state; + failed = vrrp_del_ipv6(vr, ipv6); + vrrp_check_start(vr); + deactivated = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE + && oldstate != VRRP_STATE_INITIALIZE); + } else { + oldstate = vr->v6->fsm.state; + failed = vrrp_add_ipv6(vr, ipv6); + vrrp_check_start(vr); + activated = (vr->v6->fsm.state != VRRP_STATE_INITIALIZE + && oldstate == VRRP_STATE_INITIALIZE); + } + + if (activated) + vty_out(vty, "%% Activated IPv6 Virtual Router %ld\n", vrid); + if (deactivated) + vty_out(vty, "%% Deactivated IPv6 Virtual Router %ld\n", vrid); + if (failed) { + vty_out(vty, "%% Failed to %s virtual IP\n", + no ? "remove" : "add"); + ret = CMD_WARNING_CONFIG_FAILED; + if (will_activate && !activated) { + vty_out(vty, + "%% Failed to activate IPv6 Virtual Router %ld\n", + vrid); + } + } + + return ret; +} + +DEFPY(vrrp_preempt, + vrrp_preempt_cmd, + "[no] vrrp (1-255)$vrid preempt", + NO_STR + VRRP_STR + VRRP_VRID_STR + "Preempt mode\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + struct vrrp_vrouter *vr; + + VROUTER_GET_VTY(vty, ifp, vrid, vr); + + vr->preempt_mode = !no; + + return CMD_SUCCESS; +} + +DEFPY(vrrp_autoconfigure, + vrrp_autoconfigure_cmd, + "[no] vrrp autoconfigure [version (2-3)]", + NO_STR + VRRP_STR + "Automatically set up VRRP instances on VRRP-compatible interfaces\n" + "Version for automatically configured instances\n" + VRRP_VERSION_STR) +{ + version = version ? version : 3; + + if (!no) + vrrp_autoconfig_on(version); + else + vrrp_autoconfig_off(); + + return CMD_SUCCESS; +} + +DEFPY(vrrp_default, + vrrp_default_cmd, + "[no] vrrp default ", + NO_STR + VRRP_STR + "Configure defaults for new VRRP instances\n" + VRRP_ADVINT_STR + "Advertisement interval in milliseconds\n" + "Preempt mode\n" + VRRP_PRIORITY_STR + "Priority value\n" + "Force VRRP router into administrative shutdown\n") +{ + if (adv) { + if (advint % 10 != 0) { + vty_out(vty, "%% Value must be a multiple of 10\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* all internal computations are in centiseconds */ + advint /= CS2MS; + vd.advertisement_interval = no ? VRRP_DEFAULT_ADVINT : advint; + } + if (p) + vd.preempt_mode = !no; + if (prio) + vd.priority = no ? VRRP_DEFAULT_PRIORITY : prioval; + if (s) + vd.shutdown = !no; + + return CMD_SUCCESS; +} + +/* clang-format on */ + +/* + * Build JSON representation of VRRP instance. + * + * vr + * VRRP router to build json object from + * + * Returns: + * JSON representation of VRRP instance. Must be freed by caller. + */ +static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) +{ + char ethstr4[ETHER_ADDR_STRLEN]; + char ethstr6[ETHER_ADDR_STRLEN]; + char ipstr[INET6_ADDRSTRLEN]; + const char *stastr4 = vrrp_state_names[vr->v4->fsm.state]; + const char *stastr6 = vrrp_state_names[vr->v6->fsm.state]; + char sipstr4[INET6_ADDRSTRLEN] = {}; + char sipstr6[INET6_ADDRSTRLEN] = {}; + struct listnode *ln; + struct ipaddr *ip; + struct json_object *j = json_object_new_object(); + struct json_object *v4 = json_object_new_object(); + struct json_object *v4_stats = json_object_new_object(); + struct json_object *v4_addrs = json_object_new_array(); + struct json_object *v6 = json_object_new_object(); + struct json_object *v6_stats = json_object_new_object(); + struct json_object *v6_addrs = json_object_new_array(); + + prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4)); + prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6)); + + json_object_int_add(j, "vrid", vr->vrid); + json_object_int_add(j, "version", vr->version); + json_object_boolean_add(j, "autoconfigured", vr->autoconf); + json_object_boolean_add(j, "shutdown", vr->shutdown); + json_object_boolean_add(j, "preemptMode", vr->preempt_mode); + json_object_boolean_add(j, "acceptMode", vr->accept_mode); + json_object_string_add(j, "interface", vr->ifp->name); + json_object_int_add(j, "advertisementInterval", + vr->advertisement_interval * CS2MS); + /* v4 */ + json_object_string_add(v4, "interface", + vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : ""); + json_object_string_add(v4, "vmac", ethstr4); + ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4)); + json_object_string_add(v4, "primaryAddress", sipstr4); + json_object_string_add(v4, "status", stastr4); + json_object_int_add(v4, "effectivePriority", vr->v4->priority); + json_object_int_add(v4, "masterAdverInterval", + vr->v4->master_adver_interval * CS2MS); + json_object_int_add(v4, "skewTime", vr->v4->skew_time * CS2MS); + json_object_int_add(v4, "masterDownInterval", + vr->v4->master_down_interval * CS2MS); + /* v4 stats */ + json_object_int_add(v4_stats, "adverTx", vr->v4->stats.adver_tx_cnt); + json_object_int_add(v4_stats, "adverRx", vr->v4->stats.adver_rx_cnt); + json_object_int_add(v4_stats, "garpTx", vr->v4->stats.garp_tx_cnt); + json_object_int_add(v4_stats, "transitions", vr->v4->stats.trans_cnt); + json_object_object_add(v4, "stats", v4_stats); + /* v4 addrs */ + if (vr->v4->addrs->count) { + for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) { + inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr, + sizeof(ipstr)); + json_object_array_add(v4_addrs, + json_object_new_string(ipstr)); + } + } + json_object_object_add(v4, "addresses", v4_addrs); + json_object_object_add(j, "v4", v4); + + /* v6 */ + json_object_string_add(v6, "interface", + vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : ""); + json_object_string_add(v6, "vmac", ethstr6); + ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6)); + if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00) + strlcat(sipstr6, "::", sizeof(sipstr6)); + json_object_string_add(v6, "primaryAddress", sipstr6); + json_object_string_add(v6, "status", stastr6); + json_object_int_add(v6, "effectivePriority", vr->v6->priority); + json_object_int_add(v6, "masterAdverInterval", + vr->v6->master_adver_interval * CS2MS); + json_object_int_add(v6, "skewTime", vr->v6->skew_time * CS2MS); + json_object_int_add(v6, "masterDownInterval", + vr->v6->master_down_interval * CS2MS); + /* v6 stats */ + json_object_int_add(v6_stats, "adverTx", vr->v6->stats.adver_tx_cnt); + json_object_int_add(v6_stats, "adverRx", vr->v6->stats.adver_rx_cnt); + json_object_int_add(v6_stats, "neighborAdverTx", + vr->v6->stats.una_tx_cnt); + json_object_int_add(v6_stats, "transitions", vr->v6->stats.trans_cnt); + json_object_object_add(v6, "stats", v6_stats); + /* v6 addrs */ + if (vr->v6->addrs->count) { + for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) { + inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr, + sizeof(ipstr)); + json_object_array_add(v6_addrs, + json_object_new_string(ipstr)); + } + } + json_object_object_add(v6, "addresses", v6_addrs); + json_object_object_add(j, "v6", v6); + + return j; +} + +/* + * Dump VRRP instance status to VTY. + * + * vty + * vty to dump to + * + * vr + * VRRP router to dump + */ +static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) +{ + char ethstr4[ETHER_ADDR_STRLEN]; + char ethstr6[ETHER_ADDR_STRLEN]; + char ipstr[INET6_ADDRSTRLEN]; + const char *stastr4 = vrrp_state_names[vr->v4->fsm.state]; + const char *stastr6 = vrrp_state_names[vr->v6->fsm.state]; + char sipstr4[INET6_ADDRSTRLEN] = {}; + char sipstr6[INET6_ADDRSTRLEN] = {}; + struct listnode *ln; + struct ipaddr *ip; + + struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + + ttable_add_row(tt, "%s|%" PRIu32, "Virtual Router ID", vr->vrid); + ttable_add_row(tt, "%s|%" PRIu8, "Protocol Version", vr->version); + ttable_add_row(tt, "%s|%s", "Autoconfigured", + vr->autoconf ? "Yes" : "No"); + ttable_add_row(tt, "%s|%s", "Shutdown", vr->shutdown ? "Yes" : "No"); + ttable_add_row(tt, "%s|%s", "Interface", vr->ifp->name); + prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4)); + prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6)); + ttable_add_row(tt, "%s|%s", "VRRP interface (v4)", + vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : "None"); + ttable_add_row(tt, "%s|%s", "VRRP interface (v6)", + vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : "None"); + ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4)); + ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6)); + if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00) + strlcat(sipstr6, "::", sizeof(sipstr6)); + ttable_add_row(tt, "%s|%s", "Primary IP (v4)", sipstr4); + ttable_add_row(tt, "%s|%s", "Primary IP (v6)", sipstr6); + ttable_add_row(tt, "%s|%s", "Virtual MAC (v4)", ethstr4); + ttable_add_row(tt, "%s|%s", "Virtual MAC (v6)", ethstr6); + ttable_add_row(tt, "%s|%s", "Status (v4)", stastr4); + ttable_add_row(tt, "%s|%s", "Status (v6)", stastr6); + ttable_add_row(tt, "%s|%" PRIu8, "Priority", vr->priority); + ttable_add_row(tt, "%s|%" PRIu8, "Effective Priority (v4)", + vr->v4->priority); + ttable_add_row(tt, "%s|%" PRIu8, "Effective Priority (v6)", + vr->v6->priority); + ttable_add_row(tt, "%s|%s", "Preempt Mode", + vr->preempt_mode ? "Yes" : "No"); + ttable_add_row(tt, "%s|%s", "Accept Mode", + vr->accept_mode ? "Yes" : "No"); + ttable_add_row(tt, "%s|%d ms", "Advertisement Interval", + vr->advertisement_interval * CS2MS); + ttable_add_row(tt, "%s|%d ms", + "Master Advertisement Interval (v4)", + vr->v4->master_adver_interval * CS2MS); + ttable_add_row(tt, "%s|%d ms", + "Master Advertisement Interval (v6)", + vr->v6->master_adver_interval * CS2MS); + ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v4)", + vr->v4->stats.adver_tx_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v6)", + vr->v6->stats.adver_tx_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v4)", + vr->v4->stats.adver_rx_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v6)", + vr->v6->stats.adver_rx_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "Gratuitous ARP Tx (v4)", + vr->v4->stats.garp_tx_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "Neigh. Adverts Tx (v6)", + vr->v6->stats.una_tx_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v4)", + vr->v4->stats.trans_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v6)", + vr->v6->stats.trans_cnt); + ttable_add_row(tt, "%s|%d ms", "Skew Time (v4)", + vr->v4->skew_time * CS2MS); + ttable_add_row(tt, "%s|%d ms", "Skew Time (v6)", + vr->v6->skew_time * CS2MS); + ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v4)", + vr->v4->master_down_interval * CS2MS); + ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v6)", + vr->v6->master_down_interval * CS2MS); + ttable_add_row(tt, "%s|%u", "IPv4 Addresses", vr->v4->addrs->count); + + char fill[35]; + + memset(fill, '.', sizeof(fill)); + fill[sizeof(fill) - 1] = 0x00; + if (vr->v4->addrs->count) { + for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) { + inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr, + sizeof(ipstr)); + ttable_add_row(tt, "%s|%s", fill, ipstr); + } + } + + ttable_add_row(tt, "%s|%u", "IPv6 Addresses", vr->v6->addrs->count); + + if (vr->v6->addrs->count) { + for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) { + inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr, + sizeof(ipstr)); + ttable_add_row(tt, "%s|%s", fill, ipstr); + } + } + + char *table = ttable_dump(tt, "\n"); + + vty_out(vty, "\n%s\n", table); + XFREE(MTYPE_TMP, table); + ttable_del(tt); +} + +/* + * Sort comparator, used when sorting VRRP instances for display purposes. + * + * Sorts by interface name first, then by VRID ascending. + */ +static int vrrp_instance_display_sort_cmp(const void **d1, const void **d2) +{ + const struct vrrp_vrouter *vr1 = *d1; + const struct vrrp_vrouter *vr2 = *d2; + int result; + + result = strcmp(vr1->ifp->name, vr2->ifp->name); + result += !result * (vr1->vrid - vr2->vrid); + + return result; +} + +/* clang-format off */ + +DEFPY(vrrp_vrid_show, + vrrp_vrid_show_cmd, + "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] [json$json]", + SHOW_STR + VRRP_STR + INTERFACE_STR + "Only show VRRP instances on this interface\n" + VRRP_VRID_STR + JSON_STR) +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *ll = hash_to_list(vrrp_vrouters_hash); + struct json_object *j = json_object_new_array(); + + list_sort(ll, vrrp_instance_display_sort_cmp); + + for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) { + if (ifn && !strmatch(ifn, vr->ifp->name)) + continue; + if (vrid && ((uint8_t) vrid) != vr->vrid) + continue; + + if (!json) + vrrp_show(vty, vr); + else + json_object_array_add(j, vrrp_build_json(vr)); + } + + if (json) + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY)); + + json_object_free(j); + + list_delete(&ll); + + return CMD_SUCCESS; +} + +DEFPY(vrrp_vrid_show_summary, + vrrp_vrid_show_summary_cmd, + "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] summary", + SHOW_STR + VRRP_STR + INTERFACE_STR + "Only show VRRP instances on this interface\n" + VRRP_VRID_STR + "Summarize all VRRP instances\n") +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *ll = hash_to_list(vrrp_vrouters_hash); + + list_sort(ll, vrrp_instance_display_sort_cmp); + + struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + + ttable_add_row( + tt, "Interface|VRID|Priority|IPv4|IPv6|State (v4)|State (v6)"); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) { + if (ifn && !strmatch(ifn, vr->ifp->name)) + continue; + if (vrid && ((uint8_t)vrid) != vr->vrid) + continue; + + ttable_add_row( + tt, "%s|%" PRIu8 "|%" PRIu8 "|%d|%d|%s|%s", + vr->ifp->name, vr->vrid, vr->priority, + vr->v4->addrs->count, vr->v6->addrs->count, + vr->v4->fsm.state == VRRP_STATE_MASTER ? "Master" + : "Backup", + vr->v6->fsm.state == VRRP_STATE_MASTER ? "Master" + : "Backup"); + } + + char *table = ttable_dump(tt, "\n"); + + vty_out(vty, "\n%s\n", table); + XFREE(MTYPE_TMP, table); + ttable_del(tt); + + list_delete(&ll); + + return CMD_SUCCESS; +} + + +DEFPY(debug_vrrp, + debug_vrrp_cmd, + "[no] debug vrrp [{protocol$proto|autoconfigure$ac|packets$pkt|sockets$sock|ndisc$ndisc|arp$arp|zebra$zebra}]", + NO_STR + DEBUG_STR + VRRP_STR + "Debug protocol state\n" + "Debug autoconfiguration\n" + "Debug sent and received packets\n" + "Debug socket creation and configuration\n" + "Debug Neighbor Discovery\n" + "Debug ARP\n" + "Debug Zebra events\n") +{ + /* If no specific are given on/off them all */ + if (strmatch(argv[argc - 1]->text, "vrrp")) + vrrp_debug_set(NULL, 0, vty->node, !no, true, true, true, true, + true, true, true); + else + vrrp_debug_set(NULL, 0, vty->node, !no, !!proto, !!ac, !!pkt, + !!sock, !!ndisc, !!arp, !!zebra); + + return CMD_SUCCESS; +} + +DEFUN_NOSH (show_debugging_vrrp, + show_debugging_vrrp_cmd, + "show debugging [vrrp]", + SHOW_STR + DEBUG_STR + "VRRP information\n") +{ + vty_out(vty, "VRRP debugging status:\n"); + + vrrp_debug_status_write(vty); + + return CMD_SUCCESS; +} + +/* clang-format on */ + +static struct cmd_node interface_node = {INTERFACE_NODE, "%s(config-if)# ", 1}; +static struct cmd_node debug_node = {DEBUG_NODE, "", 1}; +static struct cmd_node vrrp_node = {VRRP_NODE, "", 1}; + +void vrrp_vty_init(void) +{ + install_node(&debug_node, vrrp_config_write_debug); + install_node(&interface_node, vrrp_config_write_interface); + install_node(&vrrp_node, vrrp_config_write_global); + if_cmd_init(); + + install_element(VIEW_NODE, &vrrp_vrid_show_cmd); + install_element(VIEW_NODE, &vrrp_vrid_show_summary_cmd); + install_element(VIEW_NODE, &show_debugging_vrrp_cmd); + install_element(VIEW_NODE, &debug_vrrp_cmd); + install_element(CONFIG_NODE, &debug_vrrp_cmd); + install_element(CONFIG_NODE, &vrrp_autoconfigure_cmd); + install_element(CONFIG_NODE, &vrrp_default_cmd); + install_element(INTERFACE_NODE, &vrrp_vrid_cmd); + install_element(INTERFACE_NODE, &vrrp_shutdown_cmd); + install_element(INTERFACE_NODE, &vrrp_priority_cmd); + install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd); + install_element(INTERFACE_NODE, &vrrp_ip_cmd); + install_element(INTERFACE_NODE, &vrrp_ip6_cmd); + install_element(INTERFACE_NODE, &vrrp_preempt_cmd); +} diff --git a/vrrpd/vrrp_vty.h b/vrrpd/vrrp_vty.h new file mode 100644 index 000000000000..377321ec4a38 --- /dev/null +++ b/vrrpd/vrrp_vty.h @@ -0,0 +1,25 @@ +/* + * VRRP CLI commands. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_VTY_H__ +#define __VRRP_VTY_H__ + +void vrrp_vty_init(void); + +#endif /* __VRRP_VTY_H__ */ diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c new file mode 100644 index 000000000000..7503034de32f --- /dev/null +++ b/vrrpd/vrrp_zebra.c @@ -0,0 +1,252 @@ +/* + * VRRP Zebra interfacing. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "lib/if.h" +#include "lib/linklist.h" +#include "lib/log.h" +#include "lib/prefix.h" +#include "lib/vty.h" +#include "lib/zclient.h" + +#include "vrrp.h" +#include "vrrp_debug.h" +#include "vrrp_zebra.h" + +#define VRRP_LOGPFX "[ZEBRA] " + +static struct zclient *zclient; + +static void vrrp_zebra_debug_if_state(struct interface *ifp, vrf_id_t vrf_id, + const char *func) +{ + DEBUGD(&vrrp_dbg_zebra, + "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", + func, ifp->name, ifp->ifindex, vrf_id, (long)ifp->flags, + ifp->metric, ifp->mtu, if_is_operative(ifp)); +} + +static void vrrp_zebra_debug_if_dump_address(struct interface *ifp, + const char *func) +{ + struct connected *ifc; + struct listnode *node; + + DEBUGD(&vrrp_dbg_zebra, "%s: interface %s addresses:", func, ifp->name); + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + struct prefix *p = ifc->address; + + DEBUGD(&vrrp_dbg_zebra, "%s: interface %s address %s %s", func, + ifp->name, inet_ntoa(p->u.prefix4), + CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? "secondary" + : "primary"); + } +} + + +static void vrrp_zebra_connected(struct zclient *zclient) +{ + zclient_send_reg_requests(zclient, VRF_DEFAULT); +} + +/* Router-id update message from zebra. */ +static int vrrp_router_id_update_zebra(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct prefix router_id; + + zebra_router_id_update_read(zclient->ibuf, &router_id); + + return 0; +} + +static int vrrp_zebra_if_add(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + /* + * zebra api adds/dels interfaces using the same call + * interface_add_read below, see comments in lib/zclient.c + */ + ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); + + if (!ifp) + return 0; + + vrrp_zebra_debug_if_state(ifp, vrf_id, __func__); + + vrrp_if_add(ifp); + + return 0; +} + +static int vrrp_zebra_if_del(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + + if (!ifp) + return 0; + + vrrp_zebra_debug_if_state(ifp, vrf_id, __func__); + + vrrp_if_del(ifp); + + return 0; +} + +static int vrrp_zebra_if_state_up(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + /* + * zebra api notifies interface up/down events by using the same call + * zebra_interface_state_read below, see comments in lib/zclient.c ifp = + * zebra_interface_state_read(zclient->ibuf, vrf_id); + */ + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + + if (!ifp) + return 0; + + vrrp_zebra_debug_if_state(ifp, vrf_id, __func__); + + vrrp_if_up(ifp); + + return 0; +} + +static int vrrp_zebra_if_state_down(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + /* + * zebra api notifies interface up/down events by using the same call + * zebra_interface_state_read below, see comments in lib/zclient.c + */ + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + + if (!ifp) + return 0; + + vrrp_zebra_debug_if_state(ifp, vrf_id, __func__); + + vrrp_if_down(ifp); + + return 0; +} + +static int vrrp_zebra_if_address_add(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *c; + + /* + * zebra api notifies address adds/dels events by using the same call + * interface_add_read below, see comments in lib/zclient.c + * + * zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...) + * will add address to interface list by calling + * connected_add_by_prefix() + */ + c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + + if (!c) + return 0; + + vrrp_zebra_debug_if_state(c->ifp, vrf_id, __func__); + vrrp_zebra_debug_if_dump_address(c->ifp, __func__); + + vrrp_if_address_add(c->ifp); + + return 0; +} + +static int vrrp_zebra_if_address_del(int command, struct zclient *client, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *c; + + /* + * zebra api notifies address adds/dels events by using the same call + * interface_add_read below, see comments in lib/zclient.c + * + * zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...) + * will remove address from interface list by calling + * connected_delete_by_prefix() + */ + c = zebra_interface_address_read(command, client->ibuf, vrf_id); + + if (!c) + return 0; + + vrrp_zebra_debug_if_state(c->ifp, vrf_id, __func__); + vrrp_zebra_debug_if_dump_address(c->ifp, __func__); + + vrrp_if_address_del(c->ifp); + + return 0; +} + +void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable) +{ + DEBUGD(&vrrp_dbg_zebra, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Requesting Zebra to turn router advertisements %s for %s", + r->vr->vrid, enable ? "on" : "off", r->mvl_ifp->name); + + zclient_send_interface_radv_req(zclient, VRF_DEFAULT, r->mvl_ifp, + enable, VRRP_RADV_INT); +} + +int vrrp_zclient_send_interface_protodown(struct interface *ifp, bool down) +{ + DEBUGD(&vrrp_dbg_zebra, + VRRP_LOGPFX "Requesting Zebra to set %s protodown %s", ifp->name, + down ? "on" : "off"); + + return zclient_send_interface_protodown(zclient, VRF_DEFAULT, ifp, + down); +} + +void vrrp_zebra_init(void) +{ + /* Socket for receiving updates from Zebra daemon */ + zclient = zclient_new(master, &zclient_options_default); + + zclient->zebra_connected = vrrp_zebra_connected; + zclient->router_id_update = vrrp_router_id_update_zebra; + zclient->interface_add = vrrp_zebra_if_add; + zclient->interface_delete = vrrp_zebra_if_del; + zclient->interface_up = vrrp_zebra_if_state_up; + zclient->interface_down = vrrp_zebra_if_state_down; + zclient->interface_address_add = vrrp_zebra_if_address_add; + zclient->interface_address_delete = vrrp_zebra_if_address_del; + + zclient_init(zclient, ZEBRA_ROUTE_VRRP, 0, &vrrp_privs); + + zlog_notice("%s: zclient socket initialized", __PRETTY_FUNCTION__); +} diff --git a/vrrpd/vrrp_zebra.h b/vrrpd/vrrp_zebra.h new file mode 100644 index 000000000000..84bcba23c19a --- /dev/null +++ b/vrrpd/vrrp_zebra.h @@ -0,0 +1,32 @@ +/* + * VRRP Zebra interfacing. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_ZEBRA_H__ +#define __VRRP_ZEBRA_H__ + +#include + +#include "lib/if.h" + +extern void vrrp_zebra_init(void); +extern void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable); +extern int vrrp_zclient_send_interface_protodown(struct interface *ifp, + bool down); + +#endif /* __VRRP_ZEBRA_H__ */ diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 332fd248caf5..4dc34d10efb7 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -40,7 +40,7 @@ sub scan_file { $cppadd = $fabricd ? "-DFABRICD=1" : ""; - open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @CPPFLAGS@ $cppadd $file |"); + open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ $cppadd $file |"); local $/; undef $/; $line = ; close (FH); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index b8da90ca8e8a..c3aeb27eb950 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -137,6 +137,7 @@ struct vtysh_client vtysh_client[] = { {.fd = -1, .name = "pbrd", .flag = VTYSH_PBRD, .next = NULL}, {.fd = -1, .name = "staticd", .flag = VTYSH_STATICD, .next = NULL}, {.fd = -1, .name = "bfdd", .flag = VTYSH_BFDD, .next = NULL}, + {.fd = -1, .name = "vrrpd", .flag = VTYSH_VRRPD, .next = NULL}, }; enum vtysh_write_integrated vtysh_write_integrated = @@ -502,7 +503,7 @@ static int vtysh_execute_func(const char *line, int pager) vtysh_execute("exit"); } else if (tried) { vtysh_execute("end"); - vtysh_execute("configure terminal"); + vtysh_execute("configure"); } } /* @@ -540,7 +541,7 @@ static int vtysh_execute_func(const char *line, int pager) if (pager && strncmp(line, "exit", 4)) vty_open_pager(vty); - if (!strcmp(cmd->string, "configure terminal")) { + if (!strcmp(cmd->string, "configure")) { for (i = 0; i < array_size(vtysh_client); i++) { cmd_stat = vtysh_client_execute( &vtysh_client[i], line); @@ -674,13 +675,13 @@ int vtysh_mark_file(const char *filename) vty->node = CONFIG_NODE; vtysh_execute_no_pager("enable"); - vtysh_execute_no_pager("configure terminal"); + vtysh_execute_no_pager("configure"); vty_buf_copy = XCALLOC(MTYPE_VTYSH_CMD, VTY_BUFSIZ); while (fgets(vty->buf, VTY_BUFSIZ, confp)) { lineno++; tried = 0; - strcpy(vty_buf_copy, vty->buf); + strlcpy(vty_buf_copy, vty->buf, VTY_BUFSIZ); vty_buf_trimmed = trim(vty_buf_copy); switch (vty->node) { @@ -1677,7 +1678,7 @@ DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd, } DEFUNSH(VTYSH_PBRD, vtysh_pbr_map, vtysh_pbr_map_cmd, - "pbr-map NAME seq (1-700)", + "pbr-map PBRMAP seq (1-700)", "Create pbr-map or enter pbr-map command mode\n" "The name of the PBR MAP\n" "Sequence to insert to/delete from existing pbr-map entry\n" @@ -1713,7 +1714,7 @@ DEFUNSH(VTYSH_BFDD, bfd_peer_enter, bfd_peer_enter_cmd, } #endif /* HAVE_BFDD */ -DEFSH(VTYSH_PBRD, vtysh_no_pbr_map_cmd, "no pbr-map WORD [seq (1-700)]", +DEFSH(VTYSH_PBRD, vtysh_no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]", NO_STR "Delete pbr-map\n" "The name of the PBR MAP\n" @@ -1744,7 +1745,7 @@ DEFUNSH(VTYSH_REALLYALL, vtysh_disable, vtysh_disable_cmd, "disable", } DEFUNSH(VTYSH_REALLYALL, vtysh_config_terminal, vtysh_config_terminal_cmd, - "configure terminal", + "configure [terminal]", "Configuration from vty interface\n" "Configuration terminal\n") { @@ -1786,7 +1787,7 @@ static int vtysh_exit(struct vty *vty) case BFD_NODE: case RPKI_NODE: vtysh_execute("end"); - vtysh_execute("configure terminal"); + vtysh_execute("configure"); vty->node = CONFIG_NODE; break; case BGP_VPNV4_NODE: @@ -2113,7 +2114,7 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_logicalrouter_cmd, "The file name in " NS_RUN_DIR ", or a full pathname\n") DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_nexthop_group, vtysh_nexthop_group_cmd, - "nexthop-group NAME", + "nexthop-group NHGNAME", "Nexthop Group configuration\n" "Name of the Nexthop Group\n") { @@ -2122,7 +2123,7 @@ DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_nexthop_group, vtysh_nexthop_group_cmd, } DEFSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_no_nexthop_group_cmd, - "no nexthop-group NAME", + "no nexthop-group NHGNAME", NO_STR "Nexthop Group Configuration\n" "Name of the Nexthop Group\n") @@ -2172,7 +2173,7 @@ DEFUNSH(VTYSH_VRF, vtysh_quit_vrf, vtysh_quit_vrf_cmd, "quit", return vtysh_exit_vrf(self, vty, argc, argv); } -DEFUNSH(VTYSH_PBRD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd, +DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); @@ -2552,6 +2553,15 @@ DEFUNSH(VTYSH_ALL, vtysh_log_timestamp_precision, return CMD_SUCCESS; } +DEFUNSH(VTYSH_ALL, vtysh_debug_memstats, + vtysh_debug_memstats_cmd, "[no] debug memstats-at-exit", + NO_STR + "Debug\n" + "Print memory statistics at exit\n") +{ + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_ALL, no_vtysh_log_timestamp_precision, no_vtysh_log_timestamp_precision_cmd, "no log timestamp precision", NO_STR @@ -2701,9 +2711,10 @@ static void backup_config_file(const char *fbackup) { char *integrate_sav = NULL; - integrate_sav = malloc(strlen(fbackup) + strlen(CONF_BACKUP_EXT) + 1); - strcpy(integrate_sav, fbackup); - strcat(integrate_sav, CONF_BACKUP_EXT); + size_t integrate_sav_sz = strlen(fbackup) + strlen(CONF_BACKUP_EXT) + 1; + integrate_sav = malloc(integrate_sav_sz); + strlcpy(integrate_sav, fbackup, integrate_sav_sz); + strlcat(integrate_sav, CONF_BACKUP_EXT, integrate_sav_sz); /* Move current configuration file to backup config file. */ if (unlink(integrate_sav) != 0) { @@ -3346,7 +3357,7 @@ static void vtysh_update_all_instances(struct vtysh_client *head_client) dir = opendir(vtydir); if (dir) { while ((file = readdir(dir)) != NULL) { - if (begins_with(file->d_name, "ospfd-") + if (frrstr_startswith(file->d_name, "ospfd-") && ends_with(file->d_name, ".vty")) { if (n == MAXIMUM_INSTANCES) { fprintf(stderr, @@ -3427,7 +3438,7 @@ void vtysh_readline_init(void) char *vtysh_prompt(void) { - static char buf[100]; + static char buf[512]; snprintf(buf, sizeof buf, cmd_prompt(vty->node), cmd_hostname_get()); return buf; @@ -3847,6 +3858,8 @@ void vtysh_init_vty(void) install_element(VIEW_NODE, &vtysh_show_debugging_hashtable_cmd); install_element(ENABLE_NODE, &vtysh_debug_all_cmd); install_element(CONFIG_NODE, &vtysh_debug_all_cmd); + install_element(ENABLE_NODE, &vtysh_debug_memstats_cmd); + install_element(CONFIG_NODE, &vtysh_debug_memstats_cmd); /* misc lib show commands */ install_element(VIEW_NODE, &vtysh_show_memory_cmd); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index eb69a20b8396..3b0b570a563a 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -42,6 +42,7 @@ DECLARE_MGROUP(MVTYSH) #define VTYSH_STATICD 0x08000 #define VTYSH_BFDD 0x10000 #define VTYSH_FABRICD 0x20000 +#define VTYSH_VRRPD 0x40000 #define VTYSH_WAS_ACTIVE (-2) @@ -50,9 +51,9 @@ DECLARE_MGROUP(MVTYSH) /* watchfrr is not in ALL since library CLI functions should not be * run on it (logging & co. should stay in a fixed/frozen config, and * things like prefix lists are not even initialised) */ -#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD +#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD|VTYSH_VRRPD #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD|VTYSH_FABRICD -#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD +#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD #define VTYSH_NS VTYSH_ZEBRA #define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_STATICD #define VTYSH_KEYS VTYSH_RIPD|VTYSH_EIGRPD diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 7ca3ed9c5ea5..9c2de0f62b8c 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -257,6 +257,10 @@ void vtysh_config_parse_line(void *arg, const char *line) strlen(" exit-vrf")) == 0) { config_add_line_uniq_end(config->line, line); + } else if (!strncmp(line, " vrrp", strlen(" vrrp")) + || !strncmp(line, " no vrrp", + strlen(" no vrrp"))) { + config_add_line(config->line, line); } else if (config->index == RMAP_NODE || config->index == INTERFACE_NODE || config->index == LOGICALROUTER_NODE @@ -517,10 +521,10 @@ int vtysh_read_config(const char *config_default_dir) */ void vtysh_config_write(void) { - char line[81]; + char line[512]; if (cmd_hostname_get()) { - sprintf(line, "hostname %s", cmd_hostname_get()); + snprintf(line, sizeof(line), "hostname %s", cmd_hostname_get()); vtysh_config_parse_line(NULL, line); } diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 2e4510a45a87..966746018915 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -332,6 +332,8 @@ int main(int argc, char **argv, char **env) progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]); strlcpy(sysconfdir, frr_sysconfdir, sizeof(sysconfdir)); + + frr_init_vtydir(); strlcpy(vtydir, frr_vtydir, sizeof(vtydir)); /* Option handling. */ diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c index 34f8dabdf146..c17d38173013 100644 --- a/watchfrr/watchfrr.c +++ b/watchfrr/watchfrr.c @@ -648,6 +648,7 @@ static void daemon_send_ready(int exitcode) { FILE *fp; static int sent = 0; + char started[1024]; if (sent) return; @@ -669,7 +670,9 @@ static void daemon_send_ready(int exitcode) frr_detach(); - fp = fopen(DAEMON_VTY_DIR "/watchfrr.started", "w"); + snprintf(started, sizeof(started), "%s%s", frr_vtydir, + "watchfrr.started"); + fp = fopen(started, "w"); if (fp) fclose(fp); #if defined HAVE_SYSTEMD @@ -1018,10 +1021,11 @@ void watchfrr_status(struct vty *vty) else if (dmn->state == DAEMON_DOWN && time_elapsed(&delay, &dmn->restart.time)->tv_sec < dmn->restart.interval) - vty_out(vty, " restarting in %ld seconds" - " (%lds backoff interval)\n", - dmn->restart.interval - delay.tv_sec, - dmn->restart.interval); + vty_out(vty, " restarting in %jd seconds" + " (%jds backoff interval)\n", + (intmax_t)dmn->restart.interval + - (intmax_t)delay.tv_sec, + (intmax_t)dmn->restart.interval); } } diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index d0d11c867636..7b132cb61ed9 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -707,16 +707,16 @@ module frr-isisd { description "Log changes to the IS-IS adjacencies in this area."; } - } - container mpls-te { - presence "Present if MPLS-TE is enabled."; - description - "Enable MPLS-TE functionality."; - leaf router-address { - type inet:ipv4-address; + container mpls-te { + presence "Present if MPLS-TE is enabled."; description - "Stable IP address of the advertising router."; + "Enable MPLS-TE functionality."; + leaf router-address { + type inet:ipv4-address; + description + "Stable IP address of the advertising router."; + } } } } diff --git a/yang/libyang_plugins/subdir.am b/yang/libyang_plugins/subdir.am index fe5f34a28a03..837908a1b3b0 100644 --- a/yang/libyang_plugins/subdir.am +++ b/yang/libyang_plugins/subdir.am @@ -2,13 +2,6 @@ # libyang user types # -if LIBYANG_EXT_BUILTIN -lib_libfrr_la_SOURCES += yang/libyang_plugins/frr_user_types.c -else -libyang_plugins_LTLIBRARIES += yang/libyang_plugins/frr_user_types.la -endif - -yang_libyang_plugins_frr_user_types_la_CFLAGS = $(WERROR) $(LIBYANG_CFLAGS) -yang_libyang_plugins_frr_user_types_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -yang_libyang_plugins_frr_user_types_la_LIBADD = -yang_libyang_plugins_frr_user_types_la_SOURCES = yang/libyang_plugins/frr_user_types.c +# XXX: disable support for libyang custom user types temporarily to facilitate +# the transition from libyang 0.x to libyang 1.x. +#lib_libfrr_la_SOURCES += yang/libyang_plugins/frr_user_types.c diff --git a/zebra/connected.c b/zebra/connected.c index 7114a3286be1..bba221c2cf13 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -209,8 +209,16 @@ void connected_up(struct interface *ifp, struct connected *ifc) .ifindex = ifp->ifindex, .vrf_id = ifp->vrf_id, }; + struct zebra_vrf *zvrf; uint32_t metric; + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (!zvrf) { + flog_err(EC_ZEBRA_VRF_NOT_FOUND, + "%s: Received Up for interface but no associated zvrf: %d", + __PRETTY_FUNCTION__, ifp->vrf_id); + return; + } if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) return; @@ -246,11 +254,11 @@ void connected_up(struct interface *ifp, struct connected *ifc) metric = (ifc->metric < (uint32_t)METRIC_MAX) ? ifc->metric : ifp->metric; - rib_add(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, &p, - NULL, &nh, RT_TABLE_MAIN, metric, 0, 0, 0); + rib_add(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, + 0, 0, &p, NULL, &nh, zvrf->table_id, metric, 0, 0, 0); - rib_add(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, &p, - NULL, &nh, RT_TABLE_MAIN, metric, 0, 0, 0); + rib_add(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, + 0, 0, &p, NULL, &nh, zvrf->table_id, metric, 0, 0, 0); if (IS_ZEBRA_DEBUG_RIB_DETAILED) { char buf[PREFIX_STRLEN]; @@ -260,19 +268,19 @@ void connected_up(struct interface *ifp, struct connected *ifc) ifp->vrf_id, ifp->name, prefix2str(&p, buf, sizeof(buf))); } - rib_update(ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + rib_update(zvrf->vrf->vrf_id, RIB_UPDATE_IF_CHANGE); /* Schedule LSP forwarding entries for processing, if appropriate. */ - if (ifp->vrf_id == VRF_DEFAULT) { + if (zvrf->vrf->vrf_id == VRF_DEFAULT) { if (IS_ZEBRA_DEBUG_MPLS) { char buf[PREFIX_STRLEN]; zlog_debug( "%u: IF %s IP %s address add/up, scheduling MPLS processing", - ifp->vrf_id, ifp->name, + zvrf->vrf->vrf_id, ifp->name, prefix2str(&p, buf, sizeof(buf))); } - mpls_mark_lsps_for_processing(vrf_info_lookup(ifp->vrf_id), &p); + mpls_mark_lsps_for_processing(zvrf, &p); } } @@ -377,6 +385,15 @@ void connected_down(struct interface *ifp, struct connected *ifc) .ifindex = ifp->ifindex, .vrf_id = ifp->vrf_id, }; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (!zvrf) { + flog_err(EC_ZEBRA_VRF_NOT_FOUND, + "%s: Received Up for interface but no associated zvrf: %d", + __PRETTY_FUNCTION__, ifp->vrf_id); + return; + } if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) return; @@ -410,34 +427,34 @@ void connected_down(struct interface *ifp, struct connected *ifc) * Same logic as for connected_up(): push the changes into the * head. */ - rib_delete(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, &nh, 0, 0, 0, false); + rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, + 0, 0, &p, NULL, &nh, zvrf->table_id, 0, 0, false); - rib_delete(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, &nh, 0, 0, 0, false); + rib_delete(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, + 0, 0, &p, NULL, &nh, zvrf->table_id, 0, 0, false); if (IS_ZEBRA_DEBUG_RIB_DETAILED) { char buf[PREFIX_STRLEN]; zlog_debug( "%u: IF %s IP %s address down, scheduling RIB processing", - ifp->vrf_id, ifp->name, + zvrf->vrf->vrf_id, ifp->name, prefix2str(&p, buf, sizeof(buf))); } - rib_update(ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + rib_update(zvrf->vrf->vrf_id, RIB_UPDATE_IF_CHANGE); /* Schedule LSP forwarding entries for processing, if appropriate. */ - if (ifp->vrf_id == VRF_DEFAULT) { + if (zvrf->vrf->vrf_id == VRF_DEFAULT) { if (IS_ZEBRA_DEBUG_MPLS) { char buf[PREFIX_STRLEN]; zlog_debug( "%u: IF %s IP %s address down, scheduling MPLS processing", - ifp->vrf_id, ifp->name, + zvrf->vrf->vrf_id, ifp->name, prefix2str(&p, buf, sizeof(buf))); } - mpls_mark_lsps_for_processing(vrf_info_lookup(ifp->vrf_id), &p); + mpls_mark_lsps_for_processing(zvrf, &p); } } diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index ba518ea576fe..df8d4bfe1541 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -690,9 +690,6 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) ifp->speed = get_iflink_speed(ifp); ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; - if (desc) - ifp->desc = XSTRDUP(MTYPE_TMP, desc); - /* Set zebra interface type */ zebra_if_set_ziftype(ifp, zif_type, zif_slave_type); if (IS_ZEBRA_IF_VRF(ifp)) @@ -707,6 +704,11 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) zif = (struct zebra_if *)ifp->info; zif->link_ifindex = link_ifindex; + if (desc) { + XFREE(MTYPE_TMP, zif->desc); + zif->desc = XSTRDUP(MTYPE_TMP, desc); + } + /* Hardware type and address. */ ifp->ll_type = netlink_to_zebra_link_type(ifi->ifi_type); netlink_interface_update_hw_addr(tb, ifp); @@ -1106,7 +1108,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) ifindex_t bond_ifindex = IFINDEX_INTERNAL; ifindex_t link_ifindex = IFINDEX_INTERNAL; uint8_t old_hw_addr[INTERFACE_HWADDR_MAX]; - + struct zebra_if *zif; zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); @@ -1186,12 +1188,6 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* See if interface is present. */ ifp = if_lookup_by_name_per_ns(zns, name); - if (ifp) { - XFREE(MTYPE_TMP, ifp->desc); - if (desc) - ifp->desc = XSTRDUP(MTYPE_TMP, desc); - } - if (h->nlmsg_type == RTM_NEWLINK) { if (tb[IFLA_MASTER]) { if (slave_kind && (strcmp(slave_kind, "vrf") == 0) @@ -1390,9 +1386,42 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if_delete_update(ifp); } + zif = ifp->info; + if (zif) { + XFREE(MTYPE_TMP, zif->desc); + if (desc) + zif->desc = XSTRDUP(MTYPE_TMP, desc); + } + return 0; } +int netlink_protodown(struct interface *ifp, bool down) +{ + struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + + struct { + struct nlmsghdr n; + struct ifinfomsg ifa; + char buf[NL_PKT_BUF_SIZE]; + } req; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_SETLINK; + req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; + + req.ifa.ifi_index = ifp->ifindex; + + addattr_l(&req.n, sizeof(req), IFLA_PROTO_DOWN, &down, 4); + addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifp->ifindex, 4); + + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, + 0); +} + /* Interface information read by netlink. */ void interface_list(struct zebra_ns *zns) { diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index 710fd5255875..29fd2aca354d 100644 --- a/zebra/if_netlink.h +++ b/zebra/if_netlink.h @@ -32,6 +32,20 @@ extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int interface_lookup_netlink(struct zebra_ns *zns); +/* + * Set protodown status of interface. + * + * ifp + * Interface to set protodown on. + * + * down + * If true, set protodown on. If false, set protodown off. + * + * Returns: + * 0 + */ +int netlink_protodown(struct interface *ifp, bool down); + #ifdef __cplusplus } #endif diff --git a/zebra/interface.c b/zebra/interface.c index 10f1f9210052..13582008a702 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -47,6 +47,7 @@ #include "zebra/irdp.h" #include "zebra/zebra_ptm.h" #include "zebra/rt_netlink.h" +#include "zebra/if_netlink.h" #include "zebra/interface.h" #include "zebra/zebra_vxlan.h" #include "zebra/zebra_errors.h" @@ -181,6 +182,7 @@ static int if_zebra_delete_hook(struct interface *ifp) list_delete(&rtadv->AdvDNSSLList); #endif /* HAVE_RTADV */ + XFREE(MTYPE_TMP, zebra_if->desc); THREAD_OFF(zebra_if->speed_update); XFREE(MTYPE_ZINFO, zebra_if); @@ -1062,7 +1064,14 @@ void zebra_if_update_all_links(void) } } - +void zebra_if_set_protodown(struct interface *ifp, bool down) +{ +#ifdef HAVE_NETLINK + netlink_protodown(ifp, down); +#else + zlog_warn("Protodown is not supported on this platform"); +#endif +} /* Output prefix string to vty. */ static int prefix_vty_out(struct vty *vty, struct prefix *p) @@ -1303,6 +1312,9 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) if (ifp->desc) vty_out(vty, " Description: %s\n", ifp->desc); + if (zebra_if->desc) + vty_out(vty, " OS Description: %s\n", zebra_if->desc); + if (ifp->ifindex == IFINDEX_INTERNAL) { vty_out(vty, " pseudo interface\n"); return; @@ -1696,6 +1708,10 @@ static void if_show_description(struct vty *vty, vrf_id_t vrf_id) vty_out(vty, "Interface Status Protocol Description\n"); FOR_ALL_INTERFACES (vrf, ifp) { int len; + struct zebra_if *zif; + bool intf_desc; + + intf_desc = false; len = vty_out(vty, "%s", ifp->name); vty_out(vty, "%*s", (16 - len), " "); @@ -1715,8 +1731,19 @@ static void if_show_description(struct vty *vty, vrf_id_t vrf_id) vty_out(vty, "down down "); } - if (ifp->desc) + if (ifp->desc) { + intf_desc = true; vty_out(vty, "%s", ifp->desc); + } + zif = ifp->info; + if (zif && zif->desc) { + vty_out(vty, "%s%s", + intf_desc + ? "\n " + : "", + zif->desc); + } + vty_out(vty, "\n"); } } diff --git a/zebra/interface.h b/zebra/interface.h index ce404e825372..6a3914451aa7 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -342,6 +342,9 @@ struct zebra_if { bool v6_2_v4_ll_neigh_entry; char neigh_mac[6]; struct in6_addr v6_2_v4_ll_addr6; + + /* The description of the interface */ + char *desc; }; DECLARE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp), @@ -419,6 +422,7 @@ extern void if_handle_vrf_change(struct interface *ifp, vrf_id_t vrf_id); extern void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex, ns_id_t ns_id); extern void zebra_if_update_all_links(void); +extern void zebra_if_set_protodown(struct interface *ifp, bool down); extern void vrf_add_update(struct vrf *vrfp); diff --git a/zebra/ioctl.c b/zebra/ioctl.c index 322527015bd5..8202e076afb4 100644 --- a/zebra/ioctl.c +++ b/zebra/ioctl.c @@ -245,7 +245,7 @@ static int if_set_prefix_ctx(const struct zebra_dplane_ctx *ctx) p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx); memset(&addreq, 0, sizeof(addreq)); - strncpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx), + strlcpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx), sizeof(addreq.ifra_name)); memset(&addr, 0, sizeof(struct sockaddr_in)); @@ -296,7 +296,7 @@ static int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx) p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx); memset(&addreq, 0, sizeof(addreq)); - strncpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx), + strlcpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx), sizeof(addreq.ifra_name)); memset(&addr, 0, sizeof(struct sockaddr_in)); diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c index ccfa7a4a4c38..1f96fa23ea3a 100644 --- a/zebra/ioctl_solaris.c +++ b/zebra/ioctl_solaris.c @@ -286,7 +286,7 @@ static int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx) p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx); - strncpy(ifreq.ifr_name, dplane_ctx_get_ifname(ctx), + strlcpy(ifreq.ifr_name, dplane_ctx_get_ifname(ctx), sizeof(ifreq.ifr_name)); memset(&addr, 0, sizeof(struct sockaddr_in)); diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index fe37a333581e..387a3531bd87 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -340,8 +340,7 @@ static void netlink_write_incoming(const char *buf, const unsigned int size, char fname[MAXPATHLEN]; FILE *f; - snprintf(fname, MAXPATHLEN, "%s/%s_%u", DAEMON_VTY_DIR, "netlink", - counter); + snprintf(fname, MAXPATHLEN, "%s/%s_%u", frr_vtydir, "netlink", counter); frr_elevate_privs(&zserv_privs) { f = fopen(fname, "w"); } diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 13d2185b0fa0..156ce50725ec 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -304,12 +304,13 @@ size_t rta_getattr(caddr_t sap, void *destp, size_t destlen) size_t rta_getsdlname(caddr_t sap, void *destp, short *destlen) { struct sockaddr_dl *sdl = (struct sockaddr_dl *)sap; - struct sockaddr *sa = (struct sockaddr *)sap; uint8_t *dest = destp; size_t tlen, copylen; copylen = sdl->sdl_nlen; #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + struct sockaddr *sa = (struct sockaddr *)sap; + tlen = (sa->sa_len == 0) ? sizeof(ROUNDUP_TYPE) : ROUNDUP(sa->sa_len); #else /* !HAVE_STRUCT_SOCKADDR_SA_LEN */ tlen = SAROUNDUP(sap); @@ -522,7 +523,7 @@ static enum zebra_link_type sdl_to_zebra_link_type(unsigned int sdlt) int ifm_read(struct if_msghdr *ifm) { struct interface *ifp = NULL; - struct sockaddr_dl *sdl; + struct sockaddr_dl *sdl = NULL; char ifname[IFNAMSIZ]; short ifnlen = 0; int maskbit; @@ -1138,14 +1139,16 @@ void rtm_read(struct rt_msghdr *rtm) */ if (rtm->rtm_type == RTM_CHANGE) rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, - 0, zebra_flags, &p, NULL, NULL, 0, 0, 0, true); + 0, zebra_flags, &p, NULL, NULL, RT_TABLE_MAIN, + 0, 0, true); if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) rib_add(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, - zebra_flags, &p, NULL, &nh, 0, 0, 0, 0, 0); + zebra_flags, &p, NULL, &nh, RT_TABLE_MAIN, 0, 0, 0, 0); else rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, - 0, zebra_flags, &p, NULL, &nh, 0, 0, 0, true); + 0, zebra_flags, &p, NULL, &nh, RT_TABLE_MAIN, + 0, 0, true); } /* Interface function for the kernel routing table updates. Support diff --git a/zebra/main.c b/zebra/main.c index 184e798bd070..5797a5846aa8 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -74,8 +74,7 @@ int retain_mode = 0; /* Allow non-quagga entities to delete quagga routes */ int allow_delete = 0; -/* Don't delete kernel route. */ -int keep_kernel_mode = 0; +int graceful_restart; bool v6_rr_semantics = false; @@ -95,6 +94,7 @@ struct option longopts[] = { {"label_socket", no_argument, NULL, 'l'}, {"retain", no_argument, NULL, 'r'}, {"vrfdefaultname", required_argument, NULL, 'o'}, + {"graceful_restart", required_argument, NULL, 'K'}, #ifdef HAVE_NETLINK {"vrfwnetns", no_argument, NULL, 'n'}, {"nl-bufsize", required_argument, NULL, 's'}, @@ -119,8 +119,6 @@ struct zebra_privs_t zserv_privs = { .cap_num_p = array_size(_caps_p), .cap_num_i = 0}; -unsigned int multipath_num = MULTIPATH_NUM; - /* SIGHUP handler. */ static void sighup(void) { @@ -264,13 +262,14 @@ int main(int argc, char **argv) char *netlink_fuzzing = NULL; #endif /* HANDLE_NETLINK_FUZZING */ + graceful_restart = 0; vrf_configure_backend(VRF_BACKEND_VRF_LITE); logicalrouter_configure_backend(LOGICALROUTER_BACKEND_NETNS); frr_preinit(&zebra_di, argc, argv); frr_opt_add( - "bakz:e:l:o:r" + "baz:e:l:o:rK:" #ifdef HAVE_NETLINK "s:n" #endif @@ -282,24 +281,24 @@ int main(int argc, char **argv) #endif /* HANDLE_NETLINK_FUZZING */ , longopts, - " -b, --batch Runs in batch mode\n" - " -a, --allow_delete Allow other processes to delete zebra routes\n" - " -z, --socket Set path of zebra socket\n" - " -e, --ecmp Specify ECMP to use.\n" - " -l, --label_socket Socket to external label manager\n" - " -k, --keep_kernel Don't delete old routes which were installed by zebra.\n" - " -r, --retain When program terminates, retain added route by zebra.\n" - " -o, --vrfdefaultname Set default VRF name.\n" + " -b, --batch Runs in batch mode\n" + " -a, --allow_delete Allow other processes to delete zebra routes\n" + " -z, --socket Set path of zebra socket\n" + " -e, --ecmp Specify ECMP to use.\n" + " -l, --label_socket Socket to external label manager\n" + " -r, --retain When program terminates, retain added route by zebra.\n" + " -o, --vrfdefaultname Set default VRF name.\n" + " -K, --graceful_restart Graceful restart at the kernel level, timer in seconds for expiration\n" #ifdef HAVE_NETLINK - " -n, --vrfwnetns Use NetNS as VRF backend\n" - " -s, --nl-bufsize Set netlink receive buffer size\n" - " --v6-rr-semantics Use v6 RR semantics\n" + " -n, --vrfwnetns Use NetNS as VRF backend\n" + " -s, --nl-bufsize Set netlink receive buffer size\n" + " --v6-rr-semantics Use v6 RR semantics\n" #endif /* HAVE_NETLINK */ #if defined(HANDLE_ZAPI_FUZZING) - " -c Bypass normal startup and use this file for testing of zapi\n" + " -c Bypass normal startup and use this file for testing of zapi\n" #endif /* HANDLE_ZAPI_FUZZING */ #if defined(HANDLE_NETLINK_FUZZING) - " -w Bypass normal startup and use this file for testing of netlink input\n" + " -w Bypass normal startup and use this file for testing of netlink input\n" #endif /* HANDLE_NETLINK_FUZZING */ ); @@ -318,13 +317,10 @@ int main(int argc, char **argv) case 'a': allow_delete = 1; break; - case 'k': - keep_kernel_mode = 1; - break; case 'e': - multipath_num = atoi(optarg); - if (multipath_num > MULTIPATH_NUM - || multipath_num <= 0) { + zrouter.multipath_num = atoi(optarg); + if (zrouter.multipath_num > MULTIPATH_NUM + || zrouter.multipath_num <= 0) { flog_err( EC_ZEBRA_BAD_MULTIPATH_NUM, "Multipath Number specified must be less than %d and greater than 0", @@ -350,6 +346,9 @@ int main(int argc, char **argv) case 'r': retain_mode = 1; break; + case 'K': + graceful_restart = atoi(optarg); + break; #ifdef HAVE_NETLINK case 's': nl_rcvbufsize = atoi(optarg); @@ -437,8 +436,9 @@ int main(int argc, char **argv) * will be equal to the current getpid(). To know about such routes, * we have to have route_read() called before. */ - if (!keep_kernel_mode) - rib_sweep_route(); + zrouter.startup_time = monotime(NULL); + thread_add_timer(zrouter.master, rib_sweep_route, + NULL, graceful_restart, NULL); /* Needed for BSD routing socket. */ pid = getpid(); diff --git a/zebra/redistribute.c b/zebra/redistribute.c index f98a4c02c320..b13f1170cd5d 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -612,9 +612,9 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, newre->flags = re->flags; newre->metric = re->metric; newre->mtu = re->mtu; - newre->table = zrouter.rtm_table_default; + newre->table = 0; newre->nexthop_num = 0; - newre->uptime = time(NULL); + newre->uptime = monotime(NULL); newre->instance = re->table; route_entry_copy_nexthops(newre, re->ng.nexthop); @@ -632,8 +632,8 @@ int zebra_del_import_table_entry(struct route_node *rn, struct route_entry *re) prefix_copy(&p, &rn->p); rib_delete(afi, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE, re->table, - re->flags, &p, NULL, re->ng.nexthop, - zrouter.rtm_table_default, re->metric, re->distance, false); + re->flags, &p, NULL, re->ng.nexthop, 0, re->metric, + re->distance, false); return 0; } @@ -647,14 +647,14 @@ int zebra_import_table(afi_t afi, uint32_t table_id, uint32_t distance, struct route_node *rn; if (!is_zebra_valid_kernel_table(table_id) - || ((table_id == RT_TABLE_MAIN) - || (table_id == zrouter.rtm_table_default))) + || (table_id == RT_TABLE_MAIN)) return (-1); if (afi >= AFI_MAX) return (-1); - table = zebra_vrf_other_route_table(afi, table_id, VRF_DEFAULT); + table = zebra_vrf_table_with_table_id(afi, SAFI_UNICAST, + table_id, VRF_DEFAULT); if (table == NULL) { return 0; } else if (IS_ZEBRA_DEBUG_RIB) { @@ -768,8 +768,15 @@ void zebra_import_table_rm_update(const char *rmap) rmap_name = zebra_get_import_table_route_map(afi, i); if ((!rmap_name) || (strcmp(rmap_name, rmap) != 0)) continue; - table = zebra_vrf_other_route_table(afi, i, - VRF_DEFAULT); + table = zebra_vrf_table_with_table_id(afi, SAFI_UNICAST, + i, VRF_DEFAULT); + if (!table) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("%s: Table id=%d not found", + __func__, i); + continue; + } + for (rn = route_top(table); rn; rn = route_next(rn)) { /* For each entry in the non-default * routing table, diff --git a/zebra/rib.h b/zebra/rib.h index e26831e1a642..292f6bc60042 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -24,6 +24,7 @@ #include "zebra.h" #include "hook.h" +#include "typesafe.h" #include "linklist.h" #include "prefix.h" #include "table.h" @@ -39,17 +40,59 @@ extern "C" { #endif +typedef enum { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE } rnh_type_t; + +PREDECL_LIST(rnh_list) + +/* Nexthop structure. */ +struct rnh { + uint8_t flags; + +#define ZEBRA_NHT_CONNECTED 0x1 +#define ZEBRA_NHT_DELETED 0x2 +#define ZEBRA_NHT_EXACT_MATCH 0x4 + + /* VRF identifier. */ + vrf_id_t vrf_id; + + afi_t afi; + + rnh_type_t type; + + uint32_t seqno; + + struct route_entry *state; + struct prefix resolved_route; + struct list *client_list; + + /* pseudowires dependent on this nh */ + struct list *zebra_pseudowire_list; + + struct route_node *node; + + /* + * if this has been filtered for the client + */ + int filtered[ZEBRA_ROUTE_MAX]; + + struct rnh_list_item rnh_list_item; +}; + #define DISTANCE_INFINITY 255 #define ZEBRA_KERNEL_TABLE_MAX 252 /* support for no more than this rt tables */ +PREDECL_LIST(re_list) + struct route_entry { /* Link list. */ - struct route_entry *next; - struct route_entry *prev; + struct re_list_item next; - /* Nexthop structure */ + /* Nexthop structure (from RIB) */ struct nexthop_group ng; + /* Nexthop group from FIB (optional) */ + struct nexthop_group fib_ng; + /* Tag */ route_tag_t tag; @@ -59,9 +102,6 @@ struct route_entry { /* Type fo this route. */ int type; - /* Source protocol instance */ - unsigned short instance; - /* VRF identifier. */ vrf_id_t vrf_id; @@ -75,9 +115,6 @@ struct route_entry { uint32_t mtu; uint32_t nexthop_mtu; - /* Distance. */ - uint8_t distance; - /* Flags of this route. * This flag's definition is in lib/zebra.h ZEBRA_FLAG_* and is exposed * to clients via Zserv @@ -106,8 +143,18 @@ struct route_entry { /* Sequence value incremented for each dataplane operation */ uint32_t dplane_sequence; + + /* Source protocol instance */ + uint16_t instance; + + /* Distance. */ + uint8_t distance; }; +#define RIB_SYSTEM_ROUTE(R) RSYSTEM_ROUTE((R)->type) + +#define RIB_KERNEL_ROUTE(R) RKERNEL_ROUTE((R)->type) + /* meta-queue structure: * sub-queue 0: connected, kernel * sub-queue 1: static @@ -135,7 +182,7 @@ typedef struct rib_dest_t_ { /* * Doubly-linked list of routes for this prefix. */ - struct route_entry *routes; + struct re_list_head routes; struct route_entry *selected_fib; @@ -151,7 +198,7 @@ typedef struct rib_dest_t_ { * the data plane we will run evaluate_rnh * on these prefixes. */ - struct list *nht; + struct rnh_list_head nht; /* * Linkage to put dest on the FPM processing queue. @@ -160,6 +207,9 @@ typedef struct rib_dest_t_ { } rib_dest_t; +DECLARE_LIST(rnh_list, struct rnh, rnh_list_item); +DECLARE_LIST(re_list, struct route_entry, next); + #define RIB_ROUTE_QUEUED(x) (1 << (x)) // If MQ_SIZE is modified this value needs to be updated. #define RIB_ROUTE_ANY_QUEUED 0x1F @@ -187,14 +237,16 @@ typedef struct rib_dest_t_ { * Macro to iterate over each route for a destination (prefix). */ #define RE_DEST_FOREACH_ROUTE(dest, re) \ - for ((re) = (dest) ? (dest)->routes : NULL; (re); (re) = (re)->next) + for ((re) = (dest) ? re_list_first(&((dest)->routes)) : NULL; (re); \ + (re) = re_list_next(&((dest)->routes), (re))) /* * Same as above, but allows the current node to be unlinked. */ #define RE_DEST_FOREACH_ROUTE_SAFE(dest, re, next) \ - for ((re) = (dest) ? (dest)->routes : NULL; \ - (re) && ((next) = (re)->next, 1); (re) = (next)) + for ((re) = (dest) ? re_list_first(&((dest)->routes)) : NULL; \ + (re) && ((next) = re_list_next(&((dest)->routes), (re)), 1); \ + (re) = (next)) #define RNODE_FOREACH_RE(rn, re) \ RE_DEST_FOREACH_ROUTE (rib_dest_from_rnode(rn), re) @@ -288,19 +340,6 @@ extern void route_entry_copy_nexthops(struct route_entry *re, extern void _route_entry_dump(const char *func, union prefixconstptr pp, union prefixconstptr src_pp, const struct route_entry *re); -/* RPF lookup behaviour */ -enum multicast_mode { - MCAST_NO_CONFIG = 0, /* MIX_MRIB_FIRST, but no show in config write */ - MCAST_MRIB_ONLY, /* MRIB only */ - MCAST_URIB_ONLY, /* URIB only */ - MCAST_MIX_MRIB_FIRST, /* MRIB, if nothing at all then URIB */ - MCAST_MIX_DISTANCE, /* MRIB & URIB, lower distance wins */ - MCAST_MIX_PFXLEN, /* MRIB & URIB, longer prefix wins */ - /* on equal value, MRIB wins for last 2 */ -}; - -extern void multicast_mode_ipv4_set(enum multicast_mode mode); -extern enum multicast_mode multicast_mode_ipv4_get(void); extern void rib_lookup_and_dump(struct prefix_ipv4 *p, vrf_id_t vrf_id); extern void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id); @@ -351,7 +390,7 @@ extern struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, extern void rib_update(vrf_id_t vrf_id, rib_update_event_t event); extern void rib_update_table(struct route_table *table, rib_update_event_t event); -extern void rib_sweep_route(void); +extern int rib_sweep_route(struct thread *t); extern void rib_sweep_table(struct route_table *table); extern void rib_close_table(struct route_table *table); extern void rib_init(void); @@ -406,7 +445,7 @@ static inline struct route_entry *rnode_to_ribs(struct route_node *rn) if (!dest) return NULL; - return dest->routes; + return re_list_first(&dest->routes); } /* @@ -480,6 +519,16 @@ static inline void rib_tables_iter_cleanup(rib_tables_iter_t *iter) DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason), (rn, reason)) +/* + * Access active nexthop-group, either RIB or FIB version + */ +static inline struct nexthop_group *rib_active_nhg(struct route_entry *re) +{ + if (re->fib_ng.nexthop) + return &(re->fib_ng); + else + return &(re->ng); +} extern void zebra_vty_init(void); diff --git a/zebra/rt.h b/zebra/rt.h index 08b51fcc0bcf..04576671fe54 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -35,8 +35,10 @@ extern "C" { #endif -#define RSYSTEM_ROUTE(type) \ - ((type) == ZEBRA_ROUTE_KERNEL || (type) == ZEBRA_ROUTE_CONNECT) +#define RKERNEL_ROUTE(type) ((type) == ZEBRA_ROUTE_KERNEL) + +#define RSYSTEM_ROUTE(type) \ + ((RKERNEL_ROUTE(type)) || (type) == ZEBRA_ROUTE_CONNECT) /* * Update or delete a route, LSP, or pseudowire from the kernel, diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 289ed5a15b14..92c78a4cbb14 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -583,7 +583,7 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, re->vrf_id = vrf_id; re->table = table; re->nexthop_num = 0; - re->uptime = time(NULL); + re->uptime = monotime(NULL); re->tag = tag; for (;;) { @@ -813,7 +813,7 @@ static int netlink_route_change_read_multicast(struct nlmsghdr *h, sprintf(temp, "%s(%d) ", ifp ? ifp->name : "Unknown", oif[count]); - strcat(oif_list, temp); + strlcat(oif_list, temp, sizeof(oif_list)); } struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(vrf); ifp = if_lookup_by_index(iif, vrf); diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 5088e2e8e1f6..e181b495b8dd 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -42,7 +42,6 @@ #include "zebra/debug.h" #include "zebra/rib.h" #include "zebra/zapi_msg.h" -#include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_router.h" @@ -81,18 +80,25 @@ enum rtadv_event { RTADV_READ }; -static void rtadv_event(struct zebra_ns *, enum rtadv_event, int); +static void rtadv_event(struct zebra_vrf *, enum rtadv_event, int); static int if_join_all_router(int, struct interface *); static int if_leave_all_router(int, struct interface *); -static int rtadv_increment_received(struct zebra_ns *zns, ifindex_t *ifindex) +static int rtadv_get_socket(struct zebra_vrf *zvrf) +{ + if (zvrf->rtadv.sock > 0) + return zvrf->rtadv.sock; + return zrouter.rtadv_sock; +} + +static int rtadv_increment_received(struct zebra_vrf *zvrf, ifindex_t *ifindex) { int ret = -1; struct interface *iface; struct zebra_if *zif; - iface = if_lookup_by_index_per_ns(zns, *ifindex); + iface = if_lookup_by_index(*ifindex, zvrf->vrf->vrf_id); if (iface && iface->info) { zif = iface->info; zif->ra_rcvd++; @@ -101,7 +107,7 @@ static int rtadv_increment_received(struct zebra_ns *zns, ifindex_t *ifindex) return ret; } -static int rtadv_recv_packet(struct zebra_ns *zns, int sock, uint8_t *buf, +static int rtadv_recv_packet(struct zebra_vrf *zvrf, int sock, uint8_t *buf, int buflen, struct sockaddr_in6 *from, ifindex_t *ifindex, int *hoplimit) { @@ -149,7 +155,7 @@ static int rtadv_recv_packet(struct zebra_ns *zns, int sock, uint8_t *buf, } } - rtadv_increment_received(zns, ifindex); + rtadv_increment_received(zvrf, ifindex); return ret; } @@ -461,19 +467,19 @@ static void rtadv_send_packet(int sock, struct interface *ifp) static int rtadv_timer(struct thread *thread) { - struct zebra_ns *zns = THREAD_ARG(thread); + struct zebra_vrf *zvrf = THREAD_ARG(thread); struct vrf *vrf; struct interface *ifp; struct zebra_if *zif; int period; - zrouter.rtadv.ra_timer = NULL; - if (zrouter.rtadv.adv_msec_if_count == 0) { + zvrf->rtadv.ra_timer = NULL; + if (zvrf->rtadv.adv_msec_if_count == 0) { period = 1000; /* 1 s */ - rtadv_event(zns, RTADV_TIMER, 1 /* 1 s */); + rtadv_event(zvrf, RTADV_TIMER, 1 /* 1 s */); } else { period = 10; /* 10 ms */ - rtadv_event(zns, RTADV_TIMER_MSEC, 10 /* 10 ms */); + rtadv_event(zvrf, RTADV_TIMER_MSEC, 10 /* 10 ms */); } RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) @@ -500,7 +506,7 @@ static int rtadv_timer(struct thread *thread) "Fast RA Rexmit on interface %s", ifp->name); - rtadv_send_packet(zrouter.rtadv.sock, + rtadv_send_packet(rtadv_get_socket(zvrf), ifp); } else { zif->rtadv.AdvIntervalTimer -= period; @@ -514,8 +520,8 @@ static int rtadv_timer(struct thread *thread) zif->rtadv .MaxRtrAdvInterval; rtadv_send_packet( - zrouter.rtadv.sock, - ifp); + rtadv_get_socket(zvrf), + ifp); } } } @@ -527,10 +533,9 @@ static int rtadv_timer(struct thread *thread) static void rtadv_process_solicit(struct interface *ifp) { struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id); - struct zebra_ns *zns = zvrf->zns; - assert(zns); - rtadv_send_packet(zrouter.rtadv.sock, ifp); + assert(zvrf); + rtadv_send_packet(rtadv_get_socket(zvrf), ifp); } /* @@ -652,7 +657,7 @@ static void rtadv_process_advert(uint8_t *msg, unsigned int len, static void rtadv_process_packet(uint8_t *buf, unsigned int len, ifindex_t ifindex, int hoplimit, struct sockaddr_in6 *from, - struct zebra_ns *zns) + struct zebra_vrf *zvrf) { struct icmp6_hdr *icmph; struct interface *ifp; @@ -662,7 +667,7 @@ static void rtadv_process_packet(uint8_t *buf, unsigned int len, inet_ntop(AF_INET6, &from->sin6_addr, addr_str, INET6_ADDRSTRLEN); /* Interface search. */ - ifp = if_lookup_by_index_per_ns(zns, ifindex); + ifp = if_lookup_by_index(ifindex, zvrf->vrf->vrf_id); if (ifp == NULL) { flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE, "RA/RS received on unknown IF %u from %s", ifindex, @@ -724,15 +729,15 @@ static int rtadv_read(struct thread *thread) struct sockaddr_in6 from; ifindex_t ifindex = 0; int hoplimit = -1; - struct zebra_ns *zns = THREAD_ARG(thread); + struct zebra_vrf *zvrf = THREAD_ARG(thread); sock = THREAD_FD(thread); - zrouter.rtadv.ra_read = NULL; + zvrf->rtadv.ra_read = NULL; /* Register myself. */ - rtadv_event(zns, RTADV_READ, sock); + rtadv_event(zvrf, RTADV_READ, sock); - len = rtadv_recv_packet(zns, sock, buf, sizeof(buf), &from, &ifindex, + len = rtadv_recv_packet(zvrf, sock, buf, sizeof(buf), &from, &ifindex, &hoplimit); if (len < 0) { @@ -742,7 +747,7 @@ static int rtadv_read(struct thread *thread) return len; } - rtadv_process_packet(buf, (unsigned)len, ifindex, hoplimit, &from, zns); + rtadv_process_packet(buf, (unsigned)len, ifindex, hoplimit, &from, zvrf); return 0; } @@ -875,29 +880,27 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp, { struct zebra_if *zif; struct zebra_vrf *zvrf; - struct zebra_ns *zns; zif = ifp->info; zvrf = vrf_info_lookup(ifp->vrf_id); - zns = zvrf->zns; if (status == RA_SUPPRESS) { /* RA is currently enabled */ if (zif->rtadv.AdvSendAdvertisements) { zif->rtadv.AdvSendAdvertisements = 0; zif->rtadv.AdvIntervalTimer = 0; - zrouter.rtadv.adv_if_count--; + zvrf->rtadv.adv_if_count--; - if_leave_all_router(zrouter.rtadv.sock, ifp); + if_leave_all_router(rtadv_get_socket(zvrf), ifp); - if (zrouter.rtadv.adv_if_count == 0) - rtadv_event(zns, RTADV_STOP, 0); + if (zvrf->rtadv.adv_if_count == 0) + rtadv_event(zvrf, RTADV_STOP, 0); } } else { if (!zif->rtadv.AdvSendAdvertisements) { zif->rtadv.AdvSendAdvertisements = 1; zif->rtadv.AdvIntervalTimer = 0; - zrouter.rtadv.adv_if_count++; + zvrf->rtadv.adv_if_count++; if (zif->rtadv.MaxRtrAdvInterval >= 1000) { /* Enable Fast RA only when RA interval is in @@ -907,11 +910,11 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp, RTADV_NUM_FAST_REXMITS; } - if_join_all_router(zrouter.rtadv.sock, ifp); + if_join_all_router(rtadv_get_socket(zvrf), ifp); - if (zrouter.rtadv.adv_if_count == 1) - rtadv_event(zns, RTADV_START, - zrouter.rtadv.sock); + if (zvrf->rtadv.adv_if_count == 1) + rtadv_event(zvrf, RTADV_START, + rtadv_get_socket(zvrf)); } } } @@ -944,7 +947,7 @@ static void zebra_interface_radv_set(ZAPI_HANDLER_ARGS, int enable) zebra_route_string(client->proto), ra_interval); /* Locate interface and check VRF match. */ - ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), ifindex); + ifp = if_lookup_by_index(ifindex, zvrf->vrf->vrf_id); if (!ifp) { flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE, "%u: IF %u RA %s client %s - interface unknown", @@ -1051,6 +1054,9 @@ DEFUN (ipv6_nd_ra_interval_msec, VTY_DECLVAR_CONTEXT(interface, ifp); unsigned interval; struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup(ifp->vrf_id); interval = strtoul(argv[idx_number]->arg, NULL, 10); if ((zif->rtadv.AdvDefaultLifetime != -1 @@ -1061,10 +1067,10 @@ DEFUN (ipv6_nd_ra_interval_msec, } if (zif->rtadv.MaxRtrAdvInterval % 1000) - zrouter.rtadv.adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; if (interval % 1000) - zrouter.rtadv.adv_msec_if_count++; + zvrf->rtadv.adv_msec_if_count++; SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); zif->rtadv.MaxRtrAdvInterval = interval; @@ -1086,6 +1092,9 @@ DEFUN (ipv6_nd_ra_interval, VTY_DECLVAR_CONTEXT(interface, ifp); unsigned interval; struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup(ifp->vrf_id); interval = strtoul(argv[idx_number]->arg, NULL, 10); if ((zif->rtadv.AdvDefaultLifetime != -1 @@ -1096,7 +1105,7 @@ DEFUN (ipv6_nd_ra_interval, } if (zif->rtadv.MaxRtrAdvInterval % 1000) - zrouter.rtadv.adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; /* convert to milliseconds */ interval = interval * 1000; @@ -1122,9 +1131,12 @@ DEFUN (no_ipv6_nd_ra_interval, { VTY_DECLVAR_CONTEXT(interface, ifp); struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf = NULL; + + zvrf = vrf_info_lookup(ifp->vrf_id); if (zif->rtadv.MaxRtrAdvInterval % 1000) - zrouter.rtadv.adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); @@ -2094,15 +2106,15 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp) } -static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val) +static void rtadv_event(struct zebra_vrf *zvrf, enum rtadv_event event, int val) { - struct rtadv *rtadv = &zrouter.rtadv; + struct rtadv *rtadv = &zvrf->rtadv; switch (event) { case RTADV_START: - thread_add_read(zrouter.master, rtadv_read, zns, val, + thread_add_read(zrouter.master, rtadv_read, zvrf, val, &rtadv->ra_read); - thread_add_event(zrouter.master, rtadv_timer, zns, 0, + thread_add_event(zrouter.master, rtadv_timer, zvrf, 0, &rtadv->ra_timer); break; case RTADV_STOP: @@ -2116,15 +2128,15 @@ static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val) } break; case RTADV_TIMER: - thread_add_timer(zrouter.master, rtadv_timer, zns, val, + thread_add_timer(zrouter.master, rtadv_timer, zvrf, val, &rtadv->ra_timer); break; case RTADV_TIMER_MSEC: - thread_add_timer_msec(zrouter.master, rtadv_timer, zns, val, + thread_add_timer_msec(zrouter.master, rtadv_timer, zvrf, val, &rtadv->ra_timer); break; case RTADV_READ: - thread_add_read(zrouter.master, rtadv_read, zns, val, + thread_add_read(zrouter.master, rtadv_read, zvrf, val, &rtadv->ra_read); break; default: @@ -2133,21 +2145,30 @@ static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val) return; } -void rtadv_init(struct zebra_ns *zns) +void rtadv_init(struct zebra_vrf *zvrf) { - zrouter.rtadv.sock = rtadv_make_socket(zns->ns_id); + if (vrf_is_backend_netns()) { + zvrf->rtadv.sock = rtadv_make_socket(zvrf->zns->ns_id); + zrouter.rtadv_sock = -1; + } else if (!zrouter.rtadv_sock) { + zvrf->rtadv.sock = -1; + if (!zrouter.rtadv_sock) + zrouter.rtadv_sock = rtadv_make_socket(zvrf->zns->ns_id); + } } -void rtadv_terminate(struct zebra_ns *zns) +void rtadv_terminate(struct zebra_vrf *zvrf) { - rtadv_event(zns, RTADV_STOP, 0); - if (zrouter.rtadv.sock >= 0) { - close(zrouter.rtadv.sock); - zrouter.rtadv.sock = -1; + rtadv_event(zvrf, RTADV_STOP, 0); + if (zvrf->rtadv.sock >= 0) { + close(zvrf->rtadv.sock); + zvrf->rtadv.sock = -1; + } else if (zrouter.rtadv_sock >= 0) { + close(zrouter.rtadv_sock); + zrouter.rtadv_sock = -1; } - - zrouter.rtadv.adv_if_count = 0; - zrouter.rtadv.adv_msec_if_count = 0; + zvrf->rtadv.adv_if_count = 0; + zvrf->rtadv.adv_msec_if_count = 0; } void rtadv_cmd_init(void) @@ -2243,11 +2264,11 @@ static int if_leave_all_router(int sock, struct interface *ifp) } #else -void rtadv_init(struct zebra_ns *zns) +void rtadv_init(struct zebra_vrf *zvrf) { /* Empty.*/; } -void rtadv_terminate(struct zebra_ns *zns) +void rtadv_terminate(struct zebra_vrf *zvrf) { /* Empty.*/; } diff --git a/zebra/rtadv.h b/zebra/rtadv.h index 53c497fc09c4..d692ef241748 100644 --- a/zebra/rtadv.h +++ b/zebra/rtadv.h @@ -135,8 +135,8 @@ typedef enum { RA_SUPPRESS, } ipv6_nd_suppress_ra_status; -extern void rtadv_init(struct zebra_ns *); -extern void rtadv_terminate(struct zebra_ns *); +extern void rtadv_init(struct zebra_vrf *zvrf); +extern void rtadv_terminate(struct zebra_vrf *zvrf); extern void rtadv_cmd_init(void); extern void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS); extern void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS); diff --git a/zebra/subdir.am b/zebra/subdir.am index 1e36d020a323..25040a2717dd 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -75,6 +75,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_mpls_null.c \ zebra/zebra_mpls_vty.c \ zebra/zebra_mroute.c \ + zebra/zebra_nhg.c \ zebra/zebra_ns.c \ zebra/zebra_pbr.c \ zebra/zebra_ptm.c \ @@ -135,6 +136,7 @@ noinst_HEADERS += \ zebra/zebra_memory.h \ zebra/zebra_mpls.h \ zebra/zebra_mroute.h \ + zebra/zebra_nhg.h \ zebra/zebra_ns.h \ zebra/zebra_pbr.h \ zebra/zebra_ptm.h \ diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index f31fb53a3430..61200806ba84 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -71,6 +71,8 @@ static void zserv_encode_interface(struct stream *s, struct interface *ifp) { /* Interface information. */ + struct zebra_if *zif = ifp->info; + stream_put(s, ifp->name, INTERFACE_NAMSIZ); stream_putl(s, ifp->ifindex); stream_putc(s, ifp->status); @@ -82,6 +84,7 @@ static void zserv_encode_interface(struct stream *s, struct interface *ifp) stream_putl(s, ifp->mtu); stream_putl(s, ifp->mtu6); stream_putl(s, ifp->bandwidth); + stream_putl(s, zif->link_ifindex); stream_putl(s, ifp->ll_type); stream_putl(s, ifp->hw_addr_len); if (ifp->hw_addr_len) @@ -521,6 +524,8 @@ int zsend_redistribute_route(int cmd, struct zserv *client, struct nexthop *nexthop; int count = 0; afi_t afi; + size_t stream_size = + MAX(ZEBRA_MAX_PACKET_SIZ, sizeof(struct zapi_route)); memset(&api, 0, sizeof(api)); api.vrf_id = re->vrf_id; @@ -602,7 +607,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client, SET_FLAG(api.message, ZAPI_MESSAGE_MTU); api.mtu = re->mtu; - struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + struct stream *s = stream_new(stream_size); /* Encode route and send. */ if (zapi_route_encode(cmd, s, &api) < 0) { @@ -1336,17 +1341,48 @@ static void zread_interface_delete(ZAPI_HANDLER_ARGS) { } +/* + * Handle message requesting interface be set up or down. + */ +static void zread_interface_set_protodown(ZAPI_HANDLER_ARGS) +{ + ifindex_t ifindex; + struct interface *ifp; + char down; + + STREAM_GETL(msg, ifindex); + STREAM_GETC(msg, down); + + /* set ifdown */ + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), ifindex); + + if (ifp) { + zlog_info("Setting interface %s (%u): protodown %s", ifp->name, + ifindex, down ? "on" : "off"); + zebra_if_set_protodown(ifp, down); + } else { + zlog_warn( + "Cannot set protodown %s for interface %u; does not exist", + down ? "on" : "off", ifindex); + } + + +stream_failure: + return; +} + + void zserv_nexthop_num_warn(const char *caller, const struct prefix *p, const unsigned int nexthop_num) { - if (nexthop_num > multipath_num) { + if (nexthop_num > zrouter.multipath_num) { char buff[PREFIX2STR_BUFFER]; prefix2str(p, buff, sizeof(buff)); flog_warn( EC_ZEBRA_MORE_NH_THAN_MULTIPATH, "%s: Prefix %s has %d nexthops, but we can only use the first %d", - caller, buff, nexthop_num, multipath_num); + caller, buff, nexthop_num, zrouter.multipath_num); } } @@ -1388,7 +1424,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) re->type = api.type; re->instance = api.instance; re->flags = api.flags; - re->uptime = time(NULL); + re->uptime = monotime(NULL); re->vrf_id = vrf_id; if (api.tableid && vrf_id == VRF_DEFAULT) re->table = api.tableid; @@ -1651,7 +1687,7 @@ static void zsend_capabilities(struct zserv *client, struct zebra_vrf *zvrf) zclient_create_header(s, ZEBRA_CAPABILITIES, zvrf->vrf->vrf_id); stream_putl(s, vrf_get_backend()); stream_putc(s, mpls_enabled); - stream_putl(s, multipath_num); + stream_putl(s, zrouter.multipath_num); stream_putc(s, zebra_mlag_get_role()); stream_putw_at(s, 0, stream_get_endp(s)); @@ -2412,6 +2448,7 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete, [ZEBRA_INTERFACE_ADD] = zread_interface_add, [ZEBRA_INTERFACE_DELETE] = zread_interface_delete, + [ZEBRA_INTERFACE_SET_PROTODOWN] = zread_interface_set_protodown, [ZEBRA_ROUTE_ADD] = zread_route_add, [ZEBRA_ROUTE_DELETE] = zread_route_del, [ZEBRA_REDISTRIBUTE_ADD] = zebra_redistribute_add, @@ -2489,7 +2526,7 @@ static void zserv_write_incoming(struct stream *orig, uint16_t command) copy = stream_dup(orig); stream_set_getp(copy, 0); - snprintf(fname, MAXPATHLEN, "%s/%u", DAEMON_VTY_DIR, command); + snprintf(fname, MAXPATHLEN, "%s/%u", frr_vtydir, command); frr_elevate_privs(&zserv_privs) { fd = open(fname, O_CREAT | O_WRONLY | O_EXCL, 0644); @@ -2507,6 +2544,9 @@ void zserv_handle_commands(struct zserv *client, struct stream *msg) zapi_parse_header(msg, &hdr); + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zserv_log_message(NULL, msg, &hdr); + #if defined(HANDLE_ZAPI_FUZZING) zserv_write_incoming(msg, hdr.command); #endif diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index d1b28227c37d..1707d3a68b6d 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -172,6 +172,11 @@ struct zebra_dplane_ctx { uint32_t zd_seq; uint32_t zd_old_seq; + /* Some updates may be generated by notifications: allow the + * plugin to notice and ignore results from its own notifications. + */ + uint32_t zd_notif_provider; + /* TODO -- internal/sub-operation status? */ enum zebra_dplane_result zd_remote_status; enum zebra_dplane_result zd_kernel_status; @@ -222,6 +227,8 @@ struct zebra_dplane_provider { /* Flags */ int dp_flags; + int (*dp_start)(struct zebra_dplane_provider *prov); + int (*dp_fp)(struct zebra_dplane_provider *prov); int (*dp_fini)(struct zebra_dplane_provider *prov, bool early_p); @@ -351,7 +358,7 @@ struct thread_master *dplane_get_thread_master(void) /* * Allocate a dataplane update context */ -static struct zebra_dplane_ctx *dplane_ctx_alloc(void) +struct zebra_dplane_ctx *dplane_ctx_alloc(void) { struct zebra_dplane_ctx *p; @@ -392,6 +399,7 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx) case DPLANE_OP_ROUTE_DELETE: case DPLANE_OP_SYS_ROUTE_ADD: case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: /* Free allocated nexthops */ if ((*pctx)->u.rinfo.zd_ng.nexthop) { @@ -413,6 +421,7 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx) case DPLANE_OP_LSP_INSTALL: case DPLANE_OP_LSP_UPDATE: case DPLANE_OP_LSP_DELETE: + case DPLANE_OP_LSP_NOTIFY: { zebra_nhlfe_t *nhlfe, *next; @@ -543,6 +552,12 @@ bool dplane_ctx_is_skip_kernel(const struct zebra_dplane_ctx *ctx) return CHECK_FLAG(ctx->zd_flags, DPLANE_CTX_FLAG_NO_KERNEL); } +void dplane_ctx_set_op(struct zebra_dplane_ctx *ctx, enum dplane_op_e op) +{ + DPLANE_CTX_VALID(ctx); + ctx->zd_op = op; +} + enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -569,6 +584,9 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_ROUTE_DELETE: ret = "ROUTE_DELETE"; break; + case DPLANE_OP_ROUTE_NOTIFY: + ret = "ROUTE_NOTIFY"; + break; case DPLANE_OP_LSP_INSTALL: ret = "LSP_INSTALL"; @@ -579,6 +597,9 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_LSP_DELETE: ret = "LSP_DELETE"; break; + case DPLANE_OP_LSP_NOTIFY: + ret = "LSP_NOTIFY"; + break; case DPLANE_OP_PW_INSTALL: ret = "PW_INSTALL"; @@ -625,6 +646,14 @@ const char *dplane_res2str(enum zebra_dplane_result res) return ret; } +void dplane_ctx_set_dest(struct zebra_dplane_ctx *ctx, + const struct prefix *dest) +{ + DPLANE_CTX_VALID(ctx); + + prefix_copy(&(ctx->u.rinfo.zd_dest), dest); +} + const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -632,6 +661,16 @@ const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx) return &(ctx->u.rinfo.zd_dest); } +void dplane_ctx_set_src(struct zebra_dplane_ctx *ctx, const struct prefix *src) +{ + DPLANE_CTX_VALID(ctx); + + if (src) + prefix_copy(&(ctx->u.rinfo.zd_src), src); + else + memset(&(ctx->u.rinfo.zd_src), 0, sizeof(struct prefix)); +} + /* Source prefix is a little special - return NULL for "no src prefix" */ const struct prefix *dplane_ctx_get_src(const struct zebra_dplane_ctx *ctx) { @@ -666,6 +705,13 @@ uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx) return ctx->zd_old_seq; } +void dplane_ctx_set_vrf(struct zebra_dplane_ctx *ctx, vrf_id_t vrf) +{ + DPLANE_CTX_VALID(ctx); + + ctx->zd_vrf_id = vrf; +} + vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -673,6 +719,35 @@ vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx) return ctx->zd_vrf_id; } +bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->zd_notif_provider != 0); +} + +uint32_t dplane_ctx_get_notif_provider(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->zd_notif_provider; +} + +void dplane_ctx_set_notif_provider(struct zebra_dplane_ctx *ctx, + uint32_t id) +{ + DPLANE_CTX_VALID(ctx); + + ctx->zd_notif_provider = id; +} + +void dplane_ctx_set_type(struct zebra_dplane_ctx *ctx, int type) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.rinfo.zd_type = type; +} + int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -687,6 +762,13 @@ int dplane_ctx_get_old_type(const struct zebra_dplane_ctx *ctx) return ctx->u.rinfo.zd_old_type; } +void dplane_ctx_set_afi(struct zebra_dplane_ctx *ctx, afi_t afi) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.rinfo.zd_afi = afi; +} + afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -694,6 +776,13 @@ afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx) return ctx->u.rinfo.zd_afi; } +void dplane_ctx_set_safi(struct zebra_dplane_ctx *ctx, safi_t safi) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.rinfo.zd_safi = safi; +} + safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -701,6 +790,13 @@ safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx) return ctx->u.rinfo.zd_safi; } +void dplane_ctx_set_table(struct zebra_dplane_ctx *ctx, uint32_t table) +{ + DPLANE_CTX_VALID(ctx); + + ctx->zd_table_id = table; +} + uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -715,6 +811,13 @@ route_tag_t dplane_ctx_get_tag(const struct zebra_dplane_ctx *ctx) return ctx->u.rinfo.zd_tag; } +void dplane_ctx_set_tag(struct zebra_dplane_ctx *ctx, route_tag_t tag) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.rinfo.zd_tag = tag; +} + route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -729,6 +832,13 @@ uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx) return ctx->u.rinfo.zd_instance; } +void dplane_ctx_set_instance(struct zebra_dplane_ctx *ctx, uint16_t instance) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.rinfo.zd_instance = instance; +} + uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -771,6 +881,13 @@ uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx *ctx) return ctx->u.rinfo.zd_distance; } +void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.rinfo.zd_distance = distance; +} + uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -778,6 +895,17 @@ uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx) return ctx->u.rinfo.zd_old_distance; } +void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh) +{ + DPLANE_CTX_VALID(ctx); + + if (ctx->u.rinfo.zd_ng.nexthop) { + nexthops_free(ctx->u.rinfo.zd_ng.nexthop); + ctx->u.rinfo.zd_ng.nexthop = NULL; + } + copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), nh, NULL); +} + const struct nexthop_group *dplane_ctx_get_ng( const struct zebra_dplane_ctx *ctx) { @@ -811,6 +939,13 @@ mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx) return ctx->u.lsp.ile.in_label; } +void dplane_ctx_set_in_label(struct zebra_dplane_ctx *ctx, mpls_label_t label) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.lsp.ile.in_label = label; +} + uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -818,6 +953,14 @@ uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx) return ctx->u.lsp.addr_family; } +void dplane_ctx_set_addr_family(struct zebra_dplane_ctx *ctx, + uint8_t family) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.lsp.addr_family = family; +} + uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -825,6 +968,14 @@ uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx) return ctx->u.lsp.flags; } +void dplane_ctx_set_lsp_flags(struct zebra_dplane_ctx *ctx, + uint32_t flags) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.lsp.flags = flags; +} + const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -832,6 +983,24 @@ const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx) return ctx->u.lsp.nhlfe_list; } +zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, + enum lsp_types_t lsp_type, + enum nexthop_types_t nh_type, + union g_addr *gate, + ifindex_t ifindex, + mpls_label_t out_label) +{ + zebra_nhlfe_t *nhlfe; + + DPLANE_CTX_VALID(ctx); + + nhlfe = zebra_mpls_lsp_add_nhlfe(&(ctx->u.lsp), + lsp_type, nh_type, gate, + ifindex, out_label); + + return nhlfe; +} + const zebra_nhlfe_t * dplane_ctx_get_best_nhlfe(const struct zebra_dplane_ctx *ctx) { @@ -840,6 +1009,16 @@ dplane_ctx_get_best_nhlfe(const struct zebra_dplane_ctx *ctx) return ctx->u.lsp.best_nhlfe; } +const zebra_nhlfe_t * +dplane_ctx_set_best_nhlfe(struct zebra_dplane_ctx *ctx, + zebra_nhlfe_t *nhlfe) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.lsp.best_nhlfe = nhlfe; + return ctx->u.lsp.best_nhlfe; +} + uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -1344,10 +1523,6 @@ dplane_route_update_internal(struct route_node *rn, /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (ctx == NULL) { - ret = ENOMEM; - goto done; - } /* Init context with info from zebra data structs */ ret = dplane_ctx_route_init(ctx, op, rn, re); @@ -1382,7 +1557,6 @@ dplane_route_update_internal(struct route_node *rn, ret = dplane_route_enqueue(ctx); } -done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1, memory_order_relaxed); @@ -1501,6 +1675,59 @@ enum zebra_dplane_result dplane_sys_route_del(struct route_node *rn, return ret; } +/* + * Update from an async notification, to bring other fibs up-to-date. + */ +enum zebra_dplane_result +dplane_route_notif_update(struct route_node *rn, + struct route_entry *re, + enum dplane_op_e op, + struct zebra_dplane_ctx *ctx) +{ + enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; + struct zebra_dplane_ctx *new_ctx = NULL; + struct nexthop *nexthop; + + if (rn == NULL || re == NULL) + goto done; + + new_ctx = dplane_ctx_alloc(); + if (new_ctx == NULL) + goto done; + + /* Init context with info from zebra data structs */ + dplane_ctx_route_init(new_ctx, op, rn, re); + + /* For add/update, need to adjust the nexthops so that we match + * the notification state, which may not be the route-entry/RIB + * state. + */ + if (op == DPLANE_OP_ROUTE_UPDATE || + op == DPLANE_OP_ROUTE_INSTALL) { + + nexthops_free(new_ctx->u.rinfo.zd_ng.nexthop); + new_ctx->u.rinfo.zd_ng.nexthop = NULL; + + copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop), + (rib_active_nhg(re))->nexthop, NULL); + + for (ALL_NEXTHOPS(new_ctx->u.rinfo.zd_ng, nexthop)) + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + + } + + /* Capture info about the source of the notification, in 'ctx' */ + dplane_ctx_set_notif_provider(new_ctx, + dplane_ctx_get_notif_provider(ctx)); + + dplane_route_enqueue(new_ctx); + + ret = ZEBRA_DPLANE_REQUEST_QUEUED; + +done: + return ret; +} + /* * Enqueue LSP add for the dataplane. */ @@ -1534,6 +1761,50 @@ enum zebra_dplane_result dplane_lsp_delete(zebra_lsp_t *lsp) return ret; } +/* Update or un-install resulting from an async notification */ +enum zebra_dplane_result +dplane_lsp_notif_update(zebra_lsp_t *lsp, + enum dplane_op_e op, + struct zebra_dplane_ctx *notif_ctx) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret = EINVAL; + struct zebra_dplane_ctx *ctx = NULL; + + /* Obtain context block */ + ctx = dplane_ctx_alloc(); + if (ctx == NULL) { + ret = ENOMEM; + goto done; + } + + ret = dplane_ctx_lsp_init(ctx, op, lsp); + if (ret != AOK) + goto done; + + /* Capture info about the source of the notification */ + dplane_ctx_set_notif_provider( + ctx, + dplane_ctx_get_notif_provider(notif_ctx)); + + ret = dplane_route_enqueue(ctx); + +done: + /* Update counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_lsps_in, 1, + memory_order_relaxed); + + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else { + atomic_fetch_add_explicit(&zdplane_info.dg_lsp_errors, 1, + memory_order_relaxed); + if (ctx) + dplane_ctx_free(&ctx); + } + return result; +} + /* * Enqueue pseudowire install for the dataplane. */ @@ -1562,10 +1833,6 @@ static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp, /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (ctx == NULL) { - ret = ENOMEM; - goto done; - } ret = dplane_ctx_lsp_init(ctx, op, lsp); if (ret != AOK) @@ -1583,8 +1850,7 @@ static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp, else { atomic_fetch_add_explicit(&zdplane_info.dg_lsp_errors, 1, memory_order_relaxed); - if (ctx) - dplane_ctx_free(&ctx); + dplane_ctx_free(&ctx); } return result; @@ -1601,10 +1867,6 @@ static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw, struct zebra_dplane_ctx *ctx = NULL; ctx = dplane_ctx_alloc(); - if (ctx == NULL) { - ret = ENOMEM; - goto done; - } ret = dplane_ctx_pw_init(ctx, op, pw); if (ret != AOK) @@ -1622,8 +1884,7 @@ static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw, else { atomic_fetch_add_explicit(&zdplane_info.dg_pw_errors, 1, memory_order_relaxed); - if (ctx) - dplane_ctx_free(&ctx); + dplane_ctx_free(&ctx); } return result; @@ -1691,10 +1952,6 @@ static enum zebra_dplane_result intf_addr_update_internal( } ctx = dplane_ctx_alloc(); - if (ctx == NULL) { - ret = ENOMEM; - goto done; - } ctx->zd_op = op; ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; @@ -1706,7 +1963,7 @@ static enum zebra_dplane_result intf_addr_update_internal( /* Init the interface-addr-specific area */ memset(&ctx->u.intf, 0, sizeof(ctx->u.intf)); - strncpy(ctx->u.intf.ifname, ifp->name, sizeof(ctx->u.intf.ifname)); + strlcpy(ctx->u.intf.ifname, ifp->name, sizeof(ctx->u.intf.ifname)); ctx->u.intf.ifindex = ifp->ifindex; ctx->u.intf.prefix = *(ifc->address); @@ -1734,7 +1991,7 @@ static enum zebra_dplane_result intf_addr_update_internal( len = strlen(ifc->label); if (len < sizeof(ctx->u.intf.label_buf)) { - strncpy(ctx->u.intf.label_buf, ifc->label, + strlcpy(ctx->u.intf.label_buf, ifc->label, sizeof(ctx->u.intf.label_buf)); ctx->u.intf.label = ctx->u.intf.label_buf; } else { @@ -1744,8 +2001,6 @@ static enum zebra_dplane_result intf_addr_update_internal( ret = dplane_route_enqueue(ctx); -done: - /* Increment counter */ atomic_fetch_add_explicit(&zdplane_info.dg_intf_addrs_in, 1, memory_order_relaxed); @@ -1756,8 +2011,7 @@ static enum zebra_dplane_result intf_addr_update_internal( /* Error counter */ atomic_fetch_add_explicit(&zdplane_info.dg_intf_addr_errors, 1, memory_order_relaxed); - if (ctx) - dplane_ctx_free(&ctx); + dplane_ctx_free(&ctx); } return result; @@ -1845,6 +2099,7 @@ int dplane_show_provs_helper(struct vty *vty, bool detailed) int dplane_provider_register(const char *name, enum dplane_provider_prio prio, int flags, + int (*start_fp)(struct zebra_dplane_provider *), int (*fp)(struct zebra_dplane_provider *), int (*fini_fp)(struct zebra_dplane_provider *, bool early), @@ -1875,6 +2130,7 @@ int dplane_provider_register(const char *name, p->dp_priority = prio; p->dp_fp = fp; + p->dp_start = start_fp; p->dp_fini = fini_fp; p->dp_data = data; @@ -2067,6 +2323,20 @@ int dplane_provider_work_ready(void) return AOK; } +/* + * Enqueue a context directly to zebra main. + */ +void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx) +{ + struct dplane_ctx_q temp_list; + + /* Zebra's api takes a list, so we need to use a temporary list */ + TAILQ_INIT(&temp_list); + + TAILQ_INSERT_TAIL(&temp_list, ctx, zd_q_entries); + (zdplane_info.dg_results_cb)(&temp_list); +} + /* * Kernel dataplane provider */ @@ -2229,9 +2499,11 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) res = kernel_dplane_address_update(ctx); break; - /* Ignore system 'notifications' - the kernel already knows */ + /* Ignore 'notifications' - no-op */ case DPLANE_OP_SYS_ROUTE_ADD: case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_NOTIFY: res = ZEBRA_DPLANE_REQUEST_SUCCESS; break; @@ -2342,7 +2614,7 @@ static void dplane_provider_init(void) ret = dplane_provider_register("Kernel", DPLANE_PRIO_KERNEL, - DPLANE_PROV_FLAGS_DEFAULT, + DPLANE_PROV_FLAGS_DEFAULT, NULL, kernel_dplane_process_func, NULL, NULL, NULL); @@ -2355,7 +2627,7 @@ static void dplane_provider_init(void) /* Optional test provider ... */ ret = dplane_provider_register("Test", DPLANE_PRIO_PRE_KERNEL, - DPLANE_PROV_FLAGS_DEFAULT, + DPLANE_PROV_FLAGS_DEFAULT, NULL, test_dplane_process_func, test_dplane_shutdown_func, NULL /* data */, NULL); @@ -2674,7 +2946,6 @@ static int dplane_thread_loop(struct thread *event) TAILQ_INIT(&error_list); - /* Call through to zebra main */ (zdplane_info.dg_results_cb)(&work_list); @@ -2739,13 +3010,14 @@ static void zebra_dplane_init_internal(void) */ void zebra_dplane_start(void) { - /* Start dataplane pthread */ - + struct zebra_dplane_provider *prov; struct frr_pthread_attr pattr = { .start = frr_pthread_attr_default.start, .stop = frr_pthread_attr_default.stop }; + /* Start dataplane pthread */ + zdplane_info.dg_pthread = frr_pthread_new(&pattr, "Zebra dplane thread", "Zebra dplane"); @@ -2757,6 +3029,23 @@ void zebra_dplane_start(void) thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0, &zdplane_info.dg_t_update); + /* Call start callbacks for registered providers */ + + DPLANE_LOCK(); + prov = TAILQ_FIRST(&zdplane_info.dg_providers_q); + DPLANE_UNLOCK(); + + while (prov) { + + if (prov->dp_start) + (prov->dp_start)(prov); + + /* Locate next provider */ + DPLANE_LOCK(); + prov = TAILQ_NEXT(prov, dp_prov_link); + DPLANE_UNLOCK(); + } + frr_pthread_run(zdplane_info.dg_pthread, NULL); } diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index d45628fdd04c..6238026bcfcf 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -105,11 +105,13 @@ enum dplane_op_e { DPLANE_OP_ROUTE_INSTALL, DPLANE_OP_ROUTE_UPDATE, DPLANE_OP_ROUTE_DELETE, + DPLANE_OP_ROUTE_NOTIFY, /* LSP update */ DPLANE_OP_LSP_INSTALL, DPLANE_OP_LSP_UPDATE, DPLANE_OP_LSP_DELETE, + DPLANE_OP_LSP_NOTIFY, /* Pseudowire update */ DPLANE_OP_PW_INSTALL, @@ -139,6 +141,9 @@ void dplane_enable_sys_route_notifs(void); */ TAILQ_HEAD(dplane_ctx_q, zebra_dplane_ctx); +/* Allocate a context object */ +struct zebra_dplane_ctx *dplane_ctx_alloc(void); + /* Return a dataplane results context block after use; the caller's pointer will * be cleared. */ @@ -169,9 +174,12 @@ void dplane_ctx_set_status(struct zebra_dplane_ctx *ctx, const char *dplane_res2str(enum zebra_dplane_result res); enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_op(struct zebra_dplane_ctx *ctx, enum dplane_op_e op); const char *dplane_op2str(enum dplane_op_e op); const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_dest(struct zebra_dplane_ctx *ctx, + const struct prefix *dest); /* Retrieve last/current provider id */ uint32_t dplane_ctx_get_provider(const struct zebra_dplane_ctx *ctx); @@ -186,29 +194,44 @@ bool dplane_ctx_is_skip_kernel(const struct zebra_dplane_ctx *ctx); * to mean "no src prefix" */ const struct prefix *dplane_ctx_get_src(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_src(struct zebra_dplane_ctx *ctx, const struct prefix *src); bool dplane_ctx_is_update(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_seq(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_vrf(struct zebra_dplane_ctx *ctx, vrf_id_t vrf); vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx); +bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_notif_provider(struct zebra_dplane_ctx *ctx, + uint32_t id); +uint32_t dplane_ctx_get_notif_provider(const struct zebra_dplane_ctx *ctx); + /* Accessors for route update information */ +void dplane_ctx_set_type(struct zebra_dplane_ctx *ctx, int type); int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx); int dplane_ctx_get_old_type(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_afi(struct zebra_dplane_ctx *ctx, afi_t afi); afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_safi(struct zebra_dplane_ctx *ctx, safi_t safi); safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_table(struct zebra_dplane_ctx *ctx, uint32_t table); uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx); route_tag_t dplane_ctx_get_tag(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_tag(struct zebra_dplane_ctx *ctx, route_tag_t tag); route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx); uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_instance(struct zebra_dplane_ctx *ctx, uint16_t instance); uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_old_metric(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_mtu(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_nh_mtu(const struct zebra_dplane_ctx *ctx); uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance); uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh); const struct nexthop_group *dplane_ctx_get_ng( const struct zebra_dplane_ctx *ctx); const struct nexthop_group *dplane_ctx_get_old_ng( @@ -216,11 +239,26 @@ const struct nexthop_group *dplane_ctx_get_old_ng( /* Accessors for LSP information */ mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_in_label(struct zebra_dplane_ctx *ctx, + mpls_label_t label); uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_addr_family(struct zebra_dplane_ctx *ctx, + uint8_t family); uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_lsp_flags(struct zebra_dplane_ctx *ctx, + uint32_t flags); const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx); +zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, + enum lsp_types_t lsp_type, + enum nexthop_types_t nh_type, + union g_addr *gate, + ifindex_t ifindex, + mpls_label_t out_label); + const zebra_nhlfe_t *dplane_ctx_get_best_nhlfe( const struct zebra_dplane_ctx *ctx); +const zebra_nhlfe_t *dplane_ctx_set_best_nhlfe(struct zebra_dplane_ctx *ctx, + zebra_nhlfe_t *nhlfe); uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx); /* Accessors for pseudowire information */ @@ -282,6 +320,13 @@ enum zebra_dplane_result dplane_sys_route_add(struct route_node *rn, enum zebra_dplane_result dplane_sys_route_del(struct route_node *rn, struct route_entry *re); +/* Update from an async notification, to bring other fibs up-to-date */ +enum zebra_dplane_result dplane_route_notif_update( + struct route_node *rn, + struct route_entry *re, + enum dplane_op_e op, + struct zebra_dplane_ctx *ctx); + /* * Enqueue LSP change operations for the dataplane. */ @@ -289,6 +334,11 @@ enum zebra_dplane_result dplane_lsp_add(zebra_lsp_t *lsp); enum zebra_dplane_result dplane_lsp_update(zebra_lsp_t *lsp); enum zebra_dplane_result dplane_lsp_delete(zebra_lsp_t *lsp); +/* Update or un-install resulting from an async notification */ +enum zebra_dplane_result dplane_lsp_notif_update(zebra_lsp_t *lsp, + enum dplane_op_e op, + struct zebra_dplane_ctx *ctx); + /* * Enqueue pseudowire operations for the dataplane. */ @@ -321,7 +371,6 @@ uint32_t dplane_get_in_queue_len(void); int dplane_show_helper(struct vty *vty, bool detailed); int dplane_show_provs_helper(struct vty *vty, bool detailed); - /* * Dataplane providers: modules that process or consume dataplane events. */ @@ -363,7 +412,13 @@ enum dplane_provider_prio { * then checks the provider's outbound queue for completed work. */ -/* Providers offer an entry-point for shutdown and cleanup. This is called +/* + * Providers can offer a 'start' callback; if present, the dataplane will + * call it when it is starting - when its pthread and event-scheduling + * thread_master are available. + */ + +/* Providers can offer an entry-point for shutdown and cleanup. This is called * with 'early' during shutdown, to indicate that the dataplane subsystem * is allowing work to move through the providers and finish. * When called without 'early', the provider should release @@ -372,6 +427,7 @@ enum dplane_provider_prio { int dplane_provider_register(const char *name, enum dplane_provider_prio prio, int flags, + int (*start_fp)(struct zebra_dplane_provider *), int (*fp)(struct zebra_dplane_provider *), int (*fini_fp)(struct zebra_dplane_provider *, bool early), @@ -409,10 +465,13 @@ struct zebra_dplane_ctx *dplane_provider_dequeue_in_ctx( int dplane_provider_dequeue_in_list(struct zebra_dplane_provider *prov, struct dplane_ctx_q *listp); -/* Enqueue, maintain associated counter and locking */ +/* Enqueue completed work, maintain associated counter and locking */ void dplane_provider_enqueue_out_ctx(struct zebra_dplane_provider *prov, struct zebra_dplane_ctx *ctx); +/* Enqueue a context directly to zebra main. */ +void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx); + /* * Initialize the dataplane modules at zebra startup. This is currently called * by the rib module. Zebra registers a results callback with the dataplane. diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index 28333526a7cf..2ac79b100c32 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -32,6 +32,7 @@ #include "prefix.h" #include "zebra/zserv.h" +#include "zebra/zebra_router.h" #include "zebra/zebra_dplane.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" @@ -40,6 +41,7 @@ #include "nexthop.h" #include "zebra/zebra_fpm_private.h" +#include "zebra/zebra_vxlan_private.h" /* * addr_to_a @@ -100,6 +102,51 @@ static size_t af_addr_size(uint8_t af) } } +/* + * We plan to use RTA_ENCAP_TYPE attribute for VxLAN encap as well. + * Currently, values 0 to 8 for this attribute are used by lwtunnel_encap_types + * So, we cannot use these values for VxLAN encap. + */ +enum fpm_nh_encap_type_t { + FPM_NH_ENCAP_NONE = 0, + FPM_NH_ENCAP_VXLAN = 100, + FPM_NH_ENCAP_MAX, +}; + +/* + * fpm_nh_encap_type_to_str + */ +static const char *fpm_nh_encap_type_to_str(enum fpm_nh_encap_type_t encap_type) +{ + switch (encap_type) { + case FPM_NH_ENCAP_NONE: + return "none"; + + case FPM_NH_ENCAP_VXLAN: + return "VxLAN"; + + case FPM_NH_ENCAP_MAX: + return "invalid"; + } + + return "invalid"; +} + +struct vxlan_encap_info_t { + vni_t vni; +}; + +enum vxlan_encap_info_type_t { + VXLAN_VNI = 0, +}; + +struct fpm_nh_encap_info_t { + enum fpm_nh_encap_type_t encap_type; + union { + struct vxlan_encap_info_t vxlan_encap; + }; +}; + /* * netlink_nh_info_t * @@ -117,6 +164,7 @@ typedef struct netlink_nh_info_t_ { */ int recursive; enum nexthop_types_t type; + struct fpm_nh_encap_info_t encap_info; } netlink_nh_info_t; /* @@ -150,15 +198,17 @@ typedef struct netlink_route_info_t_ { * Returns TRUE if a nexthop was added, FALSE otherwise. */ static int netlink_route_info_add_nh(netlink_route_info_t *ri, - struct nexthop *nexthop) + struct nexthop *nexthop, + struct route_entry *re) { netlink_nh_info_t nhi; union g_addr *src; + zebra_l3vni_t *zl3vni = NULL; memset(&nhi, 0, sizeof(nhi)); src = NULL; - if (ri->num_nhs >= (int)ZEBRA_NUM_OF(ri->nhs)) + if (ri->num_nhs >= (int)array_size(ri->nhs)) return 0; nhi.recursive = nexthop->rparent ? 1 : 0; @@ -185,6 +235,17 @@ static int netlink_route_info_add_nh(netlink_route_info_t *ri, if (!nhi.gateway && nhi.if_index == 0) return 0; + if (re && CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) { + nhi.encap_info.encap_type = FPM_NH_ENCAP_VXLAN; + + zl3vni = zl3vni_from_vrf(ri->rtm_table); + if (zl3vni && is_l3vni_oper_up(zl3vni)) { + + /* Add VNI to VxLAN encap info */ + nhi.encap_info.vxlan_encap.vni = zl3vni->vni; + } + } + /* * We have a valid nhi. Copy the structure over to the route_info. */ @@ -223,6 +284,7 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, rib_dest_t *dest, struct route_entry *re) { struct nexthop *nexthop; + struct zebra_vrf *zvrf; memset(ri, 0, sizeof(*ri)); @@ -230,7 +292,9 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, ri->af = rib_dest_af(dest); ri->nlmsg_type = cmd; - ri->rtm_table = zvrf_id(rib_dest_vrf(dest)); + zvrf = rib_dest_vrf(dest); + if (zvrf) + ri->rtm_table = zvrf->table_id; ri->rtm_protocol = RTPROT_UNSPEC; /* @@ -251,7 +315,7 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, ri->metric = &re->metric; for (ALL_NEXTHOPS(re->ng, nexthop)) { - if (ri->num_nhs >= multipath_num) + if (ri->num_nhs >= zrouter.multipath_num) break; if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) @@ -270,14 +334,13 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, ri->rtm_type = RTN_BLACKHOLE; break; } - return 1; } if ((cmd == RTM_NEWROUTE && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == RTM_DELROUTE && CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED))) { - netlink_route_info_add_nh(ri, nexthop); + netlink_route_info_add_nh(ri, nexthop, re); } } @@ -303,6 +366,10 @@ static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf, unsigned int nexthop_num = 0; size_t buf_offset; netlink_nh_info_t *nhi; + enum fpm_nh_encap_type_t encap; + struct rtattr *nest; + struct vxlan_encap_info_t *vxlan; + int nest_len; struct { struct nlmsghdr n; @@ -327,7 +394,21 @@ static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf, req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; req->n.nlmsg_type = ri->nlmsg_type; req->r.rtm_family = ri->af; - req->r.rtm_table = ri->rtm_table; + + /* + * rtm_table field is a uchar field which can accomodate table_id less + * than 256. + * To support table id greater than 255, if the table_id is greater than + * 255, set rtm_table to RT_TABLE_UNSPEC and add RTA_TABLE attribute + * with 32 bit value as the table_id. + */ + if (ri->rtm_table < 256) + req->r.rtm_table = ri->rtm_table; + else { + req->r.rtm_table = RT_TABLE_UNSPEC; + addattr32(&req->n, in_buf_len, RTA_TABLE, ri->rtm_table); + } + req->r.rtm_dst_len = ri->prefix->prefixlen; req->r.rtm_protocol = ri->rtm_protocol; req->r.rtm_scope = RT_SCOPE_UNIVERSE; @@ -355,6 +436,26 @@ static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf, addattr32(&req->n, in_buf_len, RTA_OIF, nhi->if_index); } + encap = nhi->encap_info.encap_type; + if (encap > FPM_NH_ENCAP_NONE) { + addattr_l(&req->n, in_buf_len, RTA_ENCAP_TYPE, &encap, + sizeof(uint16_t)); + switch (encap) { + case FPM_NH_ENCAP_NONE: + break; + case FPM_NH_ENCAP_VXLAN: + vxlan = &nhi->encap_info.vxlan_encap; + nest = addattr_nest(&req->n, in_buf_len, + RTA_ENCAP); + addattr32(&req->n, in_buf_len, VXLAN_VNI, + vxlan->vni); + addattr_nest_end(&req->n, nest); + break; + case FPM_NH_ENCAP_MAX: + break; + } + } + goto done; } @@ -388,6 +489,28 @@ static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf, rtnh->rtnh_ifindex = nhi->if_index; } + encap = nhi->encap_info.encap_type; + if (encap > FPM_NH_ENCAP_NONE) { + rta_addattr_l(rta, sizeof(buf), RTA_ENCAP_TYPE, + &encap, sizeof(uint16_t)); + rtnh->rtnh_len += sizeof(struct rtattr) + + sizeof(uint16_t); + switch (encap) { + case FPM_NH_ENCAP_NONE: + break; + case FPM_NH_ENCAP_VXLAN: + vxlan = &nhi->encap_info.vxlan_encap; + nest = rta_nest(rta, sizeof(buf), RTA_ENCAP); + rta_addattr_l(rta, sizeof(buf), VXLAN_VNI, + &vxlan->vni, sizeof(uint32_t)); + nest_len = rta_nest_end(rta, nest); + rtnh->rtnh_len += nest_len; + break; + case FPM_NH_ENCAP_MAX: + break; + } + } + rtnh = RTNH_NEXT(rtnh); } @@ -424,10 +547,12 @@ static void zfpm_log_route_info(netlink_route_info_t *ri, const char *label) for (i = 0; i < ri->num_nhs; i++) { nhi = &ri->nhs[i]; - zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s", + zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s, Encap type: %s", nhi->if_index, addr_to_a(ri->af, nhi->gateway), nhi->recursive ? "yes" : "no", - nexthop_type_to_str(nhi->type)); + nexthop_type_to_str(nhi->type), + fpm_nh_encap_type_to_str(nhi->encap_info.encap_type) + ); } } diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c index be0f6a23be74..3054b8a34d20 100644 --- a/zebra/zebra_fpm_protobuf.c +++ b/zebra/zebra_fpm_protobuf.c @@ -34,6 +34,7 @@ #include "qpb/linear_allocator.h" #include "fpm/fpm_pb.h" +#include "zebra_router.h" #include "zebra_fpm_private.h" /* @@ -173,10 +174,10 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator, */ num_nhs = 0; for (ALL_NEXTHOPS(re->ng, nexthop)) { - if (num_nhs >= multipath_num) + if (num_nhs >= zrouter.multipath_num) break; - if (num_nhs >= ZEBRA_NUM_OF(nexthops)) + if (num_nhs >= array_size(nexthops)) break; if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 5c375a6befc0..5214f1f22d26 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -76,7 +76,7 @@ static zebra_fec_t *fec_add(struct route_table *table, struct prefix *p, uint32_t label_index); static int fec_del(zebra_fec_t *fec); -static unsigned int label_hash(void *p); +static unsigned int label_hash(const void *p); static bool label_cmp(const void *p1, const void *p2); static int nhlfe_nexthop_active_ipv4(zebra_nhlfe_t *nhlfe, struct nexthop *nexthop); @@ -577,7 +577,7 @@ static int fec_del(zebra_fec_t *fec) /* * Hash function for label. */ -static unsigned int label_hash(void *p) +static unsigned int label_hash(const void *p) { const zebra_ile_t *ile = p; @@ -1693,8 +1693,9 @@ static char *snhlfe2str(zebra_snhlfe_t *snhlfe, char *buf, int size) case NEXTHOP_TYPE_IPV6_IFINDEX: inet_ntop(AF_INET6, &snhlfe->gate.ipv6, buf, size); if (snhlfe->ifindex) - strcat(buf, - ifindex2ifname(snhlfe->ifindex, VRF_DEFAULT)); + strlcat(buf, + ifindex2ifname(snhlfe->ifindex, VRF_DEFAULT), + size); break; default: break; @@ -1809,6 +1810,214 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) dplane_ctx_fini(&ctx); } +/* + * Process async dplane notifications. + */ +void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) +{ + struct zebra_vrf *zvrf; + zebra_ile_t tmp_ile; + struct hash *lsp_table; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe; + const zebra_nhlfe_t *ctx_nhlfe; + struct nexthop *nexthop; + const struct nexthop *ctx_nexthop; + int start_count = 0, end_count = 0; /* Installed counts */ + bool changed_p = false; + bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS); + + if (is_debug) + zlog_debug("LSP dplane notif, in-label %u", + dplane_ctx_get_in_label(ctx)); + + /* Look for zebra LSP object */ + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (zvrf == NULL) + goto done; + + lsp_table = zvrf->lsp_table; + + tmp_ile.in_label = dplane_ctx_get_in_label(ctx); + lsp = hash_lookup(lsp_table, &tmp_ile); + if (lsp == NULL) { + if (is_debug) + zlog_debug("dplane LSP notif: in-label %u not found", + dplane_ctx_get_in_label(ctx)); + goto done; + } + + /* + * The dataplane/forwarding plane is notifying zebra about the state + * of the nexthops associated with this LSP. First, we take a + * pre-scan pass to determine whether the LSP has transitioned + * from installed -> uninstalled. In that case, we need to have + * the existing state of the LSP objects available before making + * any changes. + */ + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + char buf[NEXTHOP_STRLEN]; + + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + start_count++; + + ctx_nexthop = NULL; + for (ctx_nhlfe = dplane_ctx_get_nhlfe(ctx); + ctx_nhlfe; ctx_nhlfe = ctx_nhlfe->next) { + + ctx_nexthop = ctx_nhlfe->nexthop; + if (!ctx_nexthop) + continue; + + if ((ctx_nexthop->type == nexthop->type) && + nexthop_same(ctx_nexthop, nexthop)) { + /* Matched */ + break; + } + } + + if (is_debug) + nexthop2str(nexthop, buf, sizeof(buf)); + + if (ctx_nhlfe && ctx_nexthop) { + if (is_debug) { + const char *tstr = ""; + + if (!CHECK_FLAG(ctx_nhlfe->flags, + NHLFE_FLAG_INSTALLED)) + tstr = "not "; + + zlog_debug("LSP dplane notif: matched nh %s (%sinstalled)", + buf, tstr); + } + + /* Test zebra nhlfe install state */ + if (CHECK_FLAG(ctx_nhlfe->flags, + NHLFE_FLAG_INSTALLED)) { + + if (!CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_INSTALLED)) + changed_p = true; + + /* Update counter */ + end_count++; + } else { + + if (CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_INSTALLED)) + changed_p = true; + } + + } else { + /* Not mentioned in lfib set -> uninstalled */ + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) || + CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) || + CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { + changed_p = true; + } + + if (is_debug) + zlog_debug("LSP dplane notif: no match, nh %s", + buf); + } + } + + if (is_debug) + zlog_debug("LSP dplane notif: lfib start_count %d, end_count %d%s", + start_count, end_count, + changed_p ? ", changed" : ""); + + /* + * Has the LSP become uninstalled? + */ + if (start_count > 0 && end_count == 0) { + /* Inform other lfibs */ + dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_DELETE, ctx); + } + + /* + * Now we take a second pass and bring the zebra + * nexthop state into sync with the forwarding-plane state. + */ + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + char buf[NEXTHOP_STRLEN]; + + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + ctx_nexthop = NULL; + for (ctx_nhlfe = dplane_ctx_get_nhlfe(ctx); + ctx_nhlfe; ctx_nhlfe = ctx_nhlfe->next) { + + ctx_nexthop = ctx_nhlfe->nexthop; + if (!ctx_nexthop) + continue; + + if ((ctx_nexthop->type == nexthop->type) && + nexthop_same(ctx_nexthop, nexthop)) { + /* Matched */ + break; + } + } + + if (is_debug) + nexthop2str(nexthop, buf, sizeof(buf)); + + if (ctx_nhlfe && ctx_nexthop) { + + /* Bring zebra nhlfe install state into sync */ + if (CHECK_FLAG(ctx_nhlfe->flags, + NHLFE_FLAG_INSTALLED)) { + + SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + + } else { + + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + } + + if (CHECK_FLAG(ctx_nhlfe->nexthop->flags, + NEXTHOP_FLAG_FIB)) { + SET_FLAG(nhlfe->nexthop->flags, + NEXTHOP_FLAG_ACTIVE); + SET_FLAG(nhlfe->nexthop->flags, + NEXTHOP_FLAG_FIB); + } else { + UNSET_FLAG(nhlfe->nexthop->flags, + NEXTHOP_FLAG_ACTIVE); + UNSET_FLAG(nhlfe->nexthop->flags, + NEXTHOP_FLAG_FIB); + } + + } else { + /* Not mentioned in lfib set -> uninstalled */ + + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + } + + if (end_count > 0) { + SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); + + if (changed_p) + dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_UPDATE, ctx); + + } else { + UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); + clear_nhlfe_installed(lsp); + } + +done: + dplane_ctx_fini(&ctx); +} + /* * Install dynamic LSP entry. */ @@ -2874,7 +3083,11 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf, ifp = if_lookup_by_index_per_ns( zns, nexthop->ifindex); - vty_out(vty, "%15s", ifp->name); + if (ifp) + vty_out(vty, "%15s", ifp->name); + else + vty_out(vty, "%15s", "Null"); + break; } case NEXTHOP_TYPE_IPV4: @@ -2999,11 +3212,30 @@ int zebra_mpls_write_label_block_config(struct vty *vty, struct zebra_vrf *zvrf) /* * Called when VRF becomes inactive, cleans up information but keeps * the table itself. - * NOTE: Currently supported only for default VRF. */ void zebra_mpls_cleanup_tables(struct zebra_vrf *zvrf) { - hash_iterate(zvrf->lsp_table, lsp_uninstall_from_kernel, NULL); + struct zebra_vrf *def_zvrf; + afi_t afi; + + if (zvrf_id(zvrf) == VRF_DEFAULT) + hash_iterate(zvrf->lsp_table, lsp_uninstall_from_kernel, NULL); + else { + /* + * For other vrfs, we try to remove associated LSPs; we locate + * the LSPs in the default vrf. + */ + def_zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); + + /* At shutdown, the default may be gone already */ + if (def_zvrf == NULL) + return; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + if (zvrf->label[afi] != MPLS_LABEL_NONE) + lsp_uninstall(def_zvrf, zvrf->label[afi]); + } + } } /* diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 3a131e1aaf12..d983221cb533 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -352,6 +352,9 @@ struct zebra_dplane_ctx; void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx); +/* Process async dplane notifications. */ +void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx); + /* * Schedule all MPLS label forwarding entries for processing. * Called upon changes that may affect one or more of them such as diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c index 977a8eaf3c77..e7fdaf127d38 100644 --- a/zebra/zebra_mpls_openbsd.c +++ b/zebra/zebra_mpls_openbsd.c @@ -27,6 +27,7 @@ #include "zebra/zebra_mpls.h" #include "zebra/debug.h" #include "zebra/zebra_errors.h" +#include "zebra/zebra_router.h" #include "privs.h" #include "prefix.h" @@ -262,7 +263,7 @@ static int kernel_lsp_cmd(struct zebra_dplane_ctx *ctx) if (!nexthop) continue; - if (nexthop_num >= multipath_num) + if (nexthop_num >= zrouter.multipath_num) break; if (((action == RTM_ADD || action == RTM_CHANGE) diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c new file mode 100644 index 000000000000..f2a76d1c5297 --- /dev/null +++ b/zebra/zebra_nhg.c @@ -0,0 +1,511 @@ +/* Zebra Nexthop Group Code. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * Stephen Worley + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include + +#include "lib/nexthop.h" +#include "lib/routemap.h" + +#include "zebra/connected.h" +#include "zebra/debug.h" +#include "zebra/zebra_router.h" +#include "zebra/zebra_nhg.h" +#include "zebra/zebra_rnh.h" +#include "zebra/zebra_routemap.h" +#include "zebra/rt.h" + +static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, + struct nexthop *nexthop) +{ + struct nexthop *resolved_hop; + + resolved_hop = nexthop_new(); + SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); + + resolved_hop->vrf_id = nexthop->vrf_id; + switch (newhop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + /* If the resolving route specifies a gateway, use it */ + resolved_hop->type = newhop->type; + resolved_hop->gate.ipv4 = newhop->gate.ipv4; + + if (newhop->ifindex) { + resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->ifindex = newhop->ifindex; + } + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + resolved_hop->type = newhop->type; + resolved_hop->gate.ipv6 = newhop->gate.ipv6; + + if (newhop->ifindex) { + resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + resolved_hop->ifindex = newhop->ifindex; + } + break; + case NEXTHOP_TYPE_IFINDEX: + /* If the resolving route is an interface route, + * it means the gateway we are looking up is connected + * to that interface. (The actual network is _not_ onlink). + * Therefore, the resolved route should have the original + * gateway as nexthop as it is directly connected. + * + * On Linux, we have to set the onlink netlink flag because + * otherwise, the kernel won't accept the route. + */ + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + if (afi == AFI_IP) { + resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->gate.ipv4 = nexthop->gate.ipv4; + } else if (afi == AFI_IP6) { + resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + resolved_hop->gate.ipv6 = nexthop->gate.ipv6; + } + resolved_hop->ifindex = newhop->ifindex; + break; + case NEXTHOP_TYPE_BLACKHOLE: + resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE; + resolved_hop->bh_type = nexthop->bh_type; + break; + } + + if (newhop->flags & NEXTHOP_FLAG_ONLINK) + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + + /* Copy labels of the resolved route */ + if (newhop->nh_label) + nexthop_add_labels(resolved_hop, newhop->nh_label_type, + newhop->nh_label->num_labels, + &newhop->nh_label->label[0]); + + resolved_hop->rparent = nexthop; + nexthop_add(&nexthop->resolved, resolved_hop); +} + +/* + * Given a nexthop we need to properly recursively resolve + * the route. As such, do a table lookup to find and match + * if at all possible. Set the nexthop->ifindex as appropriate + */ +static int nexthop_active(afi_t afi, struct route_entry *re, + struct nexthop *nexthop, struct route_node *top) +{ + struct prefix p; + struct route_table *table; + struct route_node *rn; + struct route_entry *match = NULL; + int resolved; + struct nexthop *newhop; + struct interface *ifp; + rib_dest_t *dest; + + if ((nexthop->type == NEXTHOP_TYPE_IPV4) + || nexthop->type == NEXTHOP_TYPE_IPV6) + nexthop->ifindex = 0; + + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + nexthops_free(nexthop->resolved); + nexthop->resolved = NULL; + re->nexthop_mtu = 0; + + /* + * If the kernel has sent us a route, then + * by golly gee whiz it's a good route. + */ + if (re->type == ZEBRA_ROUTE_KERNEL || re->type == ZEBRA_ROUTE_SYSTEM) + return 1; + + /* + * Check to see if we should trust the passed in information + * for UNNUMBERED interfaces as that we won't find the GW + * address in the routing table. + * This check should suffice to handle IPv4 or IPv6 routes + * sourced from EVPN routes which are installed with the + * next hop as the remote VTEP IP. + */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) { + ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); + if (!ifp) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Onlink and interface: %u[%u] does not exist", + __PRETTY_FUNCTION__, nexthop->ifindex, + nexthop->vrf_id); + return 0; + } + if (connected_is_unnumbered(ifp)) { + if (if_is_operative(ifp)) + return 1; + else { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Onlink and interface %s is not operative", + __PRETTY_FUNCTION__, ifp->name); + return 0; + } + } + if (!if_is_operative(ifp)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Interface %s is not unnumbered", + __PRETTY_FUNCTION__, ifp->name); + return 0; + } + } + + /* Make lookup prefix. */ + memset(&p, 0, sizeof(struct prefix)); + switch (afi) { + case AFI_IP: + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.u.prefix4 = nexthop->gate.ipv4; + break; + case AFI_IP6: + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_PREFIXLEN; + p.u.prefix6 = nexthop->gate.ipv6; + break; + default: + assert(afi != AFI_IP && afi != AFI_IP6); + break; + } + /* Lookup table. */ + table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id); + if (!table) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: Table not found", + __PRETTY_FUNCTION__); + return 0; + } + + rn = route_node_match(table, (struct prefix *)&p); + while (rn) { + route_unlock_node(rn); + + /* Lookup should halt if we've matched against ourselves ('top', + * if specified) - i.e., we cannot have a nexthop NH1 is + * resolved by a route NH1. The exception is if the route is a + * host route. + */ + if (top && rn == top) + if (((afi == AFI_IP) && (rn->p.prefixlen != 32)) + || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Matched against ourself and prefix length is not max bit length", + __PRETTY_FUNCTION__); + return 0; + } + + /* Pick up selected route. */ + /* However, do not resolve over default route unless explicitly + * allowed. */ + if (is_default_prefix(&rn->p) + && !rnh_resolve_via_default(p.family)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t:%s: Resolved against default route", + __PRETTY_FUNCTION__); + return 0; + } + + dest = rib_dest_from_rnode(rn); + if (dest && dest->selected_fib + && !CHECK_FLAG(dest->selected_fib->status, + ROUTE_ENTRY_REMOVED) + && dest->selected_fib->type != ZEBRA_ROUTE_TABLE) + match = dest->selected_fib; + + /* If there is no selected route or matched route is EGP, go up + tree. */ + if (!match) { + do { + rn = rn->parent; + } while (rn && rn->info == NULL); + if (rn) + route_lock_node(rn); + + continue; + } + + if (match->type == ZEBRA_ROUTE_CONNECT) { + /* Directly point connected route. */ + newhop = match->ng.nexthop; + if (newhop) { + if (nexthop->type == NEXTHOP_TYPE_IPV4 + || nexthop->type == NEXTHOP_TYPE_IPV6) + nexthop->ifindex = newhop->ifindex; + } + return 1; + } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { + resolved = 0; + for (ALL_NEXTHOPS(match->ng, newhop)) { + if (!CHECK_FLAG(match->status, + ROUTE_ENTRY_INSTALLED)) + continue; + if (CHECK_FLAG(newhop->flags, + NEXTHOP_FLAG_RECURSIVE)) + continue; + + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + SET_FLAG(re->status, + ROUTE_ENTRY_NEXTHOPS_CHANGED); + nexthop_set_resolved(afi, newhop, nexthop); + resolved = 1; + } + if (resolved) + re->nexthop_mtu = match->mtu; + if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: Recursion failed to find", + __PRETTY_FUNCTION__); + return resolved; + } else if (re->type == ZEBRA_ROUTE_STATIC) { + resolved = 0; + for (ALL_NEXTHOPS(match->ng, newhop)) { + if (!CHECK_FLAG(match->status, + ROUTE_ENTRY_INSTALLED)) + continue; + if (CHECK_FLAG(newhop->flags, + NEXTHOP_FLAG_RECURSIVE)) + continue; + + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + nexthop_set_resolved(afi, newhop, nexthop); + resolved = 1; + } + if (resolved) + re->nexthop_mtu = match->mtu; + + if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Static route unable to resolve", + __PRETTY_FUNCTION__); + return resolved; + } else { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + zlog_debug( + "\t%s: Route Type %s has not turned on recursion", + __PRETTY_FUNCTION__, + zebra_route_string(re->type)); + if (re->type == ZEBRA_ROUTE_BGP + && !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP)) + zlog_debug( + "\tEBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\""); + } + return 0; + } + } + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: Nexthop did not lookup in table", + __PRETTY_FUNCTION__); + return 0; +} + +/* This function verifies reachability of one given nexthop, which can be + * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored + * in nexthop->flags field. The nexthop->ifindex will be updated + * appropriately as well. An existing route map can turn + * (otherwise active) nexthop into inactive, but not vice versa. + * + * The return value is the final value of 'ACTIVE' flag. + */ +static unsigned nexthop_active_check(struct route_node *rn, + struct route_entry *re, + struct nexthop *nexthop) +{ + struct interface *ifp; + route_map_result_t ret = RMAP_MATCH; + int family; + char buf[SRCDEST2STR_BUFFER]; + const struct prefix *p, *src_p; + struct zebra_vrf *zvrf; + + srcdest_rnode_prefixes(rn, &p, &src_p); + + if (rn->p.family == AF_INET) + family = AFI_IP; + else if (rn->p.family == AF_INET6) + family = AFI_IP6; + else + family = 0; + switch (nexthop->type) { + case NEXTHOP_TYPE_IFINDEX: + ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); + if (ifp && if_is_operative(ifp)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + family = AFI_IP; + if (nexthop_active(AFI_IP, re, nexthop, rn)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + case NEXTHOP_TYPE_IPV6: + family = AFI_IP6; + if (nexthop_active(AFI_IP6, re, nexthop, rn)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + /* RFC 5549, v4 prefix with v6 NH */ + if (rn->p.family != AF_INET) + family = AFI_IP6; + if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) { + ifp = if_lookup_by_index(nexthop->ifindex, + nexthop->vrf_id); + if (ifp && if_is_operative(ifp)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } else { + if (nexthop_active(AFI_IP6, re, nexthop, rn)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + break; + case NEXTHOP_TYPE_BLACKHOLE: + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + default: + break; + } + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: Unable to find a active nexthop", + __PRETTY_FUNCTION__); + return 0; + } + + /* XXX: What exactly do those checks do? Do we support + * e.g. IPv4 routes with IPv6 nexthops or vice versa? + */ + if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET) + || (family == AFI_IP6 && p->family != AF_INET6)) + return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + + /* The original code didn't determine the family correctly + * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi + * from the rib_table_info in those cases. + * Possibly it may be better to use only the rib_table_info + * in every case. + */ + if (!family) { + rib_table_info_t *info; + + info = srcdest_rnode_table_info(rn); + family = info->afi; + } + + memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr)); + + zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id); + if (!zvrf) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: zvrf is NULL", __PRETTY_FUNCTION__); + return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + + /* It'll get set if required inside */ + ret = zebra_route_map_check(family, re->type, re->instance, p, nexthop, + zvrf, re->tag); + if (ret == RMAP_DENYMATCH) { + if (IS_ZEBRA_DEBUG_RIB) { + srcdest_rnode2str(rn, buf, sizeof(buf)); + zlog_debug( + "%u:%s: Filtering out with NH out %s due to route map", + re->vrf_id, buf, + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + } + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); +} + +/* + * Iterate over all nexthops of the given RIB entry and refresh their + * ACTIVE flag. re->nexthop_active_num is updated accordingly. If any + * nexthop is found to toggle the ACTIVE flag, the whole re structure + * is flagged with ROUTE_ENTRY_CHANGED. + * + * Return value is the new number of active nexthops. + */ +int nexthop_active_update(struct route_node *rn, struct route_entry *re) +{ + struct nexthop *nexthop; + union g_addr prev_src; + unsigned int prev_active, new_active; + ifindex_t prev_index; + + re->nexthop_active_num = 0; + UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); + + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { + /* No protocol daemon provides src and so we're skipping + * tracking it */ + prev_src = nexthop->rmap_src; + prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + prev_index = nexthop->ifindex; + /* + * We need to respect the multipath_num here + * as that what we should be able to install from + * a multipath perpsective should not be a data plane + * decision point. + */ + new_active = nexthop_active_check(rn, re, nexthop); + if (new_active + && re->nexthop_active_num >= zrouter.multipath_num) { + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + new_active = 0; + } + if (new_active) + re->nexthop_active_num++; + /* Don't allow src setting on IPv6 addr for now */ + if (prev_active != new_active || prev_index != nexthop->ifindex + || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX + && nexthop->type < NEXTHOP_TYPE_IPV6) + && prev_src.ipv4.s_addr + != nexthop->rmap_src.ipv4.s_addr) + || ((nexthop->type >= NEXTHOP_TYPE_IPV6 + && nexthop->type < NEXTHOP_TYPE_BLACKHOLE) + && !(IPV6_ADDR_SAME(&prev_src.ipv6, + &nexthop->rmap_src.ipv6))) + || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) { + SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); + SET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED); + } + } + + return re->nexthop_active_num; +} + diff --git a/bfdd/bsd.c b/zebra/zebra_nhg.h similarity index 57% rename from bfdd/bsd.c rename to zebra/zebra_nhg.h index 923fbd909eba..ff2351c7599e 100644 --- a/bfdd/bsd.c +++ b/zebra/zebra_nhg.h @@ -1,7 +1,9 @@ -/* - * *BSD specific code +/* Zebra Nexthop Group header. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * Stephen Worley * - * Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF") + * This file is part of FRR. * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -18,31 +20,10 @@ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ +#ifndef __ZEBRA_NHG_H__ +#define __ZEBRA_NHG_H__ -#include +#include "zebra/rib.h" -#ifdef BFD_BSD - -#include -#include -#include -#include - -#include - -#include "bfd.h" - -/* - * Definitions. - */ -int bp_bind_dev(int sd, const char *dev) -{ - /* - * *BSDs don't support `SO_BINDTODEVICE`, instead you must - * manually specify the main address of the interface or use - * BPF on the socket descriptor. - */ - return 0; -} - -#endif /* BFD_BSD */ +extern int nexthop_active_update(struct route_node *rn, struct route_entry *re); +#endif diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 0c743d86784b..db4f9d0015c0 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -27,7 +27,6 @@ #include "lib/prefix.h" #include "lib/memory.h" -#include "rtadv.h" #include "zebra_ns.h" #include "zebra_vrf.h" #include "zebra_memory.h" @@ -122,10 +121,6 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) zns->ns_id = ns_id; -#if defined(HAVE_RTADV) - rtadv_init(zns); -#endif - kernel_init(zns); interface_list(zns); route_read(zns); @@ -142,9 +137,6 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete) { route_table_finish(zns->if_table); -#if defined(HAVE_RTADV) - rtadv_terminate(zns); -#endif kernel_terminate(zns, complete); diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 73db567eace3..a82dd4c24a85 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -135,12 +135,12 @@ void zebra_pbr_rules_free(void *arg) XFREE(MTYPE_TMP, rule); } -uint32_t zebra_pbr_rules_hash_key(void *arg) +uint32_t zebra_pbr_rules_hash_key(const void *arg) { - struct zebra_pbr_rule *rule; + const struct zebra_pbr_rule *rule; uint32_t key; - rule = (struct zebra_pbr_rule *)arg; + rule = arg; key = jhash_3words(rule->rule.seq, rule->rule.priority, rule->rule.action.table, prefix_hash_key(&rule->rule.filter.src_ip)); @@ -250,9 +250,9 @@ void zebra_pbr_ipset_free(void *arg) XFREE(MTYPE_TMP, ipset); } -uint32_t zebra_pbr_ipset_hash_key(void *arg) +uint32_t zebra_pbr_ipset_hash_key(const void *arg) { - struct zebra_pbr_ipset *ipset = (struct zebra_pbr_ipset *)arg; + const struct zebra_pbr_ipset *ipset = arg; uint32_t *pnt = (uint32_t *)&ipset->ipset_name; uint32_t key = jhash_1word(ipset->vrf_id, 0x63ab42de); @@ -290,12 +290,12 @@ void zebra_pbr_ipset_entry_free(void *arg) XFREE(MTYPE_TMP, ipset); } -uint32_t zebra_pbr_ipset_entry_hash_key(void *arg) +uint32_t zebra_pbr_ipset_entry_hash_key(const void *arg) { - struct zebra_pbr_ipset_entry *ipset; + const struct zebra_pbr_ipset_entry *ipset; uint32_t key; - ipset = (struct zebra_pbr_ipset_entry *)arg; + ipset = arg; key = prefix_hash_key(&ipset->src); key = jhash_1word(ipset->unique, key); key = jhash_1word(prefix_hash_key(&ipset->dst), key); @@ -359,9 +359,9 @@ void zebra_pbr_iptable_free(void *arg) XFREE(MTYPE_TMP, iptable); } -uint32_t zebra_pbr_iptable_hash_key(void *arg) +uint32_t zebra_pbr_iptable_hash_key(const void *arg) { - struct zebra_pbr_iptable *iptable = (struct zebra_pbr_iptable *)arg; + const struct zebra_pbr_iptable *iptable = arg; uint32_t *pnt = (uint32_t *)&(iptable->ipset_name); uint32_t key; diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 0d5549110760..cc1cc5acd58d 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -211,7 +211,7 @@ extern void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable, extern int kernel_pbr_rule_del(struct zebra_pbr_rule *rule); extern void zebra_pbr_rules_free(void *arg); -extern uint32_t zebra_pbr_rules_hash_key(void *arg); +extern uint32_t zebra_pbr_rules_hash_key(const void *arg); extern bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2); /* has operates on 32bit pointer @@ -220,16 +220,16 @@ extern bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2); #define ZEBRA_IPSET_NAME_HASH_SIZE (ZEBRA_IPSET_NAME_SIZE / 4) extern void zebra_pbr_ipset_free(void *arg); -extern uint32_t zebra_pbr_ipset_hash_key(void *arg); +extern uint32_t zebra_pbr_ipset_hash_key(const void *arg); extern bool zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2); extern void zebra_pbr_ipset_entry_free(void *arg); -extern uint32_t zebra_pbr_ipset_entry_hash_key(void *arg); +extern uint32_t zebra_pbr_ipset_entry_hash_key(const void *arg); extern bool zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2); extern void zebra_pbr_iptable_free(void *arg); -extern uint32_t zebra_pbr_iptable_hash_key(void *arg); +extern uint32_t zebra_pbr_iptable_hash_key(const void *arg); extern bool zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2); extern void zebra_pbr_init(void); diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index bb352dc2ffd6..8b52d0a0e2e7 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -93,6 +93,7 @@ const char ZEBRA_PTM_BFD_IFNAME_FIELD[] = "ifName"; const char ZEBRA_PTM_BFD_MAX_HOP_CNT_FIELD[] = "maxHopCnt"; const char ZEBRA_PTM_BFD_SEND_EVENT[] = "sendEvent"; const char ZEBRA_PTM_BFD_VRF_NAME_FIELD[] = "vrfName"; +const char ZEBRA_PTM_BFD_CBIT_FIELD[] = "bfdcbit"; static ptm_lib_handle_t *ptm_hdl; @@ -588,6 +589,8 @@ static int zebra_ptm_handle_msg_cb(void *arg, void *in_ctxt) char port_str[128]; char cbl_str[32]; char cmd_status_str[32]; + char vrf_str[64]; + struct vrf *vrf = NULL; ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_CMD_STATUS_STR, cmd_status_str); @@ -597,6 +600,19 @@ static int zebra_ptm_handle_msg_cb(void *arg, void *in_ctxt) return 0; } + ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_BFDVRF_STR, vrf_str); + + if (vrf_str[0] == '\0') { + zlog_debug("%s: Key %s not found in PTM msg", __func__, + ZEBRA_PTM_BFDVRF_STR); + vrf = vrf_lookup_by_id(VRF_DEFAULT); + } else + vrf = vrf_lookup_by_name(vrf_str); + if (!vrf) { + zlog_debug("%s: Could not obtain appropriate VRF", + __func__); + return 0; + } ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_PORT_STR, port_str); if (port_str[0] == '\0') { @@ -606,7 +622,7 @@ static int zebra_ptm_handle_msg_cb(void *arg, void *in_ctxt) } if (strcmp(ZEBRA_PTM_INVALID_PORT_NAME, port_str)) { - ifp = if_lookup_by_name_all_vrf(port_str); + ifp = if_lookup_by_name(port_str, vrf->vrf_id); if (!ifp) { flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE, @@ -688,6 +704,7 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS) char tmp_buf[64]; int data_len = ZEBRA_PTM_SEND_MAX_SOCKBUF; unsigned int pid; + uint8_t cbit_set; if (hdr->command == ZEBRA_BFD_DEST_UPDATE) client->bfd_peer_upd8_cnt++; @@ -813,6 +830,10 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS) ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_IFNAME_FIELD, if_name); } + STREAM_GETC(s, cbit_set); + sprintf(tmp_buf, "%d", cbit_set); + ptm_lib_append_msg(ptm_hdl, out_ctxt, + ZEBRA_PTM_BFD_CBIT_FIELD, tmp_buf); sprintf(tmp_buf, "%d", 1); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_SEND_EVENT, @@ -1188,8 +1209,8 @@ static void pp_free_all(void); static void zebra_ptm_send_bfdd(struct stream *msg); static void zebra_ptm_send_clients(struct stream *msg); static int _zebra_ptm_bfd_client_deregister(struct zserv *zs); -static void _zebra_ptm_reroute(struct zserv *zs, struct stream *msg, - uint32_t command); +static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf, + struct stream *msg, uint32_t command); /* @@ -1392,8 +1413,8 @@ void zebra_ptm_finish(void) /* * Message handling. */ -static void _zebra_ptm_reroute(struct zserv *zs, struct stream *msg, - uint32_t command) +static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf, + struct stream *msg, uint32_t command) { struct stream *msgc; size_t zmsglen, zhdrlen; @@ -1420,7 +1441,7 @@ static void _zebra_ptm_reroute(struct zserv *zs, struct stream *msg, * one callback at the `bfdd` side, however the real command * number will be included right after the zebra header. */ - zclient_create_header(msgc, ZEBRA_BFD_DEST_REPLAY, 0); + zclient_create_header(msgc, ZEBRA_BFD_DEST_REPLAY, zvrf->vrf->vrf_id); stream_putl(msgc, command); /* Update the data pointers. */ @@ -1446,7 +1467,7 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS) zlog_debug("bfd_dst_register msg from client %s: length=%d", zebra_route_string(client->proto), hdr->length); - _zebra_ptm_reroute(client, msg, ZEBRA_BFD_DEST_REGISTER); + _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_DEST_REGISTER); } void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS) @@ -1455,7 +1476,7 @@ void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS) zlog_debug("bfd_dst_deregister msg from client %s: length=%d", zebra_route_string(client->proto), hdr->length); - _zebra_ptm_reroute(client, msg, ZEBRA_BFD_DEST_DEREGISTER); + _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_DEST_DEREGISTER); } void zebra_ptm_bfd_client_register(ZAPI_HANDLER_ARGS) @@ -1464,7 +1485,7 @@ void zebra_ptm_bfd_client_register(ZAPI_HANDLER_ARGS) zlog_debug("bfd_client_register msg from client %s: length=%d", zebra_route_string(client->proto), hdr->length); - _zebra_ptm_reroute(client, msg, ZEBRA_BFD_CLIENT_REGISTER); + _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_CLIENT_REGISTER); } void zebra_ptm_bfd_dst_replay(ZAPI_HANDLER_ARGS) @@ -1488,7 +1509,7 @@ void zebra_ptm_bfd_dst_replay(ZAPI_HANDLER_ARGS) * special treatment. */ if (client->proto != ZEBRA_ROUTE_BFD) { - _zebra_ptm_reroute(client, msg, ZEBRA_BFD_DEST_REPLAY); + _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_DEST_REPLAY); return; } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 0e1df1cc3501..cc614abac54f 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -54,6 +54,7 @@ #include "zebra/zebra_vxlan.h" #include "zebra/zapi_msg.h" #include "zebra/zebra_dplane.h" +#include "zebra/zebra_nhg.h" /* * Event, list, and mutex for delivery of dataplane results @@ -71,7 +72,7 @@ extern int allow_delete; /* Each route type's string and default distance value. */ static const struct { int key; - int distance; + uint8_t distance; uint8_t meta_q_map; } route_info[ZEBRA_ROUTE_MAX] = { [ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0, 4}, @@ -98,14 +99,15 @@ static const struct { [ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, 20, 3}, [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100, 2}, [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, 4}, + [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 4}, + [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 4}, + [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 2}, + [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 4} + /* Any new route type added to zebra, should be mirrored here */ /* no entry/default: 150 */ }; -/* RPF lookup behaviour */ -static enum multicast_mode ipv4_multicast_mode = MCAST_NO_CONFIG; - - static void __attribute__((format(printf, 5, 6))) _rnode_zlog(const char *_func, vrf_id_t vrf_id, struct route_node *rn, int priority, const char *msgfmt, ...) @@ -123,7 +125,7 @@ _rnode_zlog(const char *_func, vrf_id_t vrf_id, struct route_node *rn, srcdest_rnode2str(rn, buf, sizeof(buf)); if (info->safi == SAFI_MULTICAST) - strcat(buf, " (MRIB)"); + strlcat(buf, " (MRIB)", sizeof(buf)); } else { snprintf(buf, sizeof(buf), "{(route_node *) NULL}"); } @@ -161,8 +163,7 @@ int is_zebra_valid_kernel_table(uint32_t table_id) int is_zebra_main_routing_table(uint32_t table_id) { - if ((table_id == RT_TABLE_MAIN) - || (table_id == zrouter.rtm_table_default)) + if (table_id == RT_TABLE_MAIN) return 1; return 0; } @@ -332,299 +333,6 @@ struct nexthop *route_entry_nexthop_blackhole_add(struct route_entry *re, return nexthop; } -static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, - struct nexthop *nexthop) -{ - struct nexthop *resolved_hop; - - resolved_hop = nexthop_new(); - SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); - - resolved_hop->vrf_id = nexthop->vrf_id; - switch (newhop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - /* If the resolving route specifies a gateway, use it */ - resolved_hop->type = newhop->type; - resolved_hop->gate.ipv4 = newhop->gate.ipv4; - - if (newhop->ifindex) { - resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; - resolved_hop->ifindex = newhop->ifindex; - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - resolved_hop->type = newhop->type; - resolved_hop->gate.ipv6 = newhop->gate.ipv6; - - if (newhop->ifindex) { - resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; - resolved_hop->ifindex = newhop->ifindex; - } - break; - case NEXTHOP_TYPE_IFINDEX: - /* If the resolving route is an interface route, - * it means the gateway we are looking up is connected - * to that interface. (The actual network is _not_ onlink). - * Therefore, the resolved route should have the original - * gateway as nexthop as it is directly connected. - * - * On Linux, we have to set the onlink netlink flag because - * otherwise, the kernel won't accept the route. - */ - resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; - if (afi == AFI_IP) { - resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; - resolved_hop->gate.ipv4 = nexthop->gate.ipv4; - } else if (afi == AFI_IP6) { - resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; - resolved_hop->gate.ipv6 = nexthop->gate.ipv6; - } - resolved_hop->ifindex = newhop->ifindex; - break; - case NEXTHOP_TYPE_BLACKHOLE: - resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE; - resolved_hop->bh_type = nexthop->bh_type; - break; - } - - if (newhop->flags & NEXTHOP_FLAG_ONLINK) - resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; - - /* Copy labels of the resolved route */ - if (newhop->nh_label) - nexthop_add_labels(resolved_hop, newhop->nh_label_type, - newhop->nh_label->num_labels, - &newhop->nh_label->label[0]); - - resolved_hop->rparent = nexthop; - nexthop_add(&nexthop->resolved, resolved_hop); -} - -/* - * Given a nexthop we need to properly recursively resolve - * the route. As such, do a table lookup to find and match - * if at all possible. Set the nexthop->ifindex as appropriate - */ -static int nexthop_active(afi_t afi, struct route_entry *re, - struct nexthop *nexthop, - struct route_node *top) -{ - struct prefix p; - struct route_table *table; - struct route_node *rn; - struct route_entry *match = NULL; - int resolved; - struct nexthop *newhop; - struct interface *ifp; - rib_dest_t *dest; - - if ((nexthop->type == NEXTHOP_TYPE_IPV4) - || nexthop->type == NEXTHOP_TYPE_IPV6) - nexthop->ifindex = 0; - - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthops_free(nexthop->resolved); - nexthop->resolved = NULL; - re->nexthop_mtu = 0; - - /* - * If the kernel has sent us a route, then - * by golly gee whiz it's a good route. - */ - if (re->type == ZEBRA_ROUTE_KERNEL || - re->type == ZEBRA_ROUTE_SYSTEM) - return 1; - - /* - * Check to see if we should trust the passed in information - * for UNNUMBERED interfaces as that we won't find the GW - * address in the routing table. - * This check should suffice to handle IPv4 or IPv6 routes - * sourced from EVPN routes which are installed with the - * next hop as the remote VTEP IP. - */ - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) { - ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); - if (!ifp) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Onlink and interface: %u[%u] does not exist", - __PRETTY_FUNCTION__, nexthop->ifindex, - nexthop->vrf_id); - return 0; - } - if (connected_is_unnumbered(ifp)) { - if (if_is_operative(ifp)) - return 1; - else { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Onlink and interface %s is not operative", - __PRETTY_FUNCTION__, ifp->name); - return 0; - } - } - if (!if_is_operative(ifp)) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Interface %s is not unnumbered", - __PRETTY_FUNCTION__, - ifp ? ifp->name : "Unknown"); - return 0; - } - } - - /* Make lookup prefix. */ - memset(&p, 0, sizeof(struct prefix)); - switch (afi) { - case AFI_IP: - p.family = AF_INET; - p.prefixlen = IPV4_MAX_PREFIXLEN; - p.u.prefix4 = nexthop->gate.ipv4; - break; - case AFI_IP6: - p.family = AF_INET6; - p.prefixlen = IPV6_MAX_PREFIXLEN; - p.u.prefix6 = nexthop->gate.ipv6; - break; - default: - assert(afi != AFI_IP && afi != AFI_IP6); - break; - } - /* Lookup table. */ - table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id); - if (!table) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: Table not found", - __PRETTY_FUNCTION__); - return 0; - } - - rn = route_node_match(table, (struct prefix *)&p); - while (rn) { - route_unlock_node(rn); - - /* Lookup should halt if we've matched against ourselves ('top', - * if specified) - i.e., we cannot have a nexthop NH1 is - * resolved by a route NH1. The exception is if the route is a - * host route. - */ - if (top && rn == top) - if (((afi == AFI_IP) && (rn->p.prefixlen != 32)) - || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Matched against ourself and prefix length is not max bit length", - __PRETTY_FUNCTION__); - return 0; - } - - /* Pick up selected route. */ - /* However, do not resolve over default route unless explicitly - * allowed. */ - if (is_default_prefix(&rn->p) - && !rnh_resolve_via_default(p.family)) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t:%s: Resolved against default route", - __PRETTY_FUNCTION__); - return 0; - } - - dest = rib_dest_from_rnode(rn); - if (dest && dest->selected_fib - && !CHECK_FLAG(dest->selected_fib->status, - ROUTE_ENTRY_REMOVED) - && dest->selected_fib->type != ZEBRA_ROUTE_TABLE) - match = dest->selected_fib; - - /* If there is no selected route or matched route is EGP, go up - tree. */ - if (!match) { - do { - rn = rn->parent; - } while (rn && rn->info == NULL); - if (rn) - route_lock_node(rn); - - continue; - } - - if (match->type == ZEBRA_ROUTE_CONNECT) { - /* Directly point connected route. */ - newhop = match->ng.nexthop; - if (newhop) { - if (nexthop->type == NEXTHOP_TYPE_IPV4 - || nexthop->type == NEXTHOP_TYPE_IPV6) - nexthop->ifindex = newhop->ifindex; - } - return 1; - } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { - resolved = 0; - for (ALL_NEXTHOPS(match->ng, newhop)) { - if (!CHECK_FLAG(match->status, - ROUTE_ENTRY_INSTALLED)) - continue; - if (CHECK_FLAG(newhop->flags, - NEXTHOP_FLAG_RECURSIVE)) - continue; - - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE); - SET_FLAG(re->status, - ROUTE_ENTRY_NEXTHOPS_CHANGED); - nexthop_set_resolved(afi, newhop, nexthop); - resolved = 1; - } - if (resolved) - re->nexthop_mtu = match->mtu; - if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: Recursion failed to find", - __PRETTY_FUNCTION__); - return resolved; - } else if (re->type == ZEBRA_ROUTE_STATIC) { - resolved = 0; - for (ALL_NEXTHOPS(match->ng, newhop)) { - if (!CHECK_FLAG(match->status, - ROUTE_ENTRY_INSTALLED)) - continue; - if (CHECK_FLAG(newhop->flags, - NEXTHOP_FLAG_RECURSIVE)) - continue; - - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE); - nexthop_set_resolved(afi, newhop, nexthop); - resolved = 1; - } - if (resolved) - re->nexthop_mtu = match->mtu; - - if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Static route unable to resolve", - __PRETTY_FUNCTION__); - return resolved; - } else { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) { - zlog_debug("\t%s: Route Type %s has not turned on recursion", - __PRETTY_FUNCTION__, - zebra_route_string(re->type)); - if (re->type == ZEBRA_ROUTE_BGP && - !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP)) - zlog_debug("\tEBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\""); - } - return 0; - } - } - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: Nexthop did not lookup in table", - __PRETTY_FUNCTION__); - return 0; -} - struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, union g_addr *addr, struct route_node **rn_out) { @@ -692,7 +400,7 @@ struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, struct route_node *m_rn = NULL, *u_rn = NULL; union g_addr gaddr = {.ipv4 = addr}; - switch (ipv4_multicast_mode) { + switch (zrouter.ipv4_multicast_mode) { case MCAST_MRIB_ONLY: return rib_match(AFI_IP, SAFI_MULTICAST, vrf_id, &gaddr, rn_out); @@ -744,19 +452,6 @@ struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, return re; } -void multicast_mode_ipv4_set(enum multicast_mode mode) -{ - if (IS_ZEBRA_DEBUG_RIB) - zlog_debug("%s: multicast lookup mode set (%d)", __func__, - mode); - ipv4_multicast_mode = mode; -} - -enum multicast_mode multicast_mode_ipv4_get(void) -{ - return ipv4_multicast_mode; -} - struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) { struct route_table *table; @@ -795,195 +490,6 @@ struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) return NULL; } -#define RIB_SYSTEM_ROUTE(R) \ - ((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT) - -#define RIB_KERNEL_ROUTE(R) \ - ((R)->type == ZEBRA_ROUTE_KERNEL) - -/* This function verifies reachability of one given nexthop, which can be - * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored - * in nexthop->flags field. The nexthop->ifindex will be updated - * appropriately as well. An existing route map can turn - * (otherwise active) nexthop into inactive, but not vice versa. - * - * The return value is the final value of 'ACTIVE' flag. - */ -static unsigned nexthop_active_check(struct route_node *rn, - struct route_entry *re, - struct nexthop *nexthop) -{ - struct interface *ifp; - route_map_result_t ret = RMAP_MATCH; - int family; - char buf[SRCDEST2STR_BUFFER]; - const struct prefix *p, *src_p; - struct zebra_vrf *zvrf; - - srcdest_rnode_prefixes(rn, &p, &src_p); - - if (rn->p.family == AF_INET) - family = AFI_IP; - else if (rn->p.family == AF_INET6) - family = AFI_IP6; - else - family = 0; - switch (nexthop->type) { - case NEXTHOP_TYPE_IFINDEX: - ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); - if (ifp && if_is_operative(ifp)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - break; - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - family = AFI_IP; - if (nexthop_active(AFI_IP, re, nexthop, rn)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - break; - case NEXTHOP_TYPE_IPV6: - family = AFI_IP6; - if (nexthop_active(AFI_IP6, re, nexthop, rn)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - /* RFC 5549, v4 prefix with v6 NH */ - if (rn->p.family != AF_INET) - family = AFI_IP6; - if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) { - ifp = if_lookup_by_index(nexthop->ifindex, - nexthop->vrf_id); - if (ifp && if_is_operative(ifp)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } else { - if (nexthop_active(AFI_IP6, re, nexthop, rn)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } - break; - case NEXTHOP_TYPE_BLACKHOLE: - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - break; - default: - break; - } - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: Unable to find a active nexthop", - __PRETTY_FUNCTION__); - return 0; - } - - /* XXX: What exactly do those checks do? Do we support - * e.g. IPv4 routes with IPv6 nexthops or vice versa? - */ - if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET) - || (family == AFI_IP6 && p->family != AF_INET6)) - return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - - /* The original code didn't determine the family correctly - * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi - * from the rib_table_info in those cases. - * Possibly it may be better to use only the rib_table_info - * in every case. - */ - if (!family) { - rib_table_info_t *info; - - info = srcdest_rnode_table_info(rn); - family = info->afi; - } - - memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr)); - - zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id); - if (!zvrf) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: zvrf is NULL", __PRETTY_FUNCTION__); - return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } - - /* It'll get set if required inside */ - ret = zebra_route_map_check(family, re->type, re->instance, p, - nexthop, zvrf, re->tag); - if (ret == RMAP_DENYMATCH) { - if (IS_ZEBRA_DEBUG_RIB) { - srcdest_rnode2str(rn, buf, sizeof(buf)); - zlog_debug( - "%u:%s: Filtering out with NH out %s due to route map", - re->vrf_id, buf, - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - } - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } - return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); -} - -/* - * Iterate over all nexthops of the given RIB entry and refresh their - * ACTIVE flag. re->nexthop_active_num is updated accordingly. If any - * nexthop is found to toggle the ACTIVE flag, the whole re structure - * is flagged with ROUTE_ENTRY_CHANGED. - * - * Return value is the new number of active nexthops. - */ -static int nexthop_active_update(struct route_node *rn, struct route_entry *re) -{ - struct nexthop *nexthop; - union g_addr prev_src; - unsigned int prev_active, new_active; - ifindex_t prev_index; - - re->nexthop_active_num = 0; - UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - - for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { - /* No protocol daemon provides src and so we're skipping - * tracking it */ - prev_src = nexthop->rmap_src; - prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - prev_index = nexthop->ifindex; - /* - * We need to respect the multipath_num here - * as that what we should be able to install from - * a multipath perpsective should not be a data plane - * decision point. - */ - new_active = nexthop_active_check(rn, re, nexthop); - if (new_active && re->nexthop_active_num >= multipath_num) { - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - new_active = 0; - } - if (new_active) - re->nexthop_active_num++; - /* Don't allow src setting on IPv6 addr for now */ - if (prev_active != new_active || prev_index != nexthop->ifindex - || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX - && nexthop->type < NEXTHOP_TYPE_IPV6) - && prev_src.ipv4.s_addr - != nexthop->rmap_src.ipv4.s_addr) - || ((nexthop->type >= NEXTHOP_TYPE_IPV6 - && nexthop->type < NEXTHOP_TYPE_BLACKHOLE) - && !(IPV6_ADDR_SAME(&prev_src.ipv6, - &nexthop->rmap_src.ipv6))) - || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) { - SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - SET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED); - } - } - - return re->nexthop_active_num; -} - /* * Is this RIB labeled-unicast? It must be of type BGP and all paths * (nexthops) must have a label. @@ -1064,8 +570,25 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, switch (ret) { case ZEBRA_DPLANE_REQUEST_QUEUED: SET_FLAG(re->status, ROUTE_ENTRY_QUEUED); - if (old) + + if (old) { SET_FLAG(old->status, ROUTE_ENTRY_QUEUED); + + /* Free old FIB nexthop group */ + if (old->fib_ng.nexthop) { + nexthops_free(old->fib_ng.nexthop); + old->fib_ng.nexthop = NULL; + } + + if (!RIB_SYSTEM_ROUTE(old)) { + /* Clear old route's FIB flags */ + for (ALL_NEXTHOPS(old->ng, nexthop)) { + UNSET_FLAG(nexthop->flags, + NEXTHOP_FLAG_FIB); + } + } + } + if (zvrf) zvrf->installs_queued++; break; @@ -1151,6 +674,12 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re) dest->selected_fib = NULL; + /* Free FIB nexthop group, if present */ + if (re->fib_ng.nexthop) { + nexthops_free(re->fib_ng.nexthop); + re->fib_ng.nexthop = NULL; + } + for (ALL_NEXTHOPS(re->ng, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); } @@ -1172,7 +701,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re) */ static int rib_can_delete_dest(rib_dest_t *dest) { - if (dest->routes) { + if (re_list_first(&dest->routes)) { return 0; } @@ -1200,7 +729,6 @@ static int rib_can_delete_dest(rib_dest_t *dest) void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq) { rib_dest_t *dest = rib_dest_from_rnode(rn); - struct listnode *node, *nnode; struct rnh *rnh; /* @@ -1232,7 +760,7 @@ void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq) * nht resolution and as such we need to call the * nexthop tracking evaluation code */ - for (ALL_LIST_ELEMENTS(dest->nht, node, nnode, rnh)) { + frr_each (rnh_list, &dest->nht, rnh) { struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(rnh->vrf_id); struct prefix *p = &rnh->node->p; @@ -1308,7 +836,7 @@ int rib_gc_dest(struct route_node *rn) zebra_rib_evaluate_rn_nexthops(rn, zebra_router_get_next_sequence()); dest->rnode = NULL; - list_delete(&dest->nht); + rnh_list_fini(&dest->nht); XFREE(MTYPE_RIB_DEST, dest); rn->info = NULL; @@ -1844,21 +1372,239 @@ static void zebra_rib_fixup_system(struct route_node *rn) } /* - * Route-update results processing after async dataplane update. + * Update a route from a dplane context. This consolidates common code + * that can be used in processing of results from FIB updates, and in + * async notification processing. + * The return is 'true' if the installed nexthops changed; 'false' otherwise. */ -static void rib_process_result(struct zebra_dplane_ctx *ctx) +static bool rib_update_re_from_ctx(struct route_entry *re, + struct route_node *rn, + struct zebra_dplane_ctx *ctx) +{ + char dest_str[PREFIX_STRLEN] = ""; + char nh_str[NEXTHOP_STRLEN]; + struct nexthop *nexthop, *ctx_nexthop; + bool matched; + const struct nexthop_group *ctxnhg; + bool is_selected = false; /* Is 're' currently the selected re? */ + bool changed_p = false; /* Change to nexthops? */ + rib_dest_t *dest; + + /* Note well: only capturing the prefix string if debug is enabled here; + * unconditional log messages will have to generate the string. + */ + if (IS_ZEBRA_DEBUG_RIB) + prefix2str(&(rn->p), dest_str, sizeof(dest_str)); + + dest = rib_dest_from_rnode(rn); + if (dest) + is_selected = (re == dest->selected_fib); + + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("update_from_ctx: %u:%s: %sSELECTED", + re->vrf_id, dest_str, (is_selected ? "" : "NOT ")); + + /* Update zebra's nexthop FIB flag for each nexthop that was installed. + * If the installed set differs from the set requested by the rib/owner, + * we use the fib-specific nexthop-group to record the actual FIB + * status. + */ + + /* + * First check the fib nexthop-group, if it's present. The comparison + * here is quite strict: we require that the fib sets match exactly. + */ + matched = false; + do { + if (re->fib_ng.nexthop == NULL) + break; + + matched = true; + + /* First check the route's fib nexthops */ + for (ALL_NEXTHOPS(re->fib_ng, nexthop)) { + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + ctx_nexthop = NULL; + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), + ctx_nexthop)) { + if (nexthop_same(ctx_nexthop, nexthop)) + break; + } + + if (ctx_nexthop == NULL) { + /* Nexthop not in the new installed set */ + if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + nexthop2str(nexthop, nh_str, + sizeof(nh_str)); + zlog_debug("update_from_ctx: no match for fib nh %s", + nh_str); + } + + matched = false; + break; + } + } + + if (!matched) + break; + + /* Check the new installed set */ + ctx_nexthop = NULL; + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), ctx_nexthop)) { + + if (CHECK_FLAG(ctx_nexthop->flags, + NEXTHOP_FLAG_RECURSIVE)) + continue; + + /* Compare with the current group's nexthops */ + nexthop = NULL; + for (ALL_NEXTHOPS(re->fib_ng, nexthop)) { + if (nexthop_same(nexthop, ctx_nexthop)) + break; + } + + if (nexthop == NULL) { + /* Nexthop not in the old installed set */ + if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + nexthop2str(ctx_nexthop, nh_str, + sizeof(nh_str)); + zlog_debug("update_from_ctx: no fib match for notif nh %s", + nh_str); + } + matched = false; + break; + } + } + + } while (0); + + /* If the new FIB set matches the existing FIB set, we're done. */ + if (matched) { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%u:%s update_from_ctx(): existing fib nhg, no change", + re->vrf_id, dest_str); + goto done; + + } else if (re->fib_ng.nexthop) { + /* + * Free stale fib list and move on to check the rib nhg. + */ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%u:%s update_from_ctx(): replacing fib nhg", + re->vrf_id, dest_str); + nexthops_free(re->fib_ng.nexthop); + re->fib_ng.nexthop = NULL; + + /* Note that the installed nexthops have changed */ + changed_p = true; + } else { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%u:%s update_from_ctx(): no fib nhg", + re->vrf_id, dest_str); + } + + /* + * Compare with the rib nexthop group. The comparison here is different: + * the RIB group may be a superset of the list installed in the FIB. We + * walk the RIB group, looking for the 'installable' candidate + * nexthops, and then check those against the set + * that is actually installed. + */ + matched = true; + for (ALL_NEXTHOPS(re->ng, nexthop)) { + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + continue; + + /* Check for a FIB nexthop corresponding to the RIB nexthop */ + ctx_nexthop = NULL; + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), ctx_nexthop)) { + if (nexthop_same(ctx_nexthop, nexthop)) + break; + } + + /* If the FIB doesn't know about the nexthop, + * it's not installed + */ + if (ctx_nexthop == NULL) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + nexthop2str(nexthop, nh_str, sizeof(nh_str)); + zlog_debug("update_from_ctx: no notif match for rib nh %s", + nh_str); + } + matched = false; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + changed_p = true; + + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + break; + } + + if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_FIB)) { + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + changed_p = true; + + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } else { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + changed_p = true; + + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } + } + + /* If all nexthops were processed, we're done */ + if (matched) { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%u:%s update_from_ctx(): rib nhg matched, changed '%s'", + re->vrf_id, dest_str, + (changed_p ? "true" : "false")); + goto done; + } + + /* FIB nexthop set differs from the RIB set: + * create a fib-specific nexthop-group + */ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%u:%s update_from_ctx(): changed %s, adding new fib nhg", + re->vrf_id, dest_str, + (changed_p ? "true" : "false")); + + ctxnhg = dplane_ctx_get_ng(ctx); + + if (ctxnhg->nexthop) + copy_nexthops(&(re->fib_ng.nexthop), ctxnhg->nexthop, NULL); + else { + /* Bit of a special case when the fib has _no_ installed + * nexthops. + */ + nexthop = nexthop_new(); + nexthop->type = NEXTHOP_TYPE_IPV4; + nexthop_add(&(re->fib_ng.nexthop), nexthop); + } + +done: + return changed_p; +} + +/* + * Helper to locate a zebra route-node from a dplane context. This is used + * when processing dplane results, e.g. Note well: the route-node is returned + * with a ref held - route_unlock_node() must be called eventually. + */ +static struct route_node * +rib_find_rn_from_ctx(const struct zebra_dplane_ctx *ctx) { struct route_table *table = NULL; - struct zebra_vrf *zvrf = NULL; struct route_node *rn = NULL; - struct route_entry *re = NULL, *old_re = NULL, *rib; - bool is_update = false; - struct nexthop *nexthop, *ctx_nexthop; - char dest_str[PREFIX_STRLEN] = ""; - enum dplane_op_e op; - enum zebra_dplane_result status; const struct prefix *dest_pfx, *src_pfx; - uint32_t seq; /* Locate rn and re(s) from ctx */ @@ -1868,7 +1614,7 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) dplane_ctx_get_table(ctx)); if (table == NULL) { if (IS_ZEBRA_DEBUG_DPLANE) { - zlog_debug("Failed to process dplane results: no table for afi %d, safi %d, vrf %u", + zlog_debug("Failed to find route for ctx: no table for afi %d, safi %d, vrf %u", dplane_ctx_get_afi(ctx), dplane_ctx_get_safi(ctx), dplane_ctx_get_vrf(ctx)); @@ -1876,8 +1622,35 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) goto done; } - zvrf = vrf_info_lookup(dplane_ctx_get_vrf(ctx)); + dest_pfx = dplane_ctx_get_dest(ctx); + src_pfx = dplane_ctx_get_src(ctx); + rn = srcdest_rnode_get(table, dest_pfx, + src_pfx ? (struct prefix_ipv6 *)src_pfx : NULL); + +done: + return rn; +} + + + +/* + * Route-update results processing after async dataplane update. + */ +static void rib_process_result(struct zebra_dplane_ctx *ctx) +{ + struct zebra_vrf *zvrf = NULL; + struct route_node *rn = NULL; + struct route_entry *re = NULL, *old_re = NULL, *rib; + bool is_update = false; + char dest_str[PREFIX_STRLEN] = ""; + enum dplane_op_e op; + enum zebra_dplane_result status; + const struct prefix *dest_pfx, *src_pfx; + uint32_t seq; + bool fib_changed = false; + + zvrf = vrf_info_lookup(dplane_ctx_get_vrf(ctx)); dest_pfx = dplane_ctx_get_dest(ctx); /* Note well: only capturing the prefix string if debug is enabled here; @@ -1886,9 +1659,8 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) if (IS_ZEBRA_DEBUG_DPLANE) prefix2str(dest_pfx, dest_str, sizeof(dest_str)); - src_pfx = dplane_ctx_get_src(ctx); - rn = srcdest_rnode_get(table, dplane_ctx_get_dest(ctx), - src_pfx ? (struct prefix_ipv6 *)src_pfx : NULL); + /* Locate rn and re(s) from ctx */ + rn = rib_find_rn_from_ctx(ctx); if (rn == NULL) { if (IS_ZEBRA_DEBUG_DPLANE) { zlog_debug("Failed to process dplane results: no route for %u:%s", @@ -1982,34 +1754,25 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) UNSET_FLAG(old_re->status, ROUTE_ENTRY_INSTALLED); } - /* Update zebra nexthop FIB flag for each - * nexthop that was installed. - */ - for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), - ctx_nexthop)) { - - if (!re) - continue; - for (ALL_NEXTHOPS(re->ng, nexthop)) { - if (nexthop_same(ctx_nexthop, nexthop)) - break; + /* Update zebra route based on the results in + * the context struct. + */ + if (re) { + fib_changed = + rib_update_re_from_ctx(re, rn, ctx); + + if (!fib_changed) { + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) + zlog_debug("%u:%s no fib change for re", + dplane_ctx_get_vrf( + ctx), + dest_str); } - if (nexthop == NULL) - continue; - - if (CHECK_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE)) - continue; - - if (CHECK_FLAG(ctx_nexthop->flags, - NEXTHOP_FLAG_FIB)) - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_FIB); - else - UNSET_FLAG(nexthop->flags, - NEXTHOP_FLAG_FIB); + /* Redistribute */ + redistribute_update(dest_pfx, src_pfx, + re, NULL); } /* @@ -2026,19 +1789,6 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) if (zvrf) zvrf->installs++; - /* Redistribute */ - /* - * TODO -- still calling the redist api using the - * route_entries, and there's a corner-case here: - * if there's no client for the 'new' route, a redist - * deleting the 'old' route will be sent. But if the - * 'old' context info was stale, 'old_re' will be - * NULL here and that delete will not be sent. - */ - if (re) - redistribute_update(dest_pfx, src_pfx, - re, old_re); - /* Notify route owner */ zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_INSTALLED); @@ -2113,6 +1863,179 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) dplane_ctx_fini(&ctx); } +/* + * Handle notification from async dataplane: the dataplane has detected + * some change to a route, and notifies zebra so that the control plane + * can reflect that change. + */ +static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) +{ + struct route_node *rn = NULL; + struct route_entry *re = NULL; + struct nexthop *nexthop; + char dest_str[PREFIX_STRLEN] = ""; + const struct prefix *dest_pfx, *src_pfx; + rib_dest_t *dest; + bool fib_changed = false; + bool debug_p = IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_RIB; + int start_count, end_count; + dest_pfx = dplane_ctx_get_dest(ctx); + + /* Note well: only capturing the prefix string if debug is enabled here; + * unconditional log messages will have to generate the string. + */ + if (debug_p) + prefix2str(dest_pfx, dest_str, sizeof(dest_str)); + + /* Locate rn and re(s) from ctx */ + rn = rib_find_rn_from_ctx(ctx); + if (rn == NULL) { + if (debug_p) { + zlog_debug("Failed to process dplane notification: no routes for %u:%s", + dplane_ctx_get_vrf(ctx), dest_str); + } + goto done; + } + + dest = rib_dest_from_rnode(rn); + srcdest_rnode_prefixes(rn, &dest_pfx, &src_pfx); + + if (debug_p) + zlog_debug("%u:%s Processing dplane notif ctx %p", + dplane_ctx_get_vrf(ctx), dest_str, ctx); + + /* + * Take a pass through the routes, look for matches with the context + * info. + */ + RNODE_FOREACH_RE(rn, re) { + if (rib_route_match_ctx(re, ctx, false /*!update*/)) + break; + } + + /* No match? Nothing we can do */ + if (re == NULL) { + if (debug_p) + zlog_debug("%u:%s Unable to process dplane notification: no entry for type %s", + dplane_ctx_get_vrf(ctx), dest_str, + zebra_route_string( + dplane_ctx_get_type(ctx))); + + goto done; + } + + /* Is this a notification that ... matters? We only really care about + * the route that is currently selected for installation. + */ + if (re != dest->selected_fib) { + /* TODO -- don't skip processing entirely? We might like to + * at least report on the event. + */ + if (debug_p) + zlog_debug("%u:%s dplane notif, but type %s not selected_fib", + dplane_ctx_get_vrf(ctx), dest_str, + zebra_route_string( + dplane_ctx_get_type(ctx))); + goto done; + } + + /* We'll want to determine whether the installation status of the + * route has changed: we'll check the status before processing, + * and then again if there's been a change. + */ + start_count = 0; + for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + start_count++; + } + + /* Update zebra's nexthop FIB flags based on the context struct's + * nexthops. + */ + fib_changed = rib_update_re_from_ctx(re, rn, ctx); + + if (!fib_changed) { + if (debug_p) + zlog_debug("%u:%s No change from dplane notification", + dplane_ctx_get_vrf(ctx), dest_str); + + goto done; + } + + /* + * Perform follow-up work if the actual status of the prefix + * changed. + */ + + end_count = 0; + for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + end_count++; + } + + /* Various fib transitions: changed nexthops; from installed to + * not-installed; or not-installed to installed. + */ + if (start_count > 0 && end_count > 0) { + + /* Changed nexthops - update kernel/others */ + dplane_route_notif_update(rn, re, + DPLANE_OP_ROUTE_UPDATE, ctx); + + } else if (start_count == 0 && end_count > 0) { + if (debug_p) + zlog_debug("%u:%s installed transition from dplane notification", + dplane_ctx_get_vrf(ctx), dest_str); + + /* We expect this to be the selected route, so we want + * to tell others about this transistion. + */ + SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); + + /* Changed nexthops - update kernel/others */ + dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_INSTALL, ctx); + + /* Redistribute, lsp, and nht update */ + redistribute_update(dest_pfx, src_pfx, re, NULL); + + zebra_rib_evaluate_rn_nexthops( + rn, zebra_router_get_next_sequence()); + + zebra_rib_evaluate_mpls(rn); + + } else if (start_count > 0 && end_count == 0) { + if (debug_p) + zlog_debug("%u:%s un-installed transition from dplane notification", + dplane_ctx_get_vrf(ctx), dest_str); + + /* Transition from _something_ installed to _nothing_ + * installed. + */ + /* We expect this to be the selected route, so we want + * to tell others about this transistion. + */ + UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); + + /* Changed nexthops - update kernel/others */ + dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_DELETE, ctx); + + /* Redistribute, lsp, and nht update */ + redistribute_delete(dest_pfx, src_pfx, re); + + zebra_rib_evaluate_rn_nexthops( + rn, zebra_router_get_next_sequence()); + + zebra_rib_evaluate_mpls(rn); + } + +done: + if (rn) + route_unlock_node(rn); + + /* Return context to dataplane module */ + dplane_ctx_fini(&ctx); +} + /* Take a list of route_node structs and return 1, if there was a record * picked from it and processed by rib_process(). Don't process more, * than one RN record; operate only in the specified sub-queue. @@ -2136,6 +2059,7 @@ static unsigned int process_subq(struct list *subq, uint8_t qindex) if (IS_ZEBRA_DEBUG_RIB_DETAILED) { char buf[SRCDEST2STR_BUFFER]; + srcdest_rnode2str(rnode, buf, sizeof(buf)); zlog_debug("%u:%s: rn %p dequeued from sub-queue %u", zvrf ? zvrf_id(zvrf) : 0, buf, rnode, qindex); @@ -2353,7 +2277,7 @@ rib_dest_t *zebra_rib_create_dest(struct route_node *rn) rib_dest_t *dest; dest = XCALLOC(MTYPE_RIB_DEST, sizeof(rib_dest_t)); - dest->nht = list_new(); + rnh_list_init(&dest->nht); route_lock_node(rn); /* rn route table reference */ rn->info = dest; dest->rnode = rn; @@ -2401,7 +2325,6 @@ rib_dest_t *zebra_rib_create_dest(struct route_node *rn) /* Add RE to head of the route node. */ static void rib_link(struct route_node *rn, struct route_entry *re, int process) { - struct route_entry *head; rib_dest_t *dest; afi_t afi; const char *rmap_name; @@ -2416,12 +2339,7 @@ static void rib_link(struct route_node *rn, struct route_entry *re, int process) dest = zebra_rib_create_dest(rn); } - head = dest->routes; - if (head) { - head->prev = re; - } - re->next = head; - dest->routes = re; + re_list_add_head(&dest->routes, re); afi = (rn->p.family == AF_INET) ? AFI_IP @@ -2471,19 +2389,14 @@ void rib_unlink(struct route_node *rn, struct route_entry *re) dest = rib_dest_from_rnode(rn); - if (re->next) - re->next->prev = re->prev; - - if (re->prev) - re->prev->next = re->next; - else { - dest->routes = re->next; - } + re_list_del(&dest->routes, re); if (dest->selected_fib == re) dest->selected_fib = NULL; nexthops_free(re->ng.nexthop); + nexthops_free(re->fib_ng.nexthop); + XFREE(MTYPE_RE, re); } @@ -2655,7 +2568,6 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) { struct route_table *table; struct route_node *rn; - unsigned changed = 0; rib_dest_t *dest; if (NULL == (table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id))) { @@ -2682,7 +2594,6 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) * of the rest of the RE. */ if (dest->selected_fib) { - changed = 1; if (IS_ZEBRA_DEBUG_RIB) { char buf[PREFIX_STRLEN]; @@ -2692,9 +2603,8 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) route_entry_dump(&rn->p, NULL, dest->selected_fib); } rib_uninstall(rn, dest->selected_fib); - } - if (changed) rib_queue_add(rn); + } } int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, @@ -2723,15 +2633,9 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, apply_mask_ipv6(src_p); /* Set default distance by route type. */ - if (re->distance == 0) { + if (re->distance == 0) re->distance = route_distance(re->type); - /* iBGP distance is 200. */ - if (re->type == ZEBRA_ROUTE_BGP - && CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP)) - re->distance = 200; - } - /* Lookup route node.*/ rn = srcdest_rnode_get(table, p, src_p); @@ -2874,7 +2778,11 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, break; } for (ALL_NEXTHOPS(re->ng, rtnh)) - if (nexthop_same_no_recurse(rtnh, nh)) { + /* + * No guarantee all kernel send nh with labels + * on delete. + */ + if (nexthop_same_no_labels(rtnh, nh)) { same = re; break; } @@ -3018,7 +2926,7 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, re->table = table_id; re->vrf_id = vrf_id; re->nexthop_num = 0; - re->uptime = time(NULL); + re->uptime = monotime(NULL); re->tag = tag; /* Add nexthop. */ @@ -3069,6 +2977,8 @@ void rib_update_table(struct route_table *table, rib_update_event_t event) continue; if (re->type != ZEBRA_ROUTE_STATIC) { + SET_FLAG(re->status, + ROUTE_ENTRY_CHANGED); rib_queue_add(rn); continue; } @@ -3082,8 +2992,11 @@ void rib_update_table(struct route_table *table, rib_update_event_t event) * gateway, NHT will * take care. */ - if (nh) + if (nh) { + SET_FLAG(re->status, + ROUTE_ENTRY_CHANGED); rib_queue_add(rn); + } } break; @@ -3093,8 +3006,12 @@ void rib_update_table(struct route_table *table, rib_update_event_t event) * protocol in * some cases (TODO). */ - if (rnode_to_ribs(rn)) + if (rnode_to_ribs(rn)) { + RNODE_FOREACH_RE_SAFE (rn, re, next) + SET_FLAG(re->status, + ROUTE_ENTRY_CHANGED); rib_queue_add(rn); + } break; default: @@ -3137,6 +3054,7 @@ void rib_sweep_table(struct route_table *table) for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) { RNODE_FOREACH_RE_SAFE (rn, re, next) { + if (IS_ZEBRA_DEBUG_RIB) route_entry_dump(&rn->p, NULL, re); @@ -3146,6 +3064,14 @@ void rib_sweep_table(struct route_table *table) if (!CHECK_FLAG(re->flags, ZEBRA_FLAG_SELFROUTE)) continue; + /* + * If routes are older than startup_time then + * we know we read them in from the kernel. + * As such we can safely remove them. + */ + if (zrouter.startup_time < re->uptime) + continue; + /* * So we are starting up and have received * routes from the kernel that we have installed @@ -3175,7 +3101,7 @@ void rib_sweep_table(struct route_table *table) } /* Sweep all RIB tables. */ -void rib_sweep_route(void) +int rib_sweep_route(struct thread *t) { struct vrf *vrf; struct zebra_vrf *zvrf; @@ -3189,6 +3115,8 @@ void rib_sweep_route(void) } zebra_router_sweep_route(); + + return 0; } /* Remove specific by protocol routes from 'table'. */ @@ -3219,18 +3147,23 @@ unsigned long rib_score_proto(uint8_t proto, unsigned short instance) { struct vrf *vrf; struct zebra_vrf *zvrf; + struct other_route_table *ort; unsigned long cnt = 0; - RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) - if ((zvrf = vrf->info) != NULL) - cnt += rib_score_proto_table( - proto, instance, - zvrf->table[AFI_IP][SAFI_UNICAST]) - + rib_score_proto_table( - proto, instance, - zvrf->table[AFI_IP6][SAFI_UNICAST]); + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { + zvrf = vrf->info; + if (!zvrf) + continue; + + cnt += rib_score_proto_table(proto, instance, + zvrf->table[AFI_IP][SAFI_UNICAST]) + + rib_score_proto_table( + proto, instance, + zvrf->table[AFI_IP6][SAFI_UNICAST]); - cnt += zebra_router_score_proto(proto, instance); + frr_each(otable, &zvrf->other_tables, ort) cnt += + rib_score_proto_table(proto, instance, ort->table); + } return cnt; } @@ -3321,13 +3254,40 @@ static int rib_process_dplane_results(struct thread *thread) case DPLANE_OP_ROUTE_INSTALL: case DPLANE_OP_ROUTE_UPDATE: case DPLANE_OP_ROUTE_DELETE: - rib_process_result(ctx); + { + /* Bit of special case for route updates + * that were generated by async notifications: + * we don't want to continue processing these + * in the rib. + */ + if (dplane_ctx_get_notif_provider(ctx) == 0) + rib_process_result(ctx); + else + dplane_ctx_fini(&ctx); + } + break; + + case DPLANE_OP_ROUTE_NOTIFY: + rib_process_dplane_notify(ctx); break; case DPLANE_OP_LSP_INSTALL: case DPLANE_OP_LSP_UPDATE: case DPLANE_OP_LSP_DELETE: - zebra_mpls_lsp_dplane_result(ctx); + { + /* Bit of special case for LSP updates + * that were generated by async notifications: + * we don't want to continue processing these. + */ + if (dplane_ctx_get_notif_provider(ctx) == 0) + zebra_mpls_lsp_dplane_result(ctx); + else + dplane_ctx_fini(&ctx); + } + break; + + case DPLANE_OP_LSP_NOTIFY: + zebra_mpls_process_dplane_notify(ctx); break; case DPLANE_OP_PW_INSTALL: @@ -3380,9 +3340,33 @@ static int rib_dplane_results(struct dplane_ctx_q *ctxlist) return 0; } +/* + * Ensure there are no empty slots in the route_info array. + * Every route type in zebra should be present there. + */ +static void check_route_info(void) +{ + int len = array_size(route_info); + + /* + * ZEBRA_ROUTE_SYSTEM is special cased since + * its key is 0 anyway. + * + * ZEBRA_ROUTE_ALL is also ignored. + */ + for (int i = 0; i < len; i++) { + if (i == ZEBRA_ROUTE_SYSTEM || i == ZEBRA_ROUTE_ALL) + continue; + assert(route_info[i].key); + assert(route_info[i].meta_q_map < MQ_SIZE); + } +} + /* Routing information base initialize. */ void rib_init(void) { + check_route_info(); + rib_queue_init(); /* Init dataplane, and register for results */ @@ -3451,7 +3435,7 @@ struct route_table *rib_tables_iter_next(rib_tables_iter_t *iter) while (1) { while (iter->afi_safi_ix - < (int)ZEBRA_NUM_OF(afi_safis)) { + < (int)array_size(afi_safis)) { table = zebra_vrf_table( afi_safis[iter->afi_safi_ix].afi, afi_safis[iter->afi_safi_ix].safi, diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 220a8006d011..1024f3a0525c 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -119,7 +119,7 @@ static void zebra_rnh_remove_from_routing_table(struct rnh *rnh) } dest = rib_dest_from_rnode(rn); - listnode_delete(dest->nht, rnh); + rnh_list_del(&dest->nht, rnh); route_unlock_node(rn); } @@ -145,7 +145,7 @@ static void zebra_rnh_store_in_routing_table(struct rnh *rnh) } dest = rib_dest_from_rnode(rn); - listnode_add(dest->nht, rnh); + rnh_list_add_tail(&dest->nht, rnh); route_unlock_node(rn); } @@ -251,7 +251,7 @@ void zebra_free_rnh(struct rnh *rnh) route_unlock_node(rern); dest = rib_dest_from_rnode(rern); - listnode_delete(dest->nht, rnh); + rnh_list_del(&dest->nht, rnh); } } free_state(rnh->vrf_id, rnh->state, rnh->node); @@ -592,54 +592,6 @@ static void zebra_rnh_notify_protocol_clients(struct zebra_vrf *zvrf, afi_t afi, zebra_rnh_clear_nexthop_rnh_filters(re); } -static void zebra_rnh_process_pbr_tables(afi_t afi, struct route_node *nrn, - struct rnh *rnh, - struct route_node *prn, - struct route_entry *re) -{ - struct zebra_router_table *zrt; - struct route_entry *o_re; - struct route_node *o_rn; - struct listnode *node; - struct zserv *client; - - /* - * We are only concerned about nexthops that change for - * anyone using PBR - */ - for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { - if (client->proto == ZEBRA_ROUTE_PBR) - break; - } - - if (!client) - return; - - RB_FOREACH (zrt, zebra_router_table_head, &zrouter.tables) { - if (afi != zrt->afi) - continue; - - for (o_rn = route_top(zrt->table); o_rn; - o_rn = srcdest_route_next(o_rn)) { - RNODE_FOREACH_RE (o_rn, o_re) { - if (o_re->type == ZEBRA_ROUTE_PBR) - break; - - } - - /* - * If we have a PBR route and a nexthop changes - * just rethink it. Yes this is a hammer, but - * a small one - */ - if (o_re) { - SET_FLAG(o_re->status, ROUTE_ENTRY_CHANGED); - rib_queue_add(o_rn); - } - } - } -} - /* * Utility to determine whether a candidate nexthop is useable. We make this * check in a couple of places, so this is a single home for the logic we @@ -834,8 +786,6 @@ static void zebra_rnh_eval_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, zebra_rnh_notify_protocol_clients(zvrf, afi, nrn, rnh, prn, rnh->state); - zebra_rnh_process_pbr_tables(afi, nrn, rnh, prn, rnh->state); - /* Process pseudowires attached to this nexthop */ zebra_rnh_process_pseudowires(zvrf->vrf->vrf_id, rnh); } @@ -1132,7 +1082,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, static void print_nh(struct nexthop *nexthop, struct vty *vty) { char buf[BUFSIZ]; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns = zebra_ns_lookup(nexthop->vrf_id); switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index 9cd9116eed01..95a39411816f 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -29,40 +29,6 @@ extern "C" { #endif -typedef enum { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE } rnh_type_t; - -/* Nexthop structure. */ -struct rnh { - uint8_t flags; - -#define ZEBRA_NHT_CONNECTED 0x1 -#define ZEBRA_NHT_DELETED 0x2 -#define ZEBRA_NHT_EXACT_MATCH 0x4 - - /* VRF identifier. */ - vrf_id_t vrf_id; - - afi_t afi; - - rnh_type_t type; - - uint32_t seqno; - - struct route_entry *state; - struct prefix resolved_route; - struct list *client_list; - - /* pseudowires dependent on this nh */ - struct list *zebra_pseudowire_list; - - struct route_node *node; - - /* - * if this has been filtered for the client - */ - int filtered[ZEBRA_ROUTE_MAX]; -}; - extern int zebra_rnh_ip_default_route; extern int zebra_rnh_ipv6_default_route; diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 5d1cbbe781d5..dbfe695a00ac 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -67,7 +67,7 @@ static int zebra_route_match_add(struct vty *vty, const char *command, int ret; int retval = CMD_SUCCESS; - ret = route_map_add_match(index, command, arg); + ret = route_map_add_match(index, command, arg, type); switch (ret) { case RMAP_RULE_MISSING: vty_out(vty, "%% Zebra Can't find rule.\n"); @@ -705,6 +705,9 @@ DEFPY (ip_protocol, { int ret, rtype; + assert(proto); + assert(rmap); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -737,6 +740,8 @@ DEFPY (no_ip_protocol, { int ret, rtype; + assert(proto); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -781,6 +786,9 @@ DEFPY (ipv6_protocol, { int ret, rtype; + assert(rmap); + assert(proto); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -813,6 +821,8 @@ DEFPY (no_ipv6_protocol, { int ret, rtype; + assert(proto); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -858,6 +868,9 @@ DEFPY (ip_protocol_nht_rmap, int ret, rtype; + assert(proto); + assert(rmap); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -890,6 +903,8 @@ DEFPY (no_ip_protocol_nht_rmap, { int ret, rtype; + assert(proto); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -935,6 +950,9 @@ DEFPY (ipv6_protocol_nht_rmap, { int ret, rtype; + assert(rmap); + assert(proto); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -967,6 +985,8 @@ DEFPY (no_ipv6_protocol_nht_rmap, { int ret, rtype; + assert(proto); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -1798,8 +1818,7 @@ static void zebra_route_map_delete(const char *rmap_name) route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED); } -static void zebra_route_map_event(route_map_event_t event, - const char *rmap_name) +static void zebra_route_map_event(const char *rmap_name) { if (route_map_mark_updated(rmap_name) == 0) zebra_route_map_mark_update(rmap_name); diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index a81752d20599..4352d688a1da 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -29,8 +29,13 @@ #include "zebra_pbr.h" #include "zebra_vxlan.h" #include "zebra_mlag.h" +#include "zebra_nhg.h" +#include "debug.h" -struct zebra_router zrouter; +struct zebra_router zrouter = { + .multipath_num = MULTIPATH_NUM, + .ipv4_multicast_mode = MCAST_NO_CONFIG, +}; static inline int zebra_router_table_entry_compare(const struct zebra_router_table *e1, @@ -117,19 +122,6 @@ struct route_table *zebra_router_get_table(struct zebra_vrf *zvrf, return zrt->table; } -unsigned long zebra_router_score_proto(uint8_t proto, unsigned short instance) -{ - struct zebra_router_table *zrt; - unsigned long cnt = 0; - - RB_FOREACH (zrt, zebra_router_table_head, &zrouter.tables) { - if (zrt->ns_id != NS_DEFAULT) - continue; - cnt += rib_score_proto_table(proto, instance, zrt->table); - } - return cnt; -} - void zebra_router_show_table_summary(struct vty *vty) { struct zebra_router_table *zrt; @@ -198,6 +190,19 @@ uint32_t zebra_router_get_next_sequence(void) memory_order_relaxed); } +void multicast_mode_ipv4_set(enum multicast_mode mode) +{ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: multicast lookup mode set (%d)", __func__, + mode); + zrouter.ipv4_multicast_mode = mode; +} + +enum multicast_mode multicast_mode_ipv4_get(void) +{ + return zrouter.ipv4_multicast_mode; +} + void zebra_router_terminate(void) { struct zebra_router_table *zrt, *tmp; @@ -226,7 +231,6 @@ void zebra_router_init(void) { zrouter.sequence_num = 0; - zrouter.rtm_table_default = 0; zrouter.packets_to_process = ZEBRA_ZAPI_PACKETS_TO_PROCESS; zebra_vxlan_init(); diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index 72b5e9b9b1e0..e50f8a1186f2 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -50,6 +50,17 @@ RB_HEAD(zebra_router_table_head, zebra_router_table); RB_PROTOTYPE(zebra_router_table_head, zebra_router_table, zebra_router_table_entry, zebra_router_table_entry_compare) +/* RPF lookup behaviour */ +enum multicast_mode { + MCAST_NO_CONFIG = 0, /* MIX_MRIB_FIRST, but no show in config write */ + MCAST_MRIB_ONLY, /* MRIB only */ + MCAST_URIB_ONLY, /* URIB only */ + MCAST_MIX_MRIB_FIRST, /* MRIB, if nothing at all then URIB */ + MCAST_MIX_DISTANCE, /* MRIB & URIB, lower distance wins */ + MCAST_MIX_PFXLEN, /* MRIB & URIB, longer prefix wins */ + /* on equal value, MRIB wins for last 2 */ +}; + struct zebra_mlag_info { /* Role this zebra router is playing */ enum mlag_role role; @@ -82,16 +93,12 @@ struct zebra_router { struct hash *iptable_hash; -#if defined(HAVE_RTADV) - struct rtadv rtadv; -#endif /* HAVE_RTADV */ + /* used if vrf backend is not network namespace */ + int rtadv_sock; /* A sequence number used for tracking routes */ _Atomic uint32_t sequence_num; - /* The default table used for this router */ - uint32_t rtm_table_default; - /* rib work queue */ #define ZEBRA_RIB_PROCESS_HOLD_TIME 10 #define ZEBRA_RIB_PROCESS_RETRY_TIME 1 @@ -113,8 +120,20 @@ struct zebra_router { * The EVPN instance, if any */ struct zebra_vrf *evpn_vrf; + + uint32_t multipath_num; + + /* RPF Lookup behavior */ + enum multicast_mode ipv4_multicast_mode; + + /* + * Time for when we sweep the rib from old routes + */ + time_t startup_time; }; +#define GRACEFUL_RESTART_TIME 60 + extern struct zebra_router zrouter; extern void zebra_router_init(void); @@ -131,8 +150,6 @@ extern void zebra_router_release_table(struct zebra_vrf *zvrf, uint32_t tableid, extern int zebra_router_config_write(struct vty *vty); -extern unsigned long zebra_router_score_proto(uint8_t proto, - unsigned short instance); extern void zebra_router_sweep_route(void); extern void zebra_router_show_table_summary(struct vty *vty); @@ -149,6 +166,10 @@ static inline struct zebra_vrf *zebra_vrf_get_evpn(void) : zebra_vrf_lookup_by_id(VRF_DEFAULT); } +extern void multicast_mode_ipv4_set(enum multicast_mode mode); + +extern enum multicast_mode multicast_mode_ipv4_get(void); + #ifdef __cplusplus } #endif diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 2d721ec8a1a5..fdf0cbc693fc 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -29,6 +29,7 @@ #include "vty.h" #include "zebra/zebra_router.h" +#include "zebra/rtadv.h" #include "zebra/debug.h" #include "zebra/zapi_msg.h" #include "zebra/rib.h" @@ -47,6 +48,8 @@ static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi, static void zebra_rnhtable_node_cleanup(struct route_table *table, struct route_node *node); +DEFINE_MTYPE_STATIC(ZEBRA, OTHER_TABLE, "Other Table"); + /* VRF information update. */ static void zebra_vrf_add_update(struct zebra_vrf *zvrf) { @@ -93,6 +96,9 @@ static int zebra_vrf_new(struct vrf *vrf) zvrf = zebra_vrf_alloc(); vrf->info = zvrf; zvrf->vrf = vrf; + + otable_init(&zvrf->other_tables); + router_id_init(zvrf); return 0; } @@ -114,6 +120,10 @@ static int zebra_vrf_enable(struct vrf *vrf) zvrf->zns = zebra_ns_lookup((ns_id_t)vrf->vrf_id); else zvrf->zns = zebra_ns_lookup(NS_DEFAULT); +#if defined(HAVE_RTADV) + rtadv_init(zvrf); +#endif + /* Inform clients that the VRF is now active. This is an * add for the clients. */ @@ -156,6 +166,10 @@ static int zebra_vrf_disable(struct vrf *vrf) /* Stop any VxLAN-EVPN processing. */ zebra_vxlan_vrf_disable(zvrf); +#if defined(HAVE_RTADV) + rtadv_terminate(zvrf); +#endif + /* Inform clients that the VRF is now inactive. This is a * delete for the clients. */ @@ -226,6 +240,7 @@ static int zebra_vrf_disable(struct vrf *vrf) static int zebra_vrf_delete(struct vrf *vrf) { struct zebra_vrf *zvrf = vrf->info; + struct other_route_table *otable; struct route_table *table; afi_t afi; safi_t safi; @@ -274,11 +289,22 @@ static int zebra_vrf_delete(struct vrf *vrf) route_table_finish(zvrf->import_check_table[afi]); } + otable = otable_pop(&zvrf->other_tables); + while (otable) { + zebra_router_release_table(zvrf, otable->table_id, + otable->afi, otable->safi); + XFREE(MTYPE_OTHER_TABLE, otable); + + otable = otable_pop(&zvrf->other_tables); + } + /* Cleanup EVPN states for vrf */ zebra_vxlan_vrf_delete(zvrf); list_delete_all_node(zvrf->rid_all_sorted_list); list_delete_all_node(zvrf->rid_lo_sorted_list); + + otable_fini(&zvrf->other_tables); XFREE(MTYPE_ZEBRA_VRF, zvrf); vrf->info = NULL; @@ -320,27 +346,34 @@ struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi, vrf_id_t vrf_id, uint32_t table_id) { - struct route_table *table = NULL; + struct zebra_vrf *zvrf = vrf_info_lookup(vrf_id); + struct other_route_table ort, *otable; + struct route_table *table; + + if (!zvrf) + return NULL; if (afi >= AFI_MAX || safi >= SAFI_MAX) return NULL; - if (vrf_id == VRF_DEFAULT) { - if (table_id == RT_TABLE_MAIN - || table_id == zrouter.rtm_table_default) - table = zebra_vrf_table(afi, safi, vrf_id); - else - table = zebra_vrf_other_route_table(afi, table_id, - vrf_id); - } else if (vrf_is_backend_netns()) { - if (table_id == RT_TABLE_MAIN - || table_id == zrouter.rtm_table_default) - table = zebra_vrf_table(afi, safi, vrf_id); - else - table = zebra_vrf_other_route_table(afi, table_id, - vrf_id); - } else - table = zebra_vrf_table(afi, safi, vrf_id); + if (table_id == zvrf->table_id) + return zebra_vrf_table(afi, safi, vrf_id); + + ort.afi = afi; + ort.safi = safi; + ort.table_id = table_id; + otable = otable_find(&zvrf->other_tables, &ort); + if (otable) + return otable->table; + + table = zebra_router_get_table(zvrf, table_id, afi, safi); + + otable = XCALLOC(MTYPE_OTHER_TABLE, sizeof(*otable)); + otable->afi = afi; + otable->safi = safi; + otable->table_id = table_id; + otable->table = table; + otable_add(&zvrf->other_tables, otable); return table; } @@ -357,7 +390,7 @@ void zebra_rtable_node_cleanup(struct route_table *table, if (node->info) { rib_dest_t *dest = node->info; - list_delete(&dest->nht); + rnh_list_fini(&dest->nht); XFREE(MTYPE_RIB_DEST, node->info); } } @@ -440,34 +473,6 @@ struct route_table *zebra_vrf_table(afi_t afi, safi_t safi, vrf_id_t vrf_id) return zvrf->table[afi][safi]; } -struct route_table *zebra_vrf_other_route_table(afi_t afi, uint32_t table_id, - vrf_id_t vrf_id) -{ - struct zebra_vrf *zvrf; - - zvrf = vrf_info_lookup(vrf_id); - if (!zvrf) - return NULL; - - if (afi >= AFI_MAX) - return NULL; - - if ((table_id != RT_TABLE_MAIN) - && (table_id != zrouter.rtm_table_default)) { - if (zvrf->table_id == RT_TABLE_MAIN || - zvrf->table_id == zrouter.rtm_table_default) { - /* this VRF use default table - * so in all cases, it does not use specific table - * so it is possible to configure tables in this VRF - */ - return zebra_router_get_table(zvrf, table_id, afi, - SAFI_UNICAST); - } - } - - return zvrf->table[afi][SAFI_UNICAST]; -} - static int vrf_config_write(struct vty *vty) { struct vrf *vrf; diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index c7a64d300a8e..972fe381cc1f 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -43,6 +43,18 @@ struct zebra_rmap { struct route_map *map; }; +PREDECL_RBTREE_UNIQ(otable); + +struct other_route_table { + struct otable_item next; + + afi_t afi; + safi_t safi; + uint32_t table_id; + + struct route_table *table; +}; + /* Routing table instance. */ struct zebra_vrf { /* Back pointer */ @@ -69,6 +81,8 @@ struct zebra_vrf { /* Import check table (used mostly by BGP */ struct route_table *import_check_table[AFI_MAX]; + struct otable_head other_tables; + /* 2nd pointer type used primarily to quell a warning on * ALL_LIST_ELEMENTS_RO */ @@ -155,6 +169,10 @@ struct zebra_vrf { uint64_t lsp_removals_queued; uint64_t lsp_installs; uint64_t lsp_removals; + +#if defined(HAVE_RTADV) + struct rtadv rtadv; +#endif /* HAVE_RTADV */ }; #define PROTO_RM_NAME(zvrf, afi, rtype) zvrf->proto_rm[afi][rtype].name #define NHT_RM_NAME(zvrf, afi, rtype) zvrf->nht_rm[afi][rtype].name @@ -192,6 +210,25 @@ static inline bool zvrf_is_active(struct zebra_vrf *zvrf) return zvrf->vrf->status & VRF_ACTIVE; } +static inline int +zvrf_other_table_compare_func(const struct other_route_table *a, + const struct other_route_table *b) +{ + if (a->afi != b->afi) + return a->afi - b->afi; + + if (a->safi != b->safi) + return a->safi - b->safi; + + if (a->table_id != b->table_id) + return a->table_id - b->table_id; + + return 0; +} + +DECLARE_RBTREE_UNIQ(otable, struct other_route_table, next, + zvrf_other_table_compare_func) + struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi, vrf_id_t vrf_id, uint32_t table_id); @@ -202,8 +239,6 @@ extern struct zebra_vrf *zebra_vrf_lookup_by_name(const char *); extern struct zebra_vrf *zebra_vrf_alloc(void); extern struct route_table *zebra_vrf_table(afi_t, safi_t, vrf_id_t); -extern struct route_table * -zebra_vrf_other_route_table(afi_t afi, uint32_t table_id, vrf_id_t vrf_id); extern int zebra_vrf_has_config(struct zebra_vrf *zvrf); extern void zebra_vrf_init(void); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 8cde07a1096b..257fb168d2f9 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -62,7 +62,7 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, bool supernets_only, int type, unsigned short ospf_instance_id); static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, - int mcast); + int mcast, bool use_fib); static void vty_show_ip_route_summary(struct vty *vty, struct route_table *table); static void vty_show_ip_route_summary_prefix(struct vty *vty, @@ -154,7 +154,7 @@ DEFUN (show_ip_rpf_addr, re = rib_match_ipv4_multicast(VRF_DEFAULT, addr, &rn); if (re) - vty_show_ip_route_detail(vty, rn, 1); + vty_show_ip_route_detail(vty, rn, 1, false); else vty_out(vty, "%% No match for RPF lookup\n"); @@ -186,14 +186,24 @@ static char re_status_output_char(struct route_entry *re, struct nexthop *nhop) /* New RIB. Detailed information for IPv4 route. */ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, - int mcast) + int mcast, bool use_fib) { struct route_entry *re; struct nexthop *nexthop; char buf[SRCDEST2STR_BUFFER]; struct zebra_vrf *zvrf; + rib_dest_t *dest; + + dest = rib_dest_from_rnode(rn); RNODE_FOREACH_RE (rn, re) { + /* + * If re not selected for forwarding, skip re + * for "show ip/ipv6 fib " + */ + if (use_fib && re != dest->selected_fib) + continue; + const char *mcast_info = ""; if (mcast) { rib_table_info_t *info = srcdest_rnode_table_info(rn); @@ -230,7 +240,7 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, time_t uptime; struct tm *tm; - uptime = time(NULL); + uptime = monotime(NULL); uptime -= re->uptime; tm = gmtime(&uptime); @@ -372,7 +382,8 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, } static void vty_show_ip_route(struct vty *vty, struct route_node *rn, - struct route_entry *re, json_object *json) + struct route_entry *re, json_object *json, + bool is_fib) { struct nexthop *nexthop; int len = 0; @@ -384,11 +395,20 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, time_t uptime; struct tm *tm; rib_dest_t *dest = rib_dest_from_rnode(rn); + struct nexthop_group *nhg; - uptime = time(NULL); + uptime = monotime(NULL); uptime -= re->uptime; tm = gmtime(&uptime); + /* If showing fib information, use the fib view of the + * nexthops. + */ + if (is_fib) + nhg = rib_active_nhg(re); + else + nhg = &(re->ng); + if (json) { json_route = json_object_new_object(); json_nexthops = json_object_new_array(); @@ -445,7 +465,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, json_object_string_add(json_route, "uptime", buf); - for (ALL_NEXTHOPS(re->ng, nexthop)) { + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { json_nexthop = json_object_new_object(); json_object_int_add(json_nexthop, "flags", @@ -615,8 +635,8 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, } /* Nexthop information. */ - for (ALL_NEXTHOPS(re->ng, nexthop)) { - if (nexthop == re->ng.nexthop) { + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + if (nexthop == nhg->nexthop) { /* Prefix information. */ len = vty_out(vty, "%c", zebra_route_char(re->type)); if (re->instance) @@ -749,18 +769,27 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, } static void vty_show_ip_route_detail_json(struct vty *vty, - struct route_node *rn) + struct route_node *rn, bool use_fib) { json_object *json = NULL; json_object *json_prefix = NULL; struct route_entry *re; char buf[BUFSIZ]; + rib_dest_t *dest; + + dest = rib_dest_from_rnode(rn); json = json_object_new_object(); json_prefix = json_object_new_array(); RNODE_FOREACH_RE (rn, re) { - vty_show_ip_route(vty, rn, re, json_prefix); + /* + * If re not selected for forwarding, skip re + * for "show ip/ipv6 fib json" + */ + if (use_fib && re != dest->selected_fib) + continue; + vty_show_ip_route(vty, rn, re, json_prefix, use_fib); } prefix2str(&rn->p, buf, sizeof(buf)); @@ -846,7 +875,7 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf, } } - vty_show_ip_route(vty, rn, re, json_prefix); + vty_show_ip_route(vty, rn, re, json_prefix, use_fib); } if (json_prefix) { @@ -1177,12 +1206,12 @@ DEFPY (show_route_detail, show_route_detail_cmd, "show\ <\ - ip$ipv4 route [vrf ]\ + ip$ipv4 [vrf ]\ <\ A.B.C.D$address\ |A.B.C.D/M$prefix\ >\ - |ipv6$ipv6 route [vrf ]\ + |ipv6$ipv6 [vrf ]\ <\ X:X::X:X$address\ |X:X::X:X/M$prefix\ @@ -1191,12 +1220,14 @@ DEFPY (show_route_detail, [json$json]", SHOW_STR IP_STR + "IPv6 forwarding table\n" "IP routing table\n" VRF_FULL_CMD_HELP_STR "Network in the IP routing table to display\n" "IP prefix /, e.g., 35.0.0.0/8\n" IP6_STR - "IP routing table\n" + "IPv6 forwarding table\n" + "IPv6 routing table\n" VRF_FULL_CMD_HELP_STR "IPv6 Address\n" "IPv6 prefix\n" @@ -1206,6 +1237,9 @@ DEFPY (show_route_detail, struct route_table *table; struct prefix p; struct route_node *rn; + bool use_fib = !!fib; + rib_dest_t *dest; + bool network_found = false; if (address_str) prefix_str = address_str; @@ -1231,13 +1265,35 @@ DEFPY (show_route_detail, continue; } + dest = rib_dest_from_rnode(rn); + if (use_fib && !dest->selected_fib) { + route_unlock_node(rn); + continue; + } + + network_found = true; if (json) - vty_show_ip_route_detail_json(vty, rn); + vty_show_ip_route_detail_json(vty, rn, + use_fib); else - vty_show_ip_route_detail(vty, rn, 0); + vty_show_ip_route_detail(vty, rn, 0, use_fib); route_unlock_node(rn); } + + if (!network_found) { + if (json) + vty_out(vty, "{}\n"); + else { + if (use_fib) + vty_out(vty, + "%% Network not in FIB\n"); + else + vty_out(vty, + "%% Network not in RIB\n"); + } + return CMD_WARNING; + } } else { vrf_id_t vrf_id = VRF_DEFAULT; @@ -1249,20 +1305,30 @@ DEFPY (show_route_detail, return CMD_SUCCESS; rn = route_node_match(table, &p); - if (!rn) { - vty_out(vty, "%% Network not in table\n"); - return CMD_WARNING; - } - if (!address_str && rn->p.prefixlen != p.prefixlen) { - vty_out(vty, "%% Network not in table\n"); - route_unlock_node(rn); + if (rn) + dest = rib_dest_from_rnode(rn); + + if (!rn || (!address_str && rn->p.prefixlen != p.prefixlen) || + (use_fib && dest && !dest->selected_fib)) { + if (json) + vty_out(vty, "{}\n"); + else { + if (use_fib) + vty_out(vty, + "%% Network not in FIB\n"); + else + vty_out(vty, + "%% Network not in table\n"); + } + if (rn) + route_unlock_node(rn); return CMD_WARNING; } if (json) - vty_show_ip_route_detail_json(vty, rn); + vty_show_ip_route_detail_json(vty, rn, use_fib); else - vty_show_ip_route_detail(vty, rn, 0); + vty_show_ip_route_detail(vty, rn, 0, use_fib); route_unlock_node(rn); } @@ -1496,7 +1562,7 @@ DEFUN (show_ipv6_mroute, vty_out(vty, SHOW_ROUTE_V6_HEADER); first = 0; } - vty_show_ip_route(vty, rn, re, NULL); + vty_show_ip_route(vty, rn, re, NULL, false); } return CMD_SUCCESS; } @@ -1528,7 +1594,7 @@ DEFUN (show_ipv6_mroute_vrf_all, vty_out(vty, SHOW_ROUTE_V6_HEADER); first = 0; } - vty_show_ip_route(vty, rn, re, NULL); + vty_show_ip_route(vty, rn, re, NULL, false); } } return CMD_SUCCESS; @@ -2556,40 +2622,6 @@ static int config_write_protocol(struct vty *vty) return 1; } -#ifdef HAVE_NETLINK -/* Display default rtm_table for all clients. */ -DEFUN (show_table, - show_table_cmd, - "show table", - SHOW_STR - "default routing table to use for all clients\n") -{ - vty_out(vty, "table %d\n", zrouter.rtm_table_default); - return CMD_SUCCESS; -} - -DEFUN (config_table, - config_table_cmd, - "table TABLENO", - "Configure target kernel routing table\n" - "TABLE integer\n") -{ - zrouter.rtm_table_default = strtol(argv[1]->arg, (char **)0, 10); - return CMD_SUCCESS; -} - -DEFUN (no_config_table, - no_config_table_cmd, - "no table [TABLENO]", - NO_STR - "Configure target kernel routing table\n" - "TABLE integer\n") -{ - zrouter.rtm_table_default = 0; - return CMD_SUCCESS; -} -#endif - DEFUN (show_zebra, show_zebra_cmd, "show zebra", @@ -2833,8 +2865,6 @@ DEFUN (zebra_show_routing_tables_summary, /* Table configuration write function. */ static int config_write_table(struct vty *vty) { - if (zrouter.rtm_table_default) - vty_out(vty, "table %d\n", zrouter.rtm_table_default); return 0; } @@ -2938,12 +2968,6 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &no_ip_forwarding_cmd); install_element(ENABLE_NODE, &show_zebra_cmd); -#ifdef HAVE_NETLINK - install_element(VIEW_NODE, &show_table_cmd); - install_element(CONFIG_NODE, &config_table_cmd); - install_element(CONFIG_NODE, &no_config_table_cmd); -#endif /* HAVE_NETLINK */ - install_element(VIEW_NODE, &show_ipv6_forwarding_cmd); install_element(CONFIG_NODE, &ipv6_forwarding_cmd); install_element(CONFIG_NODE, &no_ipv6_forwarding_cmd); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 22c489e6073f..077c1ff8f026 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -93,7 +93,7 @@ static void zvni_print_hash(struct hash_bucket *bucket, void *ctxt[]); static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, struct ipaddr *ip, uint8_t flags, uint32_t seq, int state, uint16_t cmd); -static unsigned int neigh_hash_keymake(void *p); +static unsigned int neigh_hash_keymake(const void *p); static void *zvni_neigh_alloc(void *p); static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip, struct ethaddr *mac); @@ -143,13 +143,12 @@ static zebra_l3vni_t *zl3vni_lookup(vni_t vni); static void *zl3vni_alloc(void *p); static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id); static int zl3vni_del(zebra_l3vni_t *zl3vni); -static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t); static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni); static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni); static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni); static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni); -static unsigned int mac_hash_keymake(void *p); +static unsigned int mac_hash_keymake(const void *p); static bool mac_cmp(const void *p1, const void *p2); static void *zvni_mac_alloc(void *p); static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr); @@ -168,7 +167,7 @@ static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac); static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac); static void zvni_install_mac_hash(struct hash_bucket *bucket, void *ctxt); -static unsigned int vni_hash_keymake(void *p); +static unsigned int vni_hash_keymake(const void *p); static void *zvni_alloc(void *p); static zebra_vni_t *zvni_lookup(vni_t vni); static zebra_vni_t *zvni_add(vni_t vni); @@ -213,7 +212,7 @@ static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, bool do_dad, bool *is_dup_detect, bool is_local); -static unsigned int zebra_vxlan_sg_hash_key_make(void *p); +static unsigned int zebra_vxlan_sg_hash_key_make(const void *p); static bool zebra_vxlan_sg_hash_eq(const void *p1, const void *p2); static void zebra_vxlan_sg_do_deref(struct zebra_vrf *zvrf, struct in_addr sip, struct in_addr mcast_grp); @@ -840,9 +839,9 @@ static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) return; if (json_vni == NULL) { - vty_out(vty, "%*s %-6s %-8s %-17s\n", + vty_out(vty, "%*s %-6s %-8s %-17s %u/%u\n", -wctx->addr_width, buf2, "local", - state_str, buf1); + state_str, buf1, n->loc_seq, n->rem_seq); } else { json_object_string_add(json_row, "type", "local"); json_object_string_add(json_row, "state", state_str); @@ -876,9 +875,9 @@ static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) "%*s %-6s %-8s %-17s %-21s\n", -wctx->addr_width, "Neighbor", "Type", "State", "MAC", "Remote VTEP"); - vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n", + vty_out(vty, "%*s %-6s %-8s %-17s %-21s %u/%u\n", -wctx->addr_width, buf2, "remote", state_str, - buf1, inet_ntoa(n->r_vtep_ip)); + buf1, inet_ntoa(n->r_vtep_ip), n->loc_seq, n->rem_seq); } else { json_object_string_add(json_row, "type", "remote"); json_object_string_add(json_row, "state", state_str); @@ -988,9 +987,9 @@ static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); if (json == NULL) { - vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n", + vty_out(vty, "%*s %-6s %-8s %-17s %-21s %s\n", -wctx.addr_width, "IP", "Type", - "State", "MAC", "Remote VTEP"); + "State", "MAC", "Remote VTEP", "Seq #'s"); } if (print_dup) hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, @@ -2158,16 +2157,16 @@ static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, /* * Make hash key for neighbors. */ -static unsigned int neigh_hash_keymake(void *p) +static unsigned int neigh_hash_keymake(const void *p) { - zebra_neigh_t *n = p; - struct ipaddr *ip = &n->ip; + const zebra_neigh_t *n = p; + const struct ipaddr *ip = &n->ip; if (IS_IPADDR_V4(ip)) return jhash_1word(ip->ipaddr_v4.s_addr, 0); return jhash2(ip->ipaddr_v6.s6_addr32, - ZEBRA_NUM_OF(ip->ipaddr_v6.s6_addr32), 0); + array_size(ip->ipaddr_v6.s6_addr32), 0); } /* @@ -3296,9 +3295,9 @@ static int zvni_remote_neigh_update(zebra_vni_t *zvni, /* * Make hash key for MAC. */ -static unsigned int mac_hash_keymake(void *p) +static unsigned int mac_hash_keymake(const void *p) { - zebra_mac_t *pmac = p; + const zebra_mac_t *pmac = p; const void *pnt = (void *)pmac->macaddr.octet; return jhash(pnt, ETH_ALEN, 0xa5a5a55a); @@ -3815,7 +3814,7 @@ static void zvni_read_mac_neigh(zebra_vni_t *zvni, struct interface *ifp) /* * Hash function for VNI. */ -static unsigned int vni_hash_keymake(void *p) +static unsigned int vni_hash_keymake(const void *p) { const zebra_vni_t *zvni = p; @@ -4688,7 +4687,7 @@ static int zl3vni_local_nh_del(zebra_l3vni_t *zl3vni, struct ipaddr *ip) /* * Hash function for L3 VNI. */ -static unsigned int l3vni_hash_keymake(void *p) +static unsigned int l3vni_hash_keymake(const void *p) { const zebra_l3vni_t *zl3vni = p; @@ -4842,7 +4841,7 @@ static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni) return zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); } -static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id) +zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id) { struct zebra_vrf *zvrf = NULL; @@ -7505,9 +7504,9 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "Add/update remote MAC %s intf %s(%u) VNI %u - del local", + "Add/update remote MAC %s intf %s(%u) VNI %u flags 0x%x - del local", prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, - ifp->ifindex, vni); + ifp->ifindex, vni, mac->flags); /* Remove MAC from BGP. */ zvni_mac_send_del_to_client(zvni->vni, macaddr); @@ -7520,6 +7519,7 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, zvni_mac_del(zvni, mac); } else { UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); } @@ -7603,11 +7603,6 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, return -1; } - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("DEL MAC %s intf %s(%u) VID %u -> VNI %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, - ifp->ifindex, vid, zvni->vni); - /* If entry doesn't exist, nothing to do. */ mac = zvni_mac_lookup(zvni, macaddr); if (!mac) @@ -7617,6 +7612,11 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) return 0; + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("DEL MAC %s intf %s(%u) VID %u -> VNI %u flags 0x%x", + prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, + ifp->ifindex, vid, zvni->vni, mac->flags); + /* Update all the neigh entries associated with this mac */ zvni_process_neigh_on_local_mac_del(zvni, mac); @@ -7631,6 +7631,7 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, zvni_mac_del(zvni, mac); } else { UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); } @@ -7858,12 +7859,18 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) s = msg; while (l < hdr->length) { + int flood_control __attribute__((unused)); + /* Obtain each remote VTEP and process. */ STREAM_GETL(s, vni); l += 4; STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN); l += IPV4_MAX_BYTELEN; + /* Flood control is intentionally ignored right now */ + STREAM_GETL(s, flood_control); + l += 4; + if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Recv VTEP_DEL %s VNI %u from %s", inet_ntoa(vtep_ip), vni, @@ -7949,7 +7956,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) l += 4; STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN); STREAM_GETL(s, flood_control); - l += IPV4_MAX_BYTELEN; + l += IPV4_MAX_BYTELEN + 4; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Recv VTEP_ADD %s VNI %u flood %d from %s", @@ -8779,6 +8786,13 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, return -1; } + if (zvrf->l3vni != vni) { + snprintf(err, err_str_sz, + "VNI %d doesn't exist in VRF: %s", + vni, zvrf->vrf->name); + return -1; + } + if (filter && !CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY)) { snprintf(err, ERR_STR_SZ, "prefix-routes-only is not set for the vni"); @@ -9442,9 +9456,9 @@ static int zebra_vxlan_sg_send(struct prefix_sg *sg, return zserv_send_message(client, s); } -static unsigned int zebra_vxlan_sg_hash_key_make(void *p) +static unsigned int zebra_vxlan_sg_hash_key_make(const void *p) { - zebra_vxlan_sg_t *vxlan_sg = p; + const zebra_vxlan_sg_t *vxlan_sg = p; return (jhash_2words(vxlan_sg->sg.src.s_addr, vxlan_sg->sg.grp.s_addr, 0)); diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 9f945442bba4..1dd42b7083d7 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -430,6 +430,8 @@ struct nh_walk_ctx { struct json_object *json; }; +extern zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id); + #ifdef __cplusplus } #endif diff --git a/zebra/zserv.c b/zebra/zserv.c index df5f236c04f0..49fb302ba82e 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -149,8 +149,8 @@ static void zserv_event(struct zserv *client, enum zserv_event event); * hdr (optional) * The message header */ -static void zserv_log_message(const char *errmsg, struct stream *msg, - struct zmsghdr *hdr) +void zserv_log_message(const char *errmsg, struct stream *msg, + struct zmsghdr *hdr) { zlog_debug("Rx'd ZAPI message"); if (errmsg) @@ -411,9 +411,6 @@ static int zserv_read(struct thread *thread) hdr.vrf_id, hdr.length, sock); - if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) - zserv_log_message(NULL, client->ibuf_work, &hdr); - stream_set_getp(client->ibuf_work, 0); struct stream *msg = stream_dup(client->ibuf_work); @@ -685,6 +682,8 @@ static int zserv_handle_client_fail(struct thread *thread) static struct zserv *zserv_client_create(int sock) { struct zserv *client; + size_t stream_size = + MAX(ZEBRA_MAX_PACKET_SIZ, sizeof(struct zapi_route)); int i; afi_t afi; @@ -694,15 +693,12 @@ static struct zserv *zserv_client_create(int sock) client->sock = sock; client->ibuf_fifo = stream_fifo_new(); client->obuf_fifo = stream_fifo_new(); - client->ibuf_work = stream_new(ZEBRA_MAX_PACKET_SIZ); - client->obuf_work = stream_new(ZEBRA_MAX_PACKET_SIZ); + client->ibuf_work = stream_new(stream_size); + client->obuf_work = stream_new(stream_size); pthread_mutex_init(&client->ibuf_mtx, NULL); pthread_mutex_init(&client->obuf_mtx, NULL); client->wb = buffer_new(0); - /* Set table number. */ - client->rtm_table = zrouter.rtm_table_default; - atomic_store_explicit(&client->connect_time, (uint32_t) monotime(NULL), memory_order_relaxed); @@ -910,7 +906,6 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) vty_out(vty, "------------------------ \n"); vty_out(vty, "FD: %d \n", client->sock); - vty_out(vty, "Route Table ID: %d \n", client->rtm_table); connect_time = (time_t) atomic_load_explicit(&client->connect_time, memory_order_relaxed); diff --git a/zebra/zserv.h b/zebra/zserv.h index 90fd195712be..34965618f220 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -83,9 +83,6 @@ struct zserv { /* Threads for the main pthread */ struct thread *t_cleanup; - /* default routing table this client munges */ - int rtm_table; - /* This client's redistribute flag. */ struct redist_proto mi_redist[AFI_MAX][ZEBRA_ROUTE_MAX]; vrf_bitmap_t redist[AFI_MAX][ZEBRA_ROUTE_MAX]; @@ -176,8 +173,6 @@ struct zserv { DECLARE_HOOK(zserv_client_connect, (struct zserv *client), (client)); DECLARE_KOOH(zserv_client_close, (struct zserv *client), (client)); -extern unsigned int multipath_num; - /* * Initialize Zebra API server. * @@ -240,6 +235,22 @@ extern struct zserv *zserv_find_client(uint8_t proto, unsigned short instance); */ extern void zserv_close_client(struct zserv *client); + +/* + * Log a ZAPI message hexdump. + * + * errmsg + * Error message to include with packet hexdump + * + * msg + * Message to log + * + * hdr + * Message header + */ +void zserv_log_message(const char *errmsg, struct stream *msg, + struct zmsghdr *hdr); + #if defined(HANDLE_ZAPI_FUZZING) extern void zserv_read_file(char *input); #endif