babel_if_init ()
{
/* initialize interface list */
- if_add_hook (IF_NEW_HOOK, babel_if_new_hook);
- if_add_hook (IF_DELETE_HOOK, babel_if_delete_hook);
+ hook_register_prio(if_add, 0, babel_if_new_hook);
+ hook_register_prio(if_del, 0, babel_if_delete_hook);
babel_enable_if = vector_init (1);
#endif /* NO_DEBUG */
}
+DEFUN_NOSH (show_debugging_babel,
+ show_debugging_babel_cmd,
+ "show debugging [babel]",
+ SHOW_STR
+ DEBUG_STR
+ "Babel")
+{
+ vty_out(vty, "BABEL debugging status\n");
+
+ debug_babel_config_write(vty);
+
+ return CMD_SUCCESS;
+}
+
static void
babel_zebra_connected (struct zclient *zclient)
{
install_element(ENABLE_NODE, &no_debug_babel_cmd);
install_element(CONFIG_NODE, &debug_babel_cmd);
install_element(CONFIG_NODE, &no_debug_babel_cmd);
+
+ install_element(VIEW_NODE, &show_debugging_babel_cmd);
}
static int
return CMD_SUCCESS;
}
-DEFUN (show_debugging_bgp,
- show_debugging_bgp_cmd,
- "show debugging bgp",
- SHOW_STR
- DEBUG_STR
- BGP_STR)
+DEFUN_NOSH (show_debugging_bgp,
+ show_debugging_bgp_cmd,
+ "show debugging [bgp]",
+ SHOW_STR
+ DEBUG_STR
+ BGP_STR)
{
vty_out(vty, "BGP debugging status:\n");
{
zlog_notice("Terminating on signal");
- if (!retain_mode) {
+ if (!retain_mode)
bgp_terminate();
- if (bgpd_privs.user) /* NULL if skip_runas flag set */
- zprivs_terminate(&bgpd_privs);
- }
bgp_exit(0);
/* it only makes sense for this to be called on a clean exit */
assert(status == 0);
+ frr_early_fini();
+
bfd_gbl_exit();
bgp_close();
- if (retain_mode)
- if_add_hook(IF_DELETE_HOOK, NULL);
-
/* reverse bgp_master_init */
for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
bgp_delete(bgp);
community_list_terminate(bgp_clist);
bgp_vrf_terminate();
- cmd_terminate();
- vty_terminate();
#if ENABLE_BGP_VNC
vnc_zebra_destroy();
#endif
bgp_zebra_destroy();
- /* reverse bgp_master_init */
- if (bm->master)
- thread_master_free(bm->master);
-
- closezlog();
-
list_delete(bm->bgp);
memset(bm, 0, sizeof(*bm));
+ frr_fini();
+
if (bgp_debug_count())
log_memstats_stderr("bgpd");
exit(status);
struct bgp_process_queue {
struct bgp *bgp;
- struct bgp_node *rn;
- afi_t afi;
- safi_t safi;
+ STAILQ_HEAD(, bgp_node)pqueue;
+#define BGP_PROCESS_QUEUE_EOIU_MARKER (1 << 0)
+ unsigned int flags;
+ unsigned int queued;
};
-static wq_item_status bgp_process_main(struct work_queue *wq, void *data)
+static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
+ afi_t afi, safi_t safi)
{
- struct bgp_process_queue *pq = data;
- struct bgp *bgp = pq->bgp;
- struct bgp_node *rn = pq->rn;
- afi_t afi = pq->afi;
- safi_t safi = pq->safi;
struct prefix *p = &rn->p;
struct bgp_info *new_select;
struct bgp_info *old_select;
bgp->main_peers_update_hold = 0;
bgp_start_routeadv(bgp);
- return WQ_SUCCESS;
+ return;
}
/* Best path selection. */
}
UNSET_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED);
- return WQ_SUCCESS;
+ return;
}
/* If the user did "clear ip bgp prefix x.x.x.x" this flag will be set
bgp_info_reap(rn, old_select);
UNSET_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED);
- return WQ_SUCCESS;
+ return;
}
-static void bgp_processq_del(struct work_queue *wq, void *data)
+static wq_item_status bgp_process_wq(struct work_queue *wq, void *data)
{
- struct bgp_process_queue *pq = data;
+ struct bgp_process_queue *pqnode = data;
+ struct bgp *bgp = pqnode->bgp;
struct bgp_table *table;
+ struct bgp_node *rn, *nrn;
+
+ /* eoiu marker */
+ if (CHECK_FLAG(pqnode->flags, BGP_PROCESS_QUEUE_EOIU_MARKER)) {
+ bgp_process_main_one(bgp, NULL, 0, 0);
- bgp_unlock(pq->bgp);
- if (pq->rn) {
- table = bgp_node_table(pq->rn);
- bgp_unlock_node(pq->rn);
+ return WQ_SUCCESS;
+ }
+
+ STAILQ_FOREACH_SAFE(rn, &pqnode->pqueue, pq, nrn) {
+ table = bgp_node_table(rn);
+
+ bgp_process_main_one(bgp, rn, table->afi, table->safi);
+
+ bgp_unlock_node(rn);
bgp_table_unlock(table);
}
- XFREE(MTYPE_BGP_PROCESS_QUEUE, pq);
+
+ return WQ_SUCCESS;
+}
+
+static void bgp_processq_del(struct work_queue *wq, void *data)
+{
+ struct bgp_process_queue *pqnode = data;
+
+ bgp_unlock(pqnode->bgp);
+
+ XFREE(MTYPE_BGP_PROCESS_QUEUE, pqnode);
}
void bgp_process_queue_init(void)
}
}
- bm->process_main_queue->spec.workfunc = &bgp_process_main;
+ bm->process_main_queue->spec.workfunc = &bgp_process_wq;
bm->process_main_queue->spec.del_item_data = &bgp_processq_del;
bm->process_main_queue->spec.max_retries = 0;
bm->process_main_queue->spec.hold = 50;
bm->process_main_queue->spec.yield = 50 * 1000L;
}
+static struct bgp_process_queue *bgp_process_queue_work(struct work_queue *wq,
+ struct bgp *bgp)
+{
+ struct bgp_process_queue *pqnode;
+
+ pqnode = XCALLOC(MTYPE_BGP_PROCESS_QUEUE, sizeof(struct bgp_process_queue));
+
+ /* unlocked in bgp_processq_del */
+ pqnode->bgp = bgp_lock(bgp);
+ STAILQ_INIT(&pqnode->pqueue);
+
+ work_queue_add(wq, pqnode);
+
+ return pqnode;
+}
+
void bgp_process(struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi)
{
+#define ARBITRARY_PROCESS_QLEN 10000
+ struct work_queue *wq = bm->process_main_queue;
struct bgp_process_queue *pqnode;
/* already scheduled for processing? */
if (CHECK_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED))
return;
- if (bm->process_main_queue == NULL)
+ if (wq == NULL)
return;
- pqnode = XCALLOC(MTYPE_BGP_PROCESS_QUEUE,
- sizeof(struct bgp_process_queue));
- if (!pqnode)
- return;
+ /* Add route nodes to an existing work queue item until reaching the
+ limit only if is from the same BGP view and it's not an EOIU marker */
+ if (work_queue_item_count(wq)) {
+ struct work_queue_item *item = work_queue_last_item(wq);
+ pqnode = item->data;
+
+ if (CHECK_FLAG(pqnode->flags, BGP_PROCESS_QUEUE_EOIU_MARKER) ||
+ pqnode->bgp != bgp || pqnode->queued >= ARBITRARY_PROCESS_QLEN)
+ pqnode = bgp_process_queue_work(wq, bgp);
+ } else
+ pqnode = bgp_process_queue_work(wq, bgp);
- /* all unlocked in bgp_processq_del */
+ /* all unlocked in bgp_process_wq */
bgp_table_lock(bgp_node_table(rn));
- pqnode->rn = bgp_lock_node(rn);
- pqnode->bgp = bgp;
- bgp_lock(bgp);
- pqnode->afi = afi;
- pqnode->safi = safi;
- work_queue_add(bm->process_main_queue, pqnode);
+
SET_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED);
+ bgp_lock_node(rn);
+
+ STAILQ_INSERT_TAIL(&pqnode->pqueue, rn, pq);
+ pqnode->queued++;
+
return;
}
if (bm->process_main_queue == NULL)
return;
- pqnode = XCALLOC(MTYPE_BGP_PROCESS_QUEUE,
- sizeof(struct bgp_process_queue));
- if (!pqnode)
- return;
+ pqnode = bgp_process_queue_work(bm->process_main_queue, bgp);
- pqnode->rn = NULL;
- pqnode->bgp = bgp;
- bgp_lock(bgp);
- work_queue_add(bm->process_main_queue, pqnode);
+ SET_FLAG(pqnode->flags, BGP_PROCESS_QUEUE_EOIU_MARKER);
}
static int bgp_maximum_prefix_restart_timer(struct thread *thread)
#include "mpls.h"
#include "table.h"
+#include "queue.h"
struct bgp_table {
/* afi/safi of this table */
struct bgp_node *prn;
+ STAILQ_ENTRY(bgp_node) pq;
+
mpls_label_t local_label;
uint64_t version;
IP_STR
BGP_STR
BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
"Clear all peers\n"
"BGP neighbor address to clear\n"
"BGP IPv6 neighbor to clear\n"
"Clear all external peers\n"
"Clear all members of peer-group\n"
"BGP peer-group name\n"
- BGP_AFI_HELP_STR
- BGP_SAFI_WITH_LABEL_HELP_STR
BGP_SOFT_STR
BGP_SOFT_IN_STR
BGP_SOFT_OUT_STR
peer->status = Idle;
peer->ostatus = Idle;
peer->cur_event = peer->last_event = peer->last_major_event = 0;
- peer->bgp = bgp;
+ peer->bgp = bgp_lock(bgp);
peer = peer_lock(peer); /* initial reference */
- bgp_lock(bgp);
peer->password = NULL;
/* Set default flags. */
return 0;
}
-static void bgp_free(struct bgp *);
-
-void bgp_lock(struct bgp *bgp)
-{
- ++bgp->lock;
-}
-
-void bgp_unlock(struct bgp *bgp)
-{
- assert(bgp->lock > 0);
- if (--bgp->lock == 0)
- bgp_free(bgp);
-}
-
-static void bgp_free(struct bgp *bgp)
+void bgp_free(struct bgp *bgp)
{
afi_t afi;
safi_t safi;
extern int bgp_flag_unset(struct bgp *, int);
extern int bgp_flag_check(struct bgp *, int);
-extern void bgp_lock(struct bgp *);
-extern void bgp_unlock(struct bgp *);
-
extern void bgp_router_id_zebra_bump(vrf_id_t, const struct prefix *);
extern int bgp_router_id_static_set(struct bgp *, struct in_addr);
extern int peer_af_delete(struct peer *, afi_t, safi_t);
extern void bgp_close(void);
+extern void bgp_free(struct bgp *);
+
+static inline struct bgp *bgp_lock(struct bgp *bgp)
+{
+ bgp->lock++;
+ return bgp;
+}
+
+static inline void bgp_unlock(struct bgp *bgp)
+{
+ assert(bgp->lock > 0);
+ if (--bgp->lock == 0)
+ bgp_free(bgp);
+}
static inline int afindex(afi_t afi, safi_t safi)
{
static inline void bgp_vrf_link(struct bgp *bgp, struct vrf *vrf)
{
bgp->vrf_id = vrf->vrf_id;
- if (vrf->info != (void *)bgp) {
- bgp_lock(bgp);
- vrf->info = (void *)bgp;
- }
+ if (vrf->info != (void *)bgp)
+ vrf->info = (void *)bgp_lock(bgp);
}
/* Unlink BGP instance from VRF. */
switch (pfx_un1.family) {
case AF_INET:
- if (!IPV4_ADDR_SAME(&pfx_un1.u.prefix4.s_addr,
- &pfx_un2.u.prefix4.s_addr))
+ if (!IPV4_ADDR_SAME(&pfx_un1.u.prefix4,
+ &pfx_un2.u.prefix4))
return 0;
break;
case AF_INET6:
* show/save
***********************************************************************/
-DEFUN (show_debugging_bgp_vnc,
- show_debugging_bgp_vnc_cmd,
- "show debugging bgp vnc",
- SHOW_STR
- DEBUG_STR
- BGP_STR
- VNC_STR)
+DEFUN_NOSH (show_debugging_bgp_vnc,
+ show_debugging_bgp_vnc_cmd,
+ "show debugging bgp vnc",
+ SHOW_STR
+ DEBUG_STR
+ BGP_STR
+ VNC_STR)
{
size_t i;
AC_ARG_ENABLE(rtadv,
AS_HELP_STRING([--disable-rtadv], [disable IPV6 router advertisement feature]))
AC_ARG_ENABLE(irdp,
- AS_HELP_STRING([--enable-irdp], [enable IRDP server support in zebra]))
+ AS_HELP_STRING([--disable-irdp], [enable IRDP server support in zebra (default if supported)]))
AC_ARG_ENABLE(capabilities,
AS_HELP_STRING([--disable-capabilities], [disable using POSIX capabilities]))
AC_ARG_ENABLE(rusage,
AC_MSG_RESULT(no)
fi
-if test "${enable_irdp}" = "yes"; then
- AC_DEFINE(HAVE_IRDP,, IRDP )
-fi
-
if test x"${enable_user}" = x"no"; then
enable_user=""
else
dnl ------------------
dnl check Net-SNMP library
dnl ------------------
-if test "${enable_snmp}" != ""; then
+if test "${enable_snmp}" != "" -a "${enable_snmp}" != "no"; then
AC_PATH_TOOL([NETSNMP_CONFIG], [net-snmp-config], [no])
if test x"$NETSNMP_CONFIG" = x"no"; then
AC_MSG_ERROR([--enable-snmp given but unable to find net-snmp-config])
dnl ---------------------------
dnl IRDP/pktinfo/icmphdr checks
dnl ---------------------------
-AC_CHECK_TYPES([struct in_pktinfo],
- [AC_CHECK_TYPES([struct icmphdr],
- [if test "${enable_irdp}" != "no"; then
- AC_DEFINE(HAVE_IRDP,, IRDP)
- fi],
- [if test "${enable_irdp}" = "yes"; then
- AC_MSG_ERROR(['IRDP requires in_pktinfo at the moment!'])
- fi], [FRR_INCLUDES])],
- [if test "${enable_irdp}" = "yes"; then
- AC_MSG_ERROR(['IRDP requires in_pktinfo at the moment!'])
- fi], [FRR_INCLUDES])
+
+AC_CHECK_TYPES([struct in_pktinfo], [
+ AC_CHECK_TYPES([struct icmphdr], [
+ IRDP=true
+ ], [
+ IRDP=false
+ ], [FRR_INCLUDES])
+], [
+ IRDP=false
+], [FRR_INCLUDES])
+
+case "${enable_irdp}" in
+yes)
+ $IRDP || AC_MSG_ERROR(['IRDP requires in_pktinfo at the moment!'])
+ ;;
+no)
+ IRDP=false
+ ;;
+esac
+
+AM_CONDITIONAL(IRDP, $IRDP)
dnl -----------------------
dnl checking for IP_PKTINFO
--- /dev/null
+/_templates
+/_build
+!/Makefile
--- /dev/null
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " applehelp to make an Apple Help Book"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
+
+.PHONY: clean
+clean:
+ rm -rf $(BUILDDIR)/*
+
+.PHONY: html
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: dirhtml
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+.PHONY: singlehtml
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+.PHONY: pickle
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+.PHONY: json
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+.PHONY: htmlhelp
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+.PHONY: qthelp
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/FRR.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/FRR.qhc"
+
+.PHONY: applehelp
+applehelp:
+ $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+ @echo
+ @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+ @echo "N.B. You won't be able to view it unless you put it in" \
+ "~/Library/Documentation/Help or install it in your application" \
+ "bundle."
+
+.PHONY: devhelp
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/FRR"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/FRR"
+ @echo "# devhelp"
+
+.PHONY: epub
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+.PHONY: latex
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+.PHONY: latexpdf
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: latexpdfja
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: text
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+.PHONY: man
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+.PHONY: texinfo
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+.PHONY: info
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+.PHONY: gettext
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+.PHONY: changes
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+.PHONY: linkcheck
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+.PHONY: doctest
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+.PHONY: coverage
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+.PHONY: pseudoxml
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# FRR documentation build configuration file, created by
+# sphinx-quickstart on Tue Jan 31 16:00:52 2017.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+import re
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ['sphinx.ext.todo']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'FRR'
+copyright = u'2017, FRR'
+author = u'FRR'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+
+# The short X.Y version.
+version = u'?.?'
+# The full version, including alpha/beta/rc tags.
+release = u'?.?-?'
+
+val = re.compile('^S\["([^"]+)"\]="(.*)"$')
+with open('../../config.status', 'r') as cfgstatus:
+ for ln in cfgstatus.readlines():
+ m = val.match(ln)
+ if m is None: continue
+ if m.group(1) == 'PACKAGE_VERSION':
+ release = m.group(2)
+ version = release.split('-')[0]
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = True
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'sphinx_rtd_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
+#html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# Now only 'ja' uses this config value
+#html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'FRRdoc'
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+
+# Latex figure (float) alignment
+#'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'FRR.tex', u'FRR Documentation',
+ u'FRR', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'frr', u'FRR Documentation',
+ [author], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'FRR', u'FRR Documentation',
+ author, 'FRR', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
--- /dev/null
+.. highlight:: c
+
+Hooks
+=====
+
+Libfrr provides type-safe subscribable hook points where other pieces of
+code can add one or more callback functions. "type-safe" in this case
+applies to the function pointers used for subscriptions. The
+implementations checks (at compile-time) wheter a callback to be added has
+the appropriate function signature (parameters) for the hook.
+
+Example:
+
+.. code-block:: c
+ :caption: mydaemon.h
+
+ #include "hook.h"
+ DECLARE_HOOK(some_update_event, (struct eventinfo *info), (info))
+
+.. code-block:: c
+ :caption: mydaemon.c
+
+ #include "mydaemon.h"
+ DEFINE_HOOK(some_update_event, (struct eventinfo *info), (info))
+ ...
+ hook_call(some_update_event, info);
+
+.. code-block:: c
+ :caption: mymodule.c
+
+ #include "mydaemon.h"
+ static int event_handler(struct eventinfo *info);
+ ...
+ hook_register(some_update_event, event_handler);
+
+Do not use parameter names starting with "hook", these can collide with
+names used by the hook code itself.
+
+
+Return values
+-------------
+
+Callbacks to be placed on hooks always return "int" for now; hook_call will
+sum up the return values from each called function. (The default is 0 if no
+callbacks are registered.)
+
+There are no pre-defined semantics for the value, in most cases it is
+ignored. For success/failure indication, 0 should be success, and
+handlers should make sure to only return 0 or 1 (not -1 or other values).
+
+There is no built-in way to abort executing a chain after a failure of one
+of the callbacks. If this is needed, the hook can use an extra
+``bool *aborted`` argument.
+
+
+Priorities
+----------
+
+Hooks support a "priority" value for ordering registered calls
+relative to each other. The priority is a signed integer where lower
+values are called earlier. There are also "Koohs", which is hooks with
+reverse priority ordering (for cleanup/deinit hooks, so you can use the
+same priority value).
+
+Recommended priority value ranges are:
+
+======================== ===================================================
+Range Usage
+------------------------ ---------------------------------------------------
+ -999 ... 0 ... 999 main executable / daemon, or library
+
+-1999 ... -1000 modules registering calls that should run before
+ the daemon's bits
+
+1000 ... 1999 modules' calls that should run after daemon's
+ (includes default value: 1000)
+======================== ===================================================
+
+Note: the default value is 1000, based on the following 2 expectations:
+
+- most hook_register() usage will be in loadable modules
+- usage of hook_register() in the daemon itself may need relative ordering
+ to itself, making an explicit value the expected case
+
+The priority value is passed as extra argument on hook_register_prio() /
+hook_register_arg_prio(). Whether a hook runs in reverse is determined
+solely by the code defining / calling the hook. (DECLARE_KOOH is actually
+the same thing as DECLARE_HOOK, it's just there to make it obvious.)
+
+
+Definition
+----------
+
+.. c:macro:: DECLARE_HOOK(name, arglist, passlist)
+.. c:macro:: DECLARE_KOOH(name, arglist, passlist)
+
+ :param name: Name of the hook to be defined
+ :param arglist: Function definition style parameter list in braces.
+ :param passlist: List of the same parameters without their types.
+
+ Note: the second and third macro args must be the hook function's
+ parameter list, with the same names for each parameter. The second
+ macro arg is with types (used for defining things), the third arg is
+ just the names (used for passing along parameters).
+
+ This macro must be placed in a header file; this header file must be
+ included to register a callback on the hook.
+
+ Examples:
+
+ .. code-block:: c
+
+ DECLARE_HOOK(foo, (), ())
+ DECLARE_HOOK(bar, (int arg), (arg))
+ DECLARE_HOOK(baz, (const void *x, in_addr_t y), (x, y))
+
+.. c:macro:: DEFINE_HOOK(name, arglist, passlist)
+
+ Implements an hook. Each ``DECLARE_HOOK`` must have be accompanied by
+ exactly one ``DEFINE_HOOK``, which needs to be placed in a source file.
+ **The hook can only be called from this source file.** This is intentional
+ to avoid overloading and/or misusing hooks for distinct purposes.
+
+ The compiled source file will include a global symbol with the name of the
+ hook prefixed by `_hook_`. Trying to register a callback for a hook that
+ doesn't exist will therefore result in a linker error, or a module
+ load-time error for dynamic modules.
+
+.. c:macro:: DEFINE_KOOH(name, arglist, passlist)
+
+ Same as ``DEFINE_HOOK``, but the sense of priorities / order of callbacks
+ is reversed. This should be used for cleanup hooks.
+
+.. c:function:: int hook_call(name, ...)
+
+ Calls the specified named hook. Parameters to the hook are passed right
+ after the hook name, e.g.:
+
+ .. code-block:: c
+
+ hook_call(foo);
+ hook_call(bar, 0);
+ hook_call(baz, NULL, INADDR_ANY);
+
+ Returns the sum of return values from all callbacks. The ``DEFINE_HOOK``
+ statement for the hook must be placed in the file before any ``hook_call``
+ use of the hook.
+
+
+Callback registration
+---------------------
+
+.. c:function:: void hook_register(name, int (*callback)(...))
+.. c:function:: void hook_register_prio(name, int priority, int (*callback)(...))
+.. c:function:: void hook_register_arg(name, int (*callback)(void *arg, ...), void *arg)
+.. c:function:: void hook_register_arg_prio(name, int priority, int (*callback)(void *arg, ...), void *arg)
+
+ Register a callback with an hook. If the caller needs to pass an extra
+ argument to the callback, the _arg variant can be used and the extra
+ parameter will be passed as first argument to the callback. There is no
+ typechecking for this argument.
+
+ The priority value is used as described above. The variants without a
+ priority parameter use 1000 as priority value.
+
+.. c:function:: void hook_unregister(name, int (*callback)(...))
+.. c:function:: void hook_unregister_arg(name, int (*callback)(void *arg, ...), void *arg)
+
+ Removes a previously registered callback from a hook. Note that there
+ is no _prio variant of these calls. The priority value is only used during
+ registration.
--- /dev/null
+Welcome to FRR's documentation!
+===============================
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ library
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
--- /dev/null
+libfrr library facilities
+=========================
+
+.. toctree::
+ :maxdepth: 2
+
+ memtypes
+ hooks
+
+
--- /dev/null
+.. highlight:: c
+
+Memtypes
+========
+
+FRR includes wrappers arround ``malloc()`` and ``free()`` that count the number
+of objects currently allocated, for each of a defined ``MTYPE``.
+
+To this extent, there are `memory groups` and `memory types`. Each memory
+type must belong to a memory group, this is used just to provide some basic
+structure.
+
+Example:
+
+.. code-block:: c
+ :caption: mydaemon.h
+
+ DECLARE_MGROUP(MYDAEMON)
+ DECLARE_MTYPE(MYNEIGHBOR)
+
+.. code-block:: c
+ :caption: mydaemon.c
+
+ DEFINE_MGROUP( MYDAEMON, "My daemon's memory")
+ DEFINE_MTYPE( MYDAEMON, MYNEIGHBOR, "Neighbor entry")
+ DEFINE_MTYPE_STATIC(MYDAEMON, MYNEIGHBORNAME, "Neighbor name")
+
+ struct neigh *neighbor_new(const char *name)
+ {
+ struct neigh *n = XMALLOC(MYNEIGHBOR, sizeof(*n));
+ n->name = XSTRDUP(MYNEIGHBORNAME, name);
+ return n;
+ }
+
+ void neighbor_free(struct neigh *n)
+ {
+ XFREE(MYNEIGHBORNAME, n->name);
+ XFREE(MYNEIGHBOR, n);
+ }
+
+
+Definition
+----------
+
+.. c:macro:: DECLARE_MGROUP(name)
+
+ This macro forward-declares a memory group and should be placed in a
+ ``.h`` file. It expands to an ``extern struct memgroup`` statement.
+
+.. c:macro:: DEFINE_MGROUP(mname, description)
+
+ Defines/implements a memory group. Must be placed into exactly one ``.c``
+ file (multiple inclusion will result in a link-time symbol conflict).
+
+ Contains additional logic (constructor and destructor) to register the
+ memory group in a global list.
+
+.. c:macro:: DECLARE_MTYPE(name)
+
+ Forward-declares a memory type and makes ``MTYPE_name`` available for use.
+ Note that the ``MTYPE_`` prefix must not be included in the name, it is
+ automatically prefixed.
+
+ ``MTYPE_name`` is created as a `static const` symbol, i.e. a compile-time
+ constant. It refers to an ``extern struct memtype _mt_name``, where `name`
+ is replaced with the actual name.
+
+.. c:macro:: DEFINE_MTYPE(group, name, description)
+
+ Define/implement a memory type, must be placed into exactly one ``.c``
+ file (multiple inclusion will result in a link-time symbol conflict).
+
+ Like ``DEFINE_MGROUP``, this contains actual code to register the MTYPE
+ under its group.
+
+.. c:macro:: DEFINE_MTYPE_STATIC(group, name, description)
+
+ Same as ``DEFINE_MTYPE``, but the ``DEFINE_MTYPE_STATIC`` variant places
+ the C ``static`` keyword on the definition, restricting the MTYPE's
+ availability to the current source file. This should be appropriate in
+ >80% of cases.
+
+ .. todo::
+
+ Daemons currently have ``daemon_memory.[ch]`` files listing all of
+ their MTYPEs. This is not how it should be, most of these types
+ should be moved into the appropriate files where they are used.
+ Only a few MTYPEs should remain non-static after that.
+
+
+Usage
+-----
+
+.. c:function:: void *XMALLOC(struct memtype *mtype, size_t size)
+
+.. c:function:: void *XCALLOC(struct memtype *mtype, size_t size)
+
+.. c:function:: void *XSTRDUP(struct memtype *mtype, size_t size)
+
+ Allocation wrappers for malloc/calloc/realloc/strdup, taking an extra
+ mtype parameter.
+
+.. c:function:: void *XREALLOC(struct memtype *mtype, void *ptr, size_t size)
+
+ Wrapper around realloc() with MTYPE tracking. Note that ``ptr`` may
+ be NULL, in which case the function does the same as XMALLOC (regardless
+ of whether the system realloc() supports this.)
+
+.. c:function:: void XFREE(struct memtype *mtype, void *ptr)
+
+ Wrapper around free(), again taking an extra mtype parameter. This is
+ actually a macro, with the following additional properties:
+
+ - the macro contains ``ptr = NULL``
+ - if ptr is NULL, no operation is performed (as is guaranteed by system
+ implementations.) Do not surround XFREE with ``if (ptr != NULL)``
+ checks.
}
-DEFUN (show_debugging_eigrp,
- show_debugging_eigrp_cmd,
- "show debugging eigrp",
- SHOW_STR
- DEBUG_STR
- EIGRP_STR)
+DEFUN_NOSH (show_debugging_eigrp,
+ show_debugging_eigrp_cmd,
+ "show debugging [eigrp]",
+ SHOW_STR
+ DEBUG_STR
+ EIGRP_STR)
{
int i;
inet_ntoa(nbr->src));
}
+u_int32_t FRR_MAJOR;
+u_int32_t FRR_MINOR;
+
+void eigrp_sw_version_initialize(void)
+{
+ char ver_string[] = VERSION;
+ char *dash = strstr(ver_string, "-");
+
+ if (dash)
+ dash[0] = '\0';
+
+ sscanf(ver_string, "%d.%d", &FRR_MAJOR, &FRR_MINOR);
+}
+
/**
* @fn eigrp_sw_version_encode
*
stream_putw(s, EIGRP_TLV_SW_VERSION);
stream_putw(s, length);
- // encode the version of quagga we're running
- // DVS: need to figure out a cleaner way to do this
- stream_putc(s, 0); //!< major os version
- stream_putc(s, 99); //!< minor os version
+ stream_putc(s, FRR_MAJOR); //!< major os version
+ stream_putc(s, FRR_MINOR); //!< minor os version
/* and the core eigrp version */
stream_putc(s, EIGRP_MAJOR_VERSION);
void eigrp_if_init()
{
/* Initialize Zebra interface data structure. */
- if_add_hook(IF_NEW_HOOK, eigrp_if_new_hook);
- if_add_hook(IF_DELETE_HOOK, eigrp_if_delete_hook);
+ hook_register_prio(if_add, 0, eigrp_if_new_hook);
+ hook_register_prio(if_del, 0, eigrp_if_delete_hook);
}
int eigrp_if_new_hook(struct interface *ifp)
}
}
+ eigrp_sw_version_initialize();
+
/* EIGRP master init. */
eigrp_master_init();
eigrp_om->master = frr_init();
/* Self-originated packet should be discarded silently. */
if (eigrp_if_lookup_by_local_addr(eigrp, NULL, iph->ip_src)
- || (IPV4_ADDR_SAME(&iph->ip_src.s_addr, &ei->address->u.prefix4))) {
+ || (IPV4_ADDR_SAME(&iph->ip_src, &ei->address->u.prefix4))) {
if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV))
zlog_debug(
"eigrp_read[%s]: Dropping self-originated packet",
* untill there is reason to have their own header, these externs are found in
* eigrp_hello.c
*/
+extern void eigrp_sw_version_initialize(void);
extern void eigrp_hello_send(struct eigrp_interface *, u_char,
struct in_addr *);
extern void eigrp_hello_send_ack(struct eigrp_neighbor *);
void isis_circuit_init()
{
/* Initialize Zebra interface data structure */
- if_add_hook(IF_NEW_HOOK, isis_if_new_hook);
- if_add_hook(IF_DELETE_HOOK, isis_if_delete_hook);
+ hook_register_prio(if_add, 0, isis_if_new_hook);
+ hook_register_prio(if_del, 0, isis_if_delete_hook);
/* Install interface node */
install_node(&interface_node, isis_interface_config_write);
if (if_is_loopback(circuit->interface)) {
vty_out(vty, "Can't set no passive for loopback interface\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
isis_circuit_passive_set(circuit, 0);
is_type = string2circuit_t(argv[idx_level]->arg);
if (!is_type) {
vty_out(vty, "Unknown circuit-type \n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
if (circuit->state == C_STATE_UP
&& circuit->area->is_type != is_type) {
vty_out(vty, "Invalid circuit level for area %s.\n",
circuit->area->area_tag);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
isis_circuit_is_type_set(circuit, is_type);
if (isis_circuit_circ_type_set(circuit, CIRCUIT_T_P2P)) {
vty_out(vty,
"isis network point-to-point is valid only on broadcast interfaces\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
if (isis_circuit_circ_type_set(circuit, CIRCUIT_T_BROADCAST)) {
vty_out(vty,
"isis network point-to-point is valid only on broadcast interfaces\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
argv[idx_word]->arg);
if (rv) {
vty_out(vty, "Too long circuit password (>254)\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
prio = atoi(argv[idx_number]->arg);
if (prio < MIN_PRIORITY || prio > MAX_PRIORITY) {
vty_out(vty, "Invalid priority %d - should be <0-127>\n", prio);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->priority[0] = prio;
prio = atoi(argv[idx_number]->arg);
if (prio < MIN_PRIORITY || prio > MAX_PRIORITY) {
vty_out(vty, "Invalid priority %d - should be <0-127>\n", prio);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->priority[0] = prio;
prio = atoi(argv[idx_number]->arg);
if (prio < MIN_PRIORITY || prio > MAX_PRIORITY) {
vty_out(vty, "Invalid priority %d - should be <0-127>\n", prio);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->priority[1] = prio;
"Invalid metric %d - should be <0-63> "
"when narrow metric type enabled\n",
met);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
/* RFC4444 */
"Invalid metric %d - should be <0-16777215> "
"when wide metric type enabled\n",
met);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
isis_circuit_metric_set(circuit, IS_LEVEL_1, met);
"Invalid metric %d - should be <0-63> "
"when narrow metric type enabled\n",
met);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
/* RFC4444 */
"Invalid metric %d - should be <0-16777215> "
"when wide metric type enabled\n",
met);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
isis_circuit_metric_set(circuit, IS_LEVEL_1, met);
"Invalid metric %d - should be <0-63> "
"when narrow metric type enabled\n",
met);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
/* RFC4444 */
"Invalid metric %d - should be <0-16777215> "
"when wide metric type enabled\n",
met);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
isis_circuit_metric_set(circuit, IS_LEVEL_2, met);
if (interval < MIN_HELLO_INTERVAL || interval > MAX_HELLO_INTERVAL) {
vty_out(vty, "Invalid hello-interval %d - should be <1-600>\n",
interval);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->hello_interval[0] = (u_int16_t)interval;
if (interval < MIN_HELLO_INTERVAL || interval > MAX_HELLO_INTERVAL) {
vty_out(vty, "Invalid hello-interval %ld - should be <1-600>\n",
interval);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->hello_interval[0] = (u_int16_t)interval;
if (interval < MIN_HELLO_INTERVAL || interval > MAX_HELLO_INTERVAL) {
vty_out(vty, "Invalid hello-interval %ld - should be <1-600>\n",
interval);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->hello_interval[1] = (u_int16_t)interval;
vty_out(vty,
"Invalid hello-multiplier %d - should be <2-100>\n",
mult);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->hello_multiplier[0] = (u_int16_t)mult;
vty_out(vty,
"Invalid hello-multiplier %d - should be <2-100>\n",
mult);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->hello_multiplier[0] = (u_int16_t)mult;
vty_out(vty,
"Invalid hello-multiplier %d - should be <2-100>\n",
mult);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->hello_multiplier[1] = (u_int16_t)mult;
if (interval < MIN_CSNP_INTERVAL || interval > MAX_CSNP_INTERVAL) {
vty_out(vty, "Invalid csnp-interval %lu - should be <1-600>\n",
interval);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->csnp_interval[0] = (u_int16_t)interval;
if (interval < MIN_CSNP_INTERVAL || interval > MAX_CSNP_INTERVAL) {
vty_out(vty, "Invalid csnp-interval %lu - should be <1-600>\n",
interval);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->csnp_interval[0] = (u_int16_t)interval;
if (interval < MIN_CSNP_INTERVAL || interval > MAX_CSNP_INTERVAL) {
vty_out(vty, "Invalid csnp-interval %lu - should be <1-600>\n",
interval);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->csnp_interval[1] = (u_int16_t)interval;
if (interval < MIN_PSNP_INTERVAL || interval > MAX_PSNP_INTERVAL) {
vty_out(vty, "Invalid psnp-interval %lu - should be <1-120>\n",
interval);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->psnp_interval[0] = (u_int16_t)interval;
if (interval < MIN_PSNP_INTERVAL || interval > MAX_PSNP_INTERVAL) {
vty_out(vty, "Invalid psnp-interval %lu - should be <1-120>\n",
interval);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->psnp_interval[0] = (u_int16_t)interval;
if (interval < MIN_PSNP_INTERVAL || interval > MAX_PSNP_INTERVAL) {
vty_out(vty, "Invalid psnp-interval %lu - should be <1-120>\n",
interval);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
circuit->psnp_interval[1] = (u_int16_t)interval;
if (circuit->area && circuit->area->oldmetric) {
vty_out(vty,
"Multi topology IS-IS can only be used with wide metrics\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
if (mtid == (uint16_t)-1) {
vty_out(vty, "Don't know topology '%s'\n", arg);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
return isis_circuit_mt_enabled_set(circuit, mtid, true);
if (circuit->area && circuit->area->oldmetric) {
vty_out(vty,
"Multi topology IS-IS can only be used with wide metrics\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
if (mtid == (uint16_t)-1) {
vty_out(vty, "Don't know topology '%s'\n", arg);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
return isis_circuit_mt_enabled_set(circuit, mtid, false);
struct listnode *node;
if (!vty)
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
if (!area) {
vty_out(vty, "ISIS area is invalid\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
&& (circuit->te_metric[0] > MAX_NARROW_LINK_METRIC)) {
vty_out(vty, "ISIS circuit %s metric is invalid\n",
circuit->interface->name);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
if ((area->is_type & IS_LEVEL_2)
&& (circuit->is_type & IS_LEVEL_2)
&& (circuit->te_metric[1] > MAX_NARROW_LINK_METRIC)) {
vty_out(vty, "ISIS circuit %s metric is invalid\n",
circuit->interface->name);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
}
if (area_is_mt(area)) {
vty_out(vty,
"Narrow metrics cannot be used while multi topology IS-IS is active\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
ret = validate_metric_style_narrow(vty, area);
if (area_is_mt(area)) {
vty_out(vty,
"Narrow metrics cannot be used while multi topology IS-IS is active\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
ret = validate_metric_style_narrow(vty, area);
"ISIS area contains circuit %s, which has a maximum PDU size of %zu.\n",
circuit->interface->name,
isis_circuit_pdu_size(circuit));
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
}
"LSP gen interval %us must be less than "
"the LSP refresh interval %us\n",
interval, area->lsp_refresh[lvl - 1]);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
}
"the configured LSP gen interval %us\n",
refresh_interval,
area->lsp_gen_interval[lvl - 1]);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
}
}
"LSP refresh interval %us must be greater than "
"the configured LSP gen interval %us\n",
interval, area->lsp_gen_interval[lvl - 1]);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
if (interval > (area->max_lsp_lifetime[lvl - 1] - 300)) {
vty_out(vty,
"LSP refresh interval %us must be less than "
"the configured LSP lifetime %us less 300\n",
interval, area->max_lsp_lifetime[lvl - 1]);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
}
if (passwd && strlen(passwd) > 254) {
vty_out(vty, "Too long area password (>254)\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
type_set(area, level, passwd, snp_auth);
"area address must be at least 8..20 octets long (%d)\n",
addr->addr_len);
XFREE(MTYPE_ISIS_AREA_ADDR, addr);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
if (addr->area_addr[addr->addr_len - 1] != 0) {
vty_out(vty,
"nsel byte (last byte) in area address must be 0\n");
XFREE(MTYPE_ISIS_AREA_ADDR, addr);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
if (isis->sysid_set == 0) {
vty_out(vty,
"System ID must not change when defining additional area addresses\n");
XFREE(MTYPE_ISIS_AREA_ADDR, addr);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
/* now we see that we don't already have this address */
vty_out(vty,
"Unsupported area address length %d, should be 8...20 \n",
addr.addr_len);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
memcpy(addr.area_addr, buff, (int)addr.addr_len);
vty_out(vty, "IS-IS LSP scheduling debugging is %s\n", onoffs);
}
-DEFUN (show_debugging,
- show_debugging_isis_cmd,
- "show debugging isis",
- SHOW_STR
- "State of each debugging option\n"
- ISIS_STR)
+DEFUN_NOSH (show_debugging,
+ show_debugging_isis_cmd,
+ "show debugging [isis]",
+ SHOW_STR
+ "State of each debugging option\n"
+ ISIS_STR)
{
- if (isis->debugs) {
- vty_out(vty, "IS-IS:\n");
+ vty_out (vty, "IS-IS debugging status:\n");
+
+ if (isis->debugs)
print_debug(vty, isis->debugs, 1);
- }
+
return CMD_SUCCESS;
}
(u_char)strtol((char *)number, NULL, 16);
pos -= 4;
if (strncmp(pos, ".", 1) != 0)
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING;
}
if (strncmp(pos, ".", 1) == 0) {
memcpy(number, ++pos, 2);
if (area->oldmetric) {
vty_out(vty,
"Multi topology IS-IS can only be used with wide metrics\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
if (mtid == (uint16_t)-1) {
vty_out(vty, "Don't know topology '%s'\n", arg);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
if (mtid == ISIS_MT_IPV4_UNICAST) {
vty_out(vty, "Cannot configure IPv4 unicast topology\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
area_set_mt_enabled(area, mtid, true);
if (area->oldmetric) {
vty_out(vty,
"Multi topology IS-IS can only be used with wide metrics\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
if (mtid == (uint16_t)-1) {
vty_out(vty, "Don't know topology '%s'\n", arg);
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
if (mtid == ISIS_MT_IPV4_UNICAST) {
vty_out(vty, "Cannot configure IPv4 unicast topology\n");
- return CMD_ERR_AMBIGUOUS;
+ return CMD_WARNING_CONFIG_FAILED;
}
area_set_mt_enabled(area, mtid, false);
#ifndef _CONTROL_H_
#define _CONTROL_H_
-#include "openbsd-queue.h"
+#include "queue.h"
struct ctl_conn {
TAILQ_ENTRY(ctl_conn) entry;
#ifndef _LDE_H_
#define _LDE_H_
-#include "openbsd-queue.h"
+#include "queue.h"
#include "openbsd-tree.h"
#include "if.h"
return (ldp_vty_show_atom_vc(vty, json));
}
-DEFPY (ldp_show_debugging_mpls_ldp,
- ldp_show_debugging_mpls_ldp_cmd,
- "show debugging mpls ldp",
- "Show running system information\n"
- "Debugging functions\n"
- "MPLS information\n"
- "Label Distribution Protocol\n")
+DEFUN_NOSH (ldp_show_debugging_mpls_ldp,
+ ldp_show_debugging_mpls_ldp_cmd,
+ "show debugging [mpls ldp]",
+ "Show running system information\n"
+ "Debugging functions\n"
+ "MPLS information\n"
+ "Label Distribution Protocol\n")
{
return (ldp_vty_show_debugging(vty));
}
pid_t pid;
int status;
+ frr_early_fini();
+
/* close pipes */
msgbuf_clear(&iev_ldpe->ibuf.w);
close(iev_ldpe->ibuf.fd);
vrf_terminate();
access_list_reset();
- cmd_terminate();
- vty_terminate();
ldp_zebra_destroy();
- zprivs_terminate(&ldpd_privs);
- thread_master_free(master);
- closezlog();
+ frr_fini();
exit(0);
}
#ifndef _LDPD_H_
#define _LDPD_H_
-#include "openbsd-queue.h"
+#include "queue.h"
#include "openbsd-tree.h"
#include "imsg.h"
#include "thread.h"
#ifndef _LDPE_H_
#define _LDPE_H_
-#include "openbsd-queue.h"
+#include "queue.h"
#include "openbsd-tree.h"
#ifdef __OpenBSD__
#include <net/pfkeyv2.h>
--- /dev/null
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. 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.
+ * 4. 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.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ * SLIST LIST STAILQ TAILQ
+ * _HEAD + + + +
+ * _HEAD_INITIALIZER + + + +
+ * _ENTRY + + + +
+ * _INIT + + + +
+ * _EMPTY + + + +
+ * _FIRST + + + +
+ * _NEXT + + + +
+ * _PREV - - - +
+ * _LAST - - + +
+ * _FOREACH + + + +
+ * _FOREACH_SAFE + + + +
+ * _FOREACH_REVERSE - - - +
+ * _FOREACH_REVERSE_SAFE - - - +
+ * _INSERT_HEAD + + + +
+ * _INSERT_BEFORE - + - +
+ * _INSERT_AFTER + + + +
+ * _INSERT_TAIL - - + +
+ * _CONCAT - - + +
+ * _REMOVE_AFTER + - + -
+ * _REMOVE_HEAD + - + -
+ * _REMOVE + + + +
+ * _SWAP + + + +
+ *
+ */
+#ifdef QUEUE_MACRO_DEBUG
+/* Store the last 2 places the queue element or head was altered */
+struct qm_trace {
+ char *lastfile;
+ int lastline;
+ char *prevfile;
+ int prevline;
+};
+
+#define TRACEBUF struct qm_trace trace;
+#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
+#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
+
+#define QMD_TRACE_HEAD(head) \
+ do { \
+ (head)->trace.prevline = (head)->trace.lastline; \
+ (head)->trace.prevfile = (head)->trace.lastfile; \
+ (head)->trace.lastline = __LINE__; \
+ (head)->trace.lastfile = __FILE__; \
+ } while (0)
+
+#define QMD_TRACE_ELEM(elem) \
+ do { \
+ (elem)->trace.prevline = (elem)->trace.lastline; \
+ (elem)->trace.prevfile = (elem)->trace.lastfile; \
+ (elem)->trace.lastline = __LINE__; \
+ (elem)->trace.lastfile = __FILE__; \
+ } while (0)
+
+#else
+#define QMD_TRACE_ELEM(elem)
+#define QMD_TRACE_HEAD(head)
+#define QMD_SAVELINK(name, link)
+#define TRACEBUF
+#define TRASHIT(x)
+#endif /* QUEUE_MACRO_DEBUG */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define SLIST_HEAD(name, type) \
+ struct name { \
+ struct type *slh_first; /* first element */ \
+ }
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { \
+ NULL \
+ }
+
+#define SLIST_ENTRY(type) \
+ struct { \
+ struct type *sle_next; /* next element */ \
+ }
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#define SLIST_FIRST(head) ((head)->slh_first)
+
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST((head)); (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); (var) = (tvar))
+
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); ((var) = *(varp)) != NULL; \
+ (varp) = &SLIST_NEXT((var), field))
+
+#define SLIST_INIT(head) \
+ do { \
+ SLIST_FIRST((head)) = NULL; \
+ } while (0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) \
+ do { \
+ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
+ SLIST_NEXT((slistelm), field) = (elm); \
+ } while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) \
+ do { \
+ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
+ SLIST_FIRST((head)) = (elm); \
+ } while (0)
+
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_REMOVE(head, elm, type, field) \
+ do { \
+ QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
+ if (SLIST_FIRST((head)) == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = SLIST_FIRST((head)); \
+ while (SLIST_NEXT(curelm, field) != (elm)) \
+ curelm = SLIST_NEXT(curelm, field); \
+ SLIST_REMOVE_AFTER(curelm, field); \
+ } \
+ TRASHIT(*oldnext); \
+ } while (0)
+
+#define SLIST_REMOVE_AFTER(elm, field) \
+ do { \
+ SLIST_NEXT(elm, field) = \
+ SLIST_NEXT(SLIST_NEXT(elm, field), field); \
+ } while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) \
+ do { \
+ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
+ } while (0)
+
+#define SLIST_SWAP(head1, head2, type) \
+ do { \
+ struct type *swap_first = SLIST_FIRST(head1); \
+ SLIST_FIRST(head1) = SLIST_FIRST(head2); \
+ SLIST_FIRST(head2) = swap_first; \
+ } while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+ struct name { \
+ struct type *stqh_first; /* first element */ \
+ struct type **stqh_last; /* addr of last next element */ \
+ }
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { \
+ NULL, &(head).stqh_first \
+ }
+
+#define STAILQ_ENTRY(type) \
+ struct { \
+ struct type *stqe_next; /* next element */ \
+ }
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_CONCAT(head1, head2) \
+ do { \
+ if (!STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+ } while (0)
+
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for ((var) = STAILQ_FIRST((head)); (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); (var) = (tvar))
+
+#define STAILQ_INIT(head) \
+ do { \
+ STAILQ_FIRST((head)) = NULL; \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+ } while (0)
+
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) \
+ do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) \
+ == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_NEXT((tqelm), field) = (elm); \
+ } while (0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) \
+ do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) \
+ == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_FIRST((head)) = (elm); \
+ } while (0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) \
+ do { \
+ STAILQ_NEXT((elm), field) = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) \
+ ? NULL \
+ : ((struct type *)(void *)((char *)((head)->stqh_last) \
+ - offsetof(struct type, \
+ field))))
+
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define STAILQ_REMOVE(head, elm, type, field) \
+ do { \
+ QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
+ if (STAILQ_FIRST((head)) == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = STAILQ_FIRST((head)); \
+ while (STAILQ_NEXT(curelm, field) != (elm)) \
+ curelm = STAILQ_NEXT(curelm, field); \
+ STAILQ_REMOVE_AFTER(head, curelm, field); \
+ } \
+ TRASHIT(*oldnext); \
+ } while (0)
+
+#define STAILQ_REMOVE_AFTER(head, elm, field) \
+ do { \
+ if ((STAILQ_NEXT(elm, field) = \
+ STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) \
+ == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define STAILQ_REMOVE_HEAD(head, field) \
+ do { \
+ if ((STAILQ_FIRST((head)) = \
+ STAILQ_NEXT(STAILQ_FIRST((head)), field)) \
+ == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+ } while (0)
+
+#define STAILQ_SWAP(head1, head2, type) \
+ do { \
+ struct type *swap_first = STAILQ_FIRST(head1); \
+ struct type **swap_last = (head1)->stqh_last; \
+ STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_FIRST(head2) = swap_first; \
+ (head2)->stqh_last = swap_last; \
+ if (STAILQ_EMPTY(head1)) \
+ (head1)->stqh_last = &STAILQ_FIRST(head1); \
+ if (STAILQ_EMPTY(head2)) \
+ (head2)->stqh_last = &STAILQ_FIRST(head2); \
+ } while (0)
+
+
+/*
+ * List declarations.
+ */
+#define LIST_HEAD(name, type) \
+ struct name { \
+ struct type *lh_first; /* first element */ \
+ }
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { \
+ NULL \
+ }
+
+#define LIST_ENTRY(type) \
+ struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+ }
+
+/*
+ * List functions.
+ */
+
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define QMD_LIST_CHECK_HEAD(head, field) \
+ do { \
+ if (LIST_FIRST((head)) != NULL \
+ && LIST_FIRST((head))->field.le_prev \
+ != &LIST_FIRST((head))) \
+ panic("Bad list head %p first->prev != head", (head)); \
+ } while (0)
+
+#define QMD_LIST_CHECK_NEXT(elm, field) \
+ do { \
+ if (LIST_NEXT((elm), field) != NULL \
+ && LIST_NEXT((elm), field)->field.le_prev \
+ != &((elm)->field.le_next)) \
+ panic("Bad link elm %p next->prev != elm", (elm)); \
+ } while (0)
+
+#define QMD_LIST_CHECK_PREV(elm, field) \
+ do { \
+ if (*(elm)->field.le_prev != (elm)) \
+ panic("Bad link elm %p prev->next != elm", (elm)); \
+ } while (0)
+#else
+#define QMD_LIST_CHECK_HEAD(head, field)
+#define QMD_LIST_CHECK_NEXT(elm, field)
+#define QMD_LIST_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LIST_FIRST(head) ((head)->lh_first)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST((head)); (var); (var) = LIST_NEXT((var), field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); (var) = (tvar))
+
+#define LIST_INIT(head) \
+ do { \
+ LIST_FIRST((head)) = NULL; \
+ } while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) \
+ do { \
+ QMD_LIST_CHECK_NEXT(listelm, field); \
+ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) \
+ != NULL) \
+ LIST_NEXT((listelm), field)->field.le_prev = \
+ &LIST_NEXT((elm), field); \
+ LIST_NEXT((listelm), field) = (elm); \
+ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
+ } while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) \
+ do { \
+ QMD_LIST_CHECK_PREV(listelm, field); \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ LIST_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
+ } while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) \
+ do { \
+ QMD_LIST_CHECK_HEAD((head), field); \
+ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
+ LIST_FIRST((head))->field.le_prev = \
+ &LIST_NEXT((elm), field); \
+ LIST_FIRST((head)) = (elm); \
+ (elm)->field.le_prev = &LIST_FIRST((head)); \
+ } while (0)
+
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_REMOVE(elm, field) \
+ do { \
+ QMD_SAVELINK(oldnext, (elm)->field.le_next); \
+ QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
+ QMD_LIST_CHECK_NEXT(elm, field); \
+ QMD_LIST_CHECK_PREV(elm, field); \
+ if (LIST_NEXT((elm), field) != NULL) \
+ LIST_NEXT((elm), field)->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = LIST_NEXT((elm), field); \
+ TRASHIT(*oldnext); \
+ TRASHIT(*oldprev); \
+ } while (0)
+
+#define LIST_SWAP(head1, head2, type, field) \
+ do { \
+ struct type *swap_tmp = LIST_FIRST((head1)); \
+ LIST_FIRST((head1)) = LIST_FIRST((head2)); \
+ LIST_FIRST((head2)) = swap_tmp; \
+ if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
+ swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
+ if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
+ swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
+ } while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define TAILQ_HEAD(name, type) \
+ struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+ TRACEBUF \
+ }
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { \
+ NULL, &(head).tqh_first \
+ }
+
+#define TAILQ_ENTRY(type) \
+ struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+ TRACEBUF \
+ }
+
+/*
+ * Tail queue functions.
+ */
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define QMD_TAILQ_CHECK_HEAD(head, field) \
+ do { \
+ if (!TAILQ_EMPTY(head) \
+ && TAILQ_FIRST((head))->field.tqe_prev \
+ != &TAILQ_FIRST((head))) \
+ panic("Bad tailq head %p first->prev != head", \
+ (head)); \
+ } while (0)
+
+#define QMD_TAILQ_CHECK_TAIL(head, field) \
+ do { \
+ if (*(head)->tqh_last != NULL) \
+ panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
+ } while (0)
+
+#define QMD_TAILQ_CHECK_NEXT(elm, field) \
+ do { \
+ if (TAILQ_NEXT((elm), field) != NULL \
+ && TAILQ_NEXT((elm), field)->field.tqe_prev \
+ != &((elm)->field.tqe_next)) \
+ panic("Bad link elm %p next->prev != elm", (elm)); \
+ } while (0)
+
+#define QMD_TAILQ_CHECK_PREV(elm, field) \
+ do { \
+ if (*(elm)->field.tqe_prev != (elm)) \
+ panic("Bad link elm %p prev->next != elm", (elm)); \
+ } while (0)
+#else
+#define QMD_TAILQ_CHECK_HEAD(head, field)
+#define QMD_TAILQ_CHECK_TAIL(head, headname)
+#define QMD_TAILQ_CHECK_NEXT(elm, field)
+#define QMD_TAILQ_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define TAILQ_CONCAT(head1, head2, field) \
+ do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = \
+ (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ QMD_TRACE_HEAD(head1); \
+ QMD_TRACE_HEAD(head2); \
+ } \
+ } while (0)
+
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); (var) = (tvar))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_INIT(head) \
+ do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+ } while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) \
+ do { \
+ QMD_TAILQ_CHECK_NEXT(listelm, field); \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) \
+ != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else { \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ } \
+ TAILQ_NEXT((listelm), field) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+ } while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) \
+ do { \
+ QMD_TAILQ_CHECK_PREV(listelm, field); \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ TAILQ_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+ } while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) \
+ do { \
+ QMD_TAILQ_CHECK_HEAD(head, field); \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ } while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) \
+ do { \
+ QMD_TAILQ_CHECK_TAIL(head, field); \
+ TAILQ_NEXT((elm), field) = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ } while (0)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_REMOVE(head, elm, field) \
+ do { \
+ QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
+ QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
+ QMD_TAILQ_CHECK_NEXT(elm, field); \
+ QMD_TAILQ_CHECK_PREV(elm, field); \
+ if ((TAILQ_NEXT((elm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else { \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ QMD_TRACE_HEAD(head); \
+ } \
+ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
+ TRASHIT(*oldnext); \
+ TRASHIT(*oldprev); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ } while (0)
+
+#define TAILQ_SWAP(head1, head2, type, field) \
+ do { \
+ struct type *swap_first = (head1)->tqh_first; \
+ struct type **swap_last = (head1)->tqh_last; \
+ (head1)->tqh_first = (head2)->tqh_first; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ (head2)->tqh_first = swap_first; \
+ (head2)->tqh_last = swap_last; \
+ if ((swap_first = (head1)->tqh_first) != NULL) \
+ swap_first->field.tqe_prev = &(head1)->tqh_first; \
+ else \
+ (head1)->tqh_last = &(head1)->tqh_first; \
+ if ((swap_first = (head2)->tqh_first) != NULL) \
+ swap_first->field.tqe_prev = &(head2)->tqh_first; \
+ else \
+ (head2)->tqh_last = &(head2)->tqh_first; \
+ } while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
/* CLI commands ------------------------------------------------------------ */
-DEFUN(show_hash_stats,
- show_hash_stats_cmd,
- "show hashtable [statistics]",
- SHOW_STR
- "Statistics about hash tables\n"
- "Statistics about hash tables\n")
+DEFUN_NOSH(show_hash_stats,
+ show_hash_stats_cmd,
+ "show debugging hashtable [statistics]",
+ SHOW_STR
+ DEBUG_STR
+ "Statistics about hash tables\n"
+ "Statistics about hash tables\n")
{
struct hash *h;
struct listnode *ln;
DEFINE_MTYPE_STATIC(LIB, HOOK_ENTRY, "Hook entry")
void _hook_register(struct hook *hook, void *funcptr, void *arg, bool has_arg,
- struct frrmod_runtime *module, const char *funcname)
+ struct frrmod_runtime *module, const char *funcname,
+ int priority)
{
- struct hookent *he = XCALLOC(MTYPE_HOOK_ENTRY, sizeof(*he));
+ struct hookent *he = XCALLOC(MTYPE_HOOK_ENTRY, sizeof(*he)), **pos;
he->hookfn = funcptr;
he->hookarg = arg;
he->has_arg = has_arg;
he->module = module;
he->fnname = funcname;
+ he->priority = priority;
- he->next = hook->entries;
- hook->entries = he;
+ for (pos = &hook->entries; *pos; pos = &(*pos)->next)
+ if (hook->reverse
+ ? (*pos)->priority < priority
+ : (*pos)->priority >= priority)
+ break;
+
+ he->next = *pos;
+ *pos = he;
}
void _hook_unregister(struct hook *hook, void *funcptr, void *arg, bool has_arg)
* hook_register_arg (some_update_event, event_handler, addonptr);
*
* (addonptr isn't typesafe, but that should be manageable.)
+ *
+ * Hooks also support a "priority" value for ordering registered calls
+ * relative to each other. The priority is a signed integer where lower
+ * values are called earlier. There is also "Koohs", which is hooks with
+ * reverse priority ordering (for cleanup/deinit hooks, so you can use the
+ * same priority value).
+ *
+ * Recommended priority value ranges are:
+ *
+ * -999 ... 0 ... 999 - main executable / daemon, or library
+ * -1999 ... -1000 - modules registering calls that should run before
+ * the daemon's bits
+ * 1000 ... 1999 - modules calls that should run after daemon's
+ *
+ * Note: the default value is 1000, based on the following 2 expectations:
+ * - most hook_register() usage will be in loadable modules
+ * - usage of hook_register() in the daemon itself may need relative ordering
+ * to itself, making an explicit value the expected case
+ *
+ * The priority value is passed as extra argument on hook_register_prio() /
+ * hook_register_arg_prio(). Whether a hook runs in reverse is determined
+ * solely by the code defining / calling the hook. (DECLARE_KOOH is actually
+ * the same thing as DECLARE_HOOK, it's just there to make it obvious.)
*/
/* TODO:
void *hookfn; /* actually a function pointer */
void *hookarg;
bool has_arg;
+ int priority;
struct frrmod_runtime *module;
const char *fnname;
};
struct hook {
const char *name;
struct hookent *entries;
+ bool reverse;
};
+#define HOOK_DEFAULT_PRIORITY 1000
+
/* subscribe/add callback function to a hook
*
* always use hook_register(), which uses the static inline helper from
*/
extern void _hook_register(struct hook *hook, void *funcptr, void *arg,
bool has_arg, struct frrmod_runtime *module,
- const char *funcname);
+ const char *funcname, int priority);
#define hook_register(hookname, func) \
_hook_register(&_hook_##hookname, _hook_typecheck_##hookname(func), \
- NULL, false, THIS_MODULE, #func)
+ NULL, false, THIS_MODULE, #func, HOOK_DEFAULT_PRIORITY)
#define hook_register_arg(hookname, func, arg) \
_hook_register(&_hook_##hookname, \
_hook_typecheck_arg_##hookname(func), arg, true, \
- THIS_MODULE, #func)
+ THIS_MODULE, #func, HOOK_DEFAULT_PRIORITY)
+#define hook_register_prio(hookname, prio, func) \
+ _hook_register(&_hook_##hookname, _hook_typecheck_##hookname(func), \
+ NULL, false, THIS_MODULE, #func, prio)
+#define hook_register_arg_prio(hookname, prio, func, arg) \
+ _hook_register(&_hook_##hookname, \
+ _hook_typecheck_arg_##hookname(func), \
+ arg, true, THIS_MODULE, #func, prio)
extern void _hook_unregister(struct hook *hook, void *funcptr, void *arg,
bool has_arg);
{ \
return (void *)funcptr; \
}
+#define DECLARE_KOOH(hookname, arglist, passlist) \
+ DECLARE_HOOK(hookname, arglist, passlist)
/* use in source file - contains hook-related definitions.
*/
-#define DEFINE_HOOK(hookname, arglist, passlist) \
+#define DEFINE_HOOK_INT(hookname, arglist, passlist, rev) \
struct hook _hook_##hookname = { \
- .name = #hookname, .entries = NULL, \
+ .name = #hookname, .entries = NULL, .reverse = rev, \
}; \
static int hook_call_##hookname arglist \
{ \
return hooksum; \
}
+#define DEFINE_HOOK(hookname, arglist, passlist) \
+ DEFINE_HOOK_INT(hookname, arglist, passlist, false)
+#define DEFINE_KOOH(hookname, arglist, passlist) \
+ DEFINE_HOOK_INT(hookname, arglist, passlist, true)
+
#endif /* _FRR_HOOK_H */
DEFINE_QOBJ_TYPE(interface)
+DEFINE_HOOK(if_add, (struct interface *ifp), (ifp))
+DEFINE_KOOH(if_del, (struct interface *ifp), (ifp))
+
/* List of interfaces in only the default VRF */
int ptm_enable = 0;
-/* One for each program. This structure is needed to store hooks. */
-struct if_master {
- int (*if_new_hook)(struct interface *);
- int (*if_delete_hook)(struct interface *);
-} if_master = {
- 0,
-};
-
/* Compare interface names, returning an integer greater than, equal to, or
* less than 0, (following the strcmp convention), according to the
* relationship between ifp1 and ifp2. Interface names consist of an
SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION);
QOBJ_REG(ifp, interface);
-
- if (if_master.if_new_hook)
- (*if_master.if_new_hook)(ifp);
-
+ hook_call(if_add, ifp);
return ifp;
}
/* Delete interface structure. */
void if_delete_retain(struct interface *ifp)
{
- if (if_master.if_delete_hook)
- (*if_master.if_delete_hook)(ifp);
-
+ hook_call(if_del, ifp);
QOBJ_UNREG(ifp);
/* Free connected address list */
XFREE(MTYPE_IF, ifp);
}
-/* Add hook to interface master. */
-void if_add_hook(int type, int (*func)(struct interface *ifp))
-{
- switch (type) {
- case IF_NEW_HOOK:
- if_master.if_new_hook = func;
- break;
- case IF_DELETE_HOOK:
- if_master.if_delete_hook = func;
- break;
- default:
- break;
- }
-}
-
/* Interface existance check by index. */
struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id)
{
#include "linklist.h"
#include "memory.h"
#include "qobj.h"
+#include "hook.h"
DECLARE_MTYPE(IF)
DECLARE_MTYPE(CONNECTED_LABEL)
};
DECLARE_QOBJ_TYPE(interface)
+/* called from the library code whenever interfaces are created/deleted
+ * note: interfaces may not be fully realized at that point; also they
+ * may not exist in the system (ifindex = IFINDEX_INTERNAL)
+ *
+ * priority values are important here, daemons should be at 0 while modules
+ * can use 1000+ so they run after the daemon has initialised daemon-specific
+ * interface data
+ */
+DECLARE_HOOK(if_add, (struct interface *ifp), (ifp))
+DECLARE_KOOH(if_del, (struct interface *ifp), (ifp))
+
/* Connected address structure. */
struct connected {
/* Attached interface. */
? (C)->destination \
: (C)->address)
-/* Interface hook sort. */
-#define IF_NEW_HOOK 0
-#define IF_DELETE_HOOK 1
-
/* There are some interface flags which are only supported by some
operating system. */
extern int if_is_broadcast(struct interface *);
extern int if_is_pointopoint(struct interface *);
extern int if_is_multicast(struct interface *);
-extern void if_add_hook(int, int (*)(struct interface *));
extern void if_init(struct list **);
extern void if_cmd_init(void);
extern void if_terminate(struct list **);
#include <zebra.h>
-#include "openbsd-queue.h"
+#include "queue.h"
#include "imsg.h"
int ibuf_realloc(struct ibuf *, size_t);
#include <zebra.h>
-#include "openbsd-queue.h"
+#include "queue.h"
#include "imsg.h"
int imsg_fd_overhead = 0;
#include "network.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;
while (thread_fetch(master, &thread))
thread_call(&thread);
}
+
+void frr_early_fini(void)
+{
+ hook_call(frr_early_fini);
+}
+
+void frr_fini(void)
+{
+ hook_call(frr_fini);
+
+ /* memory_init -> nothing needed */
+ vty_terminate();
+ cmd_terminate();
+ zprivs_terminate(di->privs);
+ /* signal_init -> nothing needed */
+ thread_master_free(master);
+ closezlog();
+ /* frrmod_init -> nothing needed / hooks */
+}
extern bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len,
const char *path);
+/* these two are before the protocol daemon does its own shutdown
+ * it's named this way being the counterpart to frr_late_init */
+DECLARE_KOOH(frr_early_fini, (), ())
+extern void frr_early_fini(void);
+/* and these two are after the daemon did its own cleanup */
+DECLARE_KOOH(frr_fini, (), ())
+extern void frr_fini(void);
+
extern char config_default[256];
extern char frr_zclientpath[256];
extern const char frr_sysconfdir[];
if (p1->family == p2->family && p1->prefixlen == p2->prefixlen) {
if (p1->family == AF_INET)
- if (IPV4_ADDR_SAME(&p1->u.prefix4.s_addr,
- &p2->u.prefix4.s_addr))
+ if (IPV4_ADDR_SAME(&p1->u.prefix4, &p2->u.prefix4))
return 1;
if (p1->family == AF_INET6)
if (IPV6_ADDR_SAME(&p1->u.prefix6.s6_addr,
#define IPV4_MAX_BITLEN 32
#define IPV4_MAX_PREFIXLEN 32
#define IPV4_ADDR_CMP(D,S) memcmp ((D), (S), IPV4_MAX_BYTELEN)
-#define IPV4_ADDR_SAME(D,S) (memcmp ((D), (S), IPV4_MAX_BYTELEN) == 0)
-#define IPV4_ADDR_COPY(D,S) memcpy ((D), (S), IPV4_MAX_BYTELEN)
+
+static inline bool ipv4_addr_same(const struct in_addr *a,
+ const struct in_addr *b)
+{
+ return (a->s_addr == b->s_addr);
+}
+#define IPV4_ADDR_SAME(A,B) ipv4_addr_same((A), (B))
+
+static inline void ipv4_addr_copy(struct in_addr *dst,
+ const struct in_addr *src)
+{
+ dst->s_addr = src->s_addr;
+}
+#define IPV4_ADDR_COPY(D,S) ipv4_addr_copy((D), (S))
#define IPV4_NET0(a) ((((u_int32_t) (a)) & 0xff000000) == 0x00000000)
#define IPV4_NET127(a) ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000)
}
#ifdef HAVE_CAPABILITIES
- zprivs_caps_terminate();
+ if (zprivs->user || zprivs->group || zprivs->cap_num_p
+ || zprivs->cap_num_i)
+ zprivs_caps_terminate();
#else /* !HAVE_CAPABILITIES */
/* only change uid if we don't have the correct one */
if ((zprivs_state.zuid) && (zprivs_state.zsuid != zprivs_state.zuid)) {
-/*-
- * Copyright (c) 1991, 1993
- * The Regents of the University of California. 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.
- * 4. 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.
- *
- * @(#)queue.h 8.5 (Berkeley) 8/20/94
- * $FreeBSD$
- */
-
-#ifndef _SYS_QUEUE_H_
-#define _SYS_QUEUE_H_
-
/*
- * This file defines four types of data structures: singly-linked lists,
- * singly-linked tail queues, lists and tail queues.
+ * lists and queues implementations
*
- * A singly-linked list is headed by a single forward pointer. The elements
- * are singly linked for minimum space and pointer manipulation overhead at
- * the expense of O(n) removal for arbitrary elements. New elements can be
- * added to the list after an existing element or at the head of the list.
- * Elements being removed from the head of the list should use the explicit
- * macro for this purpose for optimum efficiency. A singly-linked list may
- * only be traversed in the forward direction. Singly-linked lists are ideal
- * for applications with large datasets and few or no removals or for
- * implementing a LIFO queue.
+ * 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.
*
- * A singly-linked tail queue is headed by a pair of pointers, one to the
- * head of the list and the other to the tail of the list. The elements are
- * singly linked for minimum space and pointer manipulation overhead at the
- * expense of O(n) removal for arbitrary elements. New elements can be added
- * to the list after an existing element, at the head of the list, or at the
- * end of the list. Elements being removed from the head of the tail queue
- * should use the explicit macro for this purpose for optimum efficiency.
- * A singly-linked tail queue may only be traversed in the forward direction.
- * Singly-linked tail queues are ideal for applications with large datasets
- * and few or no removals or for implementing a FIFO queue.
+ * 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.
*
- * A list is headed by a single forward pointer (or an array of forward
- * pointers for a hash table header). The elements are doubly linked
- * so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before
- * or after an existing element or at the head of the list. A list
- * may only be traversed in the forward direction.
- *
- * A tail queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are doubly
- * linked so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before or
- * after an existing element, at the head of the list, or at the end of
- * the list. A tail queue may be traversed in either direction.
- *
- * For details on the use of these macros, see the queue(3) manual page.
- *
- *
- * SLIST LIST STAILQ TAILQ
- * _HEAD + + + +
- * _HEAD_INITIALIZER + + + +
- * _ENTRY + + + +
- * _INIT + + + +
- * _EMPTY + + + +
- * _FIRST + + + +
- * _NEXT + + + +
- * _PREV - - - +
- * _LAST - - + +
- * _FOREACH + + + +
- * _FOREACH_SAFE + + + +
- * _FOREACH_REVERSE - - - +
- * _FOREACH_REVERSE_SAFE - - - +
- * _INSERT_HEAD + + + +
- * _INSERT_BEFORE - + - +
- * _INSERT_AFTER + + + +
- * _INSERT_TAIL - - + +
- * _CONCAT - - + +
- * _REMOVE_AFTER + - + -
- * _REMOVE_HEAD + - + -
- * _REMOVE + + + +
- * _SWAP + + + +
- *
- */
-#ifdef QUEUE_MACRO_DEBUG
-/* Store the last 2 places the queue element or head was altered */
-struct qm_trace {
- char *lastfile;
- int lastline;
- char *prevfile;
- int prevline;
-};
-
-#define TRACEBUF struct qm_trace trace;
-#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
-#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
-
-#define QMD_TRACE_HEAD(head) \
- do { \
- (head)->trace.prevline = (head)->trace.lastline; \
- (head)->trace.prevfile = (head)->trace.lastfile; \
- (head)->trace.lastline = __LINE__; \
- (head)->trace.lastfile = __FILE__; \
- } while (0)
-
-#define QMD_TRACE_ELEM(elem) \
- do { \
- (elem)->trace.prevline = (elem)->trace.lastline; \
- (elem)->trace.prevfile = (elem)->trace.lastfile; \
- (elem)->trace.lastline = __LINE__; \
- (elem)->trace.lastfile = __FILE__; \
- } while (0)
-
-#else
-#define QMD_TRACE_ELEM(elem)
-#define QMD_TRACE_HEAD(head)
-#define QMD_SAVELINK(name, link)
-#define TRACEBUF
-#define TRASHIT(x)
-#endif /* QUEUE_MACRO_DEBUG */
-
-/*
- * Singly-linked List declarations.
- */
-#define SLIST_HEAD(name, type) \
- struct name { \
- struct type *slh_first; /* first element */ \
- }
-
-#define SLIST_HEAD_INITIALIZER(head) \
- { \
- NULL \
- }
-
-#define SLIST_ENTRY(type) \
- struct { \
- struct type *sle_next; /* next element */ \
- }
-
-/*
- * Singly-linked List functions.
+ * 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
*/
-#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
-
-#define SLIST_FIRST(head) ((head)->slh_first)
-
-#define SLIST_FOREACH(var, head, field) \
- for ((var) = SLIST_FIRST((head)); (var); \
- (var) = SLIST_NEXT((var), field))
-
-#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = SLIST_FIRST((head)); \
- (var) && ((tvar) = SLIST_NEXT((var), field), 1); (var) = (tvar))
-
-#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
- for ((varp) = &SLIST_FIRST((head)); ((var) = *(varp)) != NULL; \
- (varp) = &SLIST_NEXT((var), field))
-
-#define SLIST_INIT(head) \
- do { \
- SLIST_FIRST((head)) = NULL; \
- } while (0)
-
-#define SLIST_INSERT_AFTER(slistelm, elm, field) \
- do { \
- SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
- SLIST_NEXT((slistelm), field) = (elm); \
- } while (0)
-
-#define SLIST_INSERT_HEAD(head, elm, field) \
- do { \
- SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
- SLIST_FIRST((head)) = (elm); \
- } while (0)
-
-#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
-
-#define SLIST_REMOVE(head, elm, type, field) \
- do { \
- QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
- if (SLIST_FIRST((head)) == (elm)) { \
- SLIST_REMOVE_HEAD((head), field); \
- } else { \
- struct type *curelm = SLIST_FIRST((head)); \
- while (SLIST_NEXT(curelm, field) != (elm)) \
- curelm = SLIST_NEXT(curelm, field); \
- SLIST_REMOVE_AFTER(curelm, field); \
- } \
- TRASHIT(*oldnext); \
- } while (0)
-
-#define SLIST_REMOVE_AFTER(elm, field) \
- do { \
- SLIST_NEXT(elm, field) = \
- SLIST_NEXT(SLIST_NEXT(elm, field), field); \
- } while (0)
-
-#define SLIST_REMOVE_HEAD(head, field) \
- do { \
- SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
- } while (0)
-
-#define SLIST_SWAP(head1, head2, type) \
- do { \
- struct type *swap_first = SLIST_FIRST(head1); \
- SLIST_FIRST(head1) = SLIST_FIRST(head2); \
- SLIST_FIRST(head2) = swap_first; \
- } while (0)
-
-/*
- * Singly-linked Tail queue declarations.
- */
-#define STAILQ_HEAD(name, type) \
- struct name { \
- struct type *stqh_first; /* first element */ \
- struct type **stqh_last; /* addr of last next element */ \
- }
-
-#define STAILQ_HEAD_INITIALIZER(head) \
- { \
- NULL, &(head).stqh_first \
- }
-
-#define STAILQ_ENTRY(type) \
- struct { \
- struct type *stqe_next; /* next element */ \
- }
-
-/*
- * Singly-linked Tail queue functions.
- */
-#define STAILQ_CONCAT(head1, head2) \
- do { \
- if (!STAILQ_EMPTY((head2))) { \
- *(head1)->stqh_last = (head2)->stqh_first; \
- (head1)->stqh_last = (head2)->stqh_last; \
- STAILQ_INIT((head2)); \
- } \
- } while (0)
-
-#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
-
-#define STAILQ_FIRST(head) ((head)->stqh_first)
-
-#define STAILQ_FOREACH(var, head, field) \
- for ((var) = STAILQ_FIRST((head)); (var); \
- (var) = STAILQ_NEXT((var), field))
-
-
-#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = STAILQ_FIRST((head)); \
- (var) && ((tvar) = STAILQ_NEXT((var), field), 1); (var) = (tvar))
-
-#define STAILQ_INIT(head) \
- do { \
- STAILQ_FIRST((head)) = NULL; \
- (head)->stqh_last = &STAILQ_FIRST((head)); \
- } while (0)
-
-#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) \
- do { \
- if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) \
- == NULL) \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
- STAILQ_NEXT((tqelm), field) = (elm); \
- } while (0)
-
-#define STAILQ_INSERT_HEAD(head, elm, field) \
- do { \
- if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) \
- == NULL) \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
- STAILQ_FIRST((head)) = (elm); \
- } while (0)
-
-#define STAILQ_INSERT_TAIL(head, elm, field) \
- do { \
- STAILQ_NEXT((elm), field) = NULL; \
- *(head)->stqh_last = (elm); \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
- } while (0)
+#ifndef _FRR_QUEUE_H
+#define _FRR_QUEUE_H
+
+#if defined(__OpenBSD__) && !defined(STAILQ_HEAD)
+#include "openbsd-queue.h"
+
+/* Try to map FreeBSD implementation to OpenBSD one. */
+#define STAILQ_HEAD(name, type) SIMPLEQ_HEAD(name, type)
+#define STAILQ_HEAD_INITIALIZER(head) SIMPLEQ_HEAD_INITIALIZER(head)
+#define STAILQ_ENTRY(entry) SIMPLEQ_ENTRY(entry)
+
+#define STAILQ_CONCAT(head1, head2) SIMPLEQ_CONCAT(head1, head2)
+#define STAILQ_EMPTY(head) SIMPLEQ_EMPTY(head)
+#define STAILQ_FIRST(head) SIMPLEQ_FIRST(head)
+#define STAILQ_FOREACH(var, head, field) SIMPLEQ_FOREACH(var, head, field)
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) SIMPLEQ_FOREACH_SAFE(var, head, field, tvar)
+#define STAILQ_INIT(head) SIMPLEQ_INIT(head)
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) SIMPLEQ_INSERT_AFTER(head, tqelm, elm, field)
+#define STAILQ_INSERT_HEAD(head, elm, field) SIMPLEQ_INSERT_HEAD(head, elm, field)
+#define STAILQ_INSERT_TAIL(head, elm, field) SIMPLEQ_INSERT_TAIL(head, elm, field)
#define STAILQ_LAST(head, type, field) \
- (STAILQ_EMPTY((head)) \
+ (SIMPLEQ_EMPTY((head)) \
? NULL \
- : ((struct type *)(void *)((char *)((head)->stqh_last) \
- - __offsetof(struct type, \
+ : ((struct type *)(void *)((char *)((head)->sqh_last) \
+ - offsetof(struct type, \
field))))
-
-#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
-
+#define STAILQ_NEXT(elm, field) SIMPLEQ_NEXT(elm, field)
#define STAILQ_REMOVE(head, elm, type, field) \
do { \
- QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
- if (STAILQ_FIRST((head)) == (elm)) { \
- STAILQ_REMOVE_HEAD((head), field); \
+ if (SIMPLEQ_FIRST((head)) == (elm)) { \
+ SIMPLEQ_REMOVE_HEAD((head), field); \
} else { \
- struct type *curelm = STAILQ_FIRST((head)); \
- while (STAILQ_NEXT(curelm, field) != (elm)) \
- curelm = STAILQ_NEXT(curelm, field); \
- STAILQ_REMOVE_AFTER(head, curelm, field); \
+ struct type *curelm = SIMPLEQ_FIRST((head)); \
+ while (SIMPLEQ_NEXT(curelm, field) != (elm)) \
+ curelm = SIMPLEQ_NEXT(curelm, field); \
+ SIMPLEQ_REMOVE_AFTER(head, curelm, field); \
} \
- TRASHIT(*oldnext); \
- } while (0)
-
-#define STAILQ_REMOVE_AFTER(head, elm, field) \
- do { \
- if ((STAILQ_NEXT(elm, field) = \
- STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) \
- == NULL) \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
} while (0)
-
-#define STAILQ_REMOVE_HEAD(head, field) \
- do { \
- if ((STAILQ_FIRST((head)) = \
- STAILQ_NEXT(STAILQ_FIRST((head)), field)) \
- == NULL) \
- (head)->stqh_last = &STAILQ_FIRST((head)); \
- } while (0)
-
+#define STAILQ_REMOVE_AFTER(head, elm, field) SIMPLEQ_REMOVE_AFTER(head, elm, field)
+#define STAILQ_REMOVE_HEAD(head, field) SIMPLEQ_REMOVE_HEAD(head, field)
#define STAILQ_SWAP(head1, head2, type) \
do { \
struct type *swap_first = STAILQ_FIRST(head1); \
- struct type **swap_last = (head1)->stqh_last; \
+ struct type **swap_last = (head1)->sqh_last; \
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
- (head1)->stqh_last = (head2)->stqh_last; \
+ (head1)->sqh_last = (head2)->sqh_last; \
STAILQ_FIRST(head2) = swap_first; \
- (head2)->stqh_last = swap_last; \
+ (head2)->sqh_last = swap_last; \
if (STAILQ_EMPTY(head1)) \
- (head1)->stqh_last = &STAILQ_FIRST(head1); \
+ (head1)->sqh_last = &STAILQ_FIRST(head1); \
if (STAILQ_EMPTY(head2)) \
- (head2)->stqh_last = &STAILQ_FIRST(head2); \
- } while (0)
-
-
-/*
- * List declarations.
- */
-#define LIST_HEAD(name, type) \
- struct name { \
- struct type *lh_first; /* first element */ \
- }
-
-#define LIST_HEAD_INITIALIZER(head) \
- { \
- NULL \
- }
-
-#define LIST_ENTRY(type) \
- struct { \
- struct type *le_next; /* next element */ \
- struct type **le_prev; /* address of previous next element */ \
- }
-
-/*
- * List functions.
- */
-
-#if (defined(_KERNEL) && defined(INVARIANTS))
-#define QMD_LIST_CHECK_HEAD(head, field) \
- do { \
- if (LIST_FIRST((head)) != NULL \
- && LIST_FIRST((head))->field.le_prev \
- != &LIST_FIRST((head))) \
- panic("Bad list head %p first->prev != head", (head)); \
- } while (0)
-
-#define QMD_LIST_CHECK_NEXT(elm, field) \
- do { \
- if (LIST_NEXT((elm), field) != NULL \
- && LIST_NEXT((elm), field)->field.le_prev \
- != &((elm)->field.le_next)) \
- panic("Bad link elm %p next->prev != elm", (elm)); \
- } while (0)
-
-#define QMD_LIST_CHECK_PREV(elm, field) \
- do { \
- if (*(elm)->field.le_prev != (elm)) \
- panic("Bad link elm %p prev->next != elm", (elm)); \
+ (head2)->sqh_last = &STAILQ_FIRST(head2); \
} while (0)
#else
-#define QMD_LIST_CHECK_HEAD(head, field)
-#define QMD_LIST_CHECK_NEXT(elm, field)
-#define QMD_LIST_CHECK_PREV(elm, field)
-#endif /* (_KERNEL && INVARIANTS) */
-
-#define LIST_EMPTY(head) ((head)->lh_first == NULL)
-
-#define LIST_FIRST(head) ((head)->lh_first)
-
-#define LIST_FOREACH(var, head, field) \
- for ((var) = LIST_FIRST((head)); (var); (var) = LIST_NEXT((var), field))
-
-#define LIST_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = LIST_FIRST((head)); \
- (var) && ((tvar) = LIST_NEXT((var), field), 1); (var) = (tvar))
-
-#define LIST_INIT(head) \
- do { \
- LIST_FIRST((head)) = NULL; \
- } while (0)
-
-#define LIST_INSERT_AFTER(listelm, elm, field) \
- do { \
- QMD_LIST_CHECK_NEXT(listelm, field); \
- if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) \
- != NULL) \
- LIST_NEXT((listelm), field)->field.le_prev = \
- &LIST_NEXT((elm), field); \
- LIST_NEXT((listelm), field) = (elm); \
- (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
- } while (0)
-
-#define LIST_INSERT_BEFORE(listelm, elm, field) \
- do { \
- QMD_LIST_CHECK_PREV(listelm, field); \
- (elm)->field.le_prev = (listelm)->field.le_prev; \
- LIST_NEXT((elm), field) = (listelm); \
- *(listelm)->field.le_prev = (elm); \
- (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
- } while (0)
-
-#define LIST_INSERT_HEAD(head, elm, field) \
- do { \
- QMD_LIST_CHECK_HEAD((head), field); \
- if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
- LIST_FIRST((head))->field.le_prev = \
- &LIST_NEXT((elm), field); \
- LIST_FIRST((head)) = (elm); \
- (elm)->field.le_prev = &LIST_FIRST((head)); \
- } while (0)
-
-#define LIST_NEXT(elm, field) ((elm)->field.le_next)
-
-#define LIST_REMOVE(elm, field) \
- do { \
- QMD_SAVELINK(oldnext, (elm)->field.le_next); \
- QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
- QMD_LIST_CHECK_NEXT(elm, field); \
- QMD_LIST_CHECK_PREV(elm, field); \
- if (LIST_NEXT((elm), field) != NULL) \
- LIST_NEXT((elm), field)->field.le_prev = \
- (elm)->field.le_prev; \
- *(elm)->field.le_prev = LIST_NEXT((elm), field); \
- TRASHIT(*oldnext); \
- TRASHIT(*oldprev); \
- } while (0)
-
-#define LIST_SWAP(head1, head2, type, field) \
- do { \
- struct type *swap_tmp = LIST_FIRST((head1)); \
- LIST_FIRST((head1)) = LIST_FIRST((head2)); \
- LIST_FIRST((head2)) = swap_tmp; \
- if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
- swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
- if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
- swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
- } while (0)
-
-/*
- * Tail queue declarations.
- */
-#define TAILQ_HEAD(name, type) \
- struct name { \
- struct type *tqh_first; /* first element */ \
- struct type **tqh_last; /* addr of last next element */ \
- TRACEBUF \
- }
-
-#define TAILQ_HEAD_INITIALIZER(head) \
- { \
- NULL, &(head).tqh_first \
- }
-
-#define TAILQ_ENTRY(type) \
- struct { \
- struct type *tqe_next; /* next element */ \
- struct type **tqe_prev; /* address of previous next element */ \
- TRACEBUF \
- }
-
-/*
- * Tail queue functions.
- */
-#if (defined(_KERNEL) && defined(INVARIANTS))
-#define QMD_TAILQ_CHECK_HEAD(head, field) \
- do { \
- if (!TAILQ_EMPTY(head) \
- && TAILQ_FIRST((head))->field.tqe_prev \
- != &TAILQ_FIRST((head))) \
- panic("Bad tailq head %p first->prev != head", \
- (head)); \
- } while (0)
-
-#define QMD_TAILQ_CHECK_TAIL(head, field) \
- do { \
- if (*(head)->tqh_last != NULL) \
- panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
- } while (0)
-
-#define QMD_TAILQ_CHECK_NEXT(elm, field) \
- do { \
- if (TAILQ_NEXT((elm), field) != NULL \
- && TAILQ_NEXT((elm), field)->field.tqe_prev \
- != &((elm)->field.tqe_next)) \
- panic("Bad link elm %p next->prev != elm", (elm)); \
- } while (0)
-
-#define QMD_TAILQ_CHECK_PREV(elm, field) \
- do { \
- if (*(elm)->field.tqe_prev != (elm)) \
- panic("Bad link elm %p prev->next != elm", (elm)); \
- } while (0)
-#else
-#define QMD_TAILQ_CHECK_HEAD(head, field)
-#define QMD_TAILQ_CHECK_TAIL(head, headname)
-#define QMD_TAILQ_CHECK_NEXT(elm, field)
-#define QMD_TAILQ_CHECK_PREV(elm, field)
-#endif /* (_KERNEL && INVARIANTS) */
-
-#define TAILQ_CONCAT(head1, head2, field) \
- do { \
- if (!TAILQ_EMPTY(head2)) { \
- *(head1)->tqh_last = (head2)->tqh_first; \
- (head2)->tqh_first->field.tqe_prev = \
- (head1)->tqh_last; \
- (head1)->tqh_last = (head2)->tqh_last; \
- TAILQ_INIT((head2)); \
- QMD_TRACE_HEAD(head1); \
- QMD_TRACE_HEAD(head2); \
- } \
- } while (0)
-
-#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
-
-#define TAILQ_FIRST(head) ((head)->tqh_first)
-
-#define TAILQ_FOREACH(var, head, field) \
- for ((var) = TAILQ_FIRST((head)); (var); \
- (var) = TAILQ_NEXT((var), field))
-
-#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = TAILQ_FIRST((head)); \
- (var) && ((tvar) = TAILQ_NEXT((var), field), 1); (var) = (tvar))
-
-#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
- for ((var) = TAILQ_LAST((head), headname); (var); \
- (var) = TAILQ_PREV((var), headname, field))
-
-#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
- for ((var) = TAILQ_LAST((head), headname); \
- (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
- (var) = (tvar))
-
-#define TAILQ_INIT(head) \
- do { \
- TAILQ_FIRST((head)) = NULL; \
- (head)->tqh_last = &TAILQ_FIRST((head)); \
- QMD_TRACE_HEAD(head); \
- } while (0)
-
-#define TAILQ_INSERT_AFTER(head, listelm, elm, field) \
- do { \
- QMD_TAILQ_CHECK_NEXT(listelm, field); \
- if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) \
- != NULL) \
- TAILQ_NEXT((elm), field)->field.tqe_prev = \
- &TAILQ_NEXT((elm), field); \
- else { \
- (head)->tqh_last = &TAILQ_NEXT((elm), field); \
- QMD_TRACE_HEAD(head); \
- } \
- TAILQ_NEXT((listelm), field) = (elm); \
- (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
- QMD_TRACE_ELEM(&(elm)->field); \
- QMD_TRACE_ELEM(&listelm->field); \
- } while (0)
-
-#define TAILQ_INSERT_BEFORE(listelm, elm, field) \
- do { \
- QMD_TAILQ_CHECK_PREV(listelm, field); \
- (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
- TAILQ_NEXT((elm), field) = (listelm); \
- *(listelm)->field.tqe_prev = (elm); \
- (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
- QMD_TRACE_ELEM(&(elm)->field); \
- QMD_TRACE_ELEM(&listelm->field); \
- } while (0)
-
-#define TAILQ_INSERT_HEAD(head, elm, field) \
- do { \
- QMD_TAILQ_CHECK_HEAD(head, field); \
- if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
- TAILQ_FIRST((head))->field.tqe_prev = \
- &TAILQ_NEXT((elm), field); \
- else \
- (head)->tqh_last = &TAILQ_NEXT((elm), field); \
- TAILQ_FIRST((head)) = (elm); \
- (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
- QMD_TRACE_HEAD(head); \
- QMD_TRACE_ELEM(&(elm)->field); \
- } while (0)
-
-#define TAILQ_INSERT_TAIL(head, elm, field) \
- do { \
- QMD_TAILQ_CHECK_TAIL(head, field); \
- TAILQ_NEXT((elm), field) = NULL; \
- (elm)->field.tqe_prev = (head)->tqh_last; \
- *(head)->tqh_last = (elm); \
- (head)->tqh_last = &TAILQ_NEXT((elm), field); \
- QMD_TRACE_HEAD(head); \
- QMD_TRACE_ELEM(&(elm)->field); \
- } while (0)
-
-#define TAILQ_LAST(head, headname) \
- (*(((struct headname *)((head)->tqh_last))->tqh_last))
-
-#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
-
-#define TAILQ_PREV(elm, headname, field) \
- (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-
-#define TAILQ_REMOVE(head, elm, field) \
- do { \
- QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
- QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
- QMD_TAILQ_CHECK_NEXT(elm, field); \
- QMD_TAILQ_CHECK_PREV(elm, field); \
- if ((TAILQ_NEXT((elm), field)) != NULL) \
- TAILQ_NEXT((elm), field)->field.tqe_prev = \
- (elm)->field.tqe_prev; \
- else { \
- (head)->tqh_last = (elm)->field.tqe_prev; \
- QMD_TRACE_HEAD(head); \
- } \
- *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
- TRASHIT(*oldnext); \
- TRASHIT(*oldprev); \
- QMD_TRACE_ELEM(&(elm)->field); \
- } while (0)
-
-#define TAILQ_SWAP(head1, head2, type, field) \
- do { \
- struct type *swap_first = (head1)->tqh_first; \
- struct type **swap_last = (head1)->tqh_last; \
- (head1)->tqh_first = (head2)->tqh_first; \
- (head1)->tqh_last = (head2)->tqh_last; \
- (head2)->tqh_first = swap_first; \
- (head2)->tqh_last = swap_last; \
- if ((swap_first = (head1)->tqh_first) != NULL) \
- swap_first->field.tqe_prev = &(head1)->tqh_first; \
- else \
- (head1)->tqh_last = &(head1)->tqh_first; \
- if ((swap_first = (head2)->tqh_first) != NULL) \
- swap_first->field.tqe_prev = &(head2)->tqh_first; \
- else \
- (head2)->tqh_last = &(head2)->tqh_first; \
- } while (0)
+#include "freebsd-queue.h"
+#endif /* defined(__OpenBSD__) && !defined(STAILQ_HEAD) */
-#endif /* !_SYS_QUEUE_H_ */
+#endif /* _FRR_QUEUE_H */
lib/event_counter.h \
lib/fifo.h \
lib/filter.h \
+ lib/freebsd-queue.h \
lib/frr_pthread.h \
lib/frratomic.h \
lib/getopt.h \
lib/ptm_lib.h \
lib/pw.h \
lib/qobj.h \
+ lib/queue.h \
lib/routemap.h \
lib/sbuf.h \
lib/sha256.h \
lib/command_lex.h \
lib/command_parse.h \
lib/gitversion.pl \
- lib/queue.h \
lib/route_types.pl \
lib/route_types.txt \
# end
DEFINE_MTYPE(LIB, ROUTE_TABLE, "Route table")
DEFINE_MTYPE(LIB, ROUTE_NODE, "Route node")
-static void route_node_delete(struct route_node *);
static void route_table_free(struct route_table *);
static int route_table_hash_cmp(const void *a, const void *b)
new->parent = node;
}
-/* Lock node. */
-struct route_node *route_lock_node(struct route_node *node)
-{
- node->lock++;
- return node;
-}
-
-/* Unlock node. */
-void route_unlock_node(struct route_node *node)
-{
- assert(node->lock > 0);
- node->lock--;
-
- if (node->lock == 0)
- route_node_delete(node);
-}
-
/* Find matched prefix. */
struct route_node *route_node_match(const struct route_table *table,
union prefixconstptr pu)
}
/* Delete node from the routing table. */
-static void route_node_delete(struct route_node *node)
+void route_node_delete(struct route_node *node)
{
struct route_node *child;
struct route_node *parent;
extern route_table_delegate_t *route_table_get_default_delegate(void);
extern void route_table_finish(struct route_table *);
-extern void route_unlock_node(struct route_node *node);
extern struct route_node *route_top(struct route_table *);
extern struct route_node *route_next(struct route_node *);
extern struct route_node *route_next_until(struct route_node *,
union prefixconstptr);
extern struct route_node *route_node_lookup_maynull(const struct route_table *,
union prefixconstptr);
-extern struct route_node *route_lock_node(struct route_node *node);
extern struct route_node *route_node_match(const struct route_table *,
union prefixconstptr);
extern struct route_node *route_node_match_ipv4(const struct route_table *,
extern struct route_node *route_node_create(route_table_delegate_t *,
struct route_table *);
+extern void route_node_delete(struct route_node *);
extern void route_node_destroy(route_table_delegate_t *, struct route_table *,
struct route_node *);
* Inline functions.
*/
+/* Lock node. */
+static inline struct route_node *route_lock_node(struct route_node *node)
+{
+ (*(unsigned *)&node->lock)++;
+ return node;
+}
+
+/* Unlock node. */
+static inline void route_unlock_node(struct route_node *node)
+{
+ assert(node->lock > 0);
+ (*(unsigned *)&node->lock)--;
+
+ if (node->lock == 0)
+ route_node_delete(node);
+}
+
/*
* route_table_iter_next
*
new->master = m;
SET_FLAG(new->flags, WQ_UNPLUGGED);
- if ((new->items = list_new()) == NULL) {
- XFREE(MTYPE_WORK_QUEUE_NAME, new->name);
- XFREE(MTYPE_WORK_QUEUE, new);
-
- return NULL;
- }
-
- new->items->del = (void (*)(void *))work_queue_item_free;
+ STAILQ_INIT(&new->items);
listnode_add(work_queues, new);
if (wq->thread != NULL)
thread_cancel(wq->thread);
- /* list_delete frees items via callback */
- list_delete(wq->items);
listnode_delete(work_queues, wq);
XFREE(MTYPE_WORK_QUEUE_NAME, wq->name);
static int work_queue_schedule(struct work_queue *wq, unsigned int delay)
{
/* if appropriate, schedule work queue thread */
- if (CHECK_FLAG(wq->flags, WQ_UNPLUGGED) && (wq->thread == NULL)
- && (listcount(wq->items) > 0)) {
+ if (CHECK_FLAG(wq->flags, WQ_UNPLUGGED) && (wq->thread == NULL) &&
+ !work_queue_empty(wq)) {
wq->thread = NULL;
thread_add_timer_msec(wq->master, work_queue_run, wq, delay,
&wq->thread);
}
item->data = data;
- listnode_add(wq->items, item);
+ work_queue_item_enqueue(wq, item);
work_queue_schedule(wq, wq->spec.hold);
return;
}
-static void work_queue_item_remove(struct work_queue *wq, struct listnode *ln)
+static void work_queue_item_remove(struct work_queue *wq,
+ struct work_queue_item *item)
{
- struct work_queue_item *item = listgetdata(ln);
-
assert(item && item->data);
/* call private data deletion callback if needed */
if (wq->spec.del_item_data)
wq->spec.del_item_data(wq, item->data);
- list_delete_node(wq->items, ln);
+ work_queue_item_dequeue(wq, item);
+
work_queue_item_free(item);
return;
}
-static void work_queue_item_requeue(struct work_queue *wq, struct listnode *ln)
+static void work_queue_item_requeue(struct work_queue *wq, struct work_queue_item *item)
{
- LISTNODE_DETACH(wq->items, ln);
- LISTNODE_ATTACH(wq->items, ln); /* attach to end of list */
+ work_queue_item_dequeue(wq, item);
+
+ /* attach to end of list */
+ work_queue_item_enqueue(wq, item);
}
DEFUN (show_work_queues,
for (ALL_LIST_ELEMENTS_RO(work_queues, node, wq)) {
vty_out(vty, "%c %8d %5d %8ld %8ld %7d %6d %8ld %6u %s\n",
(CHECK_FLAG(wq->flags, WQ_UNPLUGGED) ? ' ' : 'P'),
- listcount(wq->items), wq->spec.hold, wq->runs,
+ work_queue_item_count(wq), wq->spec.hold, wq->runs,
wq->yields, wq->cycles.best, wq->cycles.granularity,
wq->cycles.total,
(wq->runs) ? (unsigned int)(wq->cycles.total / wq->runs)
int work_queue_run(struct thread *thread)
{
struct work_queue *wq;
- struct work_queue_item *item;
+ struct work_queue_item *item, *titem;
wq_item_status ret;
unsigned int cycles = 0;
- struct listnode *node, *nnode;
char yielded = 0;
wq = THREAD_ARG(thread);
wq->thread = NULL;
- assert(wq && wq->items);
+ assert(wq);
/* calculate cycle granularity:
* list iteration == 1 run
if (wq->cycles.granularity == 0)
wq->cycles.granularity = WORK_QUEUE_MIN_GRANULARITY;
- for (ALL_LIST_ELEMENTS(wq->items, node, nnode, item)) {
+ STAILQ_FOREACH_SAFE(item, &wq->items, wq, titem) {
assert(item && item->data);
/* dont run items which are past their allowed retries */
/* run error handler, if any */
if (wq->spec.errorfunc)
wq->spec.errorfunc(wq, item->data);
- work_queue_item_remove(wq, node);
+ work_queue_item_remove(wq, item);
continue;
}
}
case WQ_REQUEUE: {
item->ran--;
- work_queue_item_requeue(wq, node);
+ work_queue_item_requeue(wq, item);
/* If a single node is being used with a meta-queue
* (e.g., zebra),
* update the next node as we don't want to exit the
* will kick in
* to terminate the thread when time has exceeded.
*/
- if (nnode == NULL)
- nnode = node;
+ if (titem == NULL)
+ titem = item;
break;
}
case WQ_RETRY_NOW:
/* fallthru */
case WQ_SUCCESS:
default: {
- work_queue_item_remove(wq, node);
+ work_queue_item_remove(wq, item);
break;
}
}
#endif
/* Is the queue done yet? If it is, call the completion callback. */
- if (listcount(wq->items) > 0)
+ if (!work_queue_empty(wq))
work_queue_schedule(wq, 0);
else if (wq->spec.completion_func)
wq->spec.completion_func(wq);
#define _QUAGGA_WORK_QUEUE_H
#include "memory.h"
+#include "queue.h"
DECLARE_MTYPE(WORK_QUEUE)
/* Hold time for the initial schedule of a queue run, in millisec */
/* A single work queue item, unsurprisingly */
struct work_queue_item {
+ STAILQ_ENTRY(work_queue_item) wq;
void *data; /* opaque data */
unsigned short ran; /* # of times item has been run */
};
} spec;
/* remaining fields should be opaque to users */
- struct list *items; /* queue item list */
+ STAILQ_HEAD(work_queue_items, work_queue_item) items; /* queue item list */
+ int item_count; /* queued items */
unsigned long runs; /* runs count */
unsigned long yields; /* yields count */
/* User API */
+static inline int work_queue_item_count(struct work_queue *wq)
+{
+ return wq->item_count;
+}
+
+static inline bool work_queue_empty(struct work_queue *wq)
+{
+ return (wq->item_count == 0) ? true : false;
+}
+
+static inline struct work_queue_item *work_queue_last_item(struct work_queue *wq)
+{
+ return STAILQ_LAST(&wq->items, work_queue_item, wq);
+}
+
+static inline void work_queue_item_enqueue(struct work_queue *wq,
+ struct work_queue_item *item)
+{
+ STAILQ_INSERT_TAIL(&wq->items, item, wq);
+ wq->item_count++;
+}
+
+static inline void work_queue_item_dequeue(struct work_queue *wq,
+ struct work_queue_item *item)
+{
+ assert(wq->item_count > 0);
+
+ wq->item_count--;
+ STAILQ_REMOVE(&wq->items, item, work_queue_item, wq);
+}
+
/* create a new work queue, of given name.
* user must fill in the spec of the returned work queue before adding
* anything to it
void nhrp_interface_init(void)
{
- if_add_hook(IF_NEW_HOOK, nhrp_if_new_hook);
- if_add_hook(IF_DELETE_HOOK, nhrp_if_delete_hook);
+ hook_register_prio(if_add, 0, nhrp_if_new_hook);
+ hook_register_prio(if_del, 0, nhrp_if_delete_hook);
}
void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi)
static void nhrp_request_stop(void)
{
debugf(NHRP_DEBUG_COMMON, "Exiting...");
+ frr_early_fini();
nhrp_shortcut_terminate();
nhrp_nhs_terminate();
evmgr_terminate();
nhrp_vc_terminate();
vrf_terminate();
- /* memory_terminate(); */
- /* vty_terminate(); */
- cmd_terminate();
- /* signal_terminate(); */
- zprivs_terminate(&nhrpd_privs);
debugf(NHRP_DEBUG_COMMON, "Done.");
-
- closezlog();
+ frr_fini();
exit(0);
}
#ifndef NO_DEBUG
-DEFUN(show_debugging_nhrp, show_debugging_nhrp_cmd,
- "show debugging nhrp",
- SHOW_STR
- "Debugging information\n"
- "NHRP configuration\n")
+DEFUN_NOSH(show_debugging_nhrp, show_debugging_nhrp_cmd,
+ "show debugging [nhrp]",
+ SHOW_STR
+ "Debugging information\n"
+ "NHRP configuration\n")
{
int i;
struct listnode *node;
struct interface *ifp;
+ frr_early_fini();
+
if (ospf6)
ospf6_delete(ospf6);
ospf6_lsa_terminate();
vrf_terminate();
- vty_terminate();
- cmd_terminate();
if (zclient) {
zclient_stop(zclient);
zclient_free(zclient);
}
- if (master)
- thread_master_free(master);
-
- closezlog();
-
+ frr_fini();
exit(status);
}
config_write_ospf6_debug_asbr(vty);
config_write_ospf6_debug_abr(vty);
config_write_ospf6_debug_flood(vty);
- vty_out(vty, "!\n");
+
return 0;
}
+DEFUN_NOSH (show_debugging_ospf6,
+ show_debugging_ospf6_cmd,
+ "show debugging [ospf6]",
+ SHOW_STR
+ DEBUG_STR
+ OSPF6_STR)
+{
+ vty_out(vty, "OSPF6 debugging status:");
+
+ config_write_ospf6_debug(vty);
+
+ return CMD_SUCCESS;
+}
+
#define AREA_LSDB_TITLE_FORMAT \
"\n Area Scoped Link State Database (Area %s)\n\n"
#define IF_LSDB_TITLE_FORMAT \
install_element_ospf6_clear_interface();
+ install_element(VIEW_NODE, &show_debugging_ospf6_cmd);
+
install_element(VIEW_NODE, &show_ipv6_ospf6_border_routers_cmd);
install_element(VIEW_NODE, &show_ipv6_ospf6_linkstate_cmd);
return CMD_SUCCESS;
}
-DEFUN (show_debugging_ospf,
- show_debugging_ospf_cmd,
- "show debugging ospf",
- SHOW_STR
- DEBUG_STR
- OSPF_STR)
+DEFUN_NOSH (show_debugging_ospf,
+ show_debugging_ospf_cmd,
+ "show debugging [ospf]",
+ SHOW_STR
+ DEBUG_STR
+ OSPF_STR)
{
struct ospf *ospf;
return show_debugging_ospf_common(vty, ospf);
}
-DEFUN (show_debugging_ospf_instance,
- show_debugging_ospf_instance_cmd,
- "show debugging ospf (1-65535)",
- SHOW_STR
- DEBUG_STR
- OSPF_STR
- "Instance ID\n")
+DEFUN_NOSH (show_debugging_ospf_instance,
+ show_debugging_ospf_instance_cmd,
+ "show debugging ospf (1-65535)",
+ SHOW_STR
+ DEBUG_STR
+ OSPF_STR
+ "Instance ID\n")
{
int idx_number = 3;
struct ospf *ospf;
{
/* Initialize Zebra interface data structure. */
om->iflist = vrf_iflist(VRF_DEFAULT);
- if_add_hook(IF_NEW_HOOK, ospf_if_new_hook);
- if_add_hook(IF_DELETE_HOOK, ospf_if_delete_hook);
+ hook_register_prio(if_add, 0, ospf_if_new_hook);
+ hook_register_prio(if_del, 0, ospf_if_delete_hook);
}
if (flag == OSPF_SEND_PACKET_INDIRECT)
zlog_warn(
"* LS-Update is directly sent on NBMA network.");
- if (IPV4_ADDR_SAME(&oi->address->u.prefix4, &p.prefix.s_addr))
+ if (IPV4_ADDR_SAME(&oi->address->u.prefix4, &p.prefix))
zlog_warn("* LS-Update is sent to myself.");
}
ALIAS(no_debug_msdp_packets, undebug_msdp_packets_cmd, "undebug msdp packets",
UNDEBUG_STR DEBUG_MSDP_STR DEBUG_MSDP_PACKETS_STR)
-DEFUN (show_debugging_pim,
- show_debugging_pim_cmd,
- "show debugging pim",
- SHOW_STR
- DEBUG_STR
- PIM_STR)
+DEFUN_NOSH (show_debugging_pim,
+ show_debugging_pim_cmd,
+ "show debugging [pim]",
+ SHOW_STR
+ DEBUG_STR
+ PIM_STR)
{
+ vty_out(vty, "PIM debugging status\n");
+
pim_debug_config_write(vty);
+
return CMD_SUCCESS;
}
%{!?with_pam: %global with_pam 0 }
%{!?with_ospfclient: %global with_ospfclient 1 }
%{!?with_ospfapi: %global with_ospfapi 1 }
-%{!?with_irdp: %global with_irdp 1 }
%{!?with_rtadv: %global with_rtadv 1 }
%{!?with_ldpd: %global with_ldpd 1 }
%{!?with_nhrpd: %global with_nhrpd 1 }
--libexecdir=%{_libexecdir} \
--localstatedir=%{_localstatedir} \
--disable-werror \
+ --enable-irdp \
%if !%{with_shared}
--disable-shared \
%endif
%else
--enable-ospfapi=no \
%endif
-%if %{with_irdp}
- --enable-irdp=yes \
-%else
- --enable-irdp=no \
-%endif
%if %{with_rtadv}
--enable-rtadv=yes \
%else
# Remove debian init script if it was installed
rm -f %{buildroot}%{_sbindir}/frr
+# kill bogus libtool files for modules
+rm -f %{buildroot}%{_libdir}/frr/modules/*.la
+
# install /etc sources
%if "%{initsystem}" == "systemd"
mkdir -p %{buildroot}%{_unitdir}
%{_libdir}/lib*.so.0
%attr(755,root,root) %{_libdir}/lib*.so.0.*
%endif
+%attr(755,root,root) %{_libdir}/frr/modules/zebra_irdp.so
%{_bindir}/*
%config(noreplace) /etc/frr/[!v]*.conf*
%config(noreplace) %attr(750,%frr_user,%frr_user) /etc/frr/daemons
unsigned long rip_debug_packet = 0;
unsigned long rip_debug_zebra = 0;
-DEFUN (show_debugging_rip,
- show_debugging_rip_cmd,
- "show debugging rip",
- SHOW_STR
- DEBUG_STR
- RIP_STR)
+DEFUN_NOSH (show_debugging_rip,
+ show_debugging_rip_cmd,
+ "show debugging [rip]",
+ SHOW_STR
+ DEBUG_STR
+ RIP_STR)
{
vty_out(vty, "RIP debugging status:\n");
void rip_if_init(void)
{
/* Default initial size of interface vector. */
- if_add_hook(IF_NEW_HOOK, rip_interface_new_hook);
- if_add_hook(IF_DELETE_HOOK, rip_interface_delete_hook);
+ hook_register_prio(if_add, 0, rip_interface_new_hook);
+ hook_register_prio(if_del, 0, rip_interface_delete_hook);
/* RIP network init. */
rip_enable_interface = vector_init(1);
unsigned long ripng_debug_packet = 0;
unsigned long ripng_debug_zebra = 0;
-DEFUN (show_debugging_ripng,
- show_debugging_ripng_cmd,
- "show debugging ripng",
- SHOW_STR
- DEBUG_STR
- "RIPng configuration\n")
+DEFUN_NOSH (show_debugging_ripng,
+ show_debugging_ripng_cmd,
+ "show debugging [ripng]",
+ SHOW_STR
+ DEBUG_STR
+ "RIPng configuration\n")
{
vty_out(vty, "RIPng debugging status:\n");
void ripng_if_init()
{
/* Interface initialize. */
- if_add_hook(IF_NEW_HOOK, ripng_if_new_hook);
- if_add_hook(IF_DELETE_HOOK, ripng_if_delete_hook);
+ hook_register_prio(if_add, 0, ripng_if_new_hook);
+ hook_register_prio(if_del, 0, ripng_if_delete_hook);
/* RIPng enable network init. */
ripng_enable_network = route_table_init();
# returns: 0=ok, 1=error
check_daemon()
{
+ if [ $1 != "watchfrr" -a $1 != "vtysh_enable" ]; then
+ # check for daemon binary
+ if [ ! -x "$D_PATH/$1" ]; then return 1; fi
+ fi
+
# If the integrated config file is used the others are not checked.
if [ -r "$C_PATH/frr.conf" ]; then
return 0
# vtysh_enable has no config file nor binary so skip check.
# (Not sure why vtysh_enable is in this list but does not hurt)
if [ $1 != "watchfrr" -a $1 != "vtysh_enable" ]; then
- # check for daemon binary
- if [ ! -x "$D_PATH/$1" ]; then return 1; fi
-
# check for config file
if [ -n "$2" ]; then
if [ ! -r "$C_PATH/$1-$2.conf" ]; then
for line in lines_to_configure:
fh.write(line + '\n')
- output = subprocess.check_output(['/usr/bin/vtysh', '-f', filename])
-
- # exit non-zero if we see these errors
- for x in ('BGP instance name and AS number mismatch',
- 'BGP instance is already running',
- '% not a local address'):
- for line in output.splitlines():
- if x in line:
- msg = "ERROR: %s" % x
- log.error(msg)
- print msg
- reload_ok = False
-
+ try:
+ subprocess.check_output(['/usr/bin/vtysh', '-f', filename])
+ except subprocess.CalledProcessError as e:
+ log.warning("frr-reload.py failed due to\n%s" % e.output)
+ reload_ok = False
os.unlink(filename)
# Make these changes persistent
lineno, cmd_stat,
vtysh_client[i].name,
vty->buf);
+ retcode = cmd_stat;
break;
}
}
return ret;
}
+DEFUN (vtysh_show_debugging,
+ vtysh_show_debugging_cmd,
+ "show debugging",
+ SHOW_STR
+ DEBUG_STR)
+{
+ return show_per_daemon("do show debugging\n",
+ "Debugging Information for %s:\n");
+}
+
+DEFUN (vtysh_show_debugging_hashtable,
+ vtysh_show_debugging_hashtable_cmd,
+ "show debugging hashtable [statistics]",
+ SHOW_STR
+ DEBUG_STR
+ "Statistics about hash tables\n"
+ "Statistics about hash tables\n")
+{
+ return show_per_daemon("do show debugging hashtable\n",
+ "Hashtable statistics for %s:\n");
+}
+
/* Memory */
DEFUN (vtysh_show_memory,
vtysh_show_memory_cmd,
SHOW_STR
"Memory statistics\n")
{
- return show_per_daemon("show memory\n", "Memory statistics for %s:\n");
+ return show_per_daemon("show memory\n",
+ "Memory statistics for %s:\n");
}
DEFUN (vtysh_show_modules,
SHOW_STR
"Show current logging configuration\n")
{
- unsigned int i;
- int ret = CMD_SUCCESS;
- char line[] = "do show logging\n";
-
- for (i = 0; i < array_size(vtysh_client); i++)
- if (vtysh_client[i].fd >= 0) {
- fprintf(stdout, "Logging configuration for %s:\n",
- vtysh_client[i].name);
- ret = vtysh_client_execute(&vtysh_client[i], line,
- stdout);
- fprintf(stdout, "\n");
- }
-
- return ret;
+ return show_per_daemon("do show logging\n",
+ "Logging configuration for %s:\n");
}
DEFUNSH(VTYSH_ALL, vtysh_log_stdout, vtysh_log_stdout_cmd, "log stdout",
install_element(ENABLE_NODE, &vtysh_start_zsh_cmd);
#endif
+ install_element(VIEW_NODE, &vtysh_show_debugging_cmd);
+ install_element(VIEW_NODE, &vtysh_show_debugging_hashtable_cmd);
install_element(VIEW_NODE, &vtysh_show_memory_cmd);
install_element(VIEW_NODE, &vtysh_show_modules_cmd);
unsigned long zebra_debug_vxlan;
unsigned long zebra_debug_pw;
-DEFUN (show_debugging_zebra,
- show_debugging_zebra_cmd,
- "show debugging zebra",
- SHOW_STR
- "Debugging information\n"
- "Zebra configuration\n")
+DEFUN_NOSH (show_debugging_zebra,
+ show_debugging_zebra_cmd,
+ "show debugging [zebra]",
+ SHOW_STR
+ "Debugging information\n"
+ "Zebra configuration\n")
{
vty_out(vty, "Zebra debugging status:\n");
#define ZEBRA_PTM_SUPPORT
-#if defined(HAVE_RTADV)
-/* Order is intentional. Matches RFC4191. This array is also used for
- command matching, so only modify with care. */
-const char *rtadv_pref_strs[] = {"medium", "high", "INVALID", "low", 0};
-#endif /* HAVE_RTADV */
+DEFINE_HOOK(zebra_if_extra_info, (struct vty *vty, struct interface *ifp),
+ (vty, ifp))
+DEFINE_HOOK(zebra_if_config_wr, (struct vty *vty, struct interface *ifp),
+ (vty, ifp))
static void if_down_del_nbr_connected(struct interface *ifp);
vty_out(vty, "\n");
}
-#if defined(HAVE_RTADV)
-/* Dump interface ND information to vty. */
-static void nd_dump_vty(struct vty *vty, struct interface *ifp)
-{
- struct zebra_if *zif;
- struct rtadvconf *rtadv;
- int interval;
-
- zif = (struct zebra_if *)ifp->info;
- rtadv = &zif->rtadv;
-
- if (rtadv->AdvSendAdvertisements) {
- vty_out(vty,
- " ND advertised reachable time is %d milliseconds\n",
- rtadv->AdvReachableTime);
- vty_out(vty,
- " ND advertised retransmit interval is %d milliseconds\n",
- rtadv->AdvRetransTimer);
- vty_out(vty, " ND router advertisements sent: %d rcvd: %d\n",
- zif->ra_sent, zif->ra_rcvd);
- interval = rtadv->MaxRtrAdvInterval;
- if (interval % 1000)
- vty_out(vty,
- " ND router advertisements are sent every "
- "%d milliseconds\n",
- interval);
- else
- vty_out(vty,
- " ND router advertisements are sent every "
- "%d seconds\n",
- interval / 1000);
- if (rtadv->AdvDefaultLifetime != -1)
- vty_out(vty,
- " ND router advertisements live for %d seconds\n",
- rtadv->AdvDefaultLifetime);
- else
- vty_out(vty,
- " ND router advertisements lifetime tracks ra-interval\n");
- vty_out(vty,
- " ND router advertisement default router preference is "
- "%s\n",
- rtadv_pref_strs[rtadv->DefaultPreference]);
- if (rtadv->AdvManagedFlag)
- vty_out(vty,
- " Hosts use DHCP to obtain routable addresses.\n");
- else
- vty_out(vty,
- " Hosts use stateless autoconfig for addresses.\n");
- if (rtadv->AdvHomeAgentFlag) {
- vty_out(vty,
- " ND router advertisements with Home Agent flag bit set.\n");
- if (rtadv->HomeAgentLifetime != -1)
- vty_out(vty,
- " Home Agent lifetime is %u seconds\n",
- rtadv->HomeAgentLifetime);
- else
- vty_out(vty,
- " Home Agent lifetime tracks ra-lifetime\n");
- vty_out(vty, " Home Agent preference is %u\n",
- rtadv->HomeAgentPreference);
- }
- if (rtadv->AdvIntervalOption)
- vty_out(vty,
- " ND router advertisements with Adv. Interval option.\n");
- }
-}
-#endif /* HAVE_RTADV */
-
static const char *zebra_ziftype_2str(zebra_iftype_t zif_type)
{
switch (zif_type) {
inet_ntoa(iflp->rmt_ip), iflp->rmt_as);
}
-#ifdef RTADV
- nd_dump_vty(vty, ifp);
-#endif /* RTADV */
-#if defined(HAVE_RTADV)
- nd_dump_vty(vty, ifp);
-#endif /* HAVE_RTADV */
+ hook_call(zebra_if_extra_info, vty, ifp);
+
if (listhead(ifp->nbr_connected))
vty_out(vty, " Neighbor address(s):\n");
for (ALL_LIST_ELEMENTS_RO(ifp->nbr_connected, node, nbr_connected))
: "no ");
}
-#if defined(HAVE_RTADV)
- rtadv_config_write(vty, ifp);
-#endif /* HAVE_RTADV */
-
-#ifdef HAVE_IRDP
- irdp_config_write(vty, ifp);
-#endif /* IRDP */
+ hook_call(zebra_if_config_wr, vty, ifp);
link_params_config_write(vty, ifp);
void zebra_if_init(void)
{
/* Initialize interface and new hook. */
- if_add_hook(IF_NEW_HOOK, if_zebra_new_hook);
- if_add_hook(IF_DELETE_HOOK, if_zebra_delete_hook);
+ hook_register_prio(if_add, 0, if_zebra_new_hook);
+ hook_register_prio(if_del, 0, if_zebra_delete_hook);
/* Install configuration write function. */
install_node(&interface_node, if_config_write);
#include "redistribute.h"
#include "vrf.h"
-
-#ifdef HAVE_IRDP
-#include "zebra/irdp.h"
-#endif
+#include "hook.h"
#include "zebra/zebra_l2.h"
ZEBRA_IF_SLAVE_OTHER, /* Something else - e.g., bond slave */
} zebra_slave_iftype_t;
+struct irdp_interface;
+
/* `zebra' daemon local interface structure. */
struct zebra_if {
/* Shutdown configuration. */
unsigned int ra_sent, ra_rcvd;
#endif /* HAVE_RTADV */
-#ifdef HAVE_IRDP
- struct irdp_interface irdp;
-#endif
+ struct irdp_interface *irdp;
#ifdef HAVE_STRUCT_SOCKADDR_DL
union {
struct interface *link;
};
+DECLARE_HOOK(zebra_if_extra_info, (struct vty *vty, struct interface *ifp),
+ (vty, ifp))
+DECLARE_HOOK(zebra_if_config_wr, (struct vty *vty, struct interface *ifp),
+ (vty, ifp))
+
static inline void zebra_if_set_ziftype(struct interface *ifp,
zebra_iftype_t zif_type,
zebra_slave_iftype_t zif_slave_type)
int pref;
};
-extern void irdp_init(void);
+extern void irdp_if_init(void);
extern int irdp_sock_init(void);
-extern void irdp_finish(void);
-extern void irdp_config_write(struct vty *, struct interface *);
+extern int irdp_config_write(struct vty *, struct interface *);
extern int irdp_send_thread(struct thread *t_advert);
extern void irdp_advert_off(struct interface *ifp);
extern void process_solicit(struct interface *ifp);
#include <zebra.h>
-#ifdef HAVE_IRDP
-
#include "if.h"
#include "vty.h"
#include "sockunion.h"
extern int irdp_sock;
+DEFINE_MTYPE_STATIC(ZEBRA, IRDP_IF, "IRDP interface data")
+
+static struct irdp_interface *irdp_if_get(struct interface *ifp)
+{
+ struct zebra_if *zi = ifp->info;
+ if (!zi->irdp)
+ zi->irdp = XCALLOC(MTYPE_IRDP_IF, sizeof(*zi->irdp));
+ return zi->irdp;
+}
+
+static int irdp_if_delete(struct interface *ifp)
+{
+ struct zebra_if *zi = ifp->info;
+ if (!zi)
+ return 0;
+ XFREE(MTYPE_IRDP_IF, zi->irdp);
+ return 0;
+}
+
static const char *inet_2a(u_int32_t a, char *b)
{
sprintf(b, "%u.%u.%u.%u", (a)&0xFF, (a >> 8) & 0xFF, (a >> 16) & 0xFF,
static int if_add_group(struct interface *ifp)
{
struct zebra_if *zi = ifp->info;
- struct irdp_interface *irdp = &zi->irdp;
+ struct irdp_interface *irdp = zi->irdp;
int ret;
char b1[INET_ADDRSTRLEN];
+ if (!irdp)
+ return -1;
+
ret = if_group(ifp, irdp_sock, INADDR_ALLRTRS_GROUP, IP_ADD_MEMBERSHIP);
if (ret < 0) {
return ret;
static int if_drop_group(struct interface *ifp)
{
struct zebra_if *zi = ifp->info;
- struct irdp_interface *irdp = &zi->irdp;
+ struct irdp_interface *irdp = zi->irdp;
int ret;
char b1[INET_ADDRSTRLEN];
+ if (!irdp)
+ return -1;
+
ret = if_group(ifp, irdp_sock, INADDR_ALLRTRS_GROUP,
IP_DROP_MEMBERSHIP);
if (ret < 0)
return 0;
}
-static void if_set_defaults(struct interface *ifp)
+static void if_set_defaults(struct irdp_interface *irdp)
{
- struct zebra_if *zi = ifp->info;
- struct irdp_interface *irdp = &zi->irdp;
-
irdp->MaxAdvertInterval = IRDP_MAXADVERTINTERVAL;
irdp->MinAdvertInterval = IRDP_MINADVERTINTERVAL;
irdp->Preference = IRDP_PREFERENCE;
int set_defaults)
{
struct zebra_if *zi = ifp->info;
- struct irdp_interface *irdp = &zi->irdp;
+ struct irdp_interface *irdp = zi->irdp;
struct listnode *node;
struct connected *ifc;
u_int32_t timer, seed;
+ assert(irdp);
+
if (irdp->flags & IF_ACTIVE) {
zlog_warn("IRDP: Interface is already active %s", ifp->name);
return;
}
if (set_defaults)
- if_set_defaults(ifp);
+ if_set_defaults(irdp);
irdp->irdp_sent = 0;
static void irdp_if_stop(struct interface *ifp)
{
struct zebra_if *zi = ifp->info;
- struct irdp_interface *irdp = &zi->irdp;
+ struct irdp_interface *irdp = zi->irdp;
if (irdp == NULL) {
zlog_warn("Interface %s structure is NULL", ifp->name);
static void irdp_if_shutdown(struct interface *ifp)
{
struct zebra_if *zi = ifp->info;
- struct irdp_interface *irdp = &zi->irdp;
+ struct irdp_interface *irdp = zi->irdp;
+ if (!irdp)
+ return;
if (irdp->flags & IF_SHUTDOWN) {
zlog_warn("IRDP: Interface is already shutdown %s", ifp->name);
return;
static void irdp_if_no_shutdown(struct interface *ifp)
{
- struct zebra_if *zi = ifp->info;
- struct irdp_interface *irdp = &zi->irdp;
+ struct irdp_interface *irdp = irdp_if_get(ifp);
if (!(irdp->flags & IF_SHUTDOWN)) {
zlog_warn("IRDP: Interface is not shutdown %s", ifp->name);
/* Write configuration to user */
-void irdp_config_write(struct vty *vty, struct interface *ifp)
+int irdp_config_write(struct vty *vty, struct interface *ifp)
{
struct zebra_if *zi = ifp->info;
- struct irdp_interface *irdp = &zi->irdp;
+ struct irdp_interface *irdp = zi->irdp;
struct Adv *adv;
struct listnode *node;
char b1[INET_ADDRSTRLEN];
+ if (!irdp)
+ return 0;
+
if (irdp->flags & IF_ACTIVE || irdp->flags & IF_SHUTDOWN) {
if (irdp->flags & IF_SHUTDOWN)
vty_out(vty, " ip irdp maxadvertinterval %ld\n",
irdp->MaxAdvertInterval);
}
+ return 0;
}
"Use multicast mode\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
+ irdp_if_get(ifp);
irdp_if_start(ifp, TRUE, TRUE);
return CMD_SUCCESS;
"Use broadcast mode\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
+ irdp_if_get(ifp);
irdp_if_start(ifp, FALSE, TRUE);
return CMD_SUCCESS;
{
int idx_number = 3;
VTY_DECLVAR_CONTEXT(interface, ifp);
- struct zebra_if *zi;
- struct irdp_interface *irdp;
-
- zi = ifp->info;
- irdp = &zi->irdp;
+ struct irdp_interface *irdp = irdp_if_get(ifp);
irdp->Lifetime = atoi(argv[idx_number]->arg);
return CMD_SUCCESS;
{
int idx_number = 3;
VTY_DECLVAR_CONTEXT(interface, ifp);
- struct zebra_if *zi;
- struct irdp_interface *irdp;
-
- zi = ifp->info;
- irdp = &zi->irdp;
+ struct irdp_interface *irdp = irdp_if_get(ifp);
if ((unsigned)atoi(argv[idx_number]->arg) <= irdp->MaxAdvertInterval) {
irdp->MinAdvertInterval = atoi(argv[idx_number]->arg);
{
int idx_number = 3;
VTY_DECLVAR_CONTEXT(interface, ifp);
- struct zebra_if *zi;
- struct irdp_interface *irdp;
-
- zi = ifp->info;
- irdp = &zi->irdp;
+ struct irdp_interface *irdp = irdp_if_get(ifp);
if (irdp->MinAdvertInterval <= (unsigned)atoi(argv[idx_number]->arg)) {
irdp->MaxAdvertInterval = atoi(argv[idx_number]->arg);
{
int idx_number = 3;
VTY_DECLVAR_CONTEXT(interface, ifp);
- struct zebra_if *zi;
- struct irdp_interface *irdp;
-
- zi = ifp->info;
- irdp = &zi->irdp;
+ struct irdp_interface *irdp = irdp_if_get(ifp);
irdp->Preference = atoi(argv[idx_number]->arg);
return CMD_SUCCESS;
int idx_ipv4 = 3;
int idx_number = 5;
VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct irdp_interface *irdp = irdp_if_get(ifp);
struct listnode *node;
struct in_addr ip;
int pref;
int ret;
- struct zebra_if *zi;
- struct irdp_interface *irdp;
struct Adv *adv;
- zi = ifp->info;
- irdp = &zi->irdp;
-
ret = inet_aton(argv[idx_ipv4]->arg, &ip);
if (!ret)
return CMD_WARNING_CONFIG_FAILED;
{
int idx_ipv4 = 4;
VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct irdp_interface *irdp = irdp_if_get(ifp);
struct listnode *node, *nnode;
struct in_addr ip;
int ret;
- struct zebra_if *zi;
- struct irdp_interface *irdp;
struct Adv *adv;
- zi = ifp->info;
- irdp = &zi->irdp;
-
ret = inet_aton(argv[idx_ipv4]->arg, &ip);
if (!ret)
return CMD_WARNING_CONFIG_FAILED;
"Enable debugging for IRDP messages\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
- struct zebra_if *zi;
- struct irdp_interface *irdp;
-
- zi = ifp->info;
- irdp = &zi->irdp;
+ struct irdp_interface *irdp = irdp_if_get(ifp);
irdp->flags |= IF_DEBUG_MESSAGES;
"Enable debugging for miscellaneous IRDP events\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
- struct zebra_if *zi;
- struct irdp_interface *irdp;
-
- zi = ifp->info;
- irdp = &zi->irdp;
+ struct irdp_interface *irdp = irdp_if_get(ifp);
irdp->flags |= IF_DEBUG_MISC;
"Enable debugging for IRDP packets\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
- struct zebra_if *zi;
- struct irdp_interface *irdp;
-
- zi = ifp->info;
- irdp = &zi->irdp;
+ struct irdp_interface *irdp = irdp_if_get(ifp);
irdp->flags |= IF_DEBUG_PACKET;
"Disable debugging for all IRDP events\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
- struct zebra_if *zi;
- struct irdp_interface *irdp;
-
- zi = ifp->info;
- irdp = &zi->irdp;
+ struct irdp_interface *irdp = irdp_if_get(ifp);
irdp->flags &= ~IF_DEBUG_PACKET;
irdp->flags &= ~IF_DEBUG_MESSAGES;
return CMD_SUCCESS;
}
-void irdp_init()
+void irdp_if_init()
{
+ hook_register(zebra_if_config_wr, irdp_config_write);
+ hook_register(if_del, irdp_if_delete);
+
install_element(INTERFACE_NODE, &ip_irdp_broadcast_cmd);
install_element(INTERFACE_NODE, &ip_irdp_multicast_cmd);
install_element(INTERFACE_NODE, &no_ip_irdp_cmd);
install_element(INTERFACE_NODE, &ip_irdp_debug_packet_cmd);
install_element(INTERFACE_NODE, &ip_irdp_debug_disable_cmd);
}
-
-#endif /* HAVE_IRDP */
#include <zebra.h>
-#ifdef HAVE_IRDP
-
#include "if.h"
#include "vty.h"
#include "sockunion.h"
#include "zclient.h"
#include "thread.h"
#include "privs.h"
+#include "libfrr.h"
+#include "version.h"
#include "zebra/interface.h"
#include "zebra/rtadv.h"
#include "zebra/rib.h"
struct stream *s)
{
struct zebra_if *zi = ifp->info;
- struct irdp_interface *irdp = &zi->irdp;
+ struct irdp_interface *irdp = zi->irdp;
int size;
int pref;
u_int16_t checksum;
static void irdp_send(struct interface *ifp, struct prefix *p, struct stream *s)
{
struct zebra_if *zi = ifp->info;
- struct irdp_interface *irdp = &zi->irdp;
+ struct irdp_interface *irdp = zi->irdp;
char buf[PREFIX_STRLEN];
u_int32_t dst;
u_int32_t ttl = 1;
+ if (!irdp)
+ return;
if (!(ifp->flags & IFF_UP))
return;
u_int32_t timer, tmp;
struct interface *ifp = THREAD_ARG(t_advert);
struct zebra_if *zi = ifp->info;
- struct irdp_interface *irdp = &zi->irdp;
+ struct irdp_interface *irdp = zi->irdp;
struct prefix *p;
struct listnode *node, *nnode;
struct connected *ifc;
+ if (!irdp)
+ return 0;
+
irdp->flags &= ~IF_SOLICIT;
if (ifp->connected)
void irdp_advert_off(struct interface *ifp)
{
struct zebra_if *zi = ifp->info;
- struct irdp_interface *irdp = &zi->irdp;
+ struct irdp_interface *irdp = zi->irdp;
struct listnode *node, *nnode;
int i;
struct connected *ifc;
struct prefix *p;
+ if (!irdp)
+ return;
+
if (irdp->t_advertise)
thread_cancel(irdp->t_advertise);
irdp->t_advertise = NULL;
void process_solicit(struct interface *ifp)
{
struct zebra_if *zi = ifp->info;
- struct irdp_interface *irdp = &zi->irdp;
+ struct irdp_interface *irdp = zi->irdp;
u_int32_t timer;
+ if (!irdp)
+ return;
+
/* When SOLICIT is active we reject further incoming solicits
this keeps down the answering rate so we don't have think
about DoS attacks here. */
&irdp->t_advertise);
}
-void irdp_finish()
+static int irdp_finish(void)
{
struct vrf *vrf;
struct listnode *node, *nnode;
if (!zi)
continue;
- irdp = &zi->irdp;
+ irdp = zi->irdp;
if (!irdp)
continue;
irdp_advert_off(ifp);
}
}
+ return 0;
+}
+
+static int irdp_init(struct thread_master *master)
+{
+ irdp_if_init();
+
+ hook_register(frr_early_fini, irdp_finish);
+ return 0;
+}
+
+static int irdp_module_init(void)
+{
+ hook_register(frr_late_init, irdp_init);
+ return 0;
}
-#endif /* HAVE_IRDP */
+FRR_MODULE_SETUP(
+ .name = "zebra_irdp",
+ .version = FRR_VERSION,
+ .description = "zebra IRDP module",
+ .init = irdp_module_init,
+)
#include <zebra.h>
-#ifdef HAVE_IRDP
-
#include "if.h"
#include "vty.h"
#include "sockunion.h"
if (!zi)
return;
- irdp = &zi->irdp;
+ irdp = zi->irdp;
if (!irdp)
return;
if (!zi)
return ret;
- irdp = &zi->irdp;
+ irdp = zi->irdp;
if (!irdp)
return ret;
}
/* printf("TX on %s idx %d\n", ifp->name, ifp->ifindex); */
}
-
-
-#endif /* HAVE_IRDP */
zlog_notice("Terminating on signal");
-#ifdef HAVE_IRDP
- irdp_finish();
-#endif
+ frr_early_fini();
zebra_ptm_finish();
list_delete_all_node(zebrad.client_list);
access_list_reset();
prefix_list_reset();
route_map_finish();
- cmd_terminate();
- vty_terminate();
- zprivs_terminate(&zserv_privs);
+
list_delete(zebrad.client_list);
work_queue_free(zebrad.ribq);
if (zebrad.lsp_process_q)
work_queue_free(zebrad.lsp_process_q);
meta_queue_free(zebrad.mq);
- thread_master_free(zebrad.master);
- closezlog();
+ frr_fini();
exit(0);
}
#if defined(HAVE_RTADV)
rtadv_cmd_init();
#endif
-#ifdef HAVE_IRDP
- irdp_init();
-#endif
/* PTM socket */
#ifdef ZEBRA_PTM_SUPPORT
zebra_ptm_init();
if (!table)
return;
- for (rn = route_top(table); rn; rn = route_next(rn))
+ for (rn = route_top(table); rn; rn = srcdest_route_next(rn))
RNODE_FOREACH_RE(rn, newre)
{
struct prefix *dst_p, *src_p;
#define ALLNODE "ff02::1"
#define ALLROUTER "ff02::2"
+/* Order is intentional. Matches RFC4191. This array is also used for
+ command matching, so only modify with care. */
+const char *rtadv_pref_strs[] = {"medium", "high", "INVALID", "low", 0};
+
enum rtadv_event {
RTADV_START,
RTADV_STOP,
return CMD_SUCCESS;
}
+/* Dump interface ND information to vty. */
+static int nd_dump_vty(struct vty *vty, struct interface *ifp)
+{
+ struct zebra_if *zif;
+ struct rtadvconf *rtadv;
+ int interval;
+
+ zif = (struct zebra_if *)ifp->info;
+ rtadv = &zif->rtadv;
+
+ if (rtadv->AdvSendAdvertisements) {
+ vty_out(vty,
+ " ND advertised reachable time is %d milliseconds\n",
+ rtadv->AdvReachableTime);
+ vty_out(vty,
+ " ND advertised retransmit interval is %d milliseconds\n",
+ rtadv->AdvRetransTimer);
+ vty_out(vty, " ND router advertisements sent: %d rcvd: %d\n",
+ zif->ra_sent, zif->ra_rcvd);
+ interval = rtadv->MaxRtrAdvInterval;
+ if (interval % 1000)
+ vty_out(vty,
+ " ND router advertisements are sent every "
+ "%d milliseconds\n",
+ interval);
+ else
+ vty_out(vty,
+ " ND router advertisements are sent every "
+ "%d seconds\n",
+ interval / 1000);
+ if (rtadv->AdvDefaultLifetime != -1)
+ vty_out(vty,
+ " ND router advertisements live for %d seconds\n",
+ rtadv->AdvDefaultLifetime);
+ else
+ vty_out(vty,
+ " ND router advertisements lifetime tracks ra-interval\n");
+ vty_out(vty,
+ " ND router advertisement default router preference is "
+ "%s\n",
+ rtadv_pref_strs[rtadv->DefaultPreference]);
+ if (rtadv->AdvManagedFlag)
+ vty_out(vty,
+ " Hosts use DHCP to obtain routable addresses.\n");
+ else
+ vty_out(vty,
+ " Hosts use stateless autoconfig for addresses.\n");
+ if (rtadv->AdvHomeAgentFlag) {
+ vty_out(vty,
+ " ND router advertisements with Home Agent flag bit set.\n");
+ if (rtadv->HomeAgentLifetime != -1)
+ vty_out(vty,
+ " Home Agent lifetime is %u seconds\n",
+ rtadv->HomeAgentLifetime);
+ else
+ vty_out(vty,
+ " Home Agent lifetime tracks ra-lifetime\n");
+ vty_out(vty, " Home Agent preference is %u\n",
+ rtadv->HomeAgentPreference);
+ }
+ if (rtadv->AdvIntervalOption)
+ vty_out(vty,
+ " ND router advertisements with Adv. Interval option.\n");
+ }
+ return 0;
+}
+
/* Write configuration about router advertisement. */
-void rtadv_config_write(struct vty *vty, struct interface *ifp)
+static int rtadv_config_write(struct vty *vty, struct interface *ifp)
{
struct zebra_if *zif;
struct listnode *node;
vty_out(vty, " router-address");
vty_out(vty, "\n");
}
+ return 0;
}
void rtadv_cmd_init(void)
{
+ hook_register(zebra_if_extra_info, nd_dump_vty);
+ hook_register(zebra_if_config_wr, rtadv_config_write);
+
install_element(INTERFACE_NODE, &ipv6_nd_suppress_ra_cmd);
install_element(INTERFACE_NODE, &no_ipv6_nd_suppress_ra_cmd);
install_element(INTERFACE_NODE, &ipv6_nd_ra_interval_cmd);
#endif
};
-extern void rtadv_config_write(struct vty *, struct interface *);
-
/* RFC4584 Extension to Sockets API for Mobile IPv6 */
#ifndef ND_OPT_ADV_INTERVAL
sbin_PROGRAMS += zebra/zebra
dist_examples_DATA += zebra/zebra.conf.sample
+if IRDP
+module_LTLIBRARIES += zebra/zebra_irdp.la
+endif
if SNMP
module_LTLIBRARIES += zebra/zebra_snmp.la
endif
zebra/ipforward_proc.c \
zebra/ipforward_solaris.c \
zebra/ipforward_sysctl.c \
- zebra/irdp_interface.c \
- zebra/irdp_main.c \
- zebra/irdp_packet.c \
zebra/kernel_netlink.c \
zebra/kernel_socket.c \
zebra/label_manager.c \
zebra/zserv.h \
# end
+zebra_zebra_irdp_la_SOURCES = \
+ zebra/irdp_interface.c \
+ zebra/irdp_main.c \
+ zebra/irdp_packet.c \
+ # end
+zebra_zebra_irdp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+
zebra_zebra_snmp_la_SOURCES = zebra/zebra_snmp.c
zebra_zebra_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS)
zebra_zebra_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
DEFUN (show_pseudowires,
show_pseudowires_cmd,
- "show pseudowires",
+ "show mpls pseudowires",
SHOW_STR
+ MPLS_STR
"Pseudowires")
{
struct zebra_vrf *zvrf;
* holder, if necessary, then push the work into it in any case.
* This semantics was introduced after 0.99.9 release.
*/
- if (!zebrad.ribq->items->count)
+ if (work_queue_empty(zebrad.ribq))
work_queue_add(zebrad.ribq, zebrad.mq);
rib_meta_queue_add(zebrad.mq, rn);
break;
}
for (ALL_NEXTHOPS(re->nexthop, nexthop))
- if (IPV4_ADDR_SAME(&nexthop->gate.ipv4, gate)
+ if (IPV4_ADDR_SAME(&nexthop->gate.ipv4, &gate->ipv4)
|| IPV6_ADDR_SAME(&nexthop->gate.ipv6,
gate)) {
same = re;
for (si = rn->info; si; si = si->next) {
if (type == si->type
&& (!gate || ((afi == AFI_IP
- && IPV4_ADDR_SAME(gate, &si->addr.ipv4))
+ && IPV4_ADDR_SAME(&gate->ipv4, &si->addr.ipv4))
|| (afi == AFI_IP6
&& IPV6_ADDR_SAME(gate, &si->addr.ipv6))))
&& (!strcmp (ifname ? ifname : "", si->ifname))) {
for (si = rn->info; si; si = si->next)
if (type == si->type
&& (!gate || ((afi == AFI_IP
- && IPV4_ADDR_SAME(gate, &si->addr.ipv4))
+ && IPV4_ADDR_SAME(&gate->ipv4, &si->addr.ipv4))
|| (afi == AFI_IP6
&& IPV6_ADDR_SAME(gate, &si->addr.ipv6))))
&& (!strcmp(ifname ? ifname : "", si->ifname))
inet_ntop(AF_INET,
&si->addr.ipv4, buf,
sizeof buf),
- ifindex2ifname(si->ifindex,
- si->vrf_id));
+ si->ifname);
break;
case STATIC_IPV6_GATEWAY_IFNAME:
vty_out(vty, " %s %s",
inet_ntop(AF_INET6,
&si->addr.ipv6, buf,
sizeof buf),
- ifindex2ifname(si->ifindex,
- si->vrf_id));
+ si->ifname);
break;
}