From 20c87e98d8399c322f3f8da9d34ca19fd4ca1865 Mon Sep 17 00:00:00 2001 From: Thibaut Collet Date: Thu, 30 Aug 2018 11:42:55 +0200 Subject: [PATCH 01/84] vrf: return vrf implementation for default vrf To correct potential crash with netns implementation of vrf (see next commit) it is necessary to allow any daemons to know the vrf implementation whatever the vrf. With current implementation the daemons do not know the vrf implementation for the default vrf. For this vrf the returned vrf implementation is always vrf-lite. To solve this issue a netns name is set to the default vrf to just test is presence to know the used implementation. For zebra a netns name (if needed) is set in the vrf_init function just before enabling the vrf. So this information is propagated to the other daemons thanks the zapi message called when the vrf is enable at zebra layer and override the default configuration (vrf-lite) of the daemon. Signed-off-by: Thibaut Collet --- lib/vrf.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/vrf.c b/lib/vrf.c index 1fb1b786c7..696ae3f48c 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -497,6 +497,16 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *), strlcpy(default_vrf->data.l.netns_name, VRF_DEFAULT_NAME, NS_NAMSIZ); + if (vrf_is_backend_netns()) { + struct ns *ns; + + strlcpy(default_vrf->data.l.netns_name, + VRF_DEFAULT_NAME, NS_NAMSIZ); + ns = ns_lookup(ns_get_default_id()); + ns->vrf_ctxt = (void *)default_vrf; + default_vrf->ns_ctxt = (void *)ns; + } + /* Enable the default VRF. */ if (!vrf_enable(default_vrf)) { flog_err(LIB_ERR_VRF_START, @@ -711,8 +721,6 @@ int vrf_is_mapped_on_netns(struct vrf *vrf) { if (!vrf || vrf->data.l.netns_name[0] == '\0') return 0; - if (vrf->vrf_id == VRF_DEFAULT) - return 0; return 1; } From ee2f2c23ca2778feafa398e773d24da07d6f174e Mon Sep 17 00:00:00 2001 From: Thibaut Collet Date: Tue, 29 May 2018 10:34:38 +0200 Subject: [PATCH 02/84] zebra: fix crash when interface vrf changes This crash occurs only with netns implementation. vrf meaning is different regarging its implementation (netns or vrf-lite) - With vrf-lite implementation vrf is a property of the interface that can be changed as the speed or the state (iproute2 command: "ip link set dev IF_NAME master VRF_NAME"). All interfaces of the system are in the same netns and so interface name is unique. - With netns implementation vrf is a characteristic of the interface that CANNOT be changed: it is the id of the netns where the interface is located. To change the vrf of an interface (iproute2 command to move an interface "ip netns exec VRF_NAME1 ip link set dev IF_NAME netns VRF_NAME2") the interface is deleted from the old vrf and created in the new vrf. Interface name is not unique, the same name can be present in the different netns (typically the lo interface) and search of interface must be done by the tuple (interface name, netns id). Current tests on the vrf implementation (vrf-lite or netns) are not sufficient. In some cases (for example when an interface is moved from a vrf X to the default vrf and then move back to VRF X) we can have a corruption message and then a crash of zebra. To avoid this corruption test on the vrf implementation, needed when an interface changes, has been rewritten: - For all interface changes except deletion the if_get_by_name function, that checks if an interface exists and creates or updates it if needed, is changed: * The vrf-lite implementation is unchanged: search of the interface is based only on the name and update the vrf-id if needed. * The netns implementation search of the interface is based on the (name, vrf-id) tuple and interface is created if not found, the vrf-id is never updated. - deletion of an interface (reception of a RTM_DELLINK netlink message): * The vrf-lite implementation is unchanged: the interface information are cleared and the interface is moved to the default vrf if it does not belong to (to allow vrf deletion) * The netns implementation is changed: only the interface information are cleared and the interface stays in its vrf to avoid conflict with interface with the same name in the default vrf. This implementation reverts (partially or totally): commit 393ec5424e35 ("zebra: fix missing node attribute set in ifp") commit e9e9b1150f0c ("lib: create interface even if name is the same") commit 9373219c67e1 ("zebra: improve logs when replacing interface to an other netns") Fixes: b53686c52a59 ("zebra: delete interface that disappeared") Signed-off-by: Thibaut Collet Signed-off-by: Philippe Guibert --- lib/if.c | 66 ++++++++++++++++++++++++++------------------- zebra/if_netlink.c | 67 ---------------------------------------------- zebra/interface.c | 33 +++++------------------ zebra/interface.h | 2 -- 4 files changed, 44 insertions(+), 124 deletions(-) diff --git a/lib/if.c b/lib/if.c index 2bf0c6e6b5..c630dd140a 100644 --- a/lib/if.c +++ b/lib/if.c @@ -371,37 +371,47 @@ struct interface *if_lookup_prefix(struct prefix *prefix, vrf_id_t vrf_id) one. */ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id, int vty) { - struct interface *ifp; + struct interface *ifp = NULL; - ifp = if_lookup_by_name(name, vrf_id); - if (ifp) - return ifp; - /* Not Found on same VRF. If the interface command - * was entered in vty without a VRF (passed as VRF_DEFAULT), - * accept the ifp we found. If a vrf was entered and there is - * a mismatch, reject it if from vty. - */ - ifp = if_lookup_by_name_all_vrf(name); - if (!ifp) - return if_create(name, vrf_id); - if (vty) { - if (vrf_id == VRF_DEFAULT) + if (vrf_is_mapped_on_netns(vrf_lookup_by_id(vrf_id))) { + ifp = if_lookup_by_name(name, vrf_id); + if (ifp) return ifp; - return NULL; - } - /* if vrf backend uses NETNS, then - * this should not be considered as an update - * then create the new interface - */ - if (ifp->vrf_id != vrf_id && vrf_is_mapped_on_netns( - vrf_lookup_by_id(vrf_id))) + if (vty) { + /* If the interface command was entered in vty without a + * VRF (passed as VRF_DEFAULT), search an interface with + * this name in all VRs + */ + if (vrf_id == VRF_DEFAULT) + return if_lookup_by_name_all_vrf(name); + return NULL; + } return if_create(name, vrf_id); - /* If it came from the kernel - * or by way of zclient, believe it and update - * the ifp accordingly. - */ - if_update_to_new_vrf(ifp, vrf_id); - return ifp; + } else { + ifp = if_lookup_by_name_all_vrf(name); + if (ifp) { + if (ifp->vrf_id == vrf_id) + return ifp; + /* Found a match on a different VRF. If the interface + * command was entered in vty without a VRF (passed as + * VRF_DEFAULT), accept the ifp we found. If a vrf was + * entered and there is a mismatch, reject it if from + * vty. If it came from the kernel or by way of zclient, + * believe it and update the ifp accordingly. + */ + if (vty) { + if (vrf_id == VRF_DEFAULT) + return ifp; + return NULL; + } + /* If it came from the kernel or by way of zclient, + * believe it and update the ifp accordingly. + */ + if_update_to_new_vrf(ifp, vrf_id); + return ifp; + } + return if_create(name, vrf_id); + } } void if_set_index(struct interface *ifp, ifindex_t ifindex) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index a15d914243..1fc3e61a3b 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -1042,67 +1042,6 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) return 0; } -/* helper function called by if_netlink_change - * to delete interfaces in case the interface moved - * to an other netns - */ -static void if_netlink_check_ifp_instance_consistency(uint16_t cmd, - struct interface *ifp, - ns_id_t ns_id) -{ - struct interface *other_ifp; - - /* - * look if interface name is also found on other netns - * - only if vrf backend is netns - * - do not concern lo interface - * - then remove previous one - * - for new link case, check found interface is not active - */ - if (!vrf_is_backend_netns() || - !strcmp(ifp->name, "lo")) - return; - other_ifp = if_lookup_by_name_not_ns(ns_id, ifp->name); - if (!other_ifp) - return; - /* because previous interface may be inactive, - * interface is moved back to default vrf - * then one may find the same pointer; ignore - */ - if (other_ifp == ifp) - return; - if ((cmd == RTM_NEWLINK) - && (CHECK_FLAG(other_ifp->status, ZEBRA_INTERFACE_ACTIVE))) - return; - if (IS_ZEBRA_DEBUG_KERNEL && cmd == RTM_NEWLINK) { - zlog_debug("RTM_NEWLINK %s(%u, VRF %u) replaces %s(%u, VRF %u)\n", - ifp->name, - ifp->ifindex, - ifp->vrf_id, - other_ifp->name, - other_ifp->ifindex, - other_ifp->vrf_id); - } else if (IS_ZEBRA_DEBUG_KERNEL && cmd == RTM_DELLINK) { - zlog_debug("RTM_DELLINK %s(%u, VRF %u) is replaced by %s(%u, VRF %u)\n", - ifp->name, - ifp->ifindex, - ifp->vrf_id, - other_ifp->name, - other_ifp->ifindex, - other_ifp->vrf_id); - } - /* the found interface replaces the current one - * remove it - */ - if (cmd == RTM_DELLINK) - if_delete(ifp); - else - if_delete(other_ifp); - /* the found interface is replaced by the current one - * suppress it - */ -} - int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; @@ -1276,8 +1215,6 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); - if_netlink_check_ifp_instance_consistency(RTM_NEWLINK, - ifp, ns_id); } else if (ifp->vrf_id != vrf_id) { /* VRF change for an interface. */ if (IS_ZEBRA_DEBUG_KERNEL) @@ -1351,8 +1288,6 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave) zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); - if_netlink_check_ifp_instance_consistency(RTM_NEWLINK, - ifp, ns_id); } } else { /* Delete interface notification from kernel */ @@ -1376,8 +1311,6 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (!IS_ZEBRA_IF_VRF(ifp)) if_delete_update(ifp); - if_netlink_check_ifp_instance_consistency(RTM_DELLINK, - ifp, ns_id); } return 0; diff --git a/zebra/interface.c b/zebra/interface.c index 763931d350..bb87728b5d 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -202,7 +202,6 @@ struct interface *if_link_per_ns(struct zebra_ns *ns, struct interface *ifp) if (rn->info) { ifp = (struct interface *)rn->info; route_unlock_node(rn); /* get */ - ifp->node = rn; return ifp; } @@ -253,30 +252,6 @@ struct interface *if_lookup_by_name_per_ns(struct zebra_ns *ns, return NULL; } -/* this function must be used only if the vrf backend - * is a netns backend - */ -struct interface *if_lookup_by_name_not_ns(ns_id_t ns_id, - const char *ifname) -{ - struct interface *ifp; - struct ns *ns; - - RB_FOREACH (ns, ns_head, &ns_tree) { - if (ns->ns_id == ns_id) - continue; - /* if_delete_update has removed interface - * from zns->if_table - * so to look for interface, use the vrf list - */ - ifp = if_lookup_by_name(ifname, (vrf_id_t)ns->ns_id); - if (!ifp) - continue; - return ifp; - } - return NULL; -} - const char *ifindex2ifname_per_ns(struct zebra_ns *zns, unsigned int ifindex) { struct interface *ifp; @@ -753,8 +728,12 @@ void if_delete_update(struct interface *ifp) ifp->node = NULL; /* if the ifp is in a vrf, move it to default so vrf can be deleted if - * desired */ - if (ifp->vrf_id) + * desired. This operation is not done for netns implementation to avoid + * collision with interface with the same name in the default vrf (can + * occur with this implementation whereas it is not possible with + * vrf-lite). + */ + if ((ifp->vrf_id) && !vrf_is_backend_netns()) if_handle_vrf_change(ifp, VRF_DEFAULT); /* Reset some zebra interface params to default values. */ diff --git a/zebra/interface.h b/zebra/interface.h index 9634bfdb3f..2add0a2de6 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -324,8 +324,6 @@ extern void zebra_if_init(void); extern struct interface *if_lookup_by_index_per_ns(struct zebra_ns *, uint32_t); extern struct interface *if_lookup_by_name_per_ns(struct zebra_ns *, const char *); -extern struct interface *if_lookup_by_name_not_ns(ns_id_t ns_id, - const char *ifname); extern struct interface *if_link_per_ns(struct zebra_ns *, struct interface *); extern const char *ifindex2ifname_per_ns(struct zebra_ns *, unsigned int); From 379eb245f649f04e2ceed0c577bed919dfebd5f6 Mon Sep 17 00:00:00 2001 From: Thibaut Collet Date: Thu, 30 Aug 2018 16:06:03 +0200 Subject: [PATCH 03/84] lib/if.c: fix CLANG warning Fix CLANG warning: Report for if.c | 2 issues =============================================== < WARNING: else is not generally useful after a break or return < #390: FILE: /tmp/f1-28557/if.c:390: Signed-off-by: Thibaut Collet --- lib/if.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/if.c b/lib/if.c index c630dd140a..25da7a8188 100644 --- a/lib/if.c +++ b/lib/if.c @@ -387,31 +387,31 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id, int vty) return NULL; } return if_create(name, vrf_id); - } else { - ifp = if_lookup_by_name_all_vrf(name); - if (ifp) { - if (ifp->vrf_id == vrf_id) - return ifp; - /* Found a match on a different VRF. If the interface - * command was entered in vty without a VRF (passed as - * VRF_DEFAULT), accept the ifp we found. If a vrf was - * entered and there is a mismatch, reject it if from - * vty. If it came from the kernel or by way of zclient, - * believe it and update the ifp accordingly. - */ - if (vty) { - if (vrf_id == VRF_DEFAULT) - return ifp; - return NULL; - } - /* If it came from the kernel or by way of zclient, - * believe it and update the ifp accordingly. - */ - if_update_to_new_vrf(ifp, vrf_id); - return ifp; - } - return if_create(name, vrf_id); } + /* vrf is based on vrf-lite */ + ifp = if_lookup_by_name_all_vrf(name); + if (ifp) { + if (ifp->vrf_id == vrf_id) + return ifp; + /* Found a match on a different VRF. If the interface command + * was entered in vty without a VRF (passed as VRF_DEFAULT), + * accept the ifp we found. If a vrf was entered and there is a + * mismatch, reject it if from vty. If it came from the kernel + * or by way of zclient, believe it and update the ifp + * accordingly. + */ + if (vty) { + if (vrf_id == VRF_DEFAULT) + return ifp; + return NULL; + } + /* If it came from the kernel or by way of zclient, believe it + * and update the ifp accordingly. + */ + if_update_to_new_vrf(ifp, vrf_id); + return ifp; + } + return if_create(name, vrf_id); } void if_set_index(struct interface *ifp, ifindex_t ifindex) From d0b6f68174b1d4a3fb89ca45096389d4b46f6870 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 30 Aug 2018 14:29:04 -0400 Subject: [PATCH 04/84] ospf6d: Remove deprecated code after 1 year Signed-off-by: Donald Sharp --- ospf6d/ospf6_top.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index fde47c74fe..ca1a65ff0b 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -425,19 +425,6 @@ DEFUN(no_ospf6_router_id, return CMD_SUCCESS; } -#if CONFDATE > 20180828 -CPP_NOTICE("ospf6: `router-id A.B.C.D` deprecated 2017/08/28") -#endif -ALIAS_HIDDEN(ospf6_router_id, ospf6_router_id_hdn_cmd, "router-id A.B.C.D", - "Configure OSPF6 Router-ID\n" V4NOTATION_STR) - -#if CONFDATE > 20180828 -CPP_NOTICE("ospf6: `no router-id A.B.C.D` deprecated 2017/08/28") -#endif -ALIAS_HIDDEN(no_ospf6_router_id, no_ospf6_router_id_hdn_cmd, - "no router-id [A.B.C.D]", - NO_STR "Configure OSPF6 Router-ID\n" V4NOTATION_STR) - DEFUN (ospf6_log_adjacency_changes, ospf6_log_adjacency_changes_cmd, "log-adjacency-changes", @@ -1144,8 +1131,6 @@ void ospf6_top_init(void) install_default(OSPF6_NODE); install_element(OSPF6_NODE, &ospf6_router_id_cmd); install_element(OSPF6_NODE, &no_ospf6_router_id_cmd); - install_element(OSPF6_NODE, &ospf6_router_id_hdn_cmd); - install_element(OSPF6_NODE, &no_ospf6_router_id_hdn_cmd); install_element(OSPF6_NODE, &ospf6_log_adjacency_changes_cmd); install_element(OSPF6_NODE, &ospf6_log_adjacency_changes_detail_cmd); install_element(OSPF6_NODE, &no_ospf6_log_adjacency_changes_cmd); From 7c0cbd0e8879ef85469cd2da606cdabdb374cf01 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 22 Mar 2018 15:01:08 +0100 Subject: [PATCH 05/84] fabricd: add new daemon as build of isisd fabricd is built using the sources of isisd. To allow differentiation in the code, -DFABRICD=1 is added to its preprocessor flags. Signed-off-by: Christian Franke --- configure.ac | 6 +- doc/manpages/common-options.rst | 1 + isisd/.gitignore | 1 + isisd/fabricd.conf.sample | 27 ++++ isisd/isis_circuit.c | 42 ++--- isisd/isis_main.c | 11 ++ isisd/isis_mt.c | 2 +- isisd/isis_redist.c | 20 +-- isisd/isis_spf.c | 4 +- isisd/isis_te.c | 18 +-- isisd/isis_vty.c | 270 ++++++++++++++++---------------- isisd/isis_zebra.c | 8 +- isisd/isisd.c | 237 ++++++++++++++-------------- isisd/isisd.h | 18 +++ isisd/subdir.am | 15 ++ lib/command.c | 3 + lib/command.h | 2 +- lib/log.c | 4 + lib/route_types.txt | 2 + lib/vty.c | 2 + 20 files changed, 392 insertions(+), 301 deletions(-) create mode 100644 isisd/fabricd.conf.sample diff --git a/configure.ac b/configure.ac index 09a6f364fb..8934add2a4 100755 --- a/configure.ac +++ b/configure.ac @@ -381,6 +381,8 @@ AC_ARG_ENABLE(sharpd, AS_HELP_STRING([--enable-sharpd], [build sharpd])) AC_ARG_ENABLE(staticd, AS_HELP_STRING([--disable-staticd], [do not build staticd])) +AC_ARG_ENABLE(fabricd, + AS_HELP_STRING([--disable-fabricd], [do not build fabricd])) AC_ARG_ENABLE(bgp-announce, AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement])) AC_ARG_ENABLE(bgp-vnc, @@ -1197,11 +1199,12 @@ case "$host_os" in if test $ac_cv_header_net_bpf_h = no; then if test $ac_cv_header_sys_dlpi_h = no; then AC_MSG_RESULT(none) - if test "${enable_isisd}" = yes; then + if test "${enable_isisd}" = yes -o "${enable_fabricd}" = yes; then AC_MSG_FAILURE([IS-IS support requested but no packet backend found]) fi AC_MSG_WARN([*** IS-IS support will not be built ***]) enable_isisd="no" + enable_fabricd="no" else AC_MSG_RESULT(DLPI) fi @@ -1429,6 +1432,7 @@ AM_CONDITIONAL(PIMD, test "${enable_pimd}" != "no") AM_CONDITIONAL(PBRD, test "${enable_pbrd}" != "no") AM_CONDITIONAL(SHARPD, test "${enable_sharpd}" = "yes") AM_CONDITIONAL(STATICD, test "${enable_staticd}" != "no") +AM_CONDITIONAL(FABRICD, test "${enable_fabricd}" != "no") if test "${enable_bgp_announce}" = "no";then AC_DEFINE(DISABLE_BGP_ANNOUNCE,1,Disable BGP installation to zebra) diff --git a/doc/manpages/common-options.rst b/doc/manpages/common-options.rst index 5fff6fca66..74d3eb7bbd 100644 --- a/doc/manpages/common-options.rst +++ b/doc/manpages/common-options.rst @@ -125,6 +125,7 @@ These following options control the daemon's VTY (interactive command line) inte pbrd 2615 staticd 2616 bfdd 2617 + fabricd 2618 Port 2607 is used for ospfd's Opaque LSA API. diff --git a/isisd/.gitignore b/isisd/.gitignore index a882bbf675..865cc38253 100644 --- a/isisd/.gitignore +++ b/isisd/.gitignore @@ -2,6 +2,7 @@ Makefile.in *.o isisd +fabricd .deps isisd.conf .nfs* diff --git a/isisd/fabricd.conf.sample b/isisd/fabricd.conf.sample new file mode 100644 index 0000000000..be9e33ba62 --- /dev/null +++ b/isisd/fabricd.conf.sample @@ -0,0 +1,27 @@ +! -*- openfabric -*- +! +! fabricd sample configuration file +! +hostname fabricd +password foo +enable password foo +log stdout +!log file /tmp/fabricd.log +! +! +router openfabric DEAD + net 47.0023.0000.0003.0300.0100.0102.0304.0506.00 +! lsp-lifetime 65535 + +! hostname isisd-router +! domain-password foobar + +interface eth0 + ip router openfabric DEAD +! openfabric hello-interval 5 +! openfabric lsp-interval 1000 + +! -- optional +! openfabric retransmit-interval 10 +! openfabric retransmit-throttle-interval +! diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index cd4b76139f..df55afac4d 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -957,32 +957,32 @@ int isis_interface_config_write(struct vty *vty) if (circuit == NULL) continue; if (circuit->ip_router) { - vty_out(vty, " ip router isis %s\n", + vty_out(vty, " ip router " PROTO_NAME " %s\n", area->area_tag); write++; } if (circuit->is_passive) { - vty_out(vty, " isis passive\n"); + vty_out(vty, " " PROTO_NAME " passive\n"); write++; } if (circuit->circ_type_config == CIRCUIT_T_P2P) { - vty_out(vty, " isis network point-to-point\n"); + vty_out(vty, " " PROTO_NAME " network point-to-point\n"); write++; } if (circuit->ipv6_router) { - vty_out(vty, " ipv6 router isis %s\n", + vty_out(vty, " ipv6 router " PROTO_NAME " %s\n", area->area_tag); write++; } /* ISIS - circuit type */ if (circuit->is_type == IS_LEVEL_1) { - vty_out(vty, " isis circuit-type level-1\n"); + vty_out(vty, " " PROTO_NAME " circuit-type level-1\n"); write++; } else { if (circuit->is_type == IS_LEVEL_2) { vty_out(vty, - " isis circuit-type level-2-only\n"); + " " PROTO_NAME " circuit-type level-2-only\n"); write++; } } @@ -992,7 +992,7 @@ int isis_interface_config_write(struct vty *vty) == circuit->csnp_interval[1]) { if (circuit->csnp_interval[0] != DEFAULT_CSNP_INTERVAL) { - vty_out(vty, " isis csnp-interval %d\n", + vty_out(vty, " " PROTO_NAME " csnp-interval %d\n", circuit->csnp_interval[0]); write++; } @@ -1001,7 +1001,7 @@ int isis_interface_config_write(struct vty *vty) if (circuit->csnp_interval[i] != DEFAULT_CSNP_INTERVAL) { vty_out(vty, - " isis csnp-interval %d level-%d\n", + " " PROTO_NAME " csnp-interval %d level-%d\n", circuit->csnp_interval [i], i + 1); @@ -1015,7 +1015,7 @@ int isis_interface_config_write(struct vty *vty) == circuit->psnp_interval[1]) { if (circuit->psnp_interval[0] != DEFAULT_PSNP_INTERVAL) { - vty_out(vty, " isis psnp-interval %d\n", + vty_out(vty, " " PROTO_NAME " psnp-interval %d\n", circuit->psnp_interval[0]); write++; } @@ -1024,7 +1024,7 @@ int isis_interface_config_write(struct vty *vty) if (circuit->psnp_interval[i] != DEFAULT_PSNP_INTERVAL) { vty_out(vty, - " isis psnp-interval %d level-%d\n", + " " PROTO_NAME " psnp-interval %d level-%d\n", circuit->psnp_interval [i], i + 1); @@ -1036,7 +1036,7 @@ int isis_interface_config_write(struct vty *vty) /* ISIS - Hello padding - Defaults to true so only * display if false */ if (circuit->pad_hellos == 0) { - vty_out(vty, " no isis hello padding\n"); + vty_out(vty, " no " PROTO_NAME " hello padding\n"); write++; } @@ -1051,7 +1051,7 @@ int isis_interface_config_write(struct vty *vty) if (circuit->hello_interval[0] != DEFAULT_HELLO_INTERVAL) { vty_out(vty, - " isis hello-interval %d\n", + " " PROTO_NAME " hello-interval %d\n", circuit->hello_interval[0]); write++; } @@ -1060,7 +1060,7 @@ int isis_interface_config_write(struct vty *vty) if (circuit->hello_interval[i] != DEFAULT_HELLO_INTERVAL) { vty_out(vty, - " isis hello-interval %d level-%d\n", + " " PROTO_NAME " hello-interval %d level-%d\n", circuit->hello_interval [i], i + 1); @@ -1075,7 +1075,7 @@ int isis_interface_config_write(struct vty *vty) if (circuit->hello_multiplier[0] != DEFAULT_HELLO_MULTIPLIER) { vty_out(vty, - " isis hello-multiplier %d\n", + " " PROTO_NAME " hello-multiplier %d\n", circuit->hello_multiplier[0]); write++; } @@ -1084,7 +1084,7 @@ int isis_interface_config_write(struct vty *vty) if (circuit->hello_multiplier[i] != DEFAULT_HELLO_MULTIPLIER) { vty_out(vty, - " isis hello-multiplier %d level-%d\n", + " " PROTO_NAME " hello-multiplier %d level-%d\n", circuit->hello_multiplier [i], i + 1); @@ -1096,7 +1096,7 @@ int isis_interface_config_write(struct vty *vty) /* ISIS - Priority */ if (circuit->priority[0] == circuit->priority[1]) { if (circuit->priority[0] != DEFAULT_PRIORITY) { - vty_out(vty, " isis priority %d\n", + vty_out(vty, " " PROTO_NAME " priority %d\n", circuit->priority[0]); write++; } @@ -1105,7 +1105,7 @@ int isis_interface_config_write(struct vty *vty) if (circuit->priority[i] != DEFAULT_PRIORITY) { vty_out(vty, - " isis priority %d level-%d\n", + " " PROTO_NAME " priority %d level-%d\n", circuit->priority[i], i + 1); write++; @@ -1117,7 +1117,7 @@ int isis_interface_config_write(struct vty *vty) if (circuit->te_metric[0] == circuit->te_metric[1]) { if (circuit->te_metric[0] != DEFAULT_CIRCUIT_METRIC) { - vty_out(vty, " isis metric %d\n", + vty_out(vty, " " PROTO_NAME " metric %d\n", circuit->te_metric[0]); write++; } @@ -1126,7 +1126,7 @@ int isis_interface_config_write(struct vty *vty) if (circuit->te_metric[i] != DEFAULT_CIRCUIT_METRIC) { vty_out(vty, - " isis metric %d level-%d\n", + " " PROTO_NAME " metric %d level-%d\n", circuit->te_metric[i], i + 1); write++; @@ -1134,12 +1134,12 @@ int isis_interface_config_write(struct vty *vty) } } if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) { - vty_out(vty, " isis password md5 %s\n", + vty_out(vty, " " PROTO_NAME " password md5 %s\n", circuit->passwd.passwd); write++; } else if (circuit->passwd.type == ISIS_PASSWD_TYPE_CLEARTXT) { - vty_out(vty, " isis password clear %s\n", + vty_out(vty, " " PROTO_NAME " password clear %s\n", circuit->passwd.passwd); write++; } diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 3b4168adb9..2d96364afa 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -59,6 +59,7 @@ #define ISISD_DEFAULT_CONFIG "isisd.conf" /* Default vty port */ #define ISISD_VTY_PORT 2608 +#define FABRICD_VTY_PORT 2618 /* isisd privileges */ zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND}; @@ -145,9 +146,15 @@ struct quagga_signal_t isisd_signals[] = { }, }; +#ifdef FABRICD +FRR_DAEMON_INFO(fabricd, OPEN_FABRIC, .vty_port = FABRICD_VTY_PORT, + + .proghelp = "Implementation of the OpenFabric routing protocol.", +#else FRR_DAEMON_INFO(isisd, ISIS, .vty_port = ISISD_VTY_PORT, .proghelp = "Implementation of the IS-IS routing protocol.", +#endif .copyright = "Copyright (c) 2001-2002 Sampo Saaristo," " Ofer Wald and Hannes Gredler", @@ -164,7 +171,11 @@ int main(int argc, char **argv, char **envp) { int opt; +#ifdef FABRICD + frr_preinit(&fabricd_di, argc, argv); +#else frr_preinit(&isisd_di, argc, argv); +#endif frr_opt_add("", longopts, ""); /* Command line argument treatment. */ diff --git a/isisd/isis_mt.c b/isisd/isis_mt.c index 2155bf584e..2dfccf9830 100644 --- a/isisd/isis_mt.c +++ b/isisd/isis_mt.c @@ -311,7 +311,7 @@ int circuit_write_mt_settings(struct isis_circuit *circuit, struct vty *vty) for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node, setting)) { const char *name = isis_mtid2str(setting->mtid); if (name && !setting->enabled) { - vty_out(vty, " no isis topology %s\n", name); + vty_out(vty, " no " PROTO_NAME " topology %s\n", name); written++; } } diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c index cd3ca44379..c2581fd493 100644 --- a/isisd/isis_redist.c +++ b/isisd/isis_redist.c @@ -377,7 +377,7 @@ static void isis_redist_update_zebra_subscriptions(struct isis *isis) * routes to Zebra and has nothing to do with * redistribution, * so skip it. */ - if (type == ZEBRA_ROUTE_ISIS) + if (type == PROTO_TYPE) continue; afi_t afi = afi_for_redist_protocol(protocol); @@ -515,11 +515,11 @@ void isis_redist_area_finish(struct isis_area *area) DEFUN (isis_redistribute, isis_redistribute_cmd, - "redistribute " FRR_REDIST_STR_ISISD " []", + "redistribute " PROTO_REDIST_STR " []", REDIST_STR "Redistribute IPv4 routes\n" "Redistribute IPv6 routes\n" - FRR_REDIST_HELP_STR_ISISD + PROTO_REDIST_HELP "Redistribute into level-1\n" "Redistribute into level-2\n" "Metric for redistributed routes\n" @@ -585,12 +585,12 @@ DEFUN (isis_redistribute, DEFUN (no_isis_redistribute, no_isis_redistribute_cmd, - "no redistribute " FRR_REDIST_STR_ISISD " ", + "no redistribute " PROTO_REDIST_STR " ", NO_STR REDIST_STR "Redistribute IPv4 routes\n" "Redistribute IPv6 routes\n" - FRR_REDIST_HELP_STR_ISISD + PROTO_REDIST_HELP "Redistribute into level-1\n" "Redistribute into level-2\n") { @@ -732,7 +732,7 @@ int isis_redist_config_write(struct vty *vty, struct isis_area *area, return 0; for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { - if (type == ZEBRA_ROUTE_ISIS) + if (type == PROTO_TYPE) continue; for (level = 1; level <= ISIS_LEVELS; level++) { @@ -772,8 +772,8 @@ int isis_redist_config_write(struct vty *vty, struct isis_area *area, void isis_redist_init(void) { - install_element(ISIS_NODE, &isis_redistribute_cmd); - install_element(ISIS_NODE, &no_isis_redistribute_cmd); - install_element(ISIS_NODE, &isis_default_originate_cmd); - install_element(ISIS_NODE, &no_isis_default_originate_cmd); + install_element(ROUTER_NODE, &isis_redistribute_cmd); + install_element(ROUTER_NODE, &no_isis_redistribute_cmd); + install_element(ROUTER_NODE, &isis_default_originate_cmd); + install_element(ROUTER_NODE, &no_isis_default_originate_cmd); } diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 341921146b..317e278e80 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -1617,9 +1617,9 @@ static void isis_print_spftree(struct vty *vty, int level, DEFUN (show_isis_topology, show_isis_topology_cmd, - "show isis topology []", + "show " PROTO_NAME " topology []", SHOW_STR - "IS-IS information\n" + PROTO_HELP "IS-IS paths to Intermediate Systems\n" "Paths to all level-1 routers in the area\n" "Paths to all level-2 routers in the domain\n") diff --git a/isisd/isis_te.c b/isisd/isis_te.c index 44ba64ce26..2430742498 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -1223,9 +1223,9 @@ DEFUN (no_isis_mpls_te_inter_as, DEFUN (show_isis_mpls_te_router, show_isis_mpls_te_router_cmd, - "show isis mpls-te router", + "show " PROTO_NAME " mpls-te router", SHOW_STR - ISIS_STR + PROTO_HELP MPLS_TE_STR "Router information\n") { @@ -1314,9 +1314,9 @@ static void show_mpls_te_sub(struct vty *vty, struct interface *ifp) DEFUN (show_isis_mpls_te_interface, show_isis_mpls_te_interface_cmd, - "show isis mpls-te interface [INTERFACE]", + "show " PROTO_NAME " mpls-te interface [INTERFACE]", SHOW_STR - ISIS_STR + PROTO_HELP MPLS_TE_STR "Interface information\n" "Interface name\n") @@ -1361,11 +1361,11 @@ void isis_mpls_te_init(void) install_element(VIEW_NODE, &show_isis_mpls_te_router_cmd); install_element(VIEW_NODE, &show_isis_mpls_te_interface_cmd); - install_element(ISIS_NODE, &isis_mpls_te_on_cmd); - install_element(ISIS_NODE, &no_isis_mpls_te_on_cmd); - install_element(ISIS_NODE, &isis_mpls_te_router_addr_cmd); - install_element(ISIS_NODE, &isis_mpls_te_inter_as_cmd); - install_element(ISIS_NODE, &no_isis_mpls_te_inter_as_cmd); + install_element(ROUTER_NODE, &isis_mpls_te_on_cmd); + install_element(ROUTER_NODE, &no_isis_mpls_te_on_cmd); + install_element(ROUTER_NODE, &isis_mpls_te_router_addr_cmd); + install_element(ROUTER_NODE, &isis_mpls_te_inter_as_cmd); + install_element(ROUTER_NODE, &no_isis_mpls_te_inter_as_cmd); return; } diff --git a/isisd/isis_vty.c b/isisd/isis_vty.c index ce2952c135..2deb813275 100644 --- a/isisd/isis_vty.c +++ b/isisd/isis_vty.c @@ -53,10 +53,10 @@ static struct isis_circuit *isis_circuit_lookup(struct vty *vty) DEFUN (ip_router_isis, ip_router_isis_cmd, - "ip router isis WORD", + "ip router " PROTO_NAME " WORD", "Interface Internet Protocol config commands\n" "IP router interface commands\n" - "IS-IS Routing for IP\n" + PROTO_HELP "Routing process tag\n") { int idx_afi = 0; @@ -104,10 +104,10 @@ DEFUN (ip_router_isis, DEFUN (ip6_router_isis, ip6_router_isis_cmd, - "ipv6 router isis WORD", + "ipv6 router " PROTO_NAME " WORD", "Interface Internet Protocol config commands\n" "IP router interface commands\n" - "IS-IS Routing for IP\n" + PROTO_HELP "Routing process tag\n") { return ip_router_isis(self, vty, argc, argv); @@ -115,12 +115,12 @@ DEFUN (ip6_router_isis, DEFUN (no_ip_router_isis, no_ip_router_isis_cmd, - "no router isis WORD", + "no router " PROTO_NAME " WORD", NO_STR "Interface Internet Protocol config commands\n" "IP router interface commands\n" "IP router interface commands\n" - "IS-IS Routing for IP\n" + PROTO_HELP "Routing process tag\n") { int idx_afi = 1; @@ -156,8 +156,8 @@ DEFUN (no_ip_router_isis, DEFUN (isis_passive, isis_passive_cmd, - "isis passive", - "IS-IS commands\n" + PROTO_NAME " passive", + PROTO_HELP "Configure the passive mode for interface\n") { struct isis_circuit *circuit = isis_circuit_lookup(vty); @@ -171,9 +171,9 @@ DEFUN (isis_passive, DEFUN (no_isis_passive, no_isis_passive_cmd, - "no isis passive", + "no " PROTO_NAME " passive", NO_STR - "IS-IS commands\n" + PROTO_HELP "Configure the passive mode for interface\n") { struct isis_circuit *circuit = isis_circuit_lookup(vty); @@ -187,8 +187,8 @@ DEFUN (no_isis_passive, DEFUN (isis_circuit_type, isis_circuit_type_cmd, - "isis circuit-type ", - "IS-IS commands\n" + PROTO_NAME " circuit-type ", + PROTO_HELP "Configure circuit type for interface\n" "Level-1 only adjacencies are formed\n" "Level-1-2 adjacencies are formed\n" @@ -220,9 +220,9 @@ DEFUN (isis_circuit_type, DEFUN (no_isis_circuit_type, no_isis_circuit_type_cmd, - "no isis circuit-type ", + "no " PROTO_NAME " circuit-type ", NO_STR - "IS-IS commands\n" + PROTO_HELP "Configure circuit type for interface\n" "Level-1 only adjacencies are formed\n" "Level-1-2 adjacencies are formed\n" @@ -247,8 +247,8 @@ DEFUN (no_isis_circuit_type, DEFUN (isis_network, isis_network_cmd, - "isis network point-to-point", - "IS-IS commands\n" + PROTO_NAME " network point-to-point", + PROTO_HELP "Set network type\n" "point-to-point network type\n") { @@ -258,7 +258,7 @@ DEFUN (isis_network, 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"); + PROTO_NAME " network point-to-point is valid only on broadcast interfaces\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -267,9 +267,9 @@ DEFUN (isis_network, DEFUN (no_isis_network, no_isis_network_cmd, - "no isis network point-to-point", + "no " PROTO_NAME " network point-to-point", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set network type for circuit\n" "point-to-point network type\n") { @@ -279,7 +279,7 @@ DEFUN (no_isis_network, 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"); + PROTO_NAME " network point-to-point is valid only on broadcast interfaces\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -288,8 +288,8 @@ DEFUN (no_isis_network, DEFUN (isis_passwd, isis_passwd_cmd, - "isis password WORD", - "IS-IS commands\n" + PROTO_NAME " password WORD", + PROTO_HELP "Configure the authentication password for a circuit\n" "HMAC-MD5 authentication\n" "Cleartext password\n" @@ -316,9 +316,9 @@ DEFUN (isis_passwd, DEFUN (no_isis_passwd, no_isis_passwd_cmd, - "no isis password [ WORD]", + "no " PROTO_NAME " password [ WORD]", NO_STR - "IS-IS commands\n" + PROTO_HELP "Configure the authentication password for a circuit\n" "HMAC-MD5 authentication\n" "Cleartext password\n" @@ -336,8 +336,8 @@ DEFUN (no_isis_passwd, DEFUN (isis_priority, isis_priority_cmd, - "isis priority (0-127)", - "IS-IS commands\n" + PROTO_NAME " priority (0-127)", + PROTO_HELP "Set priority for Designated Router election\n" "Priority value\n") { @@ -361,9 +361,9 @@ DEFUN (isis_priority, DEFUN (no_isis_priority, no_isis_priority_cmd, - "no isis priority [(0-127)]", + "no " PROTO_NAME " priority [(0-127)]", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set priority for Designated Router election\n" "Priority value\n") { @@ -380,8 +380,8 @@ DEFUN (no_isis_priority, DEFUN (isis_priority_l1, isis_priority_l1_cmd, - "isis priority (0-127) level-1", - "IS-IS commands\n" + PROTO_NAME " priority (0-127) level-1", + PROTO_HELP "Set priority for Designated Router election\n" "Priority value\n" "Specify priority for level-1 routing\n") @@ -405,9 +405,9 @@ DEFUN (isis_priority_l1, DEFUN (no_isis_priority_l1, no_isis_priority_l1_cmd, - "no isis priority [(0-127)] level-1", + "no " PROTO_NAME " priority [(0-127)] level-1", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set priority for Designated Router election\n" "Priority value\n" "Specify priority for level-1 routing\n") @@ -424,8 +424,8 @@ DEFUN (no_isis_priority_l1, DEFUN (isis_priority_l2, isis_priority_l2_cmd, - "isis priority (0-127) level-2", - "IS-IS commands\n" + PROTO_NAME " priority (0-127) level-2", + PROTO_HELP "Set priority for Designated Router election\n" "Priority value\n" "Specify priority for level-2 routing\n") @@ -449,9 +449,9 @@ DEFUN (isis_priority_l2, DEFUN (no_isis_priority_l2, no_isis_priority_l2_cmd, - "no isis priority [(0-127)] level-2", + "no " PROTO_NAME " priority [(0-127)] level-2", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set priority for Designated Router election\n" "Priority value\n" "Specify priority for level-2 routing\n") @@ -469,8 +469,8 @@ DEFUN (no_isis_priority_l2, /* Metric command */ DEFUN (isis_metric, isis_metric_cmd, - "isis metric (0-16777215)", - "IS-IS commands\n" + PROTO_NAME " metric (0-16777215)", + PROTO_HELP "Set default metric for circuit\n" "Default metric value\n") { @@ -512,9 +512,9 @@ DEFUN (isis_metric, DEFUN (no_isis_metric, no_isis_metric_cmd, - "no isis metric [(0-16777215)]", + "no " PROTO_NAME " metric [(0-16777215)]", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set default metric for circuit\n" "Default metric value\n") { @@ -534,8 +534,8 @@ DEFUN (no_isis_metric, DEFUN (isis_metric_l1, isis_metric_l1_cmd, - "isis metric (0-16777215) level-1", - "IS-IS commands\n" + PROTO_NAME " metric (0-16777215) level-1", + PROTO_HELP "Set default metric for circuit\n" "Default metric value\n" "Specify metric for level-1 routing\n") @@ -555,9 +555,9 @@ DEFUN (isis_metric_l1, DEFUN (no_isis_metric_l1, no_isis_metric_l1_cmd, - "no isis metric [(0-16777215)] level-1", + "no " PROTO_NAME " metric [(0-16777215)] level-1", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set default metric for circuit\n" "Default metric value\n" "Specify metric for level-1 routing\n") @@ -575,8 +575,8 @@ DEFUN (no_isis_metric_l1, DEFUN (isis_metric_l2, isis_metric_l2_cmd, - "isis metric (0-16777215) level-2", - "IS-IS commands\n" + PROTO_NAME " metric (0-16777215) level-2", + PROTO_HELP "Set default metric for circuit\n" "Default metric value\n" "Specify metric for level-2 routing\n") @@ -596,9 +596,9 @@ DEFUN (isis_metric_l2, DEFUN (no_isis_metric_l2, no_isis_metric_l2_cmd, - "no isis metric [(0-16777215)] level-2", + "no " PROTO_NAME " metric [(0-16777215)] level-2", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set default metric for circuit\n" "Default metric value\n" "Specify metric for level-2 routing\n") @@ -617,8 +617,8 @@ DEFUN (no_isis_metric_l2, DEFUN (isis_hello_interval, isis_hello_interval_cmd, - "isis hello-interval (1-600)", - "IS-IS commands\n" + PROTO_NAME " hello-interval (1-600)", + PROTO_HELP "Set Hello interval\n" "Holdtime 1 seconds, interval depends on multiplier\n") { @@ -644,9 +644,9 @@ DEFUN (isis_hello_interval, DEFUN (no_isis_hello_interval, no_isis_hello_interval_cmd, - "no isis hello-interval [(1-600)]", + "no " PROTO_NAME " hello-interval [(1-600)]", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set Hello interval\n" "Holdtime 1 second, interval depends on multiplier\n") { @@ -663,8 +663,8 @@ DEFUN (no_isis_hello_interval, DEFUN (isis_hello_interval_l1, isis_hello_interval_l1_cmd, - "isis hello-interval (1-600) level-1", - "IS-IS commands\n" + PROTO_NAME " hello-interval (1-600) level-1", + PROTO_HELP "Set Hello interval\n" "Holdtime 1 second, interval depends on multiplier\n" "Specify hello-interval for level-1 IIHs\n") @@ -690,9 +690,9 @@ DEFUN (isis_hello_interval_l1, DEFUN (no_isis_hello_interval_l1, no_isis_hello_interval_l1_cmd, - "no isis hello-interval [(1-600)] level-1", + "no " PROTO_NAME " hello-interval [(1-600)] level-1", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set Hello interval\n" "Holdtime 1 second, interval depends on multiplier\n" "Specify hello-interval for level-1 IIHs\n") @@ -709,8 +709,8 @@ DEFUN (no_isis_hello_interval_l1, DEFUN (isis_hello_interval_l2, isis_hello_interval_l2_cmd, - "isis hello-interval (1-600) level-2", - "IS-IS commands\n" + PROTO_NAME " hello-interval (1-600) level-2", + PROTO_HELP "Set Hello interval\n" "Holdtime 1 second, interval depends on multiplier\n" "Specify hello-interval for level-2 IIHs\n") @@ -736,9 +736,9 @@ DEFUN (isis_hello_interval_l2, DEFUN (no_isis_hello_interval_l2, no_isis_hello_interval_l2_cmd, - "no isis hello-interval [(1-600)] level-2", + "no " PROTO_NAME " hello-interval [(1-600)] level-2", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set Hello interval\n" "Holdtime 1 second, interval depends on multiplier\n" "Specify hello-interval for level-2 IIHs\n") @@ -755,8 +755,8 @@ DEFUN (no_isis_hello_interval_l2, DEFUN (isis_hello_multiplier, isis_hello_multiplier_cmd, - "isis hello-multiplier (2-100)", - "IS-IS commands\n" + PROTO_NAME " hello-multiplier (2-100)", + PROTO_HELP "Set multiplier for Hello holding time\n" "Hello multiplier value\n") { @@ -783,9 +783,9 @@ DEFUN (isis_hello_multiplier, DEFUN (no_isis_hello_multiplier, no_isis_hello_multiplier_cmd, - "no isis hello-multiplier [(2-100)]", + "no " PROTO_NAME " hello-multiplier [(2-100)]", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set multiplier for Hello holding time\n" "Hello multiplier value\n") { @@ -802,8 +802,8 @@ DEFUN (no_isis_hello_multiplier, DEFUN (isis_hello_multiplier_l1, isis_hello_multiplier_l1_cmd, - "isis hello-multiplier (2-100) level-1", - "IS-IS commands\n" + PROTO_NAME " hello-multiplier (2-100) level-1", + PROTO_HELP "Set multiplier for Hello holding time\n" "Hello multiplier value\n" "Specify hello multiplier for level-1 IIHs\n") @@ -830,9 +830,9 @@ DEFUN (isis_hello_multiplier_l1, DEFUN (no_isis_hello_multiplier_l1, no_isis_hello_multiplier_l1_cmd, - "no isis hello-multiplier [(2-100)] level-1", + "no " PROTO_NAME " hello-multiplier [(2-100)] level-1", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set multiplier for Hello holding time\n" "Hello multiplier value\n" "Specify hello multiplier for level-1 IIHs\n") @@ -849,8 +849,8 @@ DEFUN (no_isis_hello_multiplier_l1, DEFUN (isis_hello_multiplier_l2, isis_hello_multiplier_l2_cmd, - "isis hello-multiplier (2-100) level-2", - "IS-IS commands\n" + PROTO_NAME " hello-multiplier (2-100) level-2", + PROTO_HELP "Set multiplier for Hello holding time\n" "Hello multiplier value\n" "Specify hello multiplier for level-2 IIHs\n") @@ -877,9 +877,9 @@ DEFUN (isis_hello_multiplier_l2, DEFUN (no_isis_hello_multiplier_l2, no_isis_hello_multiplier_l2_cmd, - "no isis hello-multiplier [(2-100)] level-2", + "no " PROTO_NAME " hello-multiplier [(2-100)] level-2", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set multiplier for Hello holding time\n" "Hello multiplier value\n" "Specify hello multiplier for level-2 IIHs\n") @@ -896,8 +896,8 @@ DEFUN (no_isis_hello_multiplier_l2, DEFUN (isis_hello_padding, isis_hello_padding_cmd, - "isis hello padding", - "IS-IS commands\n" + PROTO_NAME " hello padding", + PROTO_HELP "Add padding to IS-IS hello packets\n" "Pad hello packets\n") { @@ -912,9 +912,9 @@ DEFUN (isis_hello_padding, DEFUN (no_isis_hello_padding, no_isis_hello_padding_cmd, - "no isis hello padding", + "no " PROTO_NAME " hello padding", NO_STR - "IS-IS commands\n" + PROTO_HELP "Add padding to IS-IS hello packets\n" "Pad hello packets\n") { @@ -944,8 +944,8 @@ DEFUN (isis_threeway_adj, DEFUN (csnp_interval, csnp_interval_cmd, - "isis csnp-interval (1-600)", - "IS-IS commands\n" + PROTO_NAME " csnp-interval (1-600)", + PROTO_HELP "Set CSNP interval in seconds\n" "CSNP interval value\n") { @@ -971,9 +971,9 @@ DEFUN (csnp_interval, DEFUN (no_csnp_interval, no_csnp_interval_cmd, - "no isis csnp-interval [(1-600)]", + "no " PROTO_NAME " csnp-interval [(1-600)]", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set CSNP interval in seconds\n" "CSNP interval value\n") { @@ -990,8 +990,8 @@ DEFUN (no_csnp_interval, DEFUN (csnp_interval_l1, csnp_interval_l1_cmd, - "isis csnp-interval (1-600) level-1", - "IS-IS commands\n" + PROTO_NAME " csnp-interval (1-600) level-1", + PROTO_HELP "Set CSNP interval in seconds\n" "CSNP interval value\n" "Specify interval for level-1 CSNPs\n") @@ -1017,9 +1017,9 @@ DEFUN (csnp_interval_l1, DEFUN (no_csnp_interval_l1, no_csnp_interval_l1_cmd, - "no isis csnp-interval [(1-600)] level-1", + "no " PROTO_NAME " csnp-interval [(1-600)] level-1", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set CSNP interval in seconds\n" "CSNP interval value\n" "Specify interval for level-1 CSNPs\n") @@ -1036,8 +1036,8 @@ DEFUN (no_csnp_interval_l1, DEFUN (csnp_interval_l2, csnp_interval_l2_cmd, - "isis csnp-interval (1-600) level-2", - "IS-IS commands\n" + PROTO_NAME " csnp-interval (1-600) level-2", + PROTO_HELP "Set CSNP interval in seconds\n" "CSNP interval value\n" "Specify interval for level-2 CSNPs\n") @@ -1063,9 +1063,9 @@ DEFUN (csnp_interval_l2, DEFUN (no_csnp_interval_l2, no_csnp_interval_l2_cmd, - "no isis csnp-interval [(1-600)] level-2", + "no " PROTO_NAME " csnp-interval [(1-600)] level-2", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set CSNP interval in seconds\n" "CSNP interval value\n" "Specify interval for level-2 CSNPs\n") @@ -1082,8 +1082,8 @@ DEFUN (no_csnp_interval_l2, DEFUN (psnp_interval, psnp_interval_cmd, - "isis psnp-interval (1-120)", - "IS-IS commands\n" + PROTO_NAME " psnp-interval (1-120)", + PROTO_HELP "Set PSNP interval in seconds\n" "PSNP interval value\n") { @@ -1109,9 +1109,9 @@ DEFUN (psnp_interval, DEFUN (no_psnp_interval, no_psnp_interval_cmd, - "no isis psnp-interval [(1-120)]", + "no " PROTO_NAME " psnp-interval [(1-120)]", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set PSNP interval in seconds\n" "PSNP interval value\n") { @@ -1128,8 +1128,8 @@ DEFUN (no_psnp_interval, DEFUN (psnp_interval_l1, psnp_interval_l1_cmd, - "isis psnp-interval (1-120) level-1", - "IS-IS commands\n" + PROTO_NAME " psnp-interval (1-120) level-1", + PROTO_HELP "Set PSNP interval in seconds\n" "PSNP interval value\n" "Specify interval for level-1 PSNPs\n") @@ -1155,9 +1155,9 @@ DEFUN (psnp_interval_l1, DEFUN (no_psnp_interval_l1, no_psnp_interval_l1_cmd, - "no isis psnp-interval [(1-120)] level-1", + "no " PROTO_NAME " psnp-interval [(1-120)] level-1", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set PSNP interval in seconds\n" "PSNP interval value\n" "Specify interval for level-1 PSNPs\n") @@ -1174,8 +1174,8 @@ DEFUN (no_psnp_interval_l1, DEFUN (psnp_interval_l2, psnp_interval_l2_cmd, - "isis psnp-interval (1-120) level-2", - "IS-IS commands\n" + PROTO_NAME " psnp-interval (1-120) level-2", + PROTO_HELP "Set PSNP interval in seconds\n" "PSNP interval value\n" "Specify interval for level-2 PSNPs\n") @@ -1201,9 +1201,9 @@ DEFUN (psnp_interval_l2, DEFUN (no_psnp_interval_l2, no_psnp_interval_l2_cmd, - "no isis psnp-interval [(1-120)] level-2", + "no " PROTO_NAME " psnp-interval [(1-120)] level-2", NO_STR - "IS-IS commands\n" + PROTO_HELP "Set PSNP interval in seconds\n" "PSNP interval value\n" "Specify interval for level-2 PSNPs\n") @@ -1219,8 +1219,8 @@ DEFUN (no_psnp_interval_l2, DEFUN (circuit_topology, circuit_topology_cmd, - "isis topology " ISIS_MT_NAMES, - "IS-IS commands\n" + PROTO_NAME " topology " ISIS_MT_NAMES, + PROTO_HELP "Configure interface IS-IS topologies\n" ISIS_MT_DESCRIPTIONS) { @@ -1246,9 +1246,9 @@ DEFUN (circuit_topology, DEFUN (no_circuit_topology, no_circuit_topology_cmd, - "no isis topology " ISIS_MT_NAMES, + "no " PROTO_NAME " topology " ISIS_MT_NAMES, NO_STR - "IS-IS commands\n" + PROTO_HELP "Configure interface IS-IS topologies\n" ISIS_MT_DESCRIPTIONS) { @@ -2120,46 +2120,46 @@ void isis_vty_init(void) install_element(INTERFACE_NODE, &circuit_topology_cmd); install_element(INTERFACE_NODE, &no_circuit_topology_cmd); - install_element(ISIS_NODE, &metric_style_cmd); - install_element(ISIS_NODE, &no_metric_style_cmd); + install_element(ROUTER_NODE, &metric_style_cmd); + install_element(ROUTER_NODE, &no_metric_style_cmd); - install_element(ISIS_NODE, &set_overload_bit_cmd); - install_element(ISIS_NODE, &no_set_overload_bit_cmd); + install_element(ROUTER_NODE, &set_overload_bit_cmd); + install_element(ROUTER_NODE, &no_set_overload_bit_cmd); - install_element(ISIS_NODE, &set_attached_bit_cmd); - install_element(ISIS_NODE, &no_set_attached_bit_cmd); + install_element(ROUTER_NODE, &set_attached_bit_cmd); + install_element(ROUTER_NODE, &no_set_attached_bit_cmd); - install_element(ISIS_NODE, &dynamic_hostname_cmd); - install_element(ISIS_NODE, &no_dynamic_hostname_cmd); + install_element(ROUTER_NODE, &dynamic_hostname_cmd); + install_element(ROUTER_NODE, &no_dynamic_hostname_cmd); - install_element(ISIS_NODE, &area_lsp_mtu_cmd); - install_element(ISIS_NODE, &no_area_lsp_mtu_cmd); + install_element(ROUTER_NODE, &area_lsp_mtu_cmd); + install_element(ROUTER_NODE, &no_area_lsp_mtu_cmd); - install_element(ISIS_NODE, &is_type_cmd); - install_element(ISIS_NODE, &no_is_type_cmd); + install_element(ROUTER_NODE, &is_type_cmd); + install_element(ROUTER_NODE, &no_is_type_cmd); - install_element(ISIS_NODE, &lsp_gen_interval_cmd); - install_element(ISIS_NODE, &no_lsp_gen_interval_cmd); + install_element(ROUTER_NODE, &lsp_gen_interval_cmd); + install_element(ROUTER_NODE, &no_lsp_gen_interval_cmd); - install_element(ISIS_NODE, &spf_interval_cmd); - install_element(ISIS_NODE, &no_spf_interval_cmd); - install_element(ISIS_NODE, &spf_interval_l1_cmd); - install_element(ISIS_NODE, &no_spf_interval_l1_cmd); - install_element(ISIS_NODE, &spf_interval_l2_cmd); - install_element(ISIS_NODE, &no_spf_interval_l2_cmd); + install_element(ROUTER_NODE, &spf_interval_cmd); + install_element(ROUTER_NODE, &no_spf_interval_cmd); + install_element(ROUTER_NODE, &spf_interval_l1_cmd); + install_element(ROUTER_NODE, &no_spf_interval_l1_cmd); + install_element(ROUTER_NODE, &spf_interval_l2_cmd); + install_element(ROUTER_NODE, &no_spf_interval_l2_cmd); - install_element(ISIS_NODE, &max_lsp_lifetime_cmd); - install_element(ISIS_NODE, &no_max_lsp_lifetime_cmd); + install_element(ROUTER_NODE, &max_lsp_lifetime_cmd); + install_element(ROUTER_NODE, &no_max_lsp_lifetime_cmd); - install_element(ISIS_NODE, &lsp_refresh_interval_cmd); - install_element(ISIS_NODE, &no_lsp_refresh_interval_cmd); + install_element(ROUTER_NODE, &lsp_refresh_interval_cmd); + install_element(ROUTER_NODE, &no_lsp_refresh_interval_cmd); - install_element(ISIS_NODE, &area_passwd_md5_cmd); - install_element(ISIS_NODE, &area_passwd_clear_cmd); - install_element(ISIS_NODE, &domain_passwd_md5_cmd); - install_element(ISIS_NODE, &domain_passwd_clear_cmd); - install_element(ISIS_NODE, &no_area_passwd_cmd); + install_element(ROUTER_NODE, &area_passwd_md5_cmd); + install_element(ROUTER_NODE, &area_passwd_clear_cmd); + install_element(ROUTER_NODE, &domain_passwd_md5_cmd); + install_element(ROUTER_NODE, &domain_passwd_clear_cmd); + install_element(ROUTER_NODE, &no_area_passwd_cmd); - install_element(ISIS_NODE, &spf_delay_ietf_cmd); - install_element(ISIS_NODE, &no_spf_delay_ietf_cmd); + install_element(ROUTER_NODE, &spf_delay_ietf_cmd); + install_element(ROUTER_NODE, &no_spf_delay_ietf_cmd); } diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 9bc0f2ef35..220f131b63 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -262,7 +262,7 @@ static void isis_zebra_route_add_route(struct prefix *prefix, memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; - api.type = ZEBRA_ROUTE_ISIS; + api.type = PROTO_TYPE; api.safi = SAFI_UNICAST; api.prefix = *prefix; if (src_p && src_p->prefixlen) { @@ -337,7 +337,7 @@ static void isis_zebra_route_del_route(struct prefix *prefix, memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; - api.type = ZEBRA_ROUTE_ISIS; + api.type = PROTO_TYPE; api.safi = SAFI_UNICAST; api.prefix = *prefix; if (src_p && src_p->prefixlen) { @@ -378,7 +378,7 @@ static int isis_zebra_read(int command, struct zclient *zclient, */ if (api.prefix.prefixlen == 0 && api.src_prefix.prefixlen == 0 - && api.type == ZEBRA_ROUTE_ISIS) { + && api.type == PROTO_TYPE) { command = ZEBRA_REDISTRIBUTE_ROUTE_DEL; } @@ -424,7 +424,7 @@ static void isis_zebra_connected(struct zclient *zclient) void isis_zebra_init(struct thread_master *master) { zclient = zclient_new_notify(master, &zclient_options_default); - zclient_init(zclient, ZEBRA_ROUTE_ISIS, 0, &isisd_privs); + zclient_init(zclient, PROTO_TYPE, 0, &isisd_privs); zclient->zebra_connected = isis_zebra_connected; zclient->router_id_update = isis_router_id_update_zebra; zclient->interface_add = isis_zebra_if_add; diff --git a/isisd/isisd.c b/isisd/isisd.c index a19f287453..a3f15e9de2 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -179,7 +179,7 @@ int isis_area_get(struct vty *vty, const char *area_tag) area = isis_area_lookup(area_tag); if (area) { - VTY_PUSH_CONTEXT(ISIS_NODE, area); + VTY_PUSH_CONTEXT(ROUTER_NODE, area); return CMD_SUCCESS; } @@ -188,7 +188,7 @@ int isis_area_get(struct vty *vty, const char *area_tag) if (isis->debugs & DEBUG_EVENTS) zlog_debug("New IS-IS area instance %s", area->area_tag); - VTY_PUSH_CONTEXT(ISIS_NODE, area); + VTY_PUSH_CONTEXT(ROUTER_NODE, area); return CMD_SUCCESS; } @@ -463,9 +463,9 @@ int show_isis_interface_common(struct vty *vty, const char *ifname, char detail) DEFUN (show_isis_interface, show_isis_interface_cmd, - "show isis interface", + "show " PROTO_NAME " interface", SHOW_STR - "ISIS network information\n" + PROTO_HELP "ISIS interface\n") { return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_BRIEF); @@ -473,9 +473,9 @@ DEFUN (show_isis_interface, DEFUN (show_isis_interface_detail, show_isis_interface_detail_cmd, - "show isis interface detail", + "show " PROTO_NAME " interface detail", SHOW_STR - "ISIS network information\n" + PROTO_HELP "ISIS interface\n" "show detailed information\n") { @@ -484,9 +484,9 @@ DEFUN (show_isis_interface_detail, DEFUN (show_isis_interface_arg, show_isis_interface_arg_cmd, - "show isis interface WORD", + "show " PROTO_NAME " interface WORD", SHOW_STR - "ISIS network information\n" + PROTO_HELP "ISIS interface\n" "ISIS interface name\n") { @@ -634,9 +634,9 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id) DEFUN (show_isis_neighbor, show_isis_neighbor_cmd, - "show isis neighbor", + "show " PROTO_NAME " neighbor", SHOW_STR - "ISIS network information\n" + PROTO_HELP "ISIS neighbor adjacencies\n") { return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_BRIEF); @@ -644,9 +644,9 @@ DEFUN (show_isis_neighbor, DEFUN (show_isis_neighbor_detail, show_isis_neighbor_detail_cmd, - "show isis neighbor detail", + "show " PROTO_NAME " neighbor detail", SHOW_STR - "ISIS network information\n" + PROTO_HELP "ISIS neighbor adjacencies\n" "show detailed information\n") { @@ -655,9 +655,9 @@ DEFUN (show_isis_neighbor_detail, DEFUN (show_isis_neighbor_arg, show_isis_neighbor_arg_cmd, - "show isis neighbor WORD", + "show " PROTO_NAME " neighbor WORD", SHOW_STR - "ISIS network information\n" + PROTO_HELP "ISIS neighbor adjacencies\n" "System id\n") { @@ -668,19 +668,19 @@ DEFUN (show_isis_neighbor_arg, DEFUN (clear_isis_neighbor, clear_isis_neighbor_cmd, - "clear isis neighbor", + "clear " PROTO_NAME " neighbor", CLEAR_STR - "Reset ISIS network information\n" - "Reset ISIS neighbor adjacencies\n") + PROTO_HELP + "ISIS neighbor adjacencies\n") { return clear_isis_neighbor_common(vty, NULL); } DEFUN (clear_isis_neighbor_arg, clear_isis_neighbor_arg_cmd, - "clear isis neighbor WORD", + "clear " PROTO_NAME " neighbor WORD", CLEAR_STR - "ISIS network information\n" + PROTO_HELP "ISIS neighbor adjacencies\n" "System id\n") { @@ -738,12 +738,12 @@ void print_debug(struct vty *vty, int flags, int onoff) DEFUN_NOSH (show_debugging, show_debugging_isis_cmd, - "show debugging [isis]", + "show debugging [" PROTO_NAME "]", SHOW_STR "State of each debugging option\n" - ISIS_STR) + PROTO_HELP) { - vty_out(vty, "IS-IS debugging status:\n"); + vty_out(vty, PROTO_NAME " debugging status:\n"); if (isis->debugs) print_debug(vty, isis->debugs, 1); @@ -760,59 +760,59 @@ static int config_write_debug(struct vty *vty) int flags = isis->debugs; if (flags & DEBUG_ADJ_PACKETS) { - vty_out(vty, "debug isis adj-packets\n"); + vty_out(vty, "debug " PROTO_NAME " adj-packets\n"); write++; } if (flags & DEBUG_CHECKSUM_ERRORS) { - vty_out(vty, "debug isis checksum-errors\n"); + vty_out(vty, "debug " PROTO_NAME " checksum-errors\n"); write++; } if (flags & DEBUG_LOCAL_UPDATES) { - vty_out(vty, "debug isis local-updates\n"); + vty_out(vty, "debug " PROTO_NAME " local-updates\n"); write++; } if (flags & DEBUG_PROTOCOL_ERRORS) { - vty_out(vty, "debug isis protocol-errors\n"); + vty_out(vty, "debug " PROTO_NAME " protocol-errors\n"); write++; } if (flags & DEBUG_SNP_PACKETS) { - vty_out(vty, "debug isis snp-packets\n"); + vty_out(vty, "debug " PROTO_NAME " snp-packets\n"); write++; } if (flags & DEBUG_SPF_EVENTS) { - vty_out(vty, "debug isis spf-events\n"); + vty_out(vty, "debug " PROTO_NAME " spf-events\n"); write++; } if (flags & DEBUG_SPF_STATS) { - vty_out(vty, "debug isis spf-statistics\n"); + vty_out(vty, "debug " PROTO_NAME " spf-statistics\n"); write++; } if (flags & DEBUG_SPF_TRIGGERS) { - vty_out(vty, "debug isis spf-triggers\n"); + vty_out(vty, "debug " PROTO_NAME " spf-triggers\n"); write++; } if (flags & DEBUG_UPDATE_PACKETS) { - vty_out(vty, "debug isis update-packets\n"); + vty_out(vty, "debug " PROTO_NAME " update-packets\n"); write++; } if (flags & DEBUG_RTE_EVENTS) { - vty_out(vty, "debug isis route-events\n"); + vty_out(vty, "debug " PROTO_NAME " route-events\n"); write++; } if (flags & DEBUG_EVENTS) { - vty_out(vty, "debug isis events\n"); + vty_out(vty, "debug " PROTO_NAME " events\n"); write++; } if (flags & DEBUG_PACKET_DUMP) { - vty_out(vty, "debug isis packet-dump\n"); + vty_out(vty, "debug " PROTO_NAME " packet-dump\n"); write++; } if (flags & DEBUG_LSP_GEN) { - vty_out(vty, "debug isis lsp-gen\n"); + vty_out(vty, "debug " PROTO_NAME " lsp-gen\n"); write++; } if (flags & DEBUG_LSP_SCHED) { - vty_out(vty, "debug isis lsp-sched\n"); + vty_out(vty, "debug " PROTO_NAME " lsp-sched\n"); write++; } write += spf_backoff_write_config(vty); @@ -822,9 +822,9 @@ static int config_write_debug(struct vty *vty) DEFUN (debug_isis_adj, debug_isis_adj_cmd, - "debug isis adj-packets", + "debug " PROTO_NAME " adj-packets", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS Adjacency related packets\n") { isis->debugs |= DEBUG_ADJ_PACKETS; @@ -835,10 +835,10 @@ DEFUN (debug_isis_adj, DEFUN (no_debug_isis_adj, no_debug_isis_adj_cmd, - "no debug isis adj-packets", + "no debug " PROTO_NAME " adj-packets", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS Adjacency related packets\n") { isis->debugs &= ~DEBUG_ADJ_PACKETS; @@ -849,9 +849,9 @@ DEFUN (no_debug_isis_adj, DEFUN (debug_isis_csum, debug_isis_csum_cmd, - "debug isis checksum-errors", + "debug " PROTO_NAME " checksum-errors", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS LSP checksum errors\n") { isis->debugs |= DEBUG_CHECKSUM_ERRORS; @@ -862,10 +862,10 @@ DEFUN (debug_isis_csum, DEFUN (no_debug_isis_csum, no_debug_isis_csum_cmd, - "no debug isis checksum-errors", + "no debug " PROTO_NAME " checksum-errors", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS LSP checksum errors\n") { isis->debugs &= ~DEBUG_CHECKSUM_ERRORS; @@ -876,9 +876,9 @@ DEFUN (no_debug_isis_csum, DEFUN (debug_isis_lupd, debug_isis_lupd_cmd, - "debug isis local-updates", + "debug " PROTO_NAME " local-updates", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS local update packets\n") { isis->debugs |= DEBUG_LOCAL_UPDATES; @@ -889,10 +889,10 @@ DEFUN (debug_isis_lupd, DEFUN (no_debug_isis_lupd, no_debug_isis_lupd_cmd, - "no debug isis local-updates", + "no debug " PROTO_NAME " local-updates", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS local update packets\n") { isis->debugs &= ~DEBUG_LOCAL_UPDATES; @@ -903,9 +903,9 @@ DEFUN (no_debug_isis_lupd, DEFUN (debug_isis_err, debug_isis_err_cmd, - "debug isis protocol-errors", + "debug " PROTO_NAME " protocol-errors", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS LSP protocol errors\n") { isis->debugs |= DEBUG_PROTOCOL_ERRORS; @@ -916,10 +916,10 @@ DEFUN (debug_isis_err, DEFUN (no_debug_isis_err, no_debug_isis_err_cmd, - "no debug isis protocol-errors", + "no debug " PROTO_NAME " protocol-errors", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS LSP protocol errors\n") { isis->debugs &= ~DEBUG_PROTOCOL_ERRORS; @@ -930,9 +930,9 @@ DEFUN (no_debug_isis_err, DEFUN (debug_isis_snp, debug_isis_snp_cmd, - "debug isis snp-packets", + "debug " PROTO_NAME " snp-packets", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS CSNP/PSNP packets\n") { isis->debugs |= DEBUG_SNP_PACKETS; @@ -943,10 +943,10 @@ DEFUN (debug_isis_snp, DEFUN (no_debug_isis_snp, no_debug_isis_snp_cmd, - "no debug isis snp-packets", + "no debug " PROTO_NAME " snp-packets", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS CSNP/PSNP packets\n") { isis->debugs &= ~DEBUG_SNP_PACKETS; @@ -957,9 +957,9 @@ DEFUN (no_debug_isis_snp, DEFUN (debug_isis_upd, debug_isis_upd_cmd, - "debug isis update-packets", + "debug " PROTO_NAME " update-packets", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS Update related packets\n") { isis->debugs |= DEBUG_UPDATE_PACKETS; @@ -970,10 +970,10 @@ DEFUN (debug_isis_upd, DEFUN (no_debug_isis_upd, no_debug_isis_upd_cmd, - "no debug isis update-packets", + "no debug " PROTO_NAME " update-packets", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS Update related packets\n") { isis->debugs &= ~DEBUG_UPDATE_PACKETS; @@ -984,9 +984,9 @@ DEFUN (no_debug_isis_upd, DEFUN (debug_isis_spfevents, debug_isis_spfevents_cmd, - "debug isis spf-events", + "debug " PROTO_NAME " spf-events", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS Shortest Path First Events\n") { isis->debugs |= DEBUG_SPF_EVENTS; @@ -997,10 +997,10 @@ DEFUN (debug_isis_spfevents, DEFUN (no_debug_isis_spfevents, no_debug_isis_spfevents_cmd, - "no debug isis spf-events", + "no debug " PROTO_NAME " spf-events", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS Shortest Path First Events\n") { isis->debugs &= ~DEBUG_SPF_EVENTS; @@ -1011,9 +1011,9 @@ DEFUN (no_debug_isis_spfevents, DEFUN (debug_isis_spfstats, debug_isis_spfstats_cmd, - "debug isis spf-statistics ", + "debug " PROTO_NAME " spf-statistics ", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS SPF Timing and Statistic Data\n") { isis->debugs |= DEBUG_SPF_STATS; @@ -1024,10 +1024,10 @@ DEFUN (debug_isis_spfstats, DEFUN (no_debug_isis_spfstats, no_debug_isis_spfstats_cmd, - "no debug isis spf-statistics", + "no debug " PROTO_NAME " spf-statistics", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS SPF Timing and Statistic Data\n") { isis->debugs &= ~DEBUG_SPF_STATS; @@ -1038,9 +1038,9 @@ DEFUN (no_debug_isis_spfstats, DEFUN (debug_isis_spftrigg, debug_isis_spftrigg_cmd, - "debug isis spf-triggers", + "debug " PROTO_NAME " spf-triggers", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS SPF triggering events\n") { isis->debugs |= DEBUG_SPF_TRIGGERS; @@ -1051,10 +1051,10 @@ DEFUN (debug_isis_spftrigg, DEFUN (no_debug_isis_spftrigg, no_debug_isis_spftrigg_cmd, - "no debug isis spf-triggers", + "no debug " PROTO_NAME " spf-triggers", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS SPF triggering events\n") { isis->debugs &= ~DEBUG_SPF_TRIGGERS; @@ -1065,9 +1065,9 @@ DEFUN (no_debug_isis_spftrigg, DEFUN (debug_isis_rtevents, debug_isis_rtevents_cmd, - "debug isis route-events", + "debug " PROTO_NAME " route-events", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS Route related events\n") { isis->debugs |= DEBUG_RTE_EVENTS; @@ -1078,10 +1078,10 @@ DEFUN (debug_isis_rtevents, DEFUN (no_debug_isis_rtevents, no_debug_isis_rtevents_cmd, - "no debug isis route-events", + "no debug " PROTO_NAME " route-events", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS Route related events\n") { isis->debugs &= ~DEBUG_RTE_EVENTS; @@ -1092,9 +1092,9 @@ DEFUN (no_debug_isis_rtevents, DEFUN (debug_isis_events, debug_isis_events_cmd, - "debug isis events", + "debug " PROTO_NAME " events", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS Events\n") { isis->debugs |= DEBUG_EVENTS; @@ -1105,10 +1105,10 @@ DEFUN (debug_isis_events, DEFUN (no_debug_isis_events, no_debug_isis_events_cmd, - "no debug isis events", + "no debug " PROTO_NAME " events", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS Events\n") { isis->debugs &= ~DEBUG_EVENTS; @@ -1119,9 +1119,9 @@ DEFUN (no_debug_isis_events, DEFUN (debug_isis_packet_dump, debug_isis_packet_dump_cmd, - "debug isis packet-dump", + "debug " PROTO_NAME " packet-dump", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS packet dump\n") { isis->debugs |= DEBUG_PACKET_DUMP; @@ -1132,10 +1132,10 @@ DEFUN (debug_isis_packet_dump, DEFUN (no_debug_isis_packet_dump, no_debug_isis_packet_dump_cmd, - "no debug isis packet-dump", + "no debug " PROTO_NAME " packet-dump", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS packet dump\n") { isis->debugs &= ~DEBUG_PACKET_DUMP; @@ -1146,9 +1146,9 @@ DEFUN (no_debug_isis_packet_dump, DEFUN (debug_isis_lsp_gen, debug_isis_lsp_gen_cmd, - "debug isis lsp-gen", + "debug " PROTO_NAME " lsp-gen", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS generation of own LSPs\n") { isis->debugs |= DEBUG_LSP_GEN; @@ -1159,10 +1159,10 @@ DEFUN (debug_isis_lsp_gen, DEFUN (no_debug_isis_lsp_gen, no_debug_isis_lsp_gen_cmd, - "no debug isis lsp-gen", + "no debug " PROTO_NAME " lsp-gen", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS generation of own LSPs\n") { isis->debugs &= ~DEBUG_LSP_GEN; @@ -1173,9 +1173,9 @@ DEFUN (no_debug_isis_lsp_gen, DEFUN (debug_isis_lsp_sched, debug_isis_lsp_sched_cmd, - "debug isis lsp-sched", + "debug " PROTO_NAME " lsp-sched", DEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS scheduling of LSP generation\n") { isis->debugs |= DEBUG_LSP_SCHED; @@ -1186,10 +1186,10 @@ DEFUN (debug_isis_lsp_sched, DEFUN (no_debug_isis_lsp_sched, no_debug_isis_lsp_sched_cmd, - "no debug isis lsp-sched", + "no debug " PROTO_NAME " lsp-sched", NO_STR UNDEBUG_STR - "IS-IS information\n" + PROTO_HELP "IS-IS scheduling of LSP generation\n") { isis->debugs &= ~DEBUG_LSP_SCHED; @@ -1200,9 +1200,9 @@ DEFUN (no_debug_isis_lsp_sched, DEFUN (show_hostname, show_hostname_cmd, - "show isis hostname", + "show " PROTO_NAME " hostname", SHOW_STR - "IS-IS information\n" + PROTO_HELP "IS-IS Dynamic hostname mapping\n") { dynhn_print_all(vty); @@ -1212,10 +1212,10 @@ DEFUN (show_hostname, DEFUN (show_isis_spf_ietf, show_isis_spf_ietf_cmd, - "show isis spf-delay-ietf", + "show " PROTO_NAME " spf-delay-ietf", SHOW_STR - "IS-IS information\n" - "IS-IS SPF delay IETF information\n") + PROTO_HELP + "SPF delay IETF information\n") { if (!isis) { vty_out(vty, "ISIS is not running\n"); @@ -1261,15 +1261,15 @@ DEFUN (show_isis_spf_ietf, DEFUN (show_isis_summary, show_isis_summary_cmd, - "show isis summary", - SHOW_STR "IS-IS information\n" "IS-IS summary\n") + "show " PROTO_NAME " summary", + SHOW_STR PROTO_HELP "summary\n") { struct listnode *node, *node2; struct isis_area *area; int level; if (isis == NULL) { - vty_out(vty, "ISIS is not running\n"); + vty_out(vty, PROTO_NAME " is not running\n"); return CMD_SUCCESS; } @@ -1471,10 +1471,10 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level) DEFUN (show_database, show_database_cmd, - "show isis database [detail] [WORD]", + "show " PROTO_NAME " database [detail] [WORD]", SHOW_STR - "IS-IS information\n" - "IS-IS link state database\n" + PROTO_HELP + "Link state database\n" "Detailed information\n" "LSP ID\n") { @@ -1491,9 +1491,9 @@ DEFUN (show_database, */ DEFUN_NOSH (router_isis, router_isis_cmd, - "router isis WORD", + "router " PROTO_NAME " WORD", ROUTER_STR - "ISO IS-IS\n" + PROTO_HELP "ISO Routing area tag\n") { int idx_word = 2; @@ -1505,8 +1505,11 @@ DEFUN_NOSH (router_isis, */ DEFUN (no_router_isis, no_router_isis_cmd, - "no router isis WORD", - "no\n" ROUTER_STR "ISO IS-IS\n" "ISO Routing area tag\n") + "no router " PROTO_NAME " WORD", + NO_STR + ROUTER_STR + PROTO_HELP + "ISO Routing area tag\n") { int idx_word = 3; return isis_area_destroy(vty, argv[idx_word]->arg); @@ -1869,7 +1872,7 @@ int isis_config_write(struct vty *vty) for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { /* ISIS - Area name */ - vty_out(vty, "router isis %s\n", area->area_tag); + vty_out(vty, "router " PROTO_NAME " %s\n", area->area_tag); write++; /* ISIS - Net */ if (listcount(area->area_addrs) > 0) { @@ -2123,12 +2126,12 @@ int isis_config_write(struct vty *vty) return write; } -struct cmd_node isis_node = {ISIS_NODE, "%s(config-router)# ", 1}; +struct cmd_node router_node = {ROUTER_NODE, "%s(config-router)# ", 1}; void isis_init() { /* Install IS-IS top node */ - install_node(&isis_node, isis_config_write); + install_node(&router_node, isis_config_write); install_element(VIEW_NODE, &show_isis_summary_cmd); @@ -2212,16 +2215,16 @@ void isis_init() install_element(CONFIG_NODE, &router_isis_cmd); install_element(CONFIG_NODE, &no_router_isis_cmd); - install_default(ISIS_NODE); + install_default(ROUTER_NODE); - install_element(ISIS_NODE, &net_cmd); - install_element(ISIS_NODE, &no_net_cmd); + install_element(ROUTER_NODE, &net_cmd); + install_element(ROUTER_NODE, &no_net_cmd); - install_element(ISIS_NODE, &isis_topology_cmd); - install_element(ISIS_NODE, &no_isis_topology_cmd); + install_element(ROUTER_NODE, &isis_topology_cmd); + install_element(ROUTER_NODE, &no_isis_topology_cmd); - install_element(ISIS_NODE, &log_adj_changes_cmd); - install_element(ISIS_NODE, &no_log_adj_changes_cmd); + install_element(ROUTER_NODE, &log_adj_changes_cmd); + install_element(ROUTER_NODE, &no_log_adj_changes_cmd); spf_backoff_cmd_init(); } diff --git a/isisd/isisd.h b/isisd/isisd.h index ce602e4402..ebccfee330 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -33,6 +33,24 @@ #include "isis_memory.h" #include "qobj.h" +#ifdef FABRICD +static const bool fabricd = true; +#define PROTO_TYPE ZEBRA_ROUTE_OPENFABRIC +#define PROTO_NAME "openfabric" +#define PROTO_HELP "OpenFabric routing protocol\n" +#define PROTO_REDIST_STR FRR_REDIST_STR_FABRICD +#define PROTO_REDIST_HELP FRR_REDIST_HELP_STR_FABRICD +#define ROUTER_NODE OPENFABRIC_NODE +#else +static const bool fabricd = false; +#define PROTO_TYPE ZEBRA_ROUTE_ISIS +#define PROTO_NAME "isis" +#define PROTO_HELP "IS-IS routing protocol\n" +#define PROTO_REDIST_STR FRR_REDIST_STR_ISISD +#define PROTO_REDIST_HELP FRR_REDIST_HELP_STR_ISISD +#define ROUTER_NODE ISIS_NODE +#endif + extern struct zebra_privs_t isisd_privs; /* uncomment if you are a developer in bug hunt */ diff --git a/isisd/subdir.am b/isisd/subdir.am index 7b8be46167..085b0cb845 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -8,6 +8,12 @@ sbin_PROGRAMS += isisd/isisd dist_examples_DATA += isisd/isisd.conf.sample endif +if FABRICD +noinst_LIBRARIES += isisd/libfabric.a +sbin_PROGRAMS += isisd/fabricd +dist_examples_DATA += isisd/fabricd.conf.sample +endif + isisd_libisis_a_SOURCES = \ isisd/dict.c \ isisd/isis_adjacency.c \ @@ -73,3 +79,12 @@ isisd_isisd_SOURCES = \ isisd/isis_main.c \ isisd/isis_pfpacket.c \ # end + +FABRICD_CPPFLAGS = -DFABRICD=1 $(AM_CPPFLAGS) + +isisd_libfabric_a_SOURCES = $(isisd_libisis_a_SOURCES) +isisd_libfabric_a_CPPFLAGS = $(FABRICD_CPPFLAGS) + +isisd_fabricd_LDADD = isisd/libfabric.a lib/libfrr.la @LIBCAP@ +isisd_fabricd_SOURCES = $(isisd_isisd_SOURCES) +isisd_fabricd_CPPFLAGS = $(FABRICD_CPPFLAGS) diff --git a/lib/command.c b/lib/command.c index 1df6442107..dd259472b8 100644 --- a/lib/command.c +++ b/lib/command.c @@ -146,6 +146,7 @@ const char *node_names[] = { */ "bfd", /* BFD_NODE */ "bfd peer", /* BFD_PEER_NODE */ + "openfabric", // OPENFABRIC_NODE }; /* clang-format on */ @@ -1435,6 +1436,7 @@ void cmd_exit(struct vty *vty) case LDP_NODE: case LDP_L2VPN_NODE: case ISIS_NODE: + case OPENFABRIC_NODE: case KEYCHAIN_NODE: case RMAP_NODE: case PBRMAP_NODE: @@ -1550,6 +1552,7 @@ DEFUN (config_end, case LDP_L2VPN_NODE: case LDP_PSEUDOWIRE_NODE: case ISIS_NODE: + case OPENFABRIC_NODE: case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: case VTY_NODE: diff --git a/lib/command.h b/lib/command.h index 75b69507ec..8e51641b88 100644 --- a/lib/command.h +++ b/lib/command.h @@ -141,6 +141,7 @@ enum node_type { BGP_FLOWSPECV6_NODE, /* BGP IPv6 FLOWSPEC Address-Family */ BFD_NODE, /* BFD protocol mode. */ BFD_PEER_NODE, /* BFD peer configuration mode. */ + OPENFABRIC_NODE, /* OpenFabric router configuration node */ NODE_TYPE_MAX, /* maximum */ }; @@ -364,7 +365,6 @@ struct cmd_node { #define PREFIX_LIST_STR "Build a prefix list\n" #define OSPF6_DUMP_TYPE_LIST \ "" -#define ISIS_STR "IS-IS information\n" #define AREA_TAG_STR "[area tag]\n" #define COMMUNITY_AANN_STR "Community number where AA and NN are (0-65535)\n" #define COMMUNITY_VAL_STR "Community number in AA:NN format (where AA and NN are (0-65535)) or local-AS|no-advertise|no-export|internet or additive\n" diff --git a/lib/log.c b/lib/log.c index 010b984786..521783e4b0 100644 --- a/lib/log.c +++ b/lib/log.c @@ -1074,6 +1074,8 @@ int proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_BABEL; else if (strmatch(s, "sharp")) return ZEBRA_ROUTE_SHARP; + else if (strmatch(s, "openfabric")) + return ZEBRA_ROUTE_OPENFABRIC; } if (afi == AFI_IP6) { if (strmatch(s, "kernel")) @@ -1102,6 +1104,8 @@ int proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_BABEL; else if (strmatch(s, "sharp")) return ZEBRA_ROUTE_SHARP; + else if (strmatch(s, "openfabric")) + return ZEBRA_ROUTE_OPENFABRIC; } return -1; } diff --git a/lib/route_types.txt b/lib/route_types.txt index 72f59a1b78..c5eff44ca7 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -82,6 +82,7 @@ ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, 1, "Babel" ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, 1, "SHARP" ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, 0, "PBR" ZEBRA_ROUTE_BFD, bfd, bfdd, '-', 0, 0, 0, "BFD" +ZEBRA_ROUTE_OPENFABRIC, openfabric, fabricd, 'f', 1, 1, 1, "OpenFabric" ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-" @@ -109,3 +110,4 @@ ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)" ZEBRA_ROUTE_PBR, "Policy Based Routing (PBR)" ZEBRA_ROUTE_BFD, "Bidirectional Fowarding Detection (BFD)" +ZEBRA_ROUTE_OPENFABRIC, "OpenFabric Routing Protocol" diff --git a/lib/vty.c b/lib/vty.c index 748c14f675..480137a7a9 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -811,6 +811,7 @@ static void vty_end_config(struct vty *vty) case LDP_L2VPN_NODE: case LDP_PSEUDOWIRE_NODE: case ISIS_NODE: + case OPENFABRIC_NODE: case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: case VTY_NODE: @@ -1210,6 +1211,7 @@ static void vty_stop_input(struct vty *vty) case LDP_L2VPN_NODE: case LDP_PSEUDOWIRE_NODE: case ISIS_NODE: + case OPENFABRIC_NODE: case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: case VTY_NODE: From da82f6b42aed82e24b591c8243bf5f4010649cf8 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 11 Jun 2018 14:09:23 +0200 Subject: [PATCH 06/84] zebra: add RTPROT_FABRICD route type 197 Add an iproute2 route type for fabricd Signed-off-by: Christian Franke --- tools/etc/iproute2/rt_protos.d/frr.conf | 1 + tools/frr | 1 + zebra/rt_netlink.c | 8 +++++++- zebra/rt_netlink.h | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/etc/iproute2/rt_protos.d/frr.conf b/tools/etc/iproute2/rt_protos.d/frr.conf index 4c6968ac27..bbb358fc6c 100644 --- a/tools/etc/iproute2/rt_protos.d/frr.conf +++ b/tools/etc/iproute2/rt_protos.d/frr.conf @@ -11,3 +11,4 @@ 194 sharp 195 pbr 196 static +197 openfabric diff --git a/tools/frr b/tools/frr index 0b170d33fd..9c8a8e9043 100755 --- a/tools/frr +++ b/tools/frr @@ -587,6 +587,7 @@ case "$1" in ip route flush proto 194 ip route flush proto 195 ip route flush proto 196 + ip route flush proto 197 else [ -n "$dmn" ] && eval "${dmn/-/_}=0" start_watchfrr diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 3683596b41..8e2cd2e418 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -99,7 +99,7 @@ static inline int is_selfroute(int proto) || (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP) || (proto == RTPROT_LDP) || (proto == RTPROT_BABEL) || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP) - || (proto == RTPROT_PBR)) { + || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC)) { return 1; } @@ -146,6 +146,9 @@ static inline int zebra2proto(int proto) case ZEBRA_ROUTE_PBR: proto = RTPROT_PBR; break; + case ZEBRA_ROUTE_OPENFABRIC: + proto = RTPROT_OPENFABRIC; + break; default: /* * When a user adds a new protocol this will show up @@ -203,6 +206,9 @@ static inline int proto2zebra(int proto, int family) case RTPROT_PBR: proto = ZEBRA_ROUTE_PBR; break; + case RTPROT_OPENFABRIC: + proto = ZEBRA_ROUTE_OPENFABRIC; + break; default: /* * When a user adds a new protocol this will show up diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index c4f21d1504..cefd1996a9 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -54,6 +54,7 @@ #define RTPROT_SHARP 194 #define RTPROT_PBR 195 #define RTPROT_ZSTATIC 196 +#define RTPROT_OPENFABRIC 197 void rt_netlink_init(void); From 13d9aad8566e6a3b2dd64f2718570959d697220d Mon Sep 17 00:00:00 2001 From: Martin Winter Date: Fri, 25 May 2018 04:33:41 -0700 Subject: [PATCH 07/84] redhat: Add fabricd to RPM package Signed-off-by: Martin Winter --- redhat/daemons | 2 ++ redhat/frr.init | 2 +- redhat/frr.logrotate | 8 ++++++++ redhat/frr.spec.in | 4 +++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/redhat/daemons b/redhat/daemons index de708cf4fd..c301a1c23a 100644 --- a/redhat/daemons +++ b/redhat/daemons @@ -53,6 +53,7 @@ sharpd=no pbrd=no staticd=no bfdd=no +fabricd=no # # Command line options for the daemons @@ -73,6 +74,7 @@ sharpd_options=("-A 127.0.0.1") pbrd_options=("-A 127.0.0.1") staticd_options=("-A 127.0.0.1") bfdd_options=("-A 127.0.0.1") +fabricd_options=("-A 127.0.0.1") # # If the vtysh_enable is yes, then the unified config is read diff --git a/redhat/frr.init b/redhat/frr.init index 2e33aee173..47a92eed32 100755 --- a/redhat/frr.init +++ b/redhat/frr.init @@ -33,7 +33,7 @@ V_PATH=/var/run/frr # Local Daemon selection may be done by using /etc/frr/daemons. # See /usr/share/doc/frr/README.Debian.gz for further information. # Keep zebra first and do not list watchfrr! -DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd pimd pbrd ldpd nhrpd eigrpd babeld staticd sharpd bfdd" +DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd pimd pbrd ldpd nhrpd eigrpd babeld staticd sharpd bfdd fabricd" MAX_INSTANCES=5 RELOAD_SCRIPT=/usr/lib/frr/frr-reload.py diff --git a/redhat/frr.logrotate b/redhat/frr.logrotate index 654d355fd7..df7c5da54e 100644 --- a/redhat/frr.logrotate +++ b/redhat/frr.logrotate @@ -93,3 +93,11 @@ /bin/kill -USR1 `cat /var/run/frr/bfdd.pid 2> /dev/null` 2> /dev/null || true endscript } + +/var/log/frr/fabricd.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/frr/fabricd.pid 2> /dev/null` 2> /dev/null || true + endscript +} diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 25b48506a6..d001f3c395 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -86,7 +86,7 @@ %{!?frr_gid: %global frr_gid 92 } %{!?vty_gid: %global vty_gid 85 } -%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d pbrd staticd bfdd +%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d pbrd staticd bfdd fabricd %if %{with_ldpd} %define daemon_ldpd ldpd @@ -459,6 +459,7 @@ zebra_spec_add_service isisd 2608/tcp "ISISd vty" %if %{with_bfdd} zebra_spec_add_service bfdd 2617/tcp "BFDd vty" %endif +zebra_spec_add_service fabricd 2618/tcp "Fabricd vty" %if "%{initsystem}" == "systemd" for daemon in %all_daemons ; do @@ -594,6 +595,7 @@ fi %{_sbindir}/pbrd %endif %{_sbindir}/isisd +%{_sbindir}/fabricd %if %{with_ldpd} %{_sbindir}/ldpd %endif From 770ccdf874da684fce9e821bb7cc92dfbb642d8b Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 22 Mar 2018 15:01:15 +0100 Subject: [PATCH 08/84] vtysh: support fabricd Extend extract.pl so it can deal with the isis source code being compiled twice, once for isisd and once for fabricd. Add the fabricd node and client to vtysh. Signed-off-by: Christian Franke --- vtysh/extract.pl.in | 30 +++++++++++++++++++++++++----- vtysh/vtysh.c | 43 +++++++++++++++++++++++++++++++++++++++---- vtysh/vtysh.h | 7 ++++--- vtysh/vtysh_config.c | 3 +++ 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 92b5686a94..0eb5944ab0 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -35,10 +35,12 @@ EOF my $cli_stomp = 0; -foreach (@ARGV) { - $file = $_; +sub scan_file { + my ( $file, $fabricd) = @_; - open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -I@top_builddir@ -I@srcdir@/ -I@srcdir@/.. -I@top_srcdir@/lib -I@top_builddir@/lib -I@top_srcdir@/bgpd -I@top_srcdir@/@LIBRFP@ -I@top_srcdir@/bgpd/rfapi @CPPFLAGS@ $file |"); + $cppadd = $fabricd ? "-DFABRICD=1" : ""; + + open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -I@top_builddir@ -I@srcdir@/ -I@srcdir@/.. -I@top_srcdir@/lib -I@top_builddir@/lib -I@top_srcdir@/bgpd -I@top_srcdir@/@LIBRFP@ -I@top_srcdir@/bgpd/rfapi @CPPFLAGS@ $cppadd $file |"); local $/; undef $/; $line = ; close (FH); @@ -77,6 +79,10 @@ foreach (@ARGV) { $cmd =~ s/^\s+//g; $cmd =~ s/\s+$//g; + if ($fabricd) { + $cmd = "fabricd_" . $cmd; + } + # $protocol is VTYSH_PROTO format for redirection of user input if ($file =~ /lib\/keychain\.c$/) { $protocol = "VTYSH_RIPD"; @@ -107,9 +113,9 @@ foreach (@ARGV) { } elsif ($file =~ /lib\/plist\.c$/) { if ($defun_array[1] =~ m/ipv6/) { - $protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_BABELD|VTYSH_ISISD"; + $protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_BABELD|VTYSH_ISISD|VTYSH_FABRICD"; } else { - $protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_ISISD"; + $protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_ISISD|VTYSH_FABRICD"; } } elsif ($file =~ /lib\/distribute\.c$/) { @@ -132,6 +138,9 @@ foreach (@ARGV) { elsif ($file =~ /librfp\/.*\.c$/ || $file =~ /rfapi\/.*\.c$/) { $protocol = "VTYSH_BGPD"; } + elsif ($fabricd) { + $protocol = "VTYSH_FABRICD"; + } else { ($protocol) = ($file =~ /^.*\/([a-z0-9]+)\/[a-zA-Z0-9_\-]+\.c$/); $protocol = "VTYSH_" . uc $protocol; @@ -170,6 +179,10 @@ foreach (@ARGV) { $ecmd =~ s/^\s+//g; $ecmd =~ s/\s+$//g; + if ($fabricd) { + $ecmd = "fabricd_" . $ecmd; + } + # Register $ecmd if (defined ($cmd2str{$ecmd})) { my ($key); @@ -187,6 +200,13 @@ foreach (@ARGV) { } } +foreach (@ARGV) { + scan_file($_, 0); + if (/\/isisd\//) { + scan_file($_, 1); + } +} + # When we have cli commands that map to the same function name, we # can introduce subtle bugs due to code not being called when # we think it is. diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index c249115fd3..98809b5a24 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -132,6 +132,7 @@ struct vtysh_client vtysh_client[] = { {.fd = -1, .name = "eigrpd", .flag = VTYSH_EIGRPD, .next = NULL}, {.fd = -1, .name = "babeld", .flag = VTYSH_BABELD, .next = NULL}, {.fd = -1, .name = "sharpd", .flag = VTYSH_SHARPD, .next = NULL}, + {.fd = -1, .name = "fabricd", .flag = VTYSH_FABRICD, .next = NULL}, {.fd = -1, .name = "watchfrr", .flag = VTYSH_WATCHFRR, .next = NULL}, {.fd = -1, .name = "pbrd", .flag = VTYSH_PBRD, .next = NULL}, {.fd = -1, .name = "staticd", .flag = VTYSH_STATICD, .next = NULL}, @@ -1141,6 +1142,10 @@ static struct cmd_node isis_node = { ISIS_NODE, "%s(config-router)# ", }; +static struct cmd_node openfabric_node = { + OPENFABRIC_NODE, "%s(config-router)# ", +}; + static struct cmd_node interface_node = { INTERFACE_NODE, "%s(config-if)# ", }; @@ -1653,6 +1658,15 @@ DEFUNSH(VTYSH_ISISD, router_isis, router_isis_cmd, "router isis WORD", return CMD_SUCCESS; } +DEFUNSH(VTYSH_FABRICD, router_openfabric, router_openfabric_cmd, "router openfabric WORD", + ROUTER_STR + "OpenFabric routing protocol\n" + "ISO Routing area tag\n") +{ + vty->node = OPENFABRIC_NODE; + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd, "route-map WORD (1-65535)", "Create route-map or enter route-map command mode\n" @@ -1767,6 +1781,7 @@ static int vtysh_exit(struct vty *vty) case LDP_NODE: case LDP_L2VPN_NODE: case ISIS_NODE: + case OPENFABRIC_NODE: case RMAP_NODE: case PBRMAP_NODE: case VTY_NODE: @@ -2042,6 +2057,18 @@ ALIAS(vtysh_exit_bfdd, vtysh_quit_bfdd_cmd, "quit", "Exit current mode and down to previous mode\n") #endif +DEFUNSH(VTYSH_FABRICD, vtysh_exit_fabricd, vtysh_exit_fabricd_cmd, "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit(vty); +} + +DEFUNSH(VTYSH_FABRICD, vtysh_quit_fabricd, vtysh_quit_fabricd_cmd, "quit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit_fabricd(self, vty, argc, argv); +} + DEFUNSH(VTYSH_ALL, vtysh_exit_line_vty, vtysh_exit_line_vty_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -2245,7 +2272,7 @@ DEFUN (vtysh_show_work_queues, DEFUN (vtysh_show_work_queues_daemon, vtysh_show_work_queues_daemon_cmd, - "show work-queues ", + "show work-queues ", SHOW_STR "Work Queue information\n" "For the zebra daemon\n" @@ -2255,7 +2282,8 @@ DEFUN (vtysh_show_work_queues_daemon, "For the ospfv6 daemon\n" "For the bgp daemon\n" "For the isis daemon\n" - "For the pbr daemon\n") + "For the pbr daemon\n" + "For the fabricd daemon\n") { int idx_protocol = 2; unsigned int i; @@ -2593,7 +2621,7 @@ DEFUNSH(VTYSH_ALL, no_vtysh_config_enable_password, DEFUN (vtysh_write_terminal, vtysh_write_terminal_cmd, - "write terminal []", + "write terminal []", "Write running configuration to memory, network, or terminal\n" "Write to terminal\n" "For the zebra daemon\n" @@ -2604,6 +2632,7 @@ DEFUN (vtysh_write_terminal, "For the ldpd daemon\n" "For the bgp daemon\n" "For the isis daemon\n" + "For the fabricd daemon\n" "For the pim daemon\n") { unsigned int i; @@ -2630,7 +2659,7 @@ DEFUN (vtysh_write_terminal, DEFUN (vtysh_show_running_config, vtysh_show_running_config_cmd, - "show running-config []", + "show running-config []", SHOW_STR "Current operating configuration\n" "For the zebra daemon\n" @@ -2641,6 +2670,7 @@ DEFUN (vtysh_show_running_config, "For the ldp daemon\n" "For the bgp daemon\n" "For the isis daemon\n" + "For the fabricd daemon\n" "For the pim daemon\n") { return vtysh_write_terminal(self, vty, argc, argv); @@ -3494,6 +3524,7 @@ void vtysh_init_vty(void) install_node(&keychain_node, NULL); install_node(&keychain_key_node, NULL); install_node(&isis_node, NULL); + install_node(&openfabric_node, NULL); install_node(&vty_node, NULL); #if defined(HAVE_RPKI) install_node(&rpki_node, NULL); @@ -3588,6 +3619,8 @@ void vtysh_init_vty(void) #endif install_element(ISIS_NODE, &vtysh_exit_isisd_cmd); install_element(ISIS_NODE, &vtysh_quit_isisd_cmd); + install_element(OPENFABRIC_NODE, &vtysh_exit_fabricd_cmd); + install_element(OPENFABRIC_NODE, &vtysh_quit_fabricd_cmd); install_element(KEYCHAIN_NODE, &vtysh_exit_ripd_cmd); install_element(KEYCHAIN_NODE, &vtysh_quit_ripd_cmd); install_element(KEYCHAIN_KEY_NODE, &vtysh_exit_ripd_cmd); @@ -3648,6 +3681,7 @@ void vtysh_init_vty(void) install_element(BGP_VNC_NVE_GROUP_NODE, &vtysh_end_all_cmd); install_element(BGP_VNC_L2_GROUP_NODE, &vtysh_end_all_cmd); install_element(ISIS_NODE, &vtysh_end_all_cmd); + install_element(OPENFABRIC_NODE, &vtysh_end_all_cmd); install_element(KEYCHAIN_NODE, &vtysh_end_all_cmd); install_element(KEYCHAIN_KEY_NODE, &vtysh_end_all_cmd); install_element(RMAP_NODE, &vtysh_end_all_cmd); @@ -3697,6 +3731,7 @@ void vtysh_init_vty(void) install_element(LDP_L2VPN_NODE, &ldp_member_pseudowire_ifname_cmd); #endif install_element(CONFIG_NODE, &router_isis_cmd); + install_element(CONFIG_NODE, &router_openfabric_cmd); install_element(CONFIG_NODE, &router_bgp_cmd); install_element(BGP_NODE, &address_family_vpnv4_cmd); install_element(BGP_NODE, &address_family_vpnv6_cmd); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 5bff01a506..ee980d5128 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -41,6 +41,7 @@ DECLARE_MGROUP(MVTYSH) #define VTYSH_PBRD 0x04000 #define VTYSH_STATICD 0x08000 #define VTYSH_BFDD 0x10000 +#define VTYSH_FABRICD 0x20000 #define VTYSH_WAS_ACTIVE (-2) @@ -49,9 +50,9 @@ DECLARE_MGROUP(MVTYSH) /* watchfrr is not in ALL since library CLI functions should not be * run on it (logging & co. should stay in a fixed/frozen config, and * things like prefix lists are not even initialised) */ -#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD -#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD -#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD +#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD +#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD|VTYSH_FABRICD +#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD #define VTYSH_NS VTYSH_ZEBRA #define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_STATICD diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 42f08342c0..9f6e20f2be 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -245,6 +245,9 @@ void vtysh_config_parse_line(void *arg, const char *line) else if (strncmp(line, "router isis", strlen("router isis")) == 0) config = config_get(ISIS_NODE, line); + else if (strncmp(line, "router openfabric", strlen("router openfabric")) + == 0) + config = config_get(OPENFABRIC_NODE, line); else if (strncmp(line, "route-map", strlen("route-map")) == 0) config = config_get(RMAP_NODE, line); else if (strncmp(line, "pbr-map", strlen("pbr-map")) == 0) From ef020087a502267607ee746c0ca2b2a0d92e4ec0 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 22 Mar 2018 14:44:01 +0100 Subject: [PATCH 09/84] isis: Cleanup CLI, split into parts which are shared, fabricd and isisd Remove isis_vty.c and create three new files isis_vty_common.c, isis_vty_fabricd.c and isis_vty_isisd.c which are built into both daemons, only fabricd and only isisd, respectively. Signed-off-by: Christian Franke --- isisd/isis_circuit.c | 2 - isisd/isis_circuit.h | 8 +- isisd/isis_main.c | 2 + isisd/isis_redist.c | 76 +- isisd/isis_spf.c | 12 +- isisd/isis_te.c | 26 +- isisd/isis_vty.c | 2165 -------------------------------------- isisd/isis_vty_common.c | 946 +++++++++++++++++ isisd/isis_vty_common.h | 38 + isisd/isis_vty_fabricd.c | 32 + isisd/isis_vty_isisd.c | 858 +++++++++++++++ isisd/isisd.h | 1 - isisd/subdir.am | 83 +- vtysh/Makefile.am | 4 +- vtysh/extract.pl.in | 15 +- 15 files changed, 2026 insertions(+), 2242 deletions(-) delete mode 100644 isisd/isis_vty.c create mode 100644 isisd/isis_vty_common.c create mode 100644 isisd/isis_vty_common.h create mode 100644 isisd/isis_vty_fabricd.c create mode 100644 isisd/isis_vty_isisd.c diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index df55afac4d..a18ee19d68 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -1343,8 +1343,6 @@ void isis_circuit_init() /* Install interface node */ install_node(&interface_node, isis_interface_config_write); if_cmd_init(); - - isis_vty_init(); } void isis_circuit_schedule_lsp_send(struct isis_circuit *circuit) diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index 8dbd7ac492..c014e4a52e 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -114,10 +114,10 @@ struct isis_circuit { struct isis_passwd passwd; /* Circuit rx/tx password */ int is_type; /* circuit is type == level of circuit * differentiated from circuit type (media) */ - uint32_t hello_interval[2]; /* l1HelloInterval in msecs */ - uint16_t hello_multiplier[2]; /* l1HelloMultiplier */ - uint16_t csnp_interval[2]; /* level-1 csnp-interval in seconds */ - uint16_t psnp_interval[2]; /* level-1 psnp-interval in seconds */ + uint32_t hello_interval[2]; /* hello-interval in seconds */ + uint16_t hello_multiplier[2]; /* hello-multiplier */ + uint16_t csnp_interval[2]; /* csnp-interval in seconds */ + uint16_t psnp_interval[2]; /* psnp-interval in seconds */ uint8_t metric[2]; uint32_t te_metric[2]; struct mpls_te_circuit diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 2d96364afa..4d6a6da5d6 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -54,6 +54,7 @@ #include "isisd/isis_zebra.h" #include "isisd/isis_te.h" #include "isisd/isis_errors.h" +#include "isisd/isis_vty_common.h" /* Default configuration file name */ #define ISISD_DEFAULT_CONFIG "isisd.conf" @@ -207,6 +208,7 @@ int main(int argc, char **argv, char **envp) prefix_list_init(); isis_init(); isis_circuit_init(); + isis_vty_init(); isis_spf_cmds_init(); isis_redist_init(); isis_route_map_init(); diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c index c2581fd493..ab7584ed3e 100644 --- a/isisd/isis_redist.c +++ b/isisd/isis_redist.c @@ -515,13 +515,19 @@ void isis_redist_area_finish(struct isis_area *area) DEFUN (isis_redistribute, isis_redistribute_cmd, - "redistribute " PROTO_REDIST_STR " []", + "redistribute " PROTO_REDIST_STR +#ifndef FABRICD + " " +#endif + " []", REDIST_STR "Redistribute IPv4 routes\n" "Redistribute IPv6 routes\n" PROTO_REDIST_HELP +#ifndef FABRICD "Redistribute into level-1\n" "Redistribute into level-2\n" +#endif "Metric for redistributed routes\n" "ISIS default metric\n" "Route map reference\n" @@ -530,7 +536,7 @@ DEFUN (isis_redistribute, int idx_afi = 1; int idx_protocol = 2; int idx_level = 3; - int idx_metric_rmap = 4; + int idx_metric_rmap = fabricd ? 3 : 4; VTY_DECLVAR_CONTEXT(isis_area, area); int family; int afi; @@ -551,7 +557,9 @@ DEFUN (isis_redistribute, if (type < 0) return CMD_WARNING_CONFIG_FAILED; - if (!strcmp("level-1", argv[idx_level]->arg)) + if (fabricd) + level = 2; + else if (!strcmp("level-1", argv[idx_level]->arg)) level = 1; else if (!strcmp("level-2", argv[idx_level]->arg)) level = 2; @@ -585,14 +593,20 @@ DEFUN (isis_redistribute, DEFUN (no_isis_redistribute, no_isis_redistribute_cmd, - "no redistribute " PROTO_REDIST_STR " ", - NO_STR + "no redistribute " PROTO_REDIST_STR +#ifndef FABRICD + " " +#endif + , NO_STR REDIST_STR "Redistribute IPv4 routes\n" "Redistribute IPv6 routes\n" PROTO_REDIST_HELP +#ifndef FABRICD "Redistribute into level-1\n" - "Redistribute into level-2\n") + "Redistribute into level-2\n" +#endif + ) { int idx_afi = 2; int idx_protocol = 3; @@ -615,7 +629,10 @@ DEFUN (no_isis_redistribute, if (type < 0) return CMD_WARNING_CONFIG_FAILED; - level = strmatch("level-1", argv[idx_level]->text) ? 1 : 2; + if (fabricd) + level = 2; + else + level = strmatch("level-1", argv[idx_level]->text) ? 1 : 2; isis_redist_unset(area, level, family, type); return 0; @@ -623,13 +640,19 @@ DEFUN (no_isis_redistribute, DEFUN (isis_default_originate, isis_default_originate_cmd, - "default-information originate [always] []", + "default-information originate " +#ifndef FABRICD + " " +#endif + " [always] []", "Control distribution of default information\n" "Distribute a default route\n" "Distribute default route for IPv4\n" "Distribute default route for IPv6\n" +#ifndef FABRICD "Distribute default route into level-1\n" "Distribute default route into level-2\n" +#endif "Always advertise default route\n" "Metric for default route\n" "ISIS default metric\n" @@ -638,8 +661,8 @@ DEFUN (isis_default_originate, { int idx_afi = 2; int idx_level = 3; - int idx_always = 4; - int idx_metric_rmap = 4; + int idx_always = fabricd ? 3 : 4; + int idx_metric_rmap = fabricd ? 3 : 4; VTY_DECLVAR_CONTEXT(isis_area, area); int family; int originate_type = DEFAULT_ORIGINATE; @@ -651,7 +674,10 @@ DEFUN (isis_default_originate, if (family < 0) return CMD_WARNING_CONFIG_FAILED; - level = strmatch("level-1", argv[idx_level]->text) ? 1 : 2; + if (fabricd) + level = 2; + else + level = strmatch("level-1", argv[idx_level]->text) ? 1 : 2; if ((area->is_type & level) != level) { vty_out(vty, "Node is not a level-%d IS\n", level); @@ -685,14 +711,20 @@ DEFUN (isis_default_originate, DEFUN (no_isis_default_originate, no_isis_default_originate_cmd, - "no default-information originate ", - NO_STR + "no default-information originate " +#ifndef FABRICD + " " +#endif + , NO_STR "Control distribution of default information\n" "Distribute a default route\n" "Distribute default route for IPv4\n" "Distribute default route for IPv6\n" +#ifndef FABRICD "Distribute default route into level-1\n" - "Distribute default route into level-2\n") + "Distribute default route into level-2\n" +#endif + ) { int idx_afi = 3; int idx_level = 4; @@ -704,7 +736,9 @@ DEFUN (no_isis_default_originate, if (family < 0) return CMD_WARNING_CONFIG_FAILED; - if (strmatch("level-1", argv[idx_level]->text)) + if (fabricd) + level = 2; + else if (strmatch("level-1", argv[idx_level]->text)) level = 1; else if (strmatch("level-2", argv[idx_level]->text)) level = 2; @@ -739,8 +773,10 @@ int isis_redist_config_write(struct vty *vty, struct isis_area *area, redist = get_redist_settings(area, family, type, level); if (!redist->redist) continue; - vty_out(vty, " redistribute %s %s level-%d", family_str, - zebra_route_string(type), level); + vty_out(vty, " redistribute %s %s", family_str, + zebra_route_string(type)); + if (!fabricd) + vty_out(vty, " level-%d", level); if (redist->metric) vty_out(vty, " metric %u", redist->metric); if (redist->map_name) @@ -755,8 +791,10 @@ int isis_redist_config_write(struct vty *vty, struct isis_area *area, get_redist_settings(area, family, DEFAULT_ROUTE, level); if (!redist->redist) continue; - vty_out(vty, " default-information originate %s level-%d", - family_str, level); + vty_out(vty, " default-information originate %s", + family_str); + if (!fabricd) + vty_out(vty, " level-%d", level); if (redist->redist == DEFAULT_ORIGINATE_ALWAYS) vty_out(vty, " always"); if (redist->metric) diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 317e278e80..5b47d3e684 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -1617,12 +1617,18 @@ static void isis_print_spftree(struct vty *vty, int level, DEFUN (show_isis_topology, show_isis_topology_cmd, - "show " PROTO_NAME " topology []", - SHOW_STR + "show " PROTO_NAME " topology" +#ifndef FABRICD + " []" +#endif + , SHOW_STR PROTO_HELP "IS-IS paths to Intermediate Systems\n" +#ifndef FABRICD "Paths to all level-1 routers in the area\n" - "Paths to all level-2 routers in the domain\n") + "Paths to all level-2 routers in the domain\n" +#endif + ) { int levels; struct listnode *node; diff --git a/isisd/isis_te.c b/isisd/isis_te.c index 2430742498..08b905c650 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -67,17 +67,6 @@ const char *mode2text[] = {"Disable", "Area", "AS", "Emulate"}; * Followings are control functions for MPLS-TE parameters management. *------------------------------------------------------------------------*/ -/* Search MPLS TE Circuit context from Interface */ -static struct mpls_te_circuit *lookup_mpls_params_by_ifp(struct interface *ifp) -{ - struct isis_circuit *circuit; - - if ((circuit = circuit_scan_by_ifp(ifp)) == NULL) - return NULL; - - return circuit->mtc; -} - /* Create new MPLS TE Circuit context */ struct mpls_te_circuit *mpls_te_circuit_new() { @@ -1085,6 +1074,18 @@ void isis_mpls_te_config_write_router(struct vty *vty) /*------------------------------------------------------------------------* * Followings are vty command functions. *------------------------------------------------------------------------*/ +#ifndef FABRICD + +/* Search MPLS TE Circuit context from Interface */ +static struct mpls_te_circuit *lookup_mpls_params_by_ifp(struct interface *ifp) +{ + struct isis_circuit *circuit; + + if ((circuit = circuit_scan_by_ifp(ifp)) == NULL) + return NULL; + + return circuit->mtc; +} DEFUN (isis_mpls_te_on, isis_mpls_te_on_cmd, @@ -1342,6 +1343,7 @@ DEFUN (show_isis_mpls_te_interface, return CMD_SUCCESS; } +#endif /* Initialize MPLS_TE */ void isis_mpls_te_init(void) @@ -1357,6 +1359,7 @@ void isis_mpls_te_init(void) isisMplsTE.cir_list = list_new(); isisMplsTE.router_id.s_addr = 0; +#ifndef FABRICD /* Register new VTY commands */ install_element(VIEW_NODE, &show_isis_mpls_te_router_cmd); install_element(VIEW_NODE, &show_isis_mpls_te_interface_cmd); @@ -1366,6 +1369,7 @@ void isis_mpls_te_init(void) install_element(ROUTER_NODE, &isis_mpls_te_router_addr_cmd); install_element(ROUTER_NODE, &isis_mpls_te_inter_as_cmd); install_element(ROUTER_NODE, &no_isis_mpls_te_inter_as_cmd); +#endif return; } diff --git a/isisd/isis_vty.c b/isisd/isis_vty.c deleted file mode 100644 index 2deb813275..0000000000 --- a/isisd/isis_vty.c +++ /dev/null @@ -1,2165 +0,0 @@ -/* - * IS-IS Rout(e)ing protocol - isis_circuit.h - * - * Copyright (C) 2001,2002 Sampo Saaristo - * Tampere University of Technology - * Institute of Communications Engineering - * Copyright (C) 2016 David Lamparter, for NetDEF, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public Licenseas published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful,but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include "command.h" -#include "spf_backoff.h" - -#include "isis_circuit.h" -#include "isis_csm.h" -#include "isis_misc.h" -#include "isis_mt.h" -#include "isisd.h" - -static struct isis_circuit *isis_circuit_lookup(struct vty *vty) -{ - struct interface *ifp = VTY_GET_CONTEXT(interface); - struct isis_circuit *circuit; - - if (!ifp) { - vty_out(vty, "Invalid interface \n"); - return NULL; - } - - circuit = circuit_scan_by_ifp(ifp); - if (!circuit) { - vty_out(vty, "ISIS is not enabled on circuit %s\n", ifp->name); - return NULL; - } - - return circuit; -} - -DEFUN (ip_router_isis, - ip_router_isis_cmd, - "ip router " PROTO_NAME " WORD", - "Interface Internet Protocol config commands\n" - "IP router interface commands\n" - PROTO_HELP - "Routing process tag\n") -{ - int idx_afi = 0; - int idx_word = 3; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct isis_circuit *circuit; - struct isis_area *area; - const char *af = argv[idx_afi]->arg; - const char *area_tag = argv[idx_word]->arg; - - /* Prevent more than one area per circuit */ - circuit = circuit_scan_by_ifp(ifp); - if (circuit && circuit->area) { - if (strcmp(circuit->area->area_tag, area_tag)) { - vty_out(vty, "ISIS circuit is already defined on %s\n", - circuit->area->area_tag); - return CMD_ERR_NOTHING_TODO; - } - } - - area = isis_area_lookup(area_tag); - if (!area) - area = isis_area_create(area_tag); - - if (!circuit || !circuit->area) { - circuit = isis_circuit_create(area, ifp); - - if (circuit->state != C_STATE_CONF - && circuit->state != C_STATE_UP) { - vty_out(vty, - "Couldn't bring up interface, please check log.\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - bool ip = circuit->ip_router, ipv6 = circuit->ipv6_router; - if (af[2] != '\0') - ipv6 = true; - else - ip = true; - - isis_circuit_af_set(circuit, ip, ipv6); - return CMD_SUCCESS; -} - -DEFUN (ip6_router_isis, - ip6_router_isis_cmd, - "ipv6 router " PROTO_NAME " WORD", - "Interface Internet Protocol config commands\n" - "IP router interface commands\n" - PROTO_HELP - "Routing process tag\n") -{ - return ip_router_isis(self, vty, argc, argv); -} - -DEFUN (no_ip_router_isis, - no_ip_router_isis_cmd, - "no router " PROTO_NAME " WORD", - NO_STR - "Interface Internet Protocol config commands\n" - "IP router interface commands\n" - "IP router interface commands\n" - PROTO_HELP - "Routing process tag\n") -{ - int idx_afi = 1; - int idx_word = 4; - VTY_DECLVAR_CONTEXT(interface, ifp); - struct isis_area *area; - struct isis_circuit *circuit; - const char *af = argv[idx_afi]->arg; - const char *area_tag = argv[idx_word]->arg; - - area = isis_area_lookup(area_tag); - if (!area) { - vty_out(vty, "Can't find ISIS instance %s\n", - argv[idx_afi]->arg); - return CMD_ERR_NO_MATCH; - } - - circuit = circuit_lookup_by_ifp(ifp, area->circuit_list); - if (!circuit) { - vty_out(vty, "ISIS is not enabled on circuit %s\n", ifp->name); - return CMD_ERR_NO_MATCH; - } - - bool ip = circuit->ip_router, ipv6 = circuit->ipv6_router; - if (af[2] != '\0') - ipv6 = false; - else - ip = false; - - isis_circuit_af_set(circuit, ip, ipv6); - return CMD_SUCCESS; -} - -DEFUN (isis_passive, - isis_passive_cmd, - PROTO_NAME " passive", - PROTO_HELP - "Configure the passive mode for interface\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - CMD_FERR_RETURN(isis_circuit_passive_set(circuit, 1), - "Cannot set passive: $ERR"); - return CMD_SUCCESS; -} - -DEFUN (no_isis_passive, - no_isis_passive_cmd, - "no " PROTO_NAME " passive", - NO_STR - PROTO_HELP - "Configure the passive mode for interface\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - CMD_FERR_RETURN(isis_circuit_passive_set(circuit, 0), - "Cannot set no passive: $ERR"); - return CMD_SUCCESS; -} - -DEFUN (isis_circuit_type, - isis_circuit_type_cmd, - PROTO_NAME " circuit-type ", - PROTO_HELP - "Configure circuit type for interface\n" - "Level-1 only adjacencies are formed\n" - "Level-1-2 adjacencies are formed\n" - "Level-2 only adjacencies are formed\n") -{ - int idx_level = 2; - int is_type; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - is_type = string2circuit_t(argv[idx_level]->arg); - if (!is_type) { - vty_out(vty, "Unknown circuit-type \n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (circuit->state == C_STATE_UP - && circuit->area->is_type != IS_LEVEL_1_AND_2 - && circuit->area->is_type != is_type) { - vty_out(vty, "Invalid circuit level for area %s.\n", - circuit->area->area_tag); - return CMD_WARNING_CONFIG_FAILED; - } - isis_circuit_is_type_set(circuit, is_type); - - return CMD_SUCCESS; -} - -DEFUN (no_isis_circuit_type, - no_isis_circuit_type_cmd, - "no " PROTO_NAME " circuit-type ", - NO_STR - PROTO_HELP - "Configure circuit type for interface\n" - "Level-1 only adjacencies are formed\n" - "Level-1-2 adjacencies are formed\n" - "Level-2 only adjacencies are formed\n") -{ - int is_type; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - /* - * Set the circuits level to its default value - */ - if (circuit->state == C_STATE_UP) - is_type = circuit->area->is_type; - else - is_type = IS_LEVEL_1_AND_2; - isis_circuit_is_type_set(circuit, is_type); - - return CMD_SUCCESS; -} - -DEFUN (isis_network, - isis_network_cmd, - PROTO_NAME " network point-to-point", - PROTO_HELP - "Set network type\n" - "point-to-point network type\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - if (isis_circuit_circ_type_set(circuit, CIRCUIT_T_P2P)) { - vty_out(vty, - PROTO_NAME " network point-to-point is valid only on broadcast interfaces\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -DEFUN (no_isis_network, - no_isis_network_cmd, - "no " PROTO_NAME " network point-to-point", - NO_STR - PROTO_HELP - "Set network type for circuit\n" - "point-to-point network type\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - if (isis_circuit_circ_type_set(circuit, CIRCUIT_T_BROADCAST)) { - vty_out(vty, - PROTO_NAME " network point-to-point is valid only on broadcast interfaces\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -DEFUN (isis_passwd, - isis_passwd_cmd, - PROTO_NAME " password WORD", - PROTO_HELP - "Configure the authentication password for a circuit\n" - "HMAC-MD5 authentication\n" - "Cleartext password\n" - "Circuit password\n") -{ - int idx_encryption = 2; - int idx_word = 3; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - ferr_r rv; - - if (!circuit) - return CMD_ERR_NO_MATCH; - - if (argv[idx_encryption]->arg[0] == 'm') - rv = isis_circuit_passwd_hmac_md5_set(circuit, - argv[idx_word]->arg); - else - rv = isis_circuit_passwd_cleartext_set(circuit, - argv[idx_word]->arg); - - CMD_FERR_RETURN(rv, "Failed to set circuit password: $ERR"); - return CMD_SUCCESS; -} - -DEFUN (no_isis_passwd, - no_isis_passwd_cmd, - "no " PROTO_NAME " password [ WORD]", - NO_STR - PROTO_HELP - "Configure the authentication password for a circuit\n" - "HMAC-MD5 authentication\n" - "Cleartext password\n" - "Circuit password\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - CMD_FERR_RETURN(isis_circuit_passwd_unset(circuit), - "Failed to unset circuit password: $ERR"); - return CMD_SUCCESS; -} - - -DEFUN (isis_priority, - isis_priority_cmd, - PROTO_NAME " priority (0-127)", - PROTO_HELP - "Set priority for Designated Router election\n" - "Priority value\n") -{ - int idx_number = 2; - int prio; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - 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_WARNING_CONFIG_FAILED; - } - - circuit->priority[0] = prio; - circuit->priority[1] = prio; - - return CMD_SUCCESS; -} - -DEFUN (no_isis_priority, - no_isis_priority_cmd, - "no " PROTO_NAME " priority [(0-127)]", - NO_STR - PROTO_HELP - "Set priority for Designated Router election\n" - "Priority value\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->priority[0] = DEFAULT_PRIORITY; - circuit->priority[1] = DEFAULT_PRIORITY; - - return CMD_SUCCESS; -} - - -DEFUN (isis_priority_l1, - isis_priority_l1_cmd, - PROTO_NAME " priority (0-127) level-1", - PROTO_HELP - "Set priority for Designated Router election\n" - "Priority value\n" - "Specify priority for level-1 routing\n") -{ - int idx_number = 2; - int prio; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - 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_WARNING_CONFIG_FAILED; - } - - circuit->priority[0] = prio; - - return CMD_SUCCESS; -} - -DEFUN (no_isis_priority_l1, - no_isis_priority_l1_cmd, - "no " PROTO_NAME " priority [(0-127)] level-1", - NO_STR - PROTO_HELP - "Set priority for Designated Router election\n" - "Priority value\n" - "Specify priority for level-1 routing\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->priority[0] = DEFAULT_PRIORITY; - - return CMD_SUCCESS; -} - - -DEFUN (isis_priority_l2, - isis_priority_l2_cmd, - PROTO_NAME " priority (0-127) level-2", - PROTO_HELP - "Set priority for Designated Router election\n" - "Priority value\n" - "Specify priority for level-2 routing\n") -{ - int idx_number = 2; - int prio; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - 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_WARNING_CONFIG_FAILED; - } - - circuit->priority[1] = prio; - - return CMD_SUCCESS; -} - -DEFUN (no_isis_priority_l2, - no_isis_priority_l2_cmd, - "no " PROTO_NAME " priority [(0-127)] level-2", - NO_STR - PROTO_HELP - "Set priority for Designated Router election\n" - "Priority value\n" - "Specify priority for level-2 routing\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->priority[1] = DEFAULT_PRIORITY; - - return CMD_SUCCESS; -} - - -/* Metric command */ -DEFUN (isis_metric, - isis_metric_cmd, - PROTO_NAME " metric (0-16777215)", - PROTO_HELP - "Set default metric for circuit\n" - "Default metric value\n") -{ - int idx_number = 2; - int met; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - met = atoi(argv[idx_number]->arg); - - /* RFC3787 section 5.1 */ - if (circuit->area && circuit->area->oldmetric == 1 - && met > MAX_NARROW_LINK_METRIC) { - vty_out(vty, - "Invalid metric %d - should be <0-63> " - "when narrow metric type enabled\n", - met); - return CMD_WARNING_CONFIG_FAILED; - } - - /* RFC4444 */ - if (circuit->area && circuit->area->newmetric == 1 - && met > MAX_WIDE_LINK_METRIC) { - vty_out(vty, - "Invalid metric %d - should be <0-16777215> " - "when wide metric type enabled\n", - met); - return CMD_WARNING_CONFIG_FAILED; - } - - CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1, met), - "Failed to set L1 metric: $ERR"); - CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2, met), - "Failed to set L2 metric: $ERR"); - return CMD_SUCCESS; -} - - -DEFUN (no_isis_metric, - no_isis_metric_cmd, - "no " PROTO_NAME " metric [(0-16777215)]", - NO_STR - PROTO_HELP - "Set default metric for circuit\n" - "Default metric value\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1, - DEFAULT_CIRCUIT_METRIC), - "Failed to set L1 metric: $ERR"); - CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2, - DEFAULT_CIRCUIT_METRIC), - "Failed to set L2 metric: $ERR"); - return CMD_SUCCESS; -} - - -DEFUN (isis_metric_l1, - isis_metric_l1_cmd, - PROTO_NAME " metric (0-16777215) level-1", - PROTO_HELP - "Set default metric for circuit\n" - "Default metric value\n" - "Specify metric for level-1 routing\n") -{ - int idx_number = 2; - int met; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - met = atoi(argv[idx_number]->arg); - CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1, met), - "Failed to set L1 metric: $ERR"); - return CMD_SUCCESS; -} - - -DEFUN (no_isis_metric_l1, - no_isis_metric_l1_cmd, - "no " PROTO_NAME " metric [(0-16777215)] level-1", - NO_STR - PROTO_HELP - "Set default metric for circuit\n" - "Default metric value\n" - "Specify metric for level-1 routing\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1, - DEFAULT_CIRCUIT_METRIC), - "Failed to set L1 metric: $ERR"); - return CMD_SUCCESS; -} - - -DEFUN (isis_metric_l2, - isis_metric_l2_cmd, - PROTO_NAME " metric (0-16777215) level-2", - PROTO_HELP - "Set default metric for circuit\n" - "Default metric value\n" - "Specify metric for level-2 routing\n") -{ - int idx_number = 2; - int met; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - met = atoi(argv[idx_number]->arg); - CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2, met), - "Failed to set L2 metric: $ERR"); - return CMD_SUCCESS; -} - - -DEFUN (no_isis_metric_l2, - no_isis_metric_l2_cmd, - "no " PROTO_NAME " metric [(0-16777215)] level-2", - NO_STR - PROTO_HELP - "Set default metric for circuit\n" - "Default metric value\n" - "Specify metric for level-2 routing\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2, - DEFAULT_CIRCUIT_METRIC), - "Failed to set L2 metric: $ERR"); - return CMD_SUCCESS; -} - -/* end of metrics */ - -DEFUN (isis_hello_interval, - isis_hello_interval_cmd, - PROTO_NAME " hello-interval (1-600)", - PROTO_HELP - "Set Hello interval\n" - "Holdtime 1 seconds, interval depends on multiplier\n") -{ - int idx_number = 2; - int interval; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - interval = atoi(argv[idx_number]->arg); - if (interval < MIN_HELLO_INTERVAL || interval > MAX_HELLO_INTERVAL) { - vty_out(vty, "Invalid hello-interval %d - should be <1-600>\n", - interval); - return CMD_WARNING_CONFIG_FAILED; - } - - circuit->hello_interval[0] = (uint16_t)interval; - circuit->hello_interval[1] = (uint16_t)interval; - - return CMD_SUCCESS; -} - - -DEFUN (no_isis_hello_interval, - no_isis_hello_interval_cmd, - "no " PROTO_NAME " hello-interval [(1-600)]", - NO_STR - PROTO_HELP - "Set Hello interval\n" - "Holdtime 1 second, interval depends on multiplier\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->hello_interval[0] = DEFAULT_HELLO_INTERVAL; - circuit->hello_interval[1] = DEFAULT_HELLO_INTERVAL; - - return CMD_SUCCESS; -} - - -DEFUN (isis_hello_interval_l1, - isis_hello_interval_l1_cmd, - PROTO_NAME " hello-interval (1-600) level-1", - PROTO_HELP - "Set Hello interval\n" - "Holdtime 1 second, interval depends on multiplier\n" - "Specify hello-interval for level-1 IIHs\n") -{ - int idx_number = 2; - long interval; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - interval = atoi(argv[idx_number]->arg); - if (interval < MIN_HELLO_INTERVAL || interval > MAX_HELLO_INTERVAL) { - vty_out(vty, "Invalid hello-interval %ld - should be <1-600>\n", - interval); - return CMD_WARNING_CONFIG_FAILED; - } - - circuit->hello_interval[0] = (uint16_t)interval; - - return CMD_SUCCESS; -} - - -DEFUN (no_isis_hello_interval_l1, - no_isis_hello_interval_l1_cmd, - "no " PROTO_NAME " hello-interval [(1-600)] level-1", - NO_STR - PROTO_HELP - "Set Hello interval\n" - "Holdtime 1 second, interval depends on multiplier\n" - "Specify hello-interval for level-1 IIHs\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->hello_interval[0] = DEFAULT_HELLO_INTERVAL; - - return CMD_SUCCESS; -} - - -DEFUN (isis_hello_interval_l2, - isis_hello_interval_l2_cmd, - PROTO_NAME " hello-interval (1-600) level-2", - PROTO_HELP - "Set Hello interval\n" - "Holdtime 1 second, interval depends on multiplier\n" - "Specify hello-interval for level-2 IIHs\n") -{ - int idx_number = 2; - long interval; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - interval = atoi(argv[idx_number]->arg); - if (interval < MIN_HELLO_INTERVAL || interval > MAX_HELLO_INTERVAL) { - vty_out(vty, "Invalid hello-interval %ld - should be <1-600>\n", - interval); - return CMD_WARNING_CONFIG_FAILED; - } - - circuit->hello_interval[1] = (uint16_t)interval; - - return CMD_SUCCESS; -} - - -DEFUN (no_isis_hello_interval_l2, - no_isis_hello_interval_l2_cmd, - "no " PROTO_NAME " hello-interval [(1-600)] level-2", - NO_STR - PROTO_HELP - "Set Hello interval\n" - "Holdtime 1 second, interval depends on multiplier\n" - "Specify hello-interval for level-2 IIHs\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->hello_interval[1] = DEFAULT_HELLO_INTERVAL; - - return CMD_SUCCESS; -} - - -DEFUN (isis_hello_multiplier, - isis_hello_multiplier_cmd, - PROTO_NAME " hello-multiplier (2-100)", - PROTO_HELP - "Set multiplier for Hello holding time\n" - "Hello multiplier value\n") -{ - int idx_number = 2; - int mult; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - mult = atoi(argv[idx_number]->arg); - if (mult < MIN_HELLO_MULTIPLIER || mult > MAX_HELLO_MULTIPLIER) { - vty_out(vty, - "Invalid hello-multiplier %d - should be <2-100>\n", - mult); - return CMD_WARNING_CONFIG_FAILED; - } - - circuit->hello_multiplier[0] = (uint16_t)mult; - circuit->hello_multiplier[1] = (uint16_t)mult; - - return CMD_SUCCESS; -} - - -DEFUN (no_isis_hello_multiplier, - no_isis_hello_multiplier_cmd, - "no " PROTO_NAME " hello-multiplier [(2-100)]", - NO_STR - PROTO_HELP - "Set multiplier for Hello holding time\n" - "Hello multiplier value\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->hello_multiplier[0] = DEFAULT_HELLO_MULTIPLIER; - circuit->hello_multiplier[1] = DEFAULT_HELLO_MULTIPLIER; - - return CMD_SUCCESS; -} - - -DEFUN (isis_hello_multiplier_l1, - isis_hello_multiplier_l1_cmd, - PROTO_NAME " hello-multiplier (2-100) level-1", - PROTO_HELP - "Set multiplier for Hello holding time\n" - "Hello multiplier value\n" - "Specify hello multiplier for level-1 IIHs\n") -{ - int idx_number = 2; - int mult; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - mult = atoi(argv[idx_number]->arg); - if (mult < MIN_HELLO_MULTIPLIER || mult > MAX_HELLO_MULTIPLIER) { - vty_out(vty, - "Invalid hello-multiplier %d - should be <2-100>\n", - mult); - return CMD_WARNING_CONFIG_FAILED; - } - - circuit->hello_multiplier[0] = (uint16_t)mult; - - return CMD_SUCCESS; -} - - -DEFUN (no_isis_hello_multiplier_l1, - no_isis_hello_multiplier_l1_cmd, - "no " PROTO_NAME " hello-multiplier [(2-100)] level-1", - NO_STR - PROTO_HELP - "Set multiplier for Hello holding time\n" - "Hello multiplier value\n" - "Specify hello multiplier for level-1 IIHs\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->hello_multiplier[0] = DEFAULT_HELLO_MULTIPLIER; - - return CMD_SUCCESS; -} - - -DEFUN (isis_hello_multiplier_l2, - isis_hello_multiplier_l2_cmd, - PROTO_NAME " hello-multiplier (2-100) level-2", - PROTO_HELP - "Set multiplier for Hello holding time\n" - "Hello multiplier value\n" - "Specify hello multiplier for level-2 IIHs\n") -{ - int idx_number = 2; - int mult; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - mult = atoi(argv[idx_number]->arg); - if (mult < MIN_HELLO_MULTIPLIER || mult > MAX_HELLO_MULTIPLIER) { - vty_out(vty, - "Invalid hello-multiplier %d - should be <2-100>\n", - mult); - return CMD_WARNING_CONFIG_FAILED; - } - - circuit->hello_multiplier[1] = (uint16_t)mult; - - return CMD_SUCCESS; -} - - -DEFUN (no_isis_hello_multiplier_l2, - no_isis_hello_multiplier_l2_cmd, - "no " PROTO_NAME " hello-multiplier [(2-100)] level-2", - NO_STR - PROTO_HELP - "Set multiplier for Hello holding time\n" - "Hello multiplier value\n" - "Specify hello multiplier for level-2 IIHs\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->hello_multiplier[1] = DEFAULT_HELLO_MULTIPLIER; - - return CMD_SUCCESS; -} - - -DEFUN (isis_hello_padding, - isis_hello_padding_cmd, - PROTO_NAME " hello padding", - PROTO_HELP - "Add padding to IS-IS hello packets\n" - "Pad hello packets\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->pad_hellos = 1; - - return CMD_SUCCESS; -} - -DEFUN (no_isis_hello_padding, - no_isis_hello_padding_cmd, - "no " PROTO_NAME " hello padding", - NO_STR - PROTO_HELP - "Add padding to IS-IS hello packets\n" - "Pad hello packets\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->pad_hellos = 0; - - return CMD_SUCCESS; -} - -DEFUN (isis_threeway_adj, - isis_threeway_adj_cmd, - "[no] isis three-way-handshake", - NO_STR - "IS-IS commands\n" - "Enable/Disable three-way handshake\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->disable_threeway_adj = !strcmp(argv[0]->text, "no"); - return CMD_SUCCESS; -} - -DEFUN (csnp_interval, - csnp_interval_cmd, - PROTO_NAME " csnp-interval (1-600)", - PROTO_HELP - "Set CSNP interval in seconds\n" - "CSNP interval value\n") -{ - int idx_number = 2; - unsigned long interval; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - interval = atol(argv[idx_number]->arg); - if (interval < MIN_CSNP_INTERVAL || interval > MAX_CSNP_INTERVAL) { - vty_out(vty, "Invalid csnp-interval %lu - should be <1-600>\n", - interval); - return CMD_WARNING_CONFIG_FAILED; - } - - circuit->csnp_interval[0] = (uint16_t)interval; - circuit->csnp_interval[1] = (uint16_t)interval; - - return CMD_SUCCESS; -} - - -DEFUN (no_csnp_interval, - no_csnp_interval_cmd, - "no " PROTO_NAME " csnp-interval [(1-600)]", - NO_STR - PROTO_HELP - "Set CSNP interval in seconds\n" - "CSNP interval value\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->csnp_interval[0] = DEFAULT_CSNP_INTERVAL; - circuit->csnp_interval[1] = DEFAULT_CSNP_INTERVAL; - - return CMD_SUCCESS; -} - - -DEFUN (csnp_interval_l1, - csnp_interval_l1_cmd, - PROTO_NAME " csnp-interval (1-600) level-1", - PROTO_HELP - "Set CSNP interval in seconds\n" - "CSNP interval value\n" - "Specify interval for level-1 CSNPs\n") -{ - int idx_number = 2; - unsigned long interval; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - interval = atol(argv[idx_number]->arg); - if (interval < MIN_CSNP_INTERVAL || interval > MAX_CSNP_INTERVAL) { - vty_out(vty, "Invalid csnp-interval %lu - should be <1-600>\n", - interval); - return CMD_WARNING_CONFIG_FAILED; - } - - circuit->csnp_interval[0] = (uint16_t)interval; - - return CMD_SUCCESS; -} - - -DEFUN (no_csnp_interval_l1, - no_csnp_interval_l1_cmd, - "no " PROTO_NAME " csnp-interval [(1-600)] level-1", - NO_STR - PROTO_HELP - "Set CSNP interval in seconds\n" - "CSNP interval value\n" - "Specify interval for level-1 CSNPs\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->csnp_interval[0] = DEFAULT_CSNP_INTERVAL; - - return CMD_SUCCESS; -} - - -DEFUN (csnp_interval_l2, - csnp_interval_l2_cmd, - PROTO_NAME " csnp-interval (1-600) level-2", - PROTO_HELP - "Set CSNP interval in seconds\n" - "CSNP interval value\n" - "Specify interval for level-2 CSNPs\n") -{ - int idx_number = 2; - unsigned long interval; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - interval = atol(argv[idx_number]->arg); - if (interval < MIN_CSNP_INTERVAL || interval > MAX_CSNP_INTERVAL) { - vty_out(vty, "Invalid csnp-interval %lu - should be <1-600>\n", - interval); - return CMD_WARNING_CONFIG_FAILED; - } - - circuit->csnp_interval[1] = (uint16_t)interval; - - return CMD_SUCCESS; -} - - -DEFUN (no_csnp_interval_l2, - no_csnp_interval_l2_cmd, - "no " PROTO_NAME " csnp-interval [(1-600)] level-2", - NO_STR - PROTO_HELP - "Set CSNP interval in seconds\n" - "CSNP interval value\n" - "Specify interval for level-2 CSNPs\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->csnp_interval[1] = DEFAULT_CSNP_INTERVAL; - - return CMD_SUCCESS; -} - - -DEFUN (psnp_interval, - psnp_interval_cmd, - PROTO_NAME " psnp-interval (1-120)", - PROTO_HELP - "Set PSNP interval in seconds\n" - "PSNP interval value\n") -{ - int idx_number = 2; - unsigned long interval; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - interval = atol(argv[idx_number]->arg); - if (interval < MIN_PSNP_INTERVAL || interval > MAX_PSNP_INTERVAL) { - vty_out(vty, "Invalid psnp-interval %lu - should be <1-120>\n", - interval); - return CMD_WARNING_CONFIG_FAILED; - } - - circuit->psnp_interval[0] = (uint16_t)interval; - circuit->psnp_interval[1] = (uint16_t)interval; - - return CMD_SUCCESS; -} - - -DEFUN (no_psnp_interval, - no_psnp_interval_cmd, - "no " PROTO_NAME " psnp-interval [(1-120)]", - NO_STR - PROTO_HELP - "Set PSNP interval in seconds\n" - "PSNP interval value\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->psnp_interval[0] = DEFAULT_PSNP_INTERVAL; - circuit->psnp_interval[1] = DEFAULT_PSNP_INTERVAL; - - return CMD_SUCCESS; -} - - -DEFUN (psnp_interval_l1, - psnp_interval_l1_cmd, - PROTO_NAME " psnp-interval (1-120) level-1", - PROTO_HELP - "Set PSNP interval in seconds\n" - "PSNP interval value\n" - "Specify interval for level-1 PSNPs\n") -{ - int idx_number = 2; - unsigned long interval; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - interval = atol(argv[idx_number]->arg); - if (interval < MIN_PSNP_INTERVAL || interval > MAX_PSNP_INTERVAL) { - vty_out(vty, "Invalid psnp-interval %lu - should be <1-120>\n", - interval); - return CMD_WARNING_CONFIG_FAILED; - } - - circuit->psnp_interval[0] = (uint16_t)interval; - - return CMD_SUCCESS; -} - - -DEFUN (no_psnp_interval_l1, - no_psnp_interval_l1_cmd, - "no " PROTO_NAME " psnp-interval [(1-120)] level-1", - NO_STR - PROTO_HELP - "Set PSNP interval in seconds\n" - "PSNP interval value\n" - "Specify interval for level-1 PSNPs\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->psnp_interval[0] = DEFAULT_PSNP_INTERVAL; - - return CMD_SUCCESS; -} - - -DEFUN (psnp_interval_l2, - psnp_interval_l2_cmd, - PROTO_NAME " psnp-interval (1-120) level-2", - PROTO_HELP - "Set PSNP interval in seconds\n" - "PSNP interval value\n" - "Specify interval for level-2 PSNPs\n") -{ - int idx_number = 2; - unsigned long interval; - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - interval = atol(argv[idx_number]->arg); - if (interval < MIN_PSNP_INTERVAL || interval > MAX_PSNP_INTERVAL) { - vty_out(vty, "Invalid psnp-interval %lu - should be <1-120>\n", - interval); - return CMD_WARNING_CONFIG_FAILED; - } - - circuit->psnp_interval[1] = (uint16_t)interval; - - return CMD_SUCCESS; -} - - -DEFUN (no_psnp_interval_l2, - no_psnp_interval_l2_cmd, - "no " PROTO_NAME " psnp-interval [(1-120)] level-2", - NO_STR - PROTO_HELP - "Set PSNP interval in seconds\n" - "PSNP interval value\n" - "Specify interval for level-2 PSNPs\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - - circuit->psnp_interval[1] = DEFAULT_PSNP_INTERVAL; - - return CMD_SUCCESS; -} - -DEFUN (circuit_topology, - circuit_topology_cmd, - PROTO_NAME " topology " ISIS_MT_NAMES, - PROTO_HELP - "Configure interface IS-IS topologies\n" - ISIS_MT_DESCRIPTIONS) -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - const char *arg = argv[2]->arg; - uint16_t mtid = isis_str2mtid(arg); - - if (circuit->area && circuit->area->oldmetric) { - vty_out(vty, - "Multi topology IS-IS can only be used with wide metrics\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (mtid == (uint16_t)-1) { - vty_out(vty, "Don't know topology '%s'\n", arg); - return CMD_WARNING_CONFIG_FAILED; - } - - return isis_circuit_mt_enabled_set(circuit, mtid, true); -} - -DEFUN (no_circuit_topology, - no_circuit_topology_cmd, - "no " PROTO_NAME " topology " ISIS_MT_NAMES, - NO_STR - PROTO_HELP - "Configure interface IS-IS topologies\n" - ISIS_MT_DESCRIPTIONS) -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - if (!circuit) - return CMD_ERR_NO_MATCH; - const char *arg = argv[3]->arg; - uint16_t mtid = isis_str2mtid(arg); - - if (circuit->area && circuit->area->oldmetric) { - vty_out(vty, - "Multi topology IS-IS can only be used with wide metrics\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (mtid == (uint16_t)-1) { - vty_out(vty, "Don't know topology '%s'\n", arg); - return CMD_WARNING_CONFIG_FAILED; - } - - return isis_circuit_mt_enabled_set(circuit, mtid, false); -} - -static int validate_metric_style_narrow(struct vty *vty, struct isis_area *area) -{ - struct isis_circuit *circuit; - struct listnode *node; - - if (!vty) - return CMD_WARNING_CONFIG_FAILED; - - if (!area) { - vty_out(vty, "ISIS area is invalid\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { - if ((area->is_type & IS_LEVEL_1) - && (circuit->is_type & IS_LEVEL_1) - && (circuit->te_metric[0] > MAX_NARROW_LINK_METRIC)) { - vty_out(vty, "ISIS circuit %s metric is invalid\n", - circuit->interface->name); - 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_WARNING_CONFIG_FAILED; - } - } - - return CMD_SUCCESS; -} - -DEFUN (metric_style, - metric_style_cmd, - "metric-style ", - "Use old-style (ISO 10589) or new-style packet formats\n" - "Use old style of TLVs with narrow metric\n" - "Send and accept both styles of TLVs during transition\n" - "Use new style of TLVs to carry wider metric\n") -{ - int idx_metric_style = 1; - VTY_DECLVAR_CONTEXT(isis_area, area); - int ret; - - if (strncmp(argv[idx_metric_style]->arg, "w", 1) == 0) { - isis_area_metricstyle_set(area, false, true); - return CMD_SUCCESS; - } - - if (area_is_mt(area)) { - vty_out(vty, - "Narrow metrics cannot be used while multi topology IS-IS is active\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = validate_metric_style_narrow(vty, area); - if (ret != CMD_SUCCESS) - return ret; - - if (strncmp(argv[idx_metric_style]->arg, "t", 1) == 0) - isis_area_metricstyle_set(area, true, true); - else if (strncmp(argv[idx_metric_style]->arg, "n", 1) == 0) - isis_area_metricstyle_set(area, true, false); - return CMD_SUCCESS; - - return CMD_SUCCESS; -} - -DEFUN (no_metric_style, - no_metric_style_cmd, - "no metric-style", - NO_STR - "Use old-style (ISO 10589) or new-style packet formats\n") -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - int ret; - - if (area_is_mt(area)) { - vty_out(vty, - "Narrow metrics cannot be used while multi topology IS-IS is active\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = validate_metric_style_narrow(vty, area); - if (ret != CMD_SUCCESS) - return ret; - - isis_area_metricstyle_set(area, true, false); - return CMD_SUCCESS; -} - -DEFUN (set_overload_bit, - set_overload_bit_cmd, - "set-overload-bit", - "Set overload bit to avoid any transit traffic\n") -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - - isis_area_overload_bit_set(area, true); - return CMD_SUCCESS; -} - -DEFUN (no_set_overload_bit, - no_set_overload_bit_cmd, - "no set-overload-bit", - "Reset overload bit to accept transit traffic\n" - "Reset overload bit\n") -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - - isis_area_overload_bit_set(area, false); - return CMD_SUCCESS; -} - -DEFUN (set_attached_bit, - set_attached_bit_cmd, - "set-attached-bit", - "Set attached bit to identify as L1/L2 router for inter-area traffic\n") -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - - isis_area_attached_bit_set(area, true); - return CMD_SUCCESS; -} - -DEFUN (no_set_attached_bit, - no_set_attached_bit_cmd, - "no set-attached-bit", - NO_STR - "Reset attached bit\n") -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - - isis_area_attached_bit_set(area, false); - return CMD_SUCCESS; -} - -DEFUN (dynamic_hostname, - dynamic_hostname_cmd, - "hostname dynamic", - "Dynamic hostname for IS-IS\n" - "Dynamic hostname\n") -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - - isis_area_dynhostname_set(area, true); - return CMD_SUCCESS; -} - -DEFUN (no_dynamic_hostname, - no_dynamic_hostname_cmd, - "no hostname dynamic", - NO_STR - "Dynamic hostname for IS-IS\n" - "Dynamic hostname\n") -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - - isis_area_dynhostname_set(area, false); - return CMD_SUCCESS; -} - -static int area_lsp_mtu_set(struct vty *vty, unsigned int lsp_mtu) -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - struct listnode *node; - struct isis_circuit *circuit; - - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { - if (circuit->state != C_STATE_INIT - && circuit->state != C_STATE_UP) - continue; - if (lsp_mtu > isis_circuit_pdu_size(circuit)) { - vty_out(vty, - "ISIS area contains circuit %s, which has a maximum PDU size of %zu.\n", - circuit->interface->name, - isis_circuit_pdu_size(circuit)); - return CMD_WARNING_CONFIG_FAILED; - } - } - - isis_area_lsp_mtu_set(area, lsp_mtu); - return CMD_SUCCESS; -} - -DEFUN (area_lsp_mtu, - area_lsp_mtu_cmd, - "lsp-mtu (128-4352)", - "Configure the maximum size of generated LSPs\n" - "Maximum size of generated LSPs\n") -{ - int idx_number = 1; - unsigned int lsp_mtu; - - lsp_mtu = strtoul(argv[idx_number]->arg, NULL, 10); - - return area_lsp_mtu_set(vty, lsp_mtu); -} - - -DEFUN (no_area_lsp_mtu, - no_area_lsp_mtu_cmd, - "no lsp-mtu [(128-4352)]", - NO_STR - "Configure the maximum size of generated LSPs\n" - "Maximum size of generated LSPs\n") -{ - return area_lsp_mtu_set(vty, DEFAULT_LSP_MTU); -} - - -DEFUN (is_type, - is_type_cmd, - "is-type ", - "IS Level for this routing process (OSI only)\n" - "Act as a station router only\n" - "Act as both a station router and an area router\n" - "Act as an area router only\n") -{ - int idx_level = 1; - VTY_DECLVAR_CONTEXT(isis_area, area); - int type; - - type = string2circuit_t(argv[idx_level]->arg); - if (!type) { - vty_out(vty, "Unknown IS level \n"); - return CMD_SUCCESS; - } - - isis_area_is_type_set(area, type); - - return CMD_SUCCESS; -} - -DEFUN (no_is_type, - no_is_type_cmd, - "no is-type ", - NO_STR - "IS Level for this routing process (OSI only)\n" - "Act as a station router only\n" - "Act as both a station router and an area router\n" - "Act as an area router only\n") -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - int type; - - /* - * Put the is-type back to defaults: - * - level-1-2 on first area - * - level-1 for the rest - */ - if (listgetdata(listhead(isis->area_list)) == area) - type = IS_LEVEL_1_AND_2; - else - type = IS_LEVEL_1; - - isis_area_is_type_set(area, type); - - return CMD_SUCCESS; -} - -static int set_lsp_gen_interval(struct vty *vty, struct isis_area *area, - uint16_t interval, int level) -{ - int lvl; - - for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) { - if (!(lvl & level)) - continue; - - if (interval >= area->lsp_refresh[lvl - 1]) { - vty_out(vty, - "LSP gen interval %us must be less than " - "the LSP refresh interval %us\n", - interval, area->lsp_refresh[lvl - 1]); - return CMD_WARNING_CONFIG_FAILED; - } - } - - for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) { - if (!(lvl & level)) - continue; - area->lsp_gen_interval[lvl - 1] = interval; - } - - return CMD_SUCCESS; -} - -DEFUN (lsp_gen_interval, - lsp_gen_interval_cmd, - "lsp-gen-interval [] (1-120)", - "Minimum interval between regenerating same LSP\n" - "Set interval for level 1 only\n" - "Set interval for level 2 only\n" - "Minimum interval in seconds\n") -{ - int idx = 0; - VTY_DECLVAR_CONTEXT(isis_area, area); - uint16_t interval; - int level; - - level = 0; - level |= argv_find(argv, argc, "level-1", &idx) ? IS_LEVEL_1 : 0; - level |= argv_find(argv, argc, "level-2", &idx) ? IS_LEVEL_2 : 0; - if (!level) - level = IS_LEVEL_1 | IS_LEVEL_2; - - argv_find(argv, argc, "(1-120)", &idx); - - interval = atoi(argv[idx]->arg); - return set_lsp_gen_interval(vty, area, interval, level); -} - -DEFUN (no_lsp_gen_interval, - no_lsp_gen_interval_cmd, - "no lsp-gen-interval [] [(1-120)]", - NO_STR - "Minimum interval between regenerating same LSP\n" - "Set interval for level 1 only\n" - "Set interval for level 2 only\n" - "Minimum interval in seconds\n") -{ - int idx = 0; - VTY_DECLVAR_CONTEXT(isis_area, area); - uint16_t interval; - int level; - - level = 0; - level |= argv_find(argv, argc, "level-1", &idx) ? IS_LEVEL_1 : 0; - level |= argv_find(argv, argc, "level-2", &idx) ? IS_LEVEL_2 : 0; - if (!level) - level = IS_LEVEL_1 | IS_LEVEL_2; - - interval = DEFAULT_MIN_LSP_GEN_INTERVAL; - return set_lsp_gen_interval(vty, area, interval, level); -} - -DEFUN (spf_interval, - spf_interval_cmd, - "spf-interval (1-120)", - "Minimum interval between SPF calculations\n" - "Minimum interval between consecutive SPFs in seconds\n") -{ - int idx_number = 1; - VTY_DECLVAR_CONTEXT(isis_area, area); - uint16_t interval; - - interval = atoi(argv[idx_number]->arg); - area->min_spf_interval[0] = interval; - area->min_spf_interval[1] = interval; - - return CMD_SUCCESS; -} - - -DEFUN (no_spf_interval, - no_spf_interval_cmd, - "no spf-interval [[] (1-120)]", - NO_STR - "Minimum interval between SPF calculations\n" - "Set interval for level 1 only\n" - "Set interval for level 2 only\n" - "Minimum interval between consecutive SPFs in seconds\n") -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - - area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL; - area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL; - - return CMD_SUCCESS; -} - - -DEFUN (spf_interval_l1, - spf_interval_l1_cmd, - "spf-interval level-1 (1-120)", - "Minimum interval between SPF calculations\n" - "Set interval for level 1 only\n" - "Minimum interval between consecutive SPFs in seconds\n") -{ - int idx_number = 2; - VTY_DECLVAR_CONTEXT(isis_area, area); - uint16_t interval; - - interval = atoi(argv[idx_number]->arg); - area->min_spf_interval[0] = interval; - - return CMD_SUCCESS; -} - -DEFUN (no_spf_interval_l1, - no_spf_interval_l1_cmd, - "no spf-interval level-1", - NO_STR - "Minimum interval between SPF calculations\n" - "Set interval for level 1 only\n") -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - - area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL; - - return CMD_SUCCESS; -} - - -DEFUN (spf_interval_l2, - spf_interval_l2_cmd, - "spf-interval level-2 (1-120)", - "Minimum interval between SPF calculations\n" - "Set interval for level 2 only\n" - "Minimum interval between consecutive SPFs in seconds\n") -{ - int idx_number = 2; - VTY_DECLVAR_CONTEXT(isis_area, area); - uint16_t interval; - - interval = atoi(argv[idx_number]->arg); - area->min_spf_interval[1] = interval; - - return CMD_SUCCESS; -} - -DEFUN (no_spf_interval_l2, - no_spf_interval_l2_cmd, - "no spf-interval level-2", - NO_STR - "Minimum interval between SPF calculations\n" - "Set interval for level 2 only\n") -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - - area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL; - - return CMD_SUCCESS; -} - -DEFUN (no_spf_delay_ietf, - no_spf_delay_ietf_cmd, - "no spf-delay-ietf", - NO_STR - "IETF SPF delay algorithm\n") -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - - spf_backoff_free(area->spf_delay_ietf[0]); - spf_backoff_free(area->spf_delay_ietf[1]); - area->spf_delay_ietf[0] = NULL; - area->spf_delay_ietf[1] = NULL; - - return CMD_SUCCESS; -} - -DEFUN (spf_delay_ietf, - spf_delay_ietf_cmd, - "spf-delay-ietf init-delay (0-60000) short-delay (0-60000) long-delay (0-60000) holddown (0-60000) time-to-learn (0-60000)", - "IETF SPF delay algorithm\n" - "Delay used while in QUIET state\n" - "Delay used while in QUIET state in milliseconds\n" - "Delay used while in SHORT_WAIT state\n" - "Delay used while in SHORT_WAIT state in milliseconds\n" - "Delay used while in LONG_WAIT\n" - "Delay used while in LONG_WAIT state in milliseconds\n" - "Time with no received IGP events before considering IGP stable\n" - "Time with no received IGP events before considering IGP stable (in milliseconds)\n" - "Maximum duration needed to learn all the events related to a single failure\n" - "Maximum duration needed to learn all the events related to a single failure (in milliseconds)\n") -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - - long init_delay = atol(argv[2]->arg); - long short_delay = atol(argv[4]->arg); - long long_delay = atol(argv[6]->arg); - long holddown = atol(argv[8]->arg); - long timetolearn = atol(argv[10]->arg); - - size_t bufsiz = strlen(area->area_tag) + sizeof("IS-IS Lx"); - char *buf = XCALLOC(MTYPE_TMP, bufsiz); - - snprintf(buf, bufsiz, "IS-IS %s L1", area->area_tag); - spf_backoff_free(area->spf_delay_ietf[0]); - area->spf_delay_ietf[0] = - spf_backoff_new(master, buf, init_delay, short_delay, - long_delay, holddown, timetolearn); - - snprintf(buf, bufsiz, "IS-IS %s L2", area->area_tag); - spf_backoff_free(area->spf_delay_ietf[1]); - area->spf_delay_ietf[1] = - spf_backoff_new(master, buf, init_delay, short_delay, - long_delay, holddown, timetolearn); - - XFREE(MTYPE_TMP, buf); - return CMD_SUCCESS; -} - -static int area_max_lsp_lifetime_set(struct vty *vty, int level, - uint16_t interval) -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - int lvl; - uint16_t refresh_interval = interval - 300; - int set_refresh_interval[ISIS_LEVELS] = {0, 0}; - - for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) { - if (!(lvl & level)) - continue; - - if (refresh_interval < area->lsp_refresh[lvl - 1]) { - vty_out(vty, - "Level %d Max LSP lifetime %us must be 300s greater than " - "the configured LSP refresh interval %us\n", - lvl, interval, area->lsp_refresh[lvl - 1]); - vty_out(vty, - "Automatically reducing level %d LSP refresh interval " - "to %us\n", - lvl, refresh_interval); - set_refresh_interval[lvl - 1] = 1; - - if (refresh_interval - <= area->lsp_gen_interval[lvl - 1]) { - vty_out(vty, - "LSP refresh interval %us must be greater than " - "the configured LSP gen interval %us\n", - refresh_interval, - area->lsp_gen_interval[lvl - 1]); - return CMD_WARNING_CONFIG_FAILED; - } - } - } - - for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) { - if (!(lvl & level)) - continue; - isis_area_max_lsp_lifetime_set(area, lvl, interval); - if (set_refresh_interval[lvl - 1]) - isis_area_lsp_refresh_set(area, lvl, refresh_interval); - } - - return CMD_SUCCESS; -} - -DEFUN (max_lsp_lifetime, - max_lsp_lifetime_cmd, - "max-lsp-lifetime [] (350-65535)", - "Maximum LSP lifetime\n" - "Maximum LSP lifetime for Level 1 only\n" - "Maximum LSP lifetime for Level 2 only\n" - "LSP lifetime in seconds\n") -{ - int idx = 0; - unsigned int level = IS_LEVEL_1_AND_2; - - if (argv_find(argv, argc, "level-1", &idx)) - level = IS_LEVEL_1; - else if (argv_find(argv, argc, "level-2", &idx)) - level = IS_LEVEL_2; - - argv_find(argv, argc, "(350-65535)", &idx); - int lifetime = atoi(argv[idx]->arg); - - return area_max_lsp_lifetime_set(vty, level, lifetime); -} - - -DEFUN (no_max_lsp_lifetime, - no_max_lsp_lifetime_cmd, - "no max-lsp-lifetime [] [(350-65535)]", - NO_STR - "Maximum LSP lifetime\n" - "Maximum LSP lifetime for Level 1 only\n" - "Maximum LSP lifetime for Level 2 only\n" - "LSP lifetime in seconds\n") -{ - int idx = 0; - unsigned int level = IS_LEVEL_1_AND_2; - - if (argv_find(argv, argc, "level-1", &idx)) - level = IS_LEVEL_1; - else if (argv_find(argv, argc, "level-2", &idx)) - level = IS_LEVEL_2; - - return area_max_lsp_lifetime_set(vty, level, DEFAULT_LSP_LIFETIME); -} - -static int area_lsp_refresh_interval_set(struct vty *vty, int level, - uint16_t interval) -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - int lvl; - - for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) { - if (!(lvl & level)) - continue; - if (interval <= area->lsp_gen_interval[lvl - 1]) { - vty_out(vty, - "LSP refresh interval %us must be greater than " - "the configured LSP gen interval %us\n", - interval, area->lsp_gen_interval[lvl - 1]); - 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_WARNING_CONFIG_FAILED; - } - } - - for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) { - if (!(lvl & level)) - continue; - isis_area_lsp_refresh_set(area, lvl, interval); - } - - return CMD_SUCCESS; -} - -DEFUN (lsp_refresh_interval, - lsp_refresh_interval_cmd, - "lsp-refresh-interval [] (1-65235)", - "LSP refresh interval\n" - "LSP refresh interval for Level 1 only\n" - "LSP refresh interval for Level 2 only\n" - "LSP refresh interval in seconds\n") -{ - int idx = 0; - unsigned int level = IS_LEVEL_1_AND_2; - unsigned int interval = 0; - - if (argv_find(argv, argc, "level-1", &idx)) - level = IS_LEVEL_1; - else if (argv_find(argv, argc, "level-2", &idx)) - level = IS_LEVEL_2; - - interval = atoi(argv[argc - 1]->arg); - return area_lsp_refresh_interval_set(vty, level, interval); -} - -DEFUN (no_lsp_refresh_interval, - no_lsp_refresh_interval_cmd, - "no lsp-refresh-interval [] [(1-65235)]", - NO_STR - "LSP refresh interval\n" - "LSP refresh interval for Level 1 only\n" - "LSP refresh interval for Level 2 only\n" - "LSP refresh interval in seconds\n") -{ - int idx = 0; - unsigned int level = IS_LEVEL_1_AND_2; - - if (argv_find(argv, argc, "level-1", &idx)) - level = IS_LEVEL_1; - else if (argv_find(argv, argc, "level-2", &idx)) - level = IS_LEVEL_2; - - return area_lsp_refresh_interval_set(vty, level, - DEFAULT_MAX_LSP_GEN_INTERVAL); -} - -static int area_passwd_set(struct vty *vty, int level, - int (*type_set)(struct isis_area *area, int level, - const char *passwd, - uint8_t snp_auth), - const char *passwd, uint8_t snp_auth) -{ - VTY_DECLVAR_CONTEXT(isis_area, area); - - if (passwd && strlen(passwd) > 254) { - vty_out(vty, "Too long area password (>254)\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - type_set(area, level, passwd, snp_auth); - return CMD_SUCCESS; -} - - -DEFUN (area_passwd_md5, - area_passwd_md5_cmd, - "area-password md5 WORD [authenticate snp ]", - "Configure the authentication password for an area\n" - "Authentication type\n" - "Level-wide password\n" - "Authentication\n" - "SNP PDUs\n" - "Send but do not check PDUs on receiving\n" - "Send and check PDUs on receiving\n") -{ - int idx_password = 0; - int idx_word = 2; - int idx_type = 5; - uint8_t snp_auth = 0; - int level = strmatch(argv[idx_password]->text, "domain-password") - ? IS_LEVEL_2 - : IS_LEVEL_1; - - if (argc > 3) { - snp_auth = SNP_AUTH_SEND; - if (strmatch(argv[idx_type]->text, "validate")) - snp_auth |= SNP_AUTH_RECV; - } - - return area_passwd_set(vty, level, isis_area_passwd_hmac_md5_set, - argv[idx_word]->arg, snp_auth); -} - -DEFUN (domain_passwd_md5, - domain_passwd_md5_cmd, - "domain-password md5 WORD [authenticate snp ]", - "Set the authentication password for a routing domain\n" - "Authentication type\n" - "Level-wide password\n" - "Authentication\n" - "SNP PDUs\n" - "Send but do not check PDUs on receiving\n" - "Send and check PDUs on receiving\n") -{ - return area_passwd_md5(self, vty, argc, argv); -} - -DEFUN (area_passwd_clear, - area_passwd_clear_cmd, - "area-password clear WORD [authenticate snp ]", - "Configure the authentication password for an area\n" - "Authentication type\n" - "Area password\n" - "Authentication\n" - "SNP PDUs\n" - "Send but do not check PDUs on receiving\n" - "Send and check PDUs on receiving\n") -{ - int idx_password = 0; - int idx_word = 2; - int idx_type = 5; - uint8_t snp_auth = 0; - int level = strmatch(argv[idx_password]->text, "domain-password") - ? IS_LEVEL_2 - : IS_LEVEL_1; - - if (argc > 3) { - snp_auth = SNP_AUTH_SEND; - if (strmatch(argv[idx_type]->text, "validate")) - snp_auth |= SNP_AUTH_RECV; - } - - return area_passwd_set(vty, level, isis_area_passwd_cleartext_set, - argv[idx_word]->arg, snp_auth); -} - -DEFUN (domain_passwd_clear, - domain_passwd_clear_cmd, - "domain-password clear WORD [authenticate snp ]", - "Set the authentication password for a routing domain\n" - "Authentication type\n" - "Area password\n" - "Authentication\n" - "SNP PDUs\n" - "Send but do not check PDUs on receiving\n" - "Send and check PDUs on receiving\n") -{ - return area_passwd_clear(self, vty, argc, argv); -} - -DEFUN (no_area_passwd, - no_area_passwd_cmd, - "no ", - NO_STR - "Configure the authentication password for an area\n" - "Set the authentication password for a routing domain\n") -{ - int idx_password = 1; - int level = strmatch(argv[idx_password]->text, "domain-password") - ? IS_LEVEL_2 - : IS_LEVEL_1; - VTY_DECLVAR_CONTEXT(isis_area, area); - - return isis_area_passwd_unset(area, level); -} - -void isis_vty_init(void) -{ - install_element(INTERFACE_NODE, &ip_router_isis_cmd); - install_element(INTERFACE_NODE, &ip6_router_isis_cmd); - install_element(INTERFACE_NODE, &no_ip_router_isis_cmd); - - install_element(INTERFACE_NODE, &isis_passive_cmd); - install_element(INTERFACE_NODE, &no_isis_passive_cmd); - - install_element(INTERFACE_NODE, &isis_circuit_type_cmd); - install_element(INTERFACE_NODE, &no_isis_circuit_type_cmd); - - install_element(INTERFACE_NODE, &isis_network_cmd); - install_element(INTERFACE_NODE, &no_isis_network_cmd); - - install_element(INTERFACE_NODE, &isis_passwd_cmd); - install_element(INTERFACE_NODE, &no_isis_passwd_cmd); - - install_element(INTERFACE_NODE, &isis_priority_cmd); - install_element(INTERFACE_NODE, &no_isis_priority_cmd); - install_element(INTERFACE_NODE, &isis_priority_l1_cmd); - install_element(INTERFACE_NODE, &no_isis_priority_l1_cmd); - install_element(INTERFACE_NODE, &isis_priority_l2_cmd); - install_element(INTERFACE_NODE, &no_isis_priority_l2_cmd); - - install_element(INTERFACE_NODE, &isis_metric_cmd); - install_element(INTERFACE_NODE, &no_isis_metric_cmd); - install_element(INTERFACE_NODE, &isis_metric_l1_cmd); - install_element(INTERFACE_NODE, &no_isis_metric_l1_cmd); - install_element(INTERFACE_NODE, &isis_metric_l2_cmd); - install_element(INTERFACE_NODE, &no_isis_metric_l2_cmd); - - install_element(INTERFACE_NODE, &isis_hello_interval_cmd); - install_element(INTERFACE_NODE, &no_isis_hello_interval_cmd); - install_element(INTERFACE_NODE, &isis_hello_interval_l1_cmd); - install_element(INTERFACE_NODE, &no_isis_hello_interval_l1_cmd); - install_element(INTERFACE_NODE, &isis_hello_interval_l2_cmd); - install_element(INTERFACE_NODE, &no_isis_hello_interval_l2_cmd); - - install_element(INTERFACE_NODE, &isis_hello_multiplier_cmd); - install_element(INTERFACE_NODE, &no_isis_hello_multiplier_cmd); - install_element(INTERFACE_NODE, &isis_hello_multiplier_l1_cmd); - install_element(INTERFACE_NODE, &no_isis_hello_multiplier_l1_cmd); - install_element(INTERFACE_NODE, &isis_hello_multiplier_l2_cmd); - install_element(INTERFACE_NODE, &no_isis_hello_multiplier_l2_cmd); - - install_element(INTERFACE_NODE, &isis_hello_padding_cmd); - install_element(INTERFACE_NODE, &no_isis_hello_padding_cmd); - - install_element(INTERFACE_NODE, &isis_threeway_adj_cmd); - - install_element(INTERFACE_NODE, &csnp_interval_cmd); - install_element(INTERFACE_NODE, &no_csnp_interval_cmd); - install_element(INTERFACE_NODE, &csnp_interval_l1_cmd); - install_element(INTERFACE_NODE, &no_csnp_interval_l1_cmd); - install_element(INTERFACE_NODE, &csnp_interval_l2_cmd); - install_element(INTERFACE_NODE, &no_csnp_interval_l2_cmd); - - install_element(INTERFACE_NODE, &psnp_interval_cmd); - install_element(INTERFACE_NODE, &no_psnp_interval_cmd); - install_element(INTERFACE_NODE, &psnp_interval_l1_cmd); - install_element(INTERFACE_NODE, &no_psnp_interval_l1_cmd); - install_element(INTERFACE_NODE, &psnp_interval_l2_cmd); - install_element(INTERFACE_NODE, &no_psnp_interval_l2_cmd); - - install_element(INTERFACE_NODE, &circuit_topology_cmd); - install_element(INTERFACE_NODE, &no_circuit_topology_cmd); - - install_element(ROUTER_NODE, &metric_style_cmd); - install_element(ROUTER_NODE, &no_metric_style_cmd); - - install_element(ROUTER_NODE, &set_overload_bit_cmd); - install_element(ROUTER_NODE, &no_set_overload_bit_cmd); - - install_element(ROUTER_NODE, &set_attached_bit_cmd); - install_element(ROUTER_NODE, &no_set_attached_bit_cmd); - - install_element(ROUTER_NODE, &dynamic_hostname_cmd); - install_element(ROUTER_NODE, &no_dynamic_hostname_cmd); - - install_element(ROUTER_NODE, &area_lsp_mtu_cmd); - install_element(ROUTER_NODE, &no_area_lsp_mtu_cmd); - - install_element(ROUTER_NODE, &is_type_cmd); - install_element(ROUTER_NODE, &no_is_type_cmd); - - install_element(ROUTER_NODE, &lsp_gen_interval_cmd); - install_element(ROUTER_NODE, &no_lsp_gen_interval_cmd); - - install_element(ROUTER_NODE, &spf_interval_cmd); - install_element(ROUTER_NODE, &no_spf_interval_cmd); - install_element(ROUTER_NODE, &spf_interval_l1_cmd); - install_element(ROUTER_NODE, &no_spf_interval_l1_cmd); - install_element(ROUTER_NODE, &spf_interval_l2_cmd); - install_element(ROUTER_NODE, &no_spf_interval_l2_cmd); - - install_element(ROUTER_NODE, &max_lsp_lifetime_cmd); - install_element(ROUTER_NODE, &no_max_lsp_lifetime_cmd); - - install_element(ROUTER_NODE, &lsp_refresh_interval_cmd); - install_element(ROUTER_NODE, &no_lsp_refresh_interval_cmd); - - install_element(ROUTER_NODE, &area_passwd_md5_cmd); - install_element(ROUTER_NODE, &area_passwd_clear_cmd); - install_element(ROUTER_NODE, &domain_passwd_md5_cmd); - install_element(ROUTER_NODE, &domain_passwd_clear_cmd); - install_element(ROUTER_NODE, &no_area_passwd_cmd); - - install_element(ROUTER_NODE, &spf_delay_ietf_cmd); - install_element(ROUTER_NODE, &no_spf_delay_ietf_cmd); -} diff --git a/isisd/isis_vty_common.c b/isisd/isis_vty_common.c new file mode 100644 index 0000000000..f1f49b962b --- /dev/null +++ b/isisd/isis_vty_common.c @@ -0,0 +1,946 @@ +/* + * IS-IS Rout(e)ing protocol - isis_vty_common.c + * + * This file contains the CLI that is shared between OpenFabric and IS-IS + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * Copyright (C) 2016 David Lamparter, for NetDEF, Inc. + * Copyright (C) 2018 Christian Franke, for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public Licenseas published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "command.h" +#include "spf_backoff.h" + +#include "isis_circuit.h" +#include "isis_csm.h" +#include "isis_misc.h" +#include "isis_mt.h" +#include "isisd.h" +#include "isis_vty_common.h" + +struct isis_circuit *isis_circuit_lookup(struct vty *vty) +{ + struct interface *ifp = VTY_GET_CONTEXT(interface); + struct isis_circuit *circuit; + + if (!ifp) { + vty_out(vty, "Invalid interface \n"); + return NULL; + } + + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) { + vty_out(vty, "ISIS is not enabled on circuit %s\n", ifp->name); + return NULL; + } + + return circuit; +} + +DEFUN (ip_router_isis, + ip_router_isis_cmd, + "ip router " PROTO_NAME " WORD", + "Interface Internet Protocol config commands\n" + "IP router interface commands\n" + PROTO_HELP + "Routing process tag\n") +{ + int idx_afi = 0; + int idx_word = 3; + VTY_DECLVAR_CONTEXT(interface, ifp); + struct isis_circuit *circuit; + struct isis_area *area; + const char *af = argv[idx_afi]->arg; + const char *area_tag = argv[idx_word]->arg; + + /* Prevent more than one area per circuit */ + circuit = circuit_scan_by_ifp(ifp); + if (circuit && circuit->area) { + if (strcmp(circuit->area->area_tag, area_tag)) { + vty_out(vty, "ISIS circuit is already defined on %s\n", + circuit->area->area_tag); + return CMD_ERR_NOTHING_TODO; + } + } + + area = isis_area_lookup(area_tag); + if (!area) + area = isis_area_create(area_tag); + + if (!circuit || !circuit->area) { + circuit = isis_circuit_create(area, ifp); + + if (circuit->state != C_STATE_CONF + && circuit->state != C_STATE_UP) { + vty_out(vty, + "Couldn't bring up interface, please check log.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + + bool ip = circuit->ip_router, ipv6 = circuit->ipv6_router; + if (af[2] != '\0') + ipv6 = true; + else + ip = true; + + isis_circuit_af_set(circuit, ip, ipv6); + return CMD_SUCCESS; +} + +DEFUN (ip6_router_isis, + ip6_router_isis_cmd, + "ipv6 router " PROTO_NAME " WORD", + "Interface Internet Protocol config commands\n" + "IP router interface commands\n" + PROTO_HELP + "Routing process tag\n") +{ + return ip_router_isis(self, vty, argc, argv); +} + +DEFUN (no_ip_router_isis, + no_ip_router_isis_cmd, + "no router " PROTO_NAME " WORD", + NO_STR + "Interface Internet Protocol config commands\n" + "IP router interface commands\n" + "IP router interface commands\n" + PROTO_HELP + "Routing process tag\n") +{ + int idx_afi = 1; + int idx_word = 4; + VTY_DECLVAR_CONTEXT(interface, ifp); + struct isis_area *area; + struct isis_circuit *circuit; + const char *af = argv[idx_afi]->arg; + const char *area_tag = argv[idx_word]->arg; + + area = isis_area_lookup(area_tag); + if (!area) { + vty_out(vty, "Can't find ISIS instance %s\n", + argv[idx_afi]->arg); + return CMD_ERR_NO_MATCH; + } + + circuit = circuit_lookup_by_ifp(ifp, area->circuit_list); + if (!circuit) { + vty_out(vty, "ISIS is not enabled on circuit %s\n", ifp->name); + return CMD_ERR_NO_MATCH; + } + + bool ip = circuit->ip_router, ipv6 = circuit->ipv6_router; + if (af[2] != '\0') + ipv6 = false; + else + ip = false; + + isis_circuit_af_set(circuit, ip, ipv6); + return CMD_SUCCESS; +} + +DEFUN (isis_passive, + isis_passive_cmd, + PROTO_NAME " passive", + PROTO_HELP + "Configure the passive mode for interface\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + CMD_FERR_RETURN(isis_circuit_passive_set(circuit, 1), + "Cannot set passive: $ERR"); + return CMD_SUCCESS; +} + +DEFUN (no_isis_passive, + no_isis_passive_cmd, + "no " PROTO_NAME " passive", + NO_STR + PROTO_HELP + "Configure the passive mode for interface\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + CMD_FERR_RETURN(isis_circuit_passive_set(circuit, 0), + "Cannot set no passive: $ERR"); + return CMD_SUCCESS; +} + +DEFUN (isis_passwd, + isis_passwd_cmd, + PROTO_NAME " password WORD", + PROTO_HELP + "Configure the authentication password for a circuit\n" + "HMAC-MD5 authentication\n" + "Cleartext password\n" + "Circuit password\n") +{ + int idx_encryption = 2; + int idx_word = 3; + struct isis_circuit *circuit = isis_circuit_lookup(vty); + ferr_r rv; + + if (!circuit) + return CMD_ERR_NO_MATCH; + + if (argv[idx_encryption]->arg[0] == 'm') + rv = isis_circuit_passwd_hmac_md5_set(circuit, + argv[idx_word]->arg); + else + rv = isis_circuit_passwd_cleartext_set(circuit, + argv[idx_word]->arg); + + CMD_FERR_RETURN(rv, "Failed to set circuit password: $ERR"); + return CMD_SUCCESS; +} + +DEFUN (no_isis_passwd, + no_isis_passwd_cmd, + "no " PROTO_NAME " password [ WORD]", + NO_STR + PROTO_HELP + "Configure the authentication password for a circuit\n" + "HMAC-MD5 authentication\n" + "Cleartext password\n" + "Circuit password\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + CMD_FERR_RETURN(isis_circuit_passwd_unset(circuit), + "Failed to unset circuit password: $ERR"); + return CMD_SUCCESS; +} + +DEFUN (isis_metric, + isis_metric_cmd, + PROTO_NAME " metric (0-16777215)", + PROTO_HELP + "Set default metric for circuit\n" + "Default metric value\n") +{ + int idx_number = 2; + int met; + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + met = atoi(argv[idx_number]->arg); + + /* RFC3787 section 5.1 */ + if (circuit->area && circuit->area->oldmetric == 1 + && met > MAX_NARROW_LINK_METRIC) { + vty_out(vty, + "Invalid metric %d - should be <0-63> " + "when narrow metric type enabled\n", + met); + return CMD_WARNING_CONFIG_FAILED; + } + + /* RFC4444 */ + if (circuit->area && circuit->area->newmetric == 1 + && met > MAX_WIDE_LINK_METRIC) { + vty_out(vty, + "Invalid metric %d - should be <0-16777215> " + "when wide metric type enabled\n", + met); + return CMD_WARNING_CONFIG_FAILED; + } + + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1, met), + "Failed to set L1 metric: $ERR"); + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2, met), + "Failed to set L2 metric: $ERR"); + return CMD_SUCCESS; +} + +DEFUN (no_isis_metric, + no_isis_metric_cmd, + "no " PROTO_NAME " metric [(0-16777215)]", + NO_STR + PROTO_HELP + "Set default metric for circuit\n" + "Default metric value\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_1, + DEFAULT_CIRCUIT_METRIC), + "Failed to set L1 metric: $ERR"); + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, IS_LEVEL_2, + DEFAULT_CIRCUIT_METRIC), + "Failed to set L2 metric: $ERR"); + return CMD_SUCCESS; +} + +DEFUN (isis_hello_interval, + isis_hello_interval_cmd, + PROTO_NAME " hello-interval (1-600)", + PROTO_HELP + "Set Hello interval\n" + "Holdtime 1 seconds, interval depends on multiplier\n") +{ + uint32_t interval = atoi(argv[2]->arg); + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_interval[0] = interval; + circuit->hello_interval[1] = interval; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_hello_interval, + no_isis_hello_interval_cmd, + "no " PROTO_NAME " hello-interval [(1-600)]", + NO_STR + PROTO_HELP + "Set Hello interval\n" + "Holdtime 1 second, interval depends on multiplier\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_interval[0] = DEFAULT_HELLO_INTERVAL; + circuit->hello_interval[1] = DEFAULT_HELLO_INTERVAL; + + return CMD_SUCCESS; +} + +DEFUN (isis_hello_multiplier, + isis_hello_multiplier_cmd, + PROTO_NAME " hello-multiplier (2-100)", + PROTO_HELP + "Set multiplier for Hello holding time\n" + "Hello multiplier value\n") +{ + uint16_t mult = atoi(argv[2]->arg); + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_multiplier[0] = mult; + circuit->hello_multiplier[1] = mult; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_hello_multiplier, + no_isis_hello_multiplier_cmd, + "no " PROTO_NAME " hello-multiplier [(2-100)]", + NO_STR + PROTO_HELP + "Set multiplier for Hello holding time\n" + "Hello multiplier value\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_multiplier[0] = DEFAULT_HELLO_MULTIPLIER; + circuit->hello_multiplier[1] = DEFAULT_HELLO_MULTIPLIER; + + return CMD_SUCCESS; +} + +DEFUN (csnp_interval, + csnp_interval_cmd, + PROTO_NAME " csnp-interval (1-600)", + PROTO_HELP + "Set CSNP interval in seconds\n" + "CSNP interval value\n") +{ + uint16_t interval = atoi(argv[2]->arg); + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->csnp_interval[0] = interval; + circuit->csnp_interval[1] = interval; + + return CMD_SUCCESS; +} + +DEFUN (no_csnp_interval, + no_csnp_interval_cmd, + "no " PROTO_NAME " csnp-interval [(1-600)]", + NO_STR + PROTO_HELP + "Set CSNP interval in seconds\n" + "CSNP interval value\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->csnp_interval[0] = DEFAULT_CSNP_INTERVAL; + circuit->csnp_interval[1] = DEFAULT_CSNP_INTERVAL; + + return CMD_SUCCESS; +} + +DEFUN (psnp_interval, + psnp_interval_cmd, + PROTO_NAME " psnp-interval (1-120)", + PROTO_HELP + "Set PSNP interval in seconds\n" + "PSNP interval value\n") +{ + uint16_t interval = atoi(argv[2]->arg); + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->psnp_interval[0] = interval; + circuit->psnp_interval[1] = interval; + + return CMD_SUCCESS; +} + +DEFUN (no_psnp_interval, + no_psnp_interval_cmd, + "no " PROTO_NAME " psnp-interval [(1-120)]", + NO_STR + PROTO_HELP + "Set PSNP interval in seconds\n" + "PSNP interval value\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->psnp_interval[0] = DEFAULT_PSNP_INTERVAL; + circuit->psnp_interval[1] = DEFAULT_PSNP_INTERVAL; + + return CMD_SUCCESS; +} + +DEFUN (circuit_topology, + circuit_topology_cmd, + PROTO_NAME " topology " ISIS_MT_NAMES, + PROTO_HELP + "Configure interface IS-IS topologies\n" + ISIS_MT_DESCRIPTIONS) +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + const char *arg = argv[2]->arg; + uint16_t mtid = isis_str2mtid(arg); + + if (circuit->area && circuit->area->oldmetric) { + vty_out(vty, + "Multi topology IS-IS can only be used with wide metrics\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (mtid == (uint16_t)-1) { + vty_out(vty, "Don't know topology '%s'\n", arg); + return CMD_WARNING_CONFIG_FAILED; + } + + return isis_circuit_mt_enabled_set(circuit, mtid, true); +} + +DEFUN (no_circuit_topology, + no_circuit_topology_cmd, + "no " PROTO_NAME " topology " ISIS_MT_NAMES, + NO_STR + PROTO_HELP + "Configure interface IS-IS topologies\n" + ISIS_MT_DESCRIPTIONS) +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + const char *arg = argv[3]->arg; + uint16_t mtid = isis_str2mtid(arg); + + if (circuit->area && circuit->area->oldmetric) { + vty_out(vty, + "Multi topology IS-IS can only be used with wide metrics\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (mtid == (uint16_t)-1) { + vty_out(vty, "Don't know topology '%s'\n", arg); + return CMD_WARNING_CONFIG_FAILED; + } + + return isis_circuit_mt_enabled_set(circuit, mtid, false); +} + +DEFUN (set_overload_bit, + set_overload_bit_cmd, + "set-overload-bit", + "Set overload bit to avoid any transit traffic\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + isis_area_overload_bit_set(area, true); + return CMD_SUCCESS; +} + +DEFUN (no_set_overload_bit, + no_set_overload_bit_cmd, + "no set-overload-bit", + "Reset overload bit to accept transit traffic\n" + "Reset overload bit\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + isis_area_overload_bit_set(area, false); + return CMD_SUCCESS; +} + +static int isis_vty_lsp_mtu_set(struct vty *vty, unsigned int lsp_mtu) +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + struct listnode *node; + struct isis_circuit *circuit; + + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { + if (circuit->state != C_STATE_INIT + && circuit->state != C_STATE_UP) + continue; + if (lsp_mtu > isis_circuit_pdu_size(circuit)) { + vty_out(vty, + "ISIS area contains circuit %s, which has a maximum PDU size of %zu.\n", + circuit->interface->name, + isis_circuit_pdu_size(circuit)); + return CMD_WARNING_CONFIG_FAILED; + } + } + + isis_area_lsp_mtu_set(area, lsp_mtu); + return CMD_SUCCESS; +} + +DEFUN (area_lsp_mtu, + area_lsp_mtu_cmd, + "lsp-mtu (128-4352)", + "Configure the maximum size of generated LSPs\n" + "Maximum size of generated LSPs\n") +{ + int idx_number = 1; + unsigned int lsp_mtu; + + lsp_mtu = strtoul(argv[idx_number]->arg, NULL, 10); + + return isis_vty_lsp_mtu_set(vty, lsp_mtu); +} + +DEFUN (no_area_lsp_mtu, + no_area_lsp_mtu_cmd, + "no lsp-mtu [(128-4352)]", + NO_STR + "Configure the maximum size of generated LSPs\n" + "Maximum size of generated LSPs\n") +{ + return isis_vty_lsp_mtu_set(vty, DEFAULT_LSP_MTU); +} + +int isis_vty_lsp_gen_interval_set(struct vty *vty, int level, uint16_t interval) +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + int lvl; + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) { + if (!(lvl & level)) + continue; + + if (interval >= area->lsp_refresh[lvl - 1]) { + vty_out(vty, + "LSP gen interval %us must be less than " + "the LSP refresh interval %us\n", + interval, area->lsp_refresh[lvl - 1]); + return CMD_WARNING_CONFIG_FAILED; + } + } + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) { + if (!(lvl & level)) + continue; + area->lsp_gen_interval[lvl - 1] = interval; + } + + return CMD_SUCCESS; +} + +DEFUN (lsp_gen_interval, + lsp_gen_interval_cmd, + "lsp-gen-interval (1-120)", + "Minimum interval between regenerating same LSP\n" + "Minimum interval in seconds\n") +{ + uint16_t interval = atoi(argv[1]->arg); + + return isis_vty_lsp_gen_interval_set(vty, IS_LEVEL_1_AND_2, interval); +} + +DEFUN (no_lsp_gen_interval, + no_lsp_gen_interval_cmd, + "no lsp-gen-interval [(1-120)]", + NO_STR + "Minimum interval between regenerating same LSP\n" + "Minimum interval in seconds\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + return isis_vty_lsp_gen_interval_set(vty, IS_LEVEL_1_AND_2, + DEFAULT_MIN_LSP_GEN_INTERVAL); +} + +DEFUN (spf_interval, + spf_interval_cmd, + "spf-interval (1-120)", + "Minimum interval between SPF calculations\n" + "Minimum interval between consecutive SPFs in seconds\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + uint16_t interval = atoi(argv[1]->arg); + + area->min_spf_interval[0] = interval; + area->min_spf_interval[1] = interval; + + return CMD_SUCCESS; +} + +DEFUN (no_spf_interval, + no_spf_interval_cmd, + "no spf-interval [(1-120)]", + NO_STR + "Minimum interval between SPF calculations\n" + "Minimum interval between consecutive SPFs in seconds\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL; + area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL; + + return CMD_SUCCESS; +} + +DEFUN (no_spf_delay_ietf, + no_spf_delay_ietf_cmd, + "no spf-delay-ietf", + NO_STR + "IETF SPF delay algorithm\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + spf_backoff_free(area->spf_delay_ietf[0]); + spf_backoff_free(area->spf_delay_ietf[1]); + area->spf_delay_ietf[0] = NULL; + area->spf_delay_ietf[1] = NULL; + + return CMD_SUCCESS; +} + +DEFUN (spf_delay_ietf, + spf_delay_ietf_cmd, + "spf-delay-ietf init-delay (0-60000) short-delay (0-60000) long-delay (0-60000) holddown (0-60000) time-to-learn (0-60000)", + "IETF SPF delay algorithm\n" + "Delay used while in QUIET state\n" + "Delay used while in QUIET state in milliseconds\n" + "Delay used while in SHORT_WAIT state\n" + "Delay used while in SHORT_WAIT state in milliseconds\n" + "Delay used while in LONG_WAIT\n" + "Delay used while in LONG_WAIT state in milliseconds\n" + "Time with no received IGP events before considering IGP stable\n" + "Time with no received IGP events before considering IGP stable (in milliseconds)\n" + "Maximum duration needed to learn all the events related to a single failure\n" + "Maximum duration needed to learn all the events related to a single failure (in milliseconds)\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + long init_delay = atol(argv[2]->arg); + long short_delay = atol(argv[4]->arg); + long long_delay = atol(argv[6]->arg); + long holddown = atol(argv[8]->arg); + long timetolearn = atol(argv[10]->arg); + + size_t bufsiz = strlen(area->area_tag) + sizeof("IS-IS Lx"); + char *buf = XCALLOC(MTYPE_TMP, bufsiz); + + snprintf(buf, bufsiz, "IS-IS %s L1", area->area_tag); + spf_backoff_free(area->spf_delay_ietf[0]); + area->spf_delay_ietf[0] = + spf_backoff_new(master, buf, init_delay, short_delay, + long_delay, holddown, timetolearn); + + snprintf(buf, bufsiz, "IS-IS %s L2", area->area_tag); + spf_backoff_free(area->spf_delay_ietf[1]); + area->spf_delay_ietf[1] = + spf_backoff_new(master, buf, init_delay, short_delay, + long_delay, holddown, timetolearn); + + XFREE(MTYPE_TMP, buf); + return CMD_SUCCESS; +} + +int isis_vty_max_lsp_lifetime_set(struct vty *vty, int level, uint16_t interval) +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + int lvl; + uint16_t refresh_interval = interval - 300; + int set_refresh_interval[ISIS_LEVELS] = {0, 0}; + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) { + if (!(lvl & level)) + continue; + + if (refresh_interval < area->lsp_refresh[lvl - 1]) { + vty_out(vty, + "Level %d Max LSP lifetime %us must be 300s greater than " + "the configured LSP refresh interval %us\n", + lvl, interval, area->lsp_refresh[lvl - 1]); + vty_out(vty, + "Automatically reducing level %d LSP refresh interval " + "to %us\n", + lvl, refresh_interval); + set_refresh_interval[lvl - 1] = 1; + + if (refresh_interval + <= area->lsp_gen_interval[lvl - 1]) { + vty_out(vty, + "LSP refresh interval %us must be greater than " + "the configured LSP gen interval %us\n", + refresh_interval, + area->lsp_gen_interval[lvl - 1]); + return CMD_WARNING_CONFIG_FAILED; + } + } + } + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) { + if (!(lvl & level)) + continue; + isis_area_max_lsp_lifetime_set(area, lvl, interval); + if (set_refresh_interval[lvl - 1]) + isis_area_lsp_refresh_set(area, lvl, refresh_interval); + } + + return CMD_SUCCESS; +} + +DEFUN (max_lsp_lifetime, + max_lsp_lifetime_cmd, + "max-lsp-lifetime (350-65535)", + "Maximum LSP lifetime\n" + "LSP lifetime in seconds\n") +{ + int lifetime = atoi(argv[1]->arg); + + return isis_vty_max_lsp_lifetime_set(vty, IS_LEVEL_1_AND_2, lifetime); +} + + +DEFUN (no_max_lsp_lifetime, + no_max_lsp_lifetime_cmd, + "no max-lsp-lifetime [(350-65535)]", + NO_STR + "Maximum LSP lifetime\n" + "LSP lifetime in seconds\n") +{ + return isis_vty_max_lsp_lifetime_set(vty, IS_LEVEL_1_AND_2, + DEFAULT_LSP_LIFETIME); +} + +int isis_vty_lsp_refresh_set(struct vty *vty, int level, uint16_t interval) +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + int lvl; + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) { + if (!(lvl & level)) + continue; + if (interval <= area->lsp_gen_interval[lvl - 1]) { + vty_out(vty, + "LSP refresh interval %us must be greater than " + "the configured LSP gen interval %us\n", + interval, area->lsp_gen_interval[lvl - 1]); + 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_WARNING_CONFIG_FAILED; + } + } + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) { + if (!(lvl & level)) + continue; + isis_area_lsp_refresh_set(area, lvl, interval); + } + + return CMD_SUCCESS; +} + +DEFUN (lsp_refresh_interval, + lsp_refresh_interval_cmd, + "lsp-refresh-interval (1-65235)", + "LSP refresh interval\n" + "LSP refresh interval in seconds\n") +{ + unsigned int interval = atoi(argv[1]->arg); + return isis_vty_lsp_refresh_set(vty, IS_LEVEL_1_AND_2, interval); +} + +DEFUN (no_lsp_refresh_interval, + no_lsp_refresh_interval_cmd, + "no lsp-refresh-interval [(1-65235)]", + NO_STR + "LSP refresh interval\n" + "LSP refresh interval in seconds\n") +{ + return isis_vty_lsp_refresh_set(vty, IS_LEVEL_1_AND_2, + DEFAULT_MAX_LSP_GEN_INTERVAL); +} + +int isis_vty_password_set(struct vty *vty, int argc, + struct cmd_token *argv[], int level) +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + int idx_algo = 1; + int idx_password = 2; + int idx_snp_auth = 5; + uint8_t snp_auth = 0; + + const char *passwd = argv[idx_password]->arg; + if (strlen(passwd) > 254) { + vty_out(vty, "Too long area password (>254)\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argc > idx_snp_auth) { + snp_auth = SNP_AUTH_SEND; + if (strmatch(argv[idx_snp_auth]->text, "validate")) + snp_auth |= SNP_AUTH_RECV; + } + + if (strmatch(argv[idx_algo]->text, "clear")) { + return isis_area_passwd_cleartext_set(area, level, + passwd, snp_auth); + } else if (strmatch(argv[idx_algo]->text, "md5")) { + return isis_area_passwd_hmac_md5_set(area, level, + passwd, snp_auth); + } + + return CMD_WARNING_CONFIG_FAILED; +} + +DEFUN (domain_passwd, + domain_passwd_cmd, + "domain-password WORD [authenticate snp ]", + "Set the authentication password for a routing domain\n" + "Authentication type\n" + "Authentication type\n" + "Level-wide password\n" + "Authentication\n" + "SNP PDUs\n" + "Send but do not check PDUs on receiving\n" + "Send and check PDUs on receiving\n") +{ + return isis_vty_password_set(vty, argc, argv, IS_LEVEL_2); +} + +DEFUN (no_domain_passwd, + no_domain_passwd_cmd, + "no domain-password", + NO_STR + "Set the authentication password for a routing domain\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + return isis_area_passwd_unset(area, IS_LEVEL_2); +} + +void isis_vty_init(void) +{ + install_element(INTERFACE_NODE, &ip_router_isis_cmd); + install_element(INTERFACE_NODE, &ip6_router_isis_cmd); + install_element(INTERFACE_NODE, &no_ip_router_isis_cmd); + + install_element(INTERFACE_NODE, &isis_passive_cmd); + install_element(INTERFACE_NODE, &no_isis_passive_cmd); + + install_element(INTERFACE_NODE, &isis_passwd_cmd); + install_element(INTERFACE_NODE, &no_isis_passwd_cmd); + + install_element(INTERFACE_NODE, &isis_metric_cmd); + install_element(INTERFACE_NODE, &no_isis_metric_cmd); + + install_element(INTERFACE_NODE, &isis_hello_interval_cmd); + install_element(INTERFACE_NODE, &no_isis_hello_interval_cmd); + + install_element(INTERFACE_NODE, &isis_hello_multiplier_cmd); + install_element(INTERFACE_NODE, &no_isis_hello_multiplier_cmd); + + install_element(INTERFACE_NODE, &csnp_interval_cmd); + install_element(INTERFACE_NODE, &no_csnp_interval_cmd); + + install_element(INTERFACE_NODE, &psnp_interval_cmd); + install_element(INTERFACE_NODE, &no_psnp_interval_cmd); + + install_element(INTERFACE_NODE, &circuit_topology_cmd); + install_element(INTERFACE_NODE, &no_circuit_topology_cmd); + + install_element(ROUTER_NODE, &set_overload_bit_cmd); + install_element(ROUTER_NODE, &no_set_overload_bit_cmd); + + install_element(ROUTER_NODE, &area_lsp_mtu_cmd); + install_element(ROUTER_NODE, &no_area_lsp_mtu_cmd); + + install_element(ROUTER_NODE, &lsp_gen_interval_cmd); + install_element(ROUTER_NODE, &no_lsp_gen_interval_cmd); + + install_element(ROUTER_NODE, &spf_interval_cmd); + install_element(ROUTER_NODE, &no_spf_interval_cmd); + + install_element(ROUTER_NODE, &max_lsp_lifetime_cmd); + install_element(ROUTER_NODE, &no_max_lsp_lifetime_cmd); + + install_element(ROUTER_NODE, &lsp_refresh_interval_cmd); + install_element(ROUTER_NODE, &no_lsp_refresh_interval_cmd); + + install_element(ROUTER_NODE, &domain_passwd_cmd); + install_element(ROUTER_NODE, &no_domain_passwd_cmd); + + install_element(ROUTER_NODE, &spf_delay_ietf_cmd); + install_element(ROUTER_NODE, &no_spf_delay_ietf_cmd); + + isis_vty_daemon_init(); +} diff --git a/isisd/isis_vty_common.h b/isisd/isis_vty_common.h new file mode 100644 index 0000000000..b726b4ee83 --- /dev/null +++ b/isisd/isis_vty_common.h @@ -0,0 +1,38 @@ +/* + * IS-IS Rout(e)ing protocol - isis_vty_common.h + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * Copyright (C) 2016 David Lamparter, for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public Licenseas published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ISIS_VTY_COMMON_H +#define ISIS_VTY_COMMON_H + +struct isis_circuit *isis_circuit_lookup(struct vty *vty); + +int isis_vty_max_lsp_lifetime_set(struct vty *vty, int level, uint16_t interval); +int isis_vty_lsp_refresh_set(struct vty *vty, int level, uint16_t interval); +int isis_vty_lsp_gen_interval_set(struct vty *vty, int level, uint16_t interval); +int isis_vty_password_set(struct vty *vty, int argc, + struct cmd_token *argv[], int level); + +void isis_vty_daemon_init(void); +void isis_vty_init(void); + +#endif diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c new file mode 100644 index 0000000000..22876301c3 --- /dev/null +++ b/isisd/isis_vty_fabricd.c @@ -0,0 +1,32 @@ +/* + * IS-IS Rout(e)ing protocol - isis_vty_fabricd.c + * + * This file contains the CLI that is specific to OpenFabric + * + * Copyright (C) 2018 Christian Franke, for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public Licenseas published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "command.h" + +#include "isisd.h" +#include "isis_vty_common.h" + +void isis_vty_daemon_init(void) +{ + return; +} diff --git a/isisd/isis_vty_isisd.c b/isisd/isis_vty_isisd.c new file mode 100644 index 0000000000..95aaeae816 --- /dev/null +++ b/isisd/isis_vty_isisd.c @@ -0,0 +1,858 @@ +/* + * IS-IS Rout(e)ing protocol - isis_vty_isisd.c + * + * This file contains the CLI that is specific to IS-IS + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * Copyright (C) 2016 David Lamparter, for NetDEF, Inc. + * Copyright (C) 2018 Christian Franke, for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public Licenseas published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "command.h" + +#include "isis_circuit.h" +#include "isis_csm.h" +#include "isis_misc.h" +#include "isis_mt.h" +#include "isisd.h" +#include "isis_vty_common.h" + +static int level_for_arg(const char *arg) +{ + if (!strcmp(arg, "level-1")) + return IS_LEVEL_1; + else + return IS_LEVEL_2; +} + +DEFUN (isis_circuit_type, + isis_circuit_type_cmd, + "isis circuit-type ", + "IS-IS routing protocol\n" + "Configure circuit type for interface\n" + "Level-1 only adjacencies are formed\n" + "Level-1-2 adjacencies are formed\n" + "Level-2 only adjacencies are formed\n") +{ + int idx_level = 2; + int is_type; + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + is_type = string2circuit_t(argv[idx_level]->arg); + if (!is_type) { + vty_out(vty, "Unknown circuit-type \n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (circuit->state == C_STATE_UP + && circuit->area->is_type != IS_LEVEL_1_AND_2 + && circuit->area->is_type != is_type) { + vty_out(vty, "Invalid circuit level for area %s.\n", + circuit->area->area_tag); + return CMD_WARNING_CONFIG_FAILED; + } + isis_circuit_is_type_set(circuit, is_type); + + return CMD_SUCCESS; +} + +DEFUN (no_isis_circuit_type, + no_isis_circuit_type_cmd, + "no isis circuit-type ", + NO_STR + "IS-IS routing protocol\n" + "Configure circuit type for interface\n" + "Level-1 only adjacencies are formed\n" + "Level-1-2 adjacencies are formed\n" + "Level-2 only adjacencies are formed\n") +{ + int is_type; + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + /* + * Set the circuits level to its default value + */ + if (circuit->state == C_STATE_UP) + is_type = circuit->area->is_type; + else + is_type = IS_LEVEL_1_AND_2; + isis_circuit_is_type_set(circuit, is_type); + + return CMD_SUCCESS; +} + +DEFUN (isis_network, + isis_network_cmd, + "isis network point-to-point", + "IS-IS routing protocol\n" + "Set network type\n" + "point-to-point network type\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + 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_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFUN (no_isis_network, + no_isis_network_cmd, + "no isis network point-to-point", + NO_STR + "IS-IS routing protocol\n" + "Set network type for circuit\n" + "point-to-point network type\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + 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_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFUN (isis_priority, + isis_priority_cmd, + "isis priority (0-127)", + "IS-IS routing protocol\n" + "Set priority for Designated Router election\n" + "Priority value\n") +{ + uint8_t prio = atoi(argv[2]->arg); + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->priority[0] = prio; + circuit->priority[1] = prio; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_priority, + no_isis_priority_cmd, + "no isis priority [(0-127)]", + NO_STR + "IS-IS routing protocol\n" + "Set priority for Designated Router election\n" + "Priority value\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->priority[0] = DEFAULT_PRIORITY; + circuit->priority[1] = DEFAULT_PRIORITY; + + return CMD_SUCCESS; +} + +DEFUN (isis_priority_level, + isis_priority_level_cmd, + "isis priority (0-127) ", + "IS-IS routing protocol\n" + "Set priority for Designated Router election\n" + "Priority value\n" + "Specify priority for level-1 routing\n" + "Specify priority for level-2 routing\n") +{ + uint8_t prio = atoi(argv[2]->arg); + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->priority[level_for_arg(argv[3]->text)] = prio; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_priority_level, + no_isis_priority_level_cmd, + "no isis priority [(0-127)] ", + NO_STR + "IS-IS routing protocol\n" + "Set priority for Designated Router election\n" + "Priority value\n" + "Specify priority for level-1 routing\n" + "Specify priority for level-2 routing\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + int level = level_for_arg(argv[argc - 1]->text); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->priority[level] = DEFAULT_PRIORITY; + + return CMD_SUCCESS; +} + +DEFUN (isis_metric_level, + isis_metric_level_cmd, + "isis metric (0-16777215) ", + "IS-IS routing protocol\n" + "Set default metric for circuit\n" + "Default metric value\n" + "Specify metric for level-1 routing\n" + "Specify metric for level-2 routing\n") +{ + uint32_t met = atoi(argv[2]->arg); + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, + level_for_arg(argv[3]->text), + met), + "Failed to set metric: $ERR"); + return CMD_SUCCESS; +} + +DEFUN (no_isis_metric_level, + no_isis_metric_level_cmd, + "no isis metric [(0-16777215)] ", + NO_STR + "IS-IS routing protocol\n" + "Set default metric for circuit\n" + "Default metric value\n" + "Specify metric for level-1 routing\n" + "Specify metric for level-2 routing\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + int level = level_for_arg(argv[argc - 1]->text); + if (!circuit) + return CMD_ERR_NO_MATCH; + + CMD_FERR_RETURN(isis_circuit_metric_set(circuit, level, + DEFAULT_CIRCUIT_METRIC), + "Failed to set L1 metric: $ERR"); + return CMD_SUCCESS; +} + +DEFUN (isis_hello_interval_level, + isis_hello_interval_level_cmd, + "isis hello-interval (1-600) ", + "IS-IS routing protocol\n" + "Set Hello interval\n" + "Holdtime 1 second, interval depends on multiplier\n" + "Specify hello-interval for level-1 IIHs\n" + "Specify hello-interval for level-2 IIHs\n") +{ + uint32_t interval = atoi(argv[2]->arg); + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_interval[level_for_arg(argv[3]->text)] = interval; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_hello_interval_level, + no_isis_hello_interval_level_cmd, + "no isis hello-interval [(1-600)] ", + NO_STR + "IS-IS routing protocol\n" + "Set Hello interval\n" + "Holdtime 1 second, interval depends on multiplier\n" + "Specify hello-interval for level-1 IIHs\n" + "Specify hello-interval for level-2 IIHs\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + int level = level_for_arg(argv[argc - 1]->text); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_interval[level] = DEFAULT_HELLO_INTERVAL; + + return CMD_SUCCESS; +} + +DEFUN (isis_hello_multiplier_level, + isis_hello_multiplier_level_cmd, + "isis hello-multiplier (2-100) ", + "IS-IS routing protocol\n" + "Set multiplier for Hello holding time\n" + "Hello multiplier value\n" + "Specify hello multiplier for level-1 IIHs\n" + "Specify hello multiplier for level-2 IIHs\n") +{ + uint16_t mult = atoi(argv[2]->arg); + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_multiplier[level_for_arg(argv[3]->text)] = mult; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_hello_multiplier_level, + no_isis_hello_multiplier_level_cmd, + "no isis hello-multiplier [(2-100)] ", + NO_STR + "IS-IS routing protocol\n" + "Set multiplier for Hello holding time\n" + "Hello multiplier value\n" + "Specify hello multiplier for level-1 IIHs\n" + "Specify hello multiplier for level-2 IIHs\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + int level = level_for_arg(argv[argc - 1]->text); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_multiplier[level] = DEFAULT_HELLO_MULTIPLIER; + + return CMD_SUCCESS; +} + +DEFUN (isis_threeway_adj, + isis_threeway_adj_cmd, + "[no] isis three-way-handshake", + NO_STR + "IS-IS commands\n" + "Enable/Disable three-way handshake\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->disable_threeway_adj = !strcmp(argv[0]->text, "no"); + return CMD_SUCCESS; +} + +DEFUN (isis_hello_padding, + isis_hello_padding_cmd, + "isis hello padding", + "IS-IS routing protocol\n" + "Add padding to IS-IS hello packets\n" + "Pad hello packets\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->pad_hellos = 1; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_hello_padding, + no_isis_hello_padding_cmd, + "no isis hello padding", + NO_STR + "IS-IS routing protocol\n" + "Add padding to IS-IS hello packets\n" + "Pad hello packets\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->pad_hellos = 0; + + return CMD_SUCCESS; +} + +DEFUN (csnp_interval_level, + csnp_interval_level_cmd, + "isis csnp-interval (1-600) ", + "IS-IS routing protocol\n" + "Set CSNP interval in seconds\n" + "CSNP interval value\n" + "Specify interval for level-1 CSNPs\n" + "Specify interval for level-2 CSNPs\n") +{ + uint16_t interval = atoi(argv[2]->arg); + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->csnp_interval[level_for_arg(argv[3]->text)] = interval; + + return CMD_SUCCESS; +} + +DEFUN (no_csnp_interval_level, + no_csnp_interval_level_cmd, + "no isis csnp-interval [(1-600)] ", + NO_STR + "IS-IS routing protocol\n" + "Set CSNP interval in seconds\n" + "CSNP interval value\n" + "Specify interval for level-1 CSNPs\n" + "Specify interval for level-2 CSNPs\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + int level = level_for_arg(argv[argc - 1]->text); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->csnp_interval[level] = DEFAULT_CSNP_INTERVAL; + + return CMD_SUCCESS; +} + +DEFUN (psnp_interval_level, + psnp_interval_level_cmd, + "isis psnp-interval (1-120) ", + "IS-IS routing protocol\n" + "Set PSNP interval in seconds\n" + "PSNP interval value\n" + "Specify interval for level-1 PSNPs\n" + "Specify interval for level-2 PSNPs\n") +{ + uint16_t interval = atoi(argv[2]->arg); + struct isis_circuit *circuit = isis_circuit_lookup(vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->psnp_interval[level_for_arg(argv[3]->text)] = (uint16_t)interval; + + return CMD_SUCCESS; +} + +DEFUN (no_psnp_interval_level, + no_psnp_interval_level_cmd, + "no isis psnp-interval [(1-120)] ", + NO_STR + "IS-IS routing protocol\n" + "Set PSNP interval in seconds\n" + "PSNP interval value\n" + "Specify interval for level-1 PSNPs\n" + "Specify interval for level-2 PSNPs\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + int level = level_for_arg(argv[argc - 1]->text); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->psnp_interval[level] = DEFAULT_PSNP_INTERVAL; + + return CMD_SUCCESS; +} + +static int validate_metric_style_narrow(struct vty *vty, struct isis_area *area) +{ + struct isis_circuit *circuit; + struct listnode *node; + + if (!vty) + return CMD_WARNING_CONFIG_FAILED; + + if (!area) { + vty_out(vty, "ISIS area is invalid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { + if ((area->is_type & IS_LEVEL_1) + && (circuit->is_type & IS_LEVEL_1) + && (circuit->te_metric[0] > MAX_NARROW_LINK_METRIC)) { + vty_out(vty, "ISIS circuit %s metric is invalid\n", + circuit->interface->name); + 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_WARNING_CONFIG_FAILED; + } + } + + return CMD_SUCCESS; +} + +DEFUN (metric_style, + metric_style_cmd, + "metric-style ", + "Use old-style (ISO 10589) or new-style packet formats\n" + "Use old style of TLVs with narrow metric\n" + "Send and accept both styles of TLVs during transition\n" + "Use new style of TLVs to carry wider metric\n") +{ + int idx_metric_style = 1; + VTY_DECLVAR_CONTEXT(isis_area, area); + int ret; + + if (strncmp(argv[idx_metric_style]->arg, "w", 1) == 0) { + isis_area_metricstyle_set(area, false, true); + return CMD_SUCCESS; + } + + if (area_is_mt(area)) { + vty_out(vty, + "Narrow metrics cannot be used while multi topology IS-IS is active\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = validate_metric_style_narrow(vty, area); + if (ret != CMD_SUCCESS) + return ret; + + if (strncmp(argv[idx_metric_style]->arg, "t", 1) == 0) + isis_area_metricstyle_set(area, true, true); + else if (strncmp(argv[idx_metric_style]->arg, "n", 1) == 0) + isis_area_metricstyle_set(area, true, false); + return CMD_SUCCESS; + + return CMD_SUCCESS; +} + +DEFUN (no_metric_style, + no_metric_style_cmd, + "no metric-style", + NO_STR + "Use old-style (ISO 10589) or new-style packet formats\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + int ret; + + if (area_is_mt(area)) { + vty_out(vty, + "Narrow metrics cannot be used while multi topology IS-IS is active\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = validate_metric_style_narrow(vty, area); + if (ret != CMD_SUCCESS) + return ret; + + isis_area_metricstyle_set(area, true, false); + return CMD_SUCCESS; +} + +DEFUN (set_attached_bit, + set_attached_bit_cmd, + "set-attached-bit", + "Set attached bit to identify as L1/L2 router for inter-area traffic\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + isis_area_attached_bit_set(area, true); + return CMD_SUCCESS; +} + +DEFUN (no_set_attached_bit, + no_set_attached_bit_cmd, + "no set-attached-bit", + NO_STR + "Reset attached bit\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + isis_area_attached_bit_set(area, false); + return CMD_SUCCESS; +} + +DEFUN (dynamic_hostname, + dynamic_hostname_cmd, + "hostname dynamic", + "Dynamic hostname for IS-IS\n" + "Dynamic hostname\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + isis_area_dynhostname_set(area, true); + return CMD_SUCCESS; +} + +DEFUN (no_dynamic_hostname, + no_dynamic_hostname_cmd, + "no hostname dynamic", + NO_STR + "Dynamic hostname for IS-IS\n" + "Dynamic hostname\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + isis_area_dynhostname_set(area, false); + return CMD_SUCCESS; +} + +DEFUN (is_type, + is_type_cmd, + "is-type ", + "IS Level for this routing process (OSI only)\n" + "Act as a station router only\n" + "Act as both a station router and an area router\n" + "Act as an area router only\n") +{ + int idx_level = 1; + VTY_DECLVAR_CONTEXT(isis_area, area); + int type; + + type = string2circuit_t(argv[idx_level]->arg); + if (!type) { + vty_out(vty, "Unknown IS level \n"); + return CMD_SUCCESS; + } + + isis_area_is_type_set(area, type); + + return CMD_SUCCESS; +} + +DEFUN (no_is_type, + no_is_type_cmd, + "no is-type ", + NO_STR + "IS Level for this routing process (OSI only)\n" + "Act as a station router only\n" + "Act as both a station router and an area router\n" + "Act as an area router only\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + int type; + + /* + * Put the is-type back to defaults: + * - level-1-2 on first area + * - level-1 for the rest + */ + if (listgetdata(listhead(isis->area_list)) == area) + type = IS_LEVEL_1_AND_2; + else + type = IS_LEVEL_1; + + isis_area_is_type_set(area, type); + + return CMD_SUCCESS; +} + +DEFUN (lsp_gen_interval_level, + lsp_gen_interval_level_cmd, + "lsp-gen-interval (1-120)", + "Minimum interval between regenerating same LSP\n" + "Set interval for level 1 only\n" + "Set interval for level 2 only\n" + "Minimum interval in seconds\n") +{ + uint16_t interval = atoi(argv[2]->arg); + + return isis_vty_lsp_gen_interval_set(vty, level_for_arg(argv[1]->text), + interval); +} + +DEFUN (no_lsp_gen_interval_level, + no_lsp_gen_interval_level_cmd, + "no lsp-gen-interval [(1-120)]", + NO_STR + "Minimum interval between regenerating same LSP\n" + "Set interval for level 1 only\n" + "Set interval for level 2 only\n" + "Minimum interval in seconds\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + return isis_vty_lsp_gen_interval_set(vty, level_for_arg(argv[2]->text), + DEFAULT_MIN_LSP_GEN_INTERVAL); +} + +DEFUN (max_lsp_lifetime_level, + max_lsp_lifetime_level_cmd, + "max-lsp-lifetime (350-65535)", + "Maximum LSP lifetime\n" + "Maximum LSP lifetime for Level 1 only\n" + "Maximum LSP lifetime for Level 2 only\n" + "LSP lifetime in seconds\n") +{ + uint16_t lifetime = atoi(argv[2]->arg); + + return isis_vty_max_lsp_lifetime_set(vty, level_for_arg(argv[1]->text), + lifetime); +} + +DEFUN (no_max_lsp_lifetime_level, + no_max_lsp_lifetime_level_cmd, + "no max-lsp-lifetime [(350-65535)]", + NO_STR + "Maximum LSP lifetime\n" + "Maximum LSP lifetime for Level 1 only\n" + "Maximum LSP lifetime for Level 2 only\n" + "LSP lifetime in seconds\n") +{ + return isis_vty_max_lsp_lifetime_set(vty, level_for_arg(argv[1]->text), + DEFAULT_LSP_LIFETIME); +} + +DEFUN (spf_interval_level, + spf_interval_level_cmd, + "spf-interval (1-120)", + "Minimum interval between SPF calculations\n" + "Set interval for level 1 only\n" + "Set interval for level 2 only\n" + "Minimum interval between consecutive SPFs in seconds\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + uint16_t interval = atoi(argv[2]->arg); + + area->min_spf_interval[level_for_arg(argv[1]->text)] = interval; + + return CMD_SUCCESS; +} + +DEFUN (no_spf_interval_level, + no_spf_interval_level_cmd, + "no spf-interval [(1-120)]", + NO_STR + "Minimum interval between SPF calculations\n" + "Set interval for level 1 only\n" + "Set interval for level 2 only\n" + "Minimum interval between consecutive SPFs in seconds\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + int level = level_for_arg(argv[1]->text); + + area->min_spf_interval[level] = MINIMUM_SPF_INTERVAL; + + return CMD_SUCCESS; +} + +DEFUN (lsp_refresh_interval_level, + lsp_refresh_interval_level_cmd, + "lsp-refresh-interval (1-65235)", + "LSP refresh interval\n" + "LSP refresh interval for Level 1 only\n" + "LSP refresh interval for Level 2 only\n" + "LSP refresh interval in seconds\n") +{ + uint16_t interval = atoi(argv[2]->arg); + return isis_vty_lsp_refresh_set(vty, level_for_arg(argv[1]->text), + interval); +} + +DEFUN (no_lsp_refresh_interval_level, + no_lsp_refresh_interval_level_cmd, + "no lsp-refresh-interval [(1-65235)]", + NO_STR + "LSP refresh interval\n" + "LSP refresh interval for Level 1 only\n" + "LSP refresh interval for Level 2 only\n" + "LSP refresh interval in seconds\n") +{ + return isis_vty_lsp_refresh_set(vty, level_for_arg(argv[2]->text), + DEFAULT_MAX_LSP_GEN_INTERVAL); +} + +DEFUN (area_passwd, + area_passwd_cmd, + "area-password WORD [authenticate snp ]", + "Configure the authentication password for an area\n" + "Authentication type\n" + "Authentication type\n" + "Area password\n" + "Authentication\n" + "SNP PDUs\n" + "Send but do not check PDUs on receiving\n" + "Send and check PDUs on receiving\n") +{ + return isis_vty_password_set(vty, argc, argv, IS_LEVEL_1); +} + +DEFUN (no_area_passwd, + no_area_passwd_cmd, + "no area-password", + NO_STR + "Configure the authentication password for an area\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + return isis_area_passwd_unset(area, IS_LEVEL_1); +} + +void isis_vty_daemon_init(void) +{ + install_element(INTERFACE_NODE, &isis_circuit_type_cmd); + install_element(INTERFACE_NODE, &no_isis_circuit_type_cmd); + + install_element(INTERFACE_NODE, &isis_network_cmd); + install_element(INTERFACE_NODE, &no_isis_network_cmd); + + install_element(INTERFACE_NODE, &isis_priority_cmd); + install_element(INTERFACE_NODE, &no_isis_priority_cmd); + install_element(INTERFACE_NODE, &isis_priority_level_cmd); + install_element(INTERFACE_NODE, &no_isis_priority_level_cmd); + + install_element(INTERFACE_NODE, &isis_metric_level_cmd); + install_element(INTERFACE_NODE, &no_isis_metric_level_cmd); + + install_element(INTERFACE_NODE, &isis_hello_interval_level_cmd); + install_element(INTERFACE_NODE, &no_isis_hello_interval_level_cmd); + + install_element(INTERFACE_NODE, &isis_hello_multiplier_level_cmd); + install_element(INTERFACE_NODE, &no_isis_hello_multiplier_level_cmd); + + install_element(INTERFACE_NODE, &isis_threeway_adj_cmd); + + install_element(INTERFACE_NODE, &isis_hello_padding_cmd); + install_element(INTERFACE_NODE, &no_isis_hello_padding_cmd); + + install_element(INTERFACE_NODE, &csnp_interval_level_cmd); + install_element(INTERFACE_NODE, &no_csnp_interval_level_cmd); + + install_element(INTERFACE_NODE, &psnp_interval_level_cmd); + install_element(INTERFACE_NODE, &no_psnp_interval_level_cmd); + + install_element(ROUTER_NODE, &metric_style_cmd); + install_element(ROUTER_NODE, &no_metric_style_cmd); + + install_element(ROUTER_NODE, &set_attached_bit_cmd); + install_element(ROUTER_NODE, &no_set_attached_bit_cmd); + + install_element(ROUTER_NODE, &dynamic_hostname_cmd); + install_element(ROUTER_NODE, &no_dynamic_hostname_cmd); + + install_element(ROUTER_NODE, &is_type_cmd); + install_element(ROUTER_NODE, &no_is_type_cmd); + + install_element(ROUTER_NODE, &lsp_gen_interval_level_cmd); + install_element(ROUTER_NODE, &no_lsp_gen_interval_level_cmd); + + install_element(ROUTER_NODE, &max_lsp_lifetime_level_cmd); + install_element(ROUTER_NODE, &no_max_lsp_lifetime_level_cmd); + + install_element(ROUTER_NODE, &spf_interval_level_cmd); + install_element(ROUTER_NODE, &no_spf_interval_level_cmd); + + install_element(ROUTER_NODE, &lsp_refresh_interval_level_cmd); + install_element(ROUTER_NODE, &no_lsp_refresh_interval_level_cmd); + + install_element(ROUTER_NODE, &area_passwd_cmd); + install_element(ROUTER_NODE, &no_area_passwd_cmd); +} diff --git a/isisd/isisd.h b/isisd/isisd.h index ebccfee330..b76b0b7847 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -186,7 +186,6 @@ int isis_area_passwd_cleartext_set(struct isis_area *area, int level, const char *passwd, uint8_t snp_auth); int isis_area_passwd_hmac_md5_set(struct isis_area *area, int level, const char *passwd, uint8_t snp_auth); -void isis_vty_init(void); /* Master of threads. */ extern struct thread_master *master; diff --git a/isisd/subdir.am b/isisd/subdir.am index 085b0cb845..5593f2a4ed 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -14,34 +14,6 @@ sbin_PROGRAMS += isisd/fabricd dist_examples_DATA += isisd/fabricd.conf.sample endif -isisd_libisis_a_SOURCES = \ - isisd/dict.c \ - isisd/isis_adjacency.c \ - isisd/isis_circuit.c \ - isisd/isis_csm.c \ - isisd/isis_dr.c \ - isisd/isis_dynhn.c \ - isisd/isis_errors.c \ - isisd/isis_events.c \ - isisd/isis_flags.c \ - isisd/isis_lsp.c \ - isisd/isis_lsp_hash.c \ - isisd/isis_memory.c \ - isisd/isis_misc.c \ - isisd/isis_mt.c \ - isisd/isis_pdu.c \ - isisd/isis_redist.c \ - isisd/isis_route.c \ - isisd/isis_routemap.c \ - isisd/isis_spf.c \ - isisd/isis_te.c \ - isisd/isis_tlvs.c \ - isisd/isis_vty.c \ - isisd/isis_zebra.c \ - isisd/isisd.c \ - isisd/iso_checksum.c \ - # end - noinst_HEADERS += \ isisd/dict.h \ isisd/isis_adjacency.h \ @@ -67,24 +39,67 @@ noinst_HEADERS += \ isisd/isis_spf.h \ isisd/isis_te.h \ isisd/isis_tlvs.h \ + isisd/isis_vty_common.h \ isisd/isis_zebra.h \ isisd/isisd.h \ isisd/iso_checksum.h \ # end -isisd_isisd_LDADD = isisd/libisis.a lib/libfrr.la @LIBCAP@ -isisd_isisd_SOURCES = \ +LIBISIS_SOURCES = \ + isisd/dict.c \ + isisd/isis_adjacency.c \ + isisd/isis_circuit.c \ + isisd/isis_csm.c \ + isisd/isis_dr.c \ + isisd/isis_dynhn.c \ + isisd/isis_errors.c \ + isisd/isis_events.c \ + isisd/isis_flags.c \ + isisd/isis_lsp.c \ + isisd/isis_lsp_hash.c \ + isisd/isis_memory.c \ + isisd/isis_misc.c \ + isisd/isis_mt.c \ + isisd/isis_pdu.c \ + isisd/isis_redist.c \ + isisd/isis_route.c \ + isisd/isis_routemap.c \ + isisd/isis_spf.c \ + isisd/isis_te.c \ + isisd/isis_tlvs.c \ + isisd/isis_vty_common.c \ + isisd/isis_zebra.c \ + isisd/isisd.c \ + isisd/iso_checksum.c \ + # end + +ISIS_SOURCES = \ isisd/isis_bpf.c \ isisd/isis_dlpi.c \ isisd/isis_main.c \ isisd/isis_pfpacket.c \ # end +ISIS_LDADD_COMMON = lib/libfrr.la @LIBCAP@ + +# Building isisd + +isisd_libisis_a_SOURCES = \ + $(LIBISIS_SOURCES) \ + isisd/isis_vty_isisd.c \ + #end +isisd_isisd_LDADD = isisd/libisis.a $(ISIS_LDADD_COMMON) +isisd_isisd_SOURCES = $(ISIS_SOURCES) + +# Building fabricd + FABRICD_CPPFLAGS = -DFABRICD=1 $(AM_CPPFLAGS) -isisd_libfabric_a_SOURCES = $(isisd_libisis_a_SOURCES) +isisd_libfabric_a_SOURCES = \ + $(LIBISIS_SOURCES) \ + isisd/isis_vty_fabricd.c \ + #end isisd_libfabric_a_CPPFLAGS = $(FABRICD_CPPFLAGS) - -isisd_fabricd_LDADD = isisd/libfabric.a lib/libfrr.la @LIBCAP@ -isisd_fabricd_SOURCES = $(isisd_isisd_SOURCES) +isisd_fabricd_LDADD = isisd/libfabric.a $(ISIS_LDADD_COMMON) +isisd_fabricd_SOURCES = $(ISIS_SOURCES) isisd_fabricd_CPPFLAGS = $(FABRICD_CPPFLAGS) diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 936640c83a..350b6fedea 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -64,7 +64,9 @@ if ISISD vtysh_scan += $(top_srcdir)/isisd/isis_redist.c vtysh_scan += $(top_srcdir)/isisd/isis_spf.c vtysh_scan += $(top_srcdir)/isisd/isis_te.c -vtysh_scan += $(top_srcdir)/isisd/isis_vty.c +vtysh_scan += $(top_srcdir)/isisd/isis_vty_common.c +vtysh_scan += $(top_srcdir)/isisd/isis_vty_fabricd.c +vtysh_scan += $(top_srcdir)/isisd/isis_vty_isisd.c vtysh_scan += $(top_srcdir)/isisd/isisd.c endif diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 0eb5944ab0..690e9a12c6 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -201,9 +201,20 @@ sub scan_file { } foreach (@ARGV) { - scan_file($_, 0); if (/\/isisd\//) { - scan_file($_, 1); + # We scan all the IS-IS files twice, once for isisd, + # once for fabricd. Exceptions are made for the files + # that are not shared between the two. + if (/isis_vty_isisd.c/) { + scan_file($_, 0); + } elsif (/isis_vty_fabricd.c/) { + scan_file($_, 1); + } else { + scan_file($_, 0); + scan_file($_, 1); + } + } else { + scan_file($_, 0); } } From 32c248ef37ac01b2b27497fca93fb408d9c4bc1b Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Tue, 29 May 2018 19:55:42 +0200 Subject: [PATCH 10/84] isisd: Fix error output for 'no ip router isis' command The 'no ip router isis' command would incorrectly output the afi if the area to delete does not exist. Make it output the area name instead. Signed-off-by: Christian Franke --- isisd/isis_vty_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isisd/isis_vty_common.c b/isisd/isis_vty_common.c index f1f49b962b..dbe5beca6e 100644 --- a/isisd/isis_vty_common.c +++ b/isisd/isis_vty_common.c @@ -138,7 +138,7 @@ DEFUN (no_ip_router_isis, area = isis_area_lookup(area_tag); if (!area) { vty_out(vty, "Can't find ISIS instance %s\n", - argv[idx_afi]->arg); + area_tag); return CMD_ERR_NO_MATCH; } From 65f18157115a1c95653551b6e0d8423bdbf8c906 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 22 Mar 2018 14:58:53 +0100 Subject: [PATCH 11/84] fabricd: adjust IS-IS defaults as per draft OpenFabric specifies that it should always be run with wide metrics via P2P links and only as Level-2. Implement this as default and remove all the knobs from fabricd which allow other configuration. Signed-off-by: Christian Franke --- isisd/isis_circuit.c | 18 ++++++++++-------- isisd/isisd.c | 45 +++++++++++++++++++++++++------------------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index a18ee19d68..7ecc71023f 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -412,7 +412,7 @@ void isis_circuit_if_add(struct isis_circuit *circuit, struct interface *ifp) isis_circuit_if_bind(circuit, ifp); if (if_is_broadcast(ifp)) { - if (circuit->circ_type_config == CIRCUIT_T_P2P) + if (fabricd || circuit->circ_type_config == CIRCUIT_T_P2P) circuit->circ_type = CIRCUIT_T_P2P; else circuit->circ_type = CIRCUIT_T_BROADCAST; @@ -976,14 +976,16 @@ int isis_interface_config_write(struct vty *vty) } /* ISIS - circuit type */ - if (circuit->is_type == IS_LEVEL_1) { - vty_out(vty, " " PROTO_NAME " circuit-type level-1\n"); - write++; - } else { - if (circuit->is_type == IS_LEVEL_2) { - vty_out(vty, - " " PROTO_NAME " circuit-type level-2-only\n"); + if (!fabricd) { + if (circuit->is_type == IS_LEVEL_1) { + vty_out(vty, " " PROTO_NAME " circuit-type level-1\n"); write++; + } else { + if (circuit->is_type == IS_LEVEL_2) { + vty_out(vty, + " " PROTO_NAME " circuit-type level-2-only\n"); + write++; + } } } diff --git a/isisd/isisd.c b/isisd/isisd.c index a3f15e9de2..609ca450df 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -105,10 +105,13 @@ struct isis_area *isis_area_create(const char *area_tag) area = XCALLOC(MTYPE_ISIS_AREA, sizeof(struct isis_area)); /* - * The first instance is level-1-2 rest are level-1, unless otherwise - * configured + * Fabricd runs only as level-2. + * For IS-IS, the first instance is level-1-2 rest are level-1, + * unless otherwise configured */ - if (listcount(isis->area_list) > 0) + if (fabricd) { + area->is_type = IS_LEVEL_2; + } else if (listcount(isis->area_list) > 0) area->is_type = IS_LEVEL_1; else area->is_type = IS_LEVEL_1_AND_2; @@ -1896,16 +1899,18 @@ int isis_config_write(struct vty *vty) write++; } /* ISIS - Metric-Style - when true displays wide */ - if (area->newmetric) { - if (!area->oldmetric) - vty_out(vty, " metric-style wide\n"); - else - vty_out(vty, - " metric-style transition\n"); - write++; - } else { - vty_out(vty, " metric-style narrow\n"); - write++; + if (!fabricd) { + if (area->newmetric) { + if (!area->oldmetric) + vty_out(vty, " metric-style wide\n"); + else + vty_out(vty, + " metric-style transition\n"); + write++; + } else { + vty_out(vty, " metric-style narrow\n"); + write++; + } } /* ISIS - overload-bit */ if (area->overload_bit) { @@ -1913,12 +1918,14 @@ int isis_config_write(struct vty *vty) write++; } /* ISIS - Area is-type (level-1-2 is default) */ - if (area->is_type == IS_LEVEL_1) { - vty_out(vty, " is-type level-1\n"); - write++; - } else if (area->is_type == IS_LEVEL_2) { - vty_out(vty, " is-type level-2-only\n"); - write++; + if (!fabricd) { + if (area->is_type == IS_LEVEL_1) { + vty_out(vty, " is-type level-1\n"); + write++; + } else if (area->is_type == IS_LEVEL_2) { + vty_out(vty, " is-type level-2-only\n"); + write++; + } } write += isis_redist_config_write(vty, area, AF_INET); write += isis_redist_config_write(vty, area, AF_INET6); From 8e6fb83b4bfccdd95e652384b60603da6d1f49bf Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 2 Apr 2018 17:55:26 +0200 Subject: [PATCH 12/84] fabricd: adjacency formation optimization as per section 2.4 OpenFabric changes IS-IS's initial database synchronization. While regular IS-IS will simultaneuously exchange LSPs with all neighboring routers during startup, this is considered too much churn for a densely connected fabric. To mitigate this, OpenFabric prescribes that a router should only bring up an adjacency with a single neighbor and perform a full synchronization with that neighbor, before bringing up further adjacencies. This is implemented by having a field `initial_sync_state` in the fabricd datastructure which tracks whether an initial sync is still pending, currently in progress, or complete. When an initial sync is pending, the state will transition to the in-progress state when the first IIH is received. During this state, all IIHs from other routers are ignored. Any IIHs transmitted on any link other than the one to the router with which we are performing the initial sync will always report the far end as DOWN in their threeway handshake state, avoiding the formation of additional adjacencies. The state will be left if all the SRM and SSN flags on the initial-sync circuit are cleared (meaning that initial sync has completed). This is checked in `lsp_tick`. When this condition occurrs, we progress to the initial-sync-complete state, allowing other adjacencies to form. The state can also be left if the initial synchronization is taking too long to succeed, for whatever reason. In that case, we fall back to the initial-sync-pending state and will reattempt initial synchronization with a different neighbor. Signed-off-by: Christian Franke --- isisd/fabricd.c | 136 +++++++++++++++++++++++++++++++++++++++++ isisd/fabricd.h | 36 +++++++++++ isisd/isis_adjacency.c | 17 ++++-- isisd/isis_lsp.c | 16 +++++ isisd/isis_pdu.c | 28 +++++++-- isisd/isisd.c | 4 ++ isisd/isisd.h | 4 ++ isisd/subdir.am | 2 + 8 files changed, 233 insertions(+), 10 deletions(-) create mode 100644 isisd/fabricd.c create mode 100644 isisd/fabricd.h diff --git a/isisd/fabricd.c b/isisd/fabricd.c new file mode 100644 index 0000000000..d37e6a71d8 --- /dev/null +++ b/isisd/fabricd.c @@ -0,0 +1,136 @@ +/* + * IS-IS Rout(e)ing protocol - OpenFabric extensions + * + * Copyright (C) 2018 Christian Franke + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include "isisd/fabricd.h" +#include "isisd/isisd.h" +#include "isisd/isis_memory.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_adjacency.h" + +DEFINE_MTYPE_STATIC(ISISD, FABRICD_STATE, "ISIS OpenFabric") + +/* Tracks initial synchronization as per section 2.4 + * + * We declare the sync complete once we have seen at least one + * CSNP and there are no more LSPs with SSN or SRM set. + */ +enum fabricd_sync_state { + FABRICD_SYNC_PENDING, + FABRICD_SYNC_STARTED, + FABRICD_SYNC_COMPLETE +}; + +struct fabricd { + enum fabricd_sync_state initial_sync_state; + time_t initial_sync_start; + struct isis_circuit *initial_sync_circuit; + struct thread *initial_sync_timeout; +}; + +struct fabricd *fabricd_new(void) +{ + struct fabricd *rv = XCALLOC(MTYPE_FABRICD_STATE, sizeof(*rv)); + + rv->initial_sync_state = FABRICD_SYNC_PENDING; + return rv; +}; + +static int fabricd_initial_sync_timeout(struct thread *thread) +{ + struct fabricd *f = THREAD_ARG(thread); + + zlog_info("OpenFabric: Initial synchronization on %s timed out!", + f->initial_sync_circuit->interface->name); + f->initial_sync_state = FABRICD_SYNC_PENDING; + f->initial_sync_circuit = NULL; + f->initial_sync_timeout = NULL; + return 0; +} + +void fabricd_initial_sync_hello(struct isis_circuit *circuit) +{ + struct fabricd *f = circuit->area->fabricd; + + if (!f) + return; + + if (f->initial_sync_state > FABRICD_SYNC_PENDING) + return; + + f->initial_sync_state = FABRICD_SYNC_STARTED; + + long timeout = 2 * circuit->hello_interval[1] * circuit->hello_multiplier[1]; + + f->initial_sync_circuit = circuit; + if (f->initial_sync_timeout) + return; + + thread_add_timer(master, fabricd_initial_sync_timeout, f, + timeout, &f->initial_sync_timeout); + f->initial_sync_start = monotime(NULL); + + zlog_info("OpenFabric: Started initial synchronization with %s on %s", + sysid_print(circuit->u.p2p.neighbor->sysid), + circuit->interface->name); +} + +bool fabricd_initial_sync_is_in_progress(struct isis_area *area) +{ + struct fabricd *f = area->fabricd; + + if (!f) + return false; + + if (f->initial_sync_state > FABRICD_SYNC_PENDING + && f->initial_sync_state < FABRICD_SYNC_COMPLETE) + return true; + + return false; +} + +struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area) +{ + struct fabricd *f = area->fabricd; + if (!f) + return NULL; + + return f->initial_sync_circuit; +} + +void fabricd_initial_sync_finish(struct isis_area *area) +{ + struct fabricd *f = area->fabricd; + + if (!f) + return; + + if (monotime(NULL) - f->initial_sync_start < 5) + return; + + zlog_info("OpenFabric: Initial synchronization on %s complete.", + f->initial_sync_circuit->interface->name); + f->initial_sync_state = FABRICD_SYNC_COMPLETE; + f->initial_sync_circuit = NULL; + thread_cancel(f->initial_sync_timeout); + f->initial_sync_timeout = NULL; +} diff --git a/isisd/fabricd.h b/isisd/fabricd.h new file mode 100644 index 0000000000..35a5bb6a3b --- /dev/null +++ b/isisd/fabricd.h @@ -0,0 +1,36 @@ +/* + * IS-IS Rout(e)ing protocol - OpenFabric extensions + * + * Copyright (C) 2018 Christian Franke + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef FABRICD_H +#define FABRICD_H + +struct fabricd; + +struct isis_circuit; +struct isis_area; + +struct fabricd *fabricd_new(void); +void fabricd_initial_sync_hello(struct isis_circuit *circuit); +bool fabricd_initial_sync_is_in_progress(struct isis_area *area); +struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area); +void fabricd_initial_sync_finish(struct isis_area *area); + +#endif diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 4b3d78421e..56dbc25829 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -48,6 +48,7 @@ #include "isisd/isis_events.h" #include "isisd/isis_mt.h" #include "isisd/isis_tlvs.h" +#include "isisd/fabricd.h" extern struct isis *isis; @@ -193,6 +194,9 @@ void isis_adj_process_threeway(struct isis_adjacency *adj, } } + if (next_tw_state != ISIS_THREEWAY_DOWN) + fabricd_initial_sync_hello(adj->circuit); + if (next_tw_state == ISIS_THREEWAY_DOWN) { isis_adj_state_change(adj, ISIS_ADJ_DOWN, "Neighbor restarted"); return; @@ -306,10 +310,15 @@ void isis_adj_state_change(struct isis_adjacency *adj, adj->last_flap = time(NULL); adj->flaps++; - /* 7.3.17 - going up on P2P -> send CSNP */ - /* FIXME: yup, I know its wrong... but i will do - * it! (for now) */ - send_csnp(circuit, level); + if (level == IS_LEVEL_1) { + thread_add_timer(master, send_l1_csnp, + circuit, 0, + &circuit->t_send_csnp[0]); + } else { + thread_add_timer(master, send_l2_csnp, + circuit, 0, + &circuit->t_send_csnp[1]); + } } else if (new_state == ISIS_ADJ_DOWN) { if (adj->circuit->u.p2p.neighbor == adj) adj->circuit->u.p2p.neighbor = NULL; diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 66c97ae897..90bcece15d 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -56,6 +56,7 @@ #include "isisd/isis_te.h" #include "isisd/isis_mt.h" #include "isisd/isis_tlvs.h" +#include "isisd/fabricd.h" static int lsp_l1_refresh(struct thread *thread); static int lsp_l2_refresh(struct thread *thread); @@ -1813,6 +1814,7 @@ int lsp_tick(struct thread *thread) int level; uint16_t rem_lifetime; time_t now = monotime(NULL); + bool fabricd_sync_incomplete = false; lsp_list = list_new(); @@ -1821,6 +1823,8 @@ int lsp_tick(struct thread *thread) area->t_tick = NULL; thread_add_timer(master, lsp_tick, area, 1, &area->t_tick); + struct isis_circuit *fabricd_init_c = fabricd_initial_sync_circuit(area); + /* * Build a list of LSPs with (any) SRMflag set * and removed the ones that have aged out @@ -1880,6 +1884,15 @@ int lsp_tick(struct thread *thread) dnode); } else if (flags_any_set(lsp->SRMflags)) listnode_add(lsp_list, lsp); + + if (fabricd_init_c) { + fabricd_sync_incomplete |= + ISIS_CHECK_FLAG(lsp->SSNflags, + fabricd_init_c); + fabricd_sync_incomplete |= + ISIS_CHECK_FLAG(lsp->SRMflags, + fabricd_init_c); + } } /* @@ -1915,6 +1928,9 @@ int lsp_tick(struct thread *thread) } } + if (fabricd_init_c && !fabricd_sync_incomplete) + fabricd_initial_sync_finish(area); + list_delete_and_null(&lsp_list); return ISIS_OK; diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 5c4e3a35bc..8a5db3c6f9 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -56,6 +56,7 @@ #include "isisd/isis_mt.h" #include "isisd/isis_tlvs.h" #include "isisd/isis_errors.h" +#include "isisd/fabricd.h" static int ack_lsp(struct isis_lsp_hdr *hdr, struct isis_circuit *circuit, int level) @@ -207,6 +208,12 @@ static int process_p2p_hello(struct iih_info *iih) thread_add_timer(master, isis_adj_expire, adj, (long)adj->hold_time, &adj->t_expire); + /* While fabricds initial sync is in progress, ignore hellos from other + * interfaces than the one we are performing the initial sync on. */ + if (fabricd_initial_sync_is_in_progress(iih->circuit->area) + && fabricd_initial_sync_circuit(iih->circuit->area) != iih->circuit) + return ISIS_OK; + /* 8.2.5.2 a) a match was detected */ if (isis_tlvs_area_addresses_match(iih->tlvs, iih->circuit->area->area_addrs)) { @@ -1157,7 +1164,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, circuit->u.bc.adjdb[level - 1])) return ISIS_OK; /* Silently discard */ } else { - if (!circuit->u.p2p.neighbor) { + if (!fabricd && !circuit->u.p2p.neighbor) { zlog_warn("no p2p neighbor on circuit %s", circuit->interface->name); return ISIS_OK; /* Silently discard */ @@ -1582,8 +1589,15 @@ int send_hello(struct isis_circuit *circuit, int level) && !circuit->disable_threeway_adj) { uint32_t ext_circuit_id = circuit->idx; if (circuit->u.p2p.neighbor) { + uint8_t threeway_state; + + if (fabricd_initial_sync_is_in_progress(circuit->area) + && fabricd_initial_sync_circuit(circuit->area) != circuit) + threeway_state = ISIS_THREEWAY_DOWN; + else + threeway_state = circuit->u.p2p.neighbor->threeway_state; isis_tlvs_add_threeway_adj(tlvs, - circuit->u.p2p.neighbor->threeway_state, + threeway_state, ext_circuit_id, circuit->u.p2p.neighbor->sysid, circuit->u.p2p.neighbor->ext_circuit_id); @@ -1889,8 +1903,9 @@ int send_l1_csnp(struct thread *thread) circuit->t_send_csnp[0] = NULL; - if (circuit->circ_type == CIRCUIT_T_BROADCAST - && circuit->u.bc.is_dr[0]) { + if ((circuit->circ_type == CIRCUIT_T_BROADCAST + && circuit->u.bc.is_dr[0]) + || circuit->circ_type == CIRCUIT_T_P2P) { send_csnp(circuit, 1); } /* set next timer thread */ @@ -1911,8 +1926,9 @@ int send_l2_csnp(struct thread *thread) circuit->t_send_csnp[1] = NULL; - if (circuit->circ_type == CIRCUIT_T_BROADCAST - && circuit->u.bc.is_dr[1]) { + if ((circuit->circ_type == CIRCUIT_T_BROADCAST + && circuit->u.bc.is_dr[1]) + || circuit->circ_type == CIRCUIT_T_P2P) { send_csnp(circuit, 2); } /* set next timer thread */ diff --git a/isisd/isisd.c b/isisd/isisd.c index 609ca450df..e8efa391e0 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -56,6 +56,7 @@ #include "isisd/isis_events.h" #include "isisd/isis_te.h" #include "isisd/isis_mt.h" +#include "isisd/fabricd.h" struct isis *isis = NULL; @@ -95,6 +96,7 @@ void isis_new(unsigned long process_id) */ /* isis->debugs = 0xFFFF; */ isisMplsTE.status = disable; /* Only support TE metric */ + QOBJ_REG(isis, isis); } @@ -156,6 +158,8 @@ struct isis_area *isis_area_create(const char *area_tag) listnode_add(isis->area_list, area); area->isis = isis; + if (fabricd) + area->fabricd = fabricd_new(); QOBJ_REG(area, isis_area); return area; diff --git a/isisd/isisd.h b/isisd/isisd.h index b76b0b7847..0e75b870a5 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -57,6 +57,8 @@ extern struct zebra_privs_t isisd_privs; /* #define EXTREME_DEBUG */ /* #define EXTREME_DICT_DEBUG */ +struct fabricd; + struct isis { unsigned long process_id; int sysid_set; @@ -111,6 +113,8 @@ struct isis_area { */ int lsp_regenerate_pending[ISIS_LEVELS]; + struct fabricd *fabricd; + /* * Configurables */ diff --git a/isisd/subdir.am b/isisd/subdir.am index 5593f2a4ed..cd8bfdbddc 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -43,6 +43,7 @@ noinst_HEADERS += \ isisd/isis_zebra.h \ isisd/isisd.h \ isisd/iso_checksum.h \ + isisd/fabricd.h \ # end LIBISIS_SOURCES = \ @@ -71,6 +72,7 @@ LIBISIS_SOURCES = \ isisd/isis_zebra.c \ isisd/isisd.c \ isisd/iso_checksum.c \ + isisd/fabricd.c \ # end ISIS_SOURCES = \ From b30e837b3f544836503ee479f02d749b7d3cf9e4 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 2 Apr 2018 18:39:46 +0200 Subject: [PATCH 13/84] fabricd: run a hop-by-hop spf OpenFabric uses an spf with the metric for all links set to one, both for flooding optimization and for fabric locality detection. So extend isisd's spf code to allow running it with such a metric and have it run whenever normal spf runs. Signed-off-by: Christian Franke --- isisd/fabricd.c | 34 ++++++++++++- isisd/fabricd.h | 6 ++- isisd/isis_spf.c | 130 +++++++++++++++++++++++++++++++++-------------- isisd/isis_spf.h | 4 ++ isisd/isisd.c | 5 +- 5 files changed, 137 insertions(+), 42 deletions(-) diff --git a/isisd/fabricd.c b/isisd/fabricd.c index d37e6a71d8..853b672075 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -26,6 +26,7 @@ #include "isisd/isis_circuit.h" #include "isisd/isis_misc.h" #include "isisd/isis_adjacency.h" +#include "isisd/isis_spf.h" DEFINE_MTYPE_STATIC(ISISD, FABRICD_STATE, "ISIS OpenFabric") @@ -45,16 +46,27 @@ struct fabricd { time_t initial_sync_start; struct isis_circuit *initial_sync_circuit; struct thread *initial_sync_timeout; + + struct isis_spftree *spftree; }; -struct fabricd *fabricd_new(void) +struct fabricd *fabricd_new(struct isis_area *area) { struct fabricd *rv = XCALLOC(MTYPE_FABRICD_STATE, sizeof(*rv)); rv->initial_sync_state = FABRICD_SYNC_PENDING; + rv->spftree = isis_spftree_new(area); return rv; }; +void fabricd_finish(struct fabricd *f) +{ + if (f->initial_sync_timeout) + thread_cancel(f->initial_sync_timeout); + + isis_spftree_del(f->spftree); +} + static int fabricd_initial_sync_timeout(struct thread *thread) { struct fabricd *f = THREAD_ARG(thread); @@ -134,3 +146,23 @@ void fabricd_initial_sync_finish(struct isis_area *area) thread_cancel(f->initial_sync_timeout); f->initial_sync_timeout = NULL; } + +void fabricd_run_spf(struct isis_area *area) +{ + struct fabricd *f = area->fabricd; + + if (!f) + return; + + isis_run_hopcount_spf(area, isis->sysid, f->spftree); +} + +struct isis_spftree *fabricd_spftree(struct isis_area *area) +{ + struct fabricd *f = area->fabricd; + + if (!f) + return NULL; + + return f->spftree; +} diff --git a/isisd/fabricd.h b/isisd/fabricd.h index 35a5bb6a3b..1707adf36e 100644 --- a/isisd/fabricd.h +++ b/isisd/fabricd.h @@ -26,11 +26,15 @@ struct fabricd; struct isis_circuit; struct isis_area; +struct isis_spftree; -struct fabricd *fabricd_new(void); +struct fabricd *fabricd_new(struct isis_area *area); +void fabricd_finish(struct fabricd *f); void fabricd_initial_sync_hello(struct isis_circuit *circuit); bool fabricd_initial_sync_is_in_progress(struct isis_area *area); struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area); void fabricd_initial_sync_finish(struct isis_area *area); +void fabricd_run_spf(struct isis_area *area); +struct isis_spftree *fabricd_spftree(struct isis_area *area); #endif diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 5b47d3e684..1e2052b652 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -56,6 +56,7 @@ #include "isis_csm.h" #include "isis_mt.h" #include "isis_tlvs.h" +#include "fabricd.h" DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_RUN, "ISIS SPF Run Info"); @@ -303,6 +304,7 @@ struct isis_spftree { int family; int level; enum spf_tree_id tree_id; + bool hopcount_metric; }; @@ -563,6 +565,9 @@ void spftree_area_adj_del(struct isis_area *area, struct isis_adjacency *adj) adj); } } + + if (fabricd_spftree(area) != NULL) + isis_spftree_adj_del(fabricd_spftree(area), adj); } /* @@ -722,6 +727,10 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype, assert(spftree && parent); + if (spftree->hopcount_metric + && !VTYPE_IS(vtype)) + return; + struct prefix_pair p; if (vtype >= VTYPE_IPREACH_INTERNAL) { memcpy(&p, id, sizeof(p)); @@ -889,7 +898,7 @@ lspfragloop: if (!pseudo_lsp && !memcmp(er->id, null_sysid, ISIS_SYS_ID_LEN)) continue; - dist = cost + er->metric; + dist = cost + (spftree->hopcount_metric ? 1 : er->metric); process_N(spftree, LSP_PSEUDO_ID(er->id) ? VTYPE_PSEUDO_TE_IS : VTYPE_NONPSEUDO_TE_IS, @@ -1037,7 +1046,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, /* * Add IP(v6) addresses of this circuit */ - if (spftree->family == AF_INET) { + if (spftree->family == AF_INET && !spftree->hopcount_metric) { memset(&ip_info, 0, sizeof(ip_info)); ip_info.dest.family = AF_INET; for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, ipnode, @@ -1050,7 +1059,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, &ip_info, NULL, 0, parent); } } - if (spftree->family == AF_INET6) { + if (spftree->family == AF_INET6 && !spftree->hopcount_metric) { memset(&ip_info, 0, sizeof(ip_info)); ip_info.dest.family = AF_INET6; for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, @@ -1094,6 +1103,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, LSP_PSEUDO_ID(lsp_id) = 0; isis_spf_add_local( spftree, VTYPE_ES, lsp_id, adj, + spftree->hopcount_metric ? 1 : circuit->te_metric [spftree->level - 1], parent); @@ -1111,6 +1121,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, ? VTYPE_NONPSEUDO_IS : VTYPE_NONPSEUDO_TE_IS, lsp_id, adj, + spftree->hopcount_metric ? 1 : circuit->te_metric [spftree->level - 1], parent); @@ -1180,10 +1191,10 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, circuit->circuit_id); continue; } - isis_spf_process_lsp( - spftree, lsp, - circuit->te_metric[spftree->level - 1], 0, - root_sysid, parent); + isis_spf_process_lsp(spftree, lsp, + spftree->hopcount_metric ? + 1 : circuit->te_metric[spftree->level - 1], + 0, root_sysid, parent); } else if (circuit->circ_type == CIRCUIT_T_P2P) { adj = circuit->u.p2p.neighbor; if (!adj || adj->adj_state != ISIS_ADJ_UP) @@ -1196,6 +1207,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, LSP_PSEUDO_ID(lsp_id) = 0; isis_spf_add_local( spftree, VTYPE_ES, lsp_id, adj, + spftree->hopcount_metric ? 1 : circuit->te_metric[spftree->level - 1], parent); break; @@ -1215,6 +1227,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, ? VTYPE_NONPSEUDO_IS : VTYPE_NONPSEUDO_TE_IS, lsp_id, adj, + spftree->hopcount_metric ? 1 : circuit->te_metric [spftree->level - 1], parent); @@ -1275,7 +1288,8 @@ static void add_to_paths(struct isis_spftree *spftree, } static void init_spt(struct isis_spftree *spftree, int mtid, int level, - int family, enum spf_tree_id tree_id) + int family, enum spf_tree_id tree_id, + bool hopcount_metric) { isis_vertex_queue_clear(&spftree->tents); isis_vertex_queue_clear(&spftree->paths); @@ -1284,7 +1298,64 @@ static void init_spt(struct isis_spftree *spftree, int mtid, int level, spftree->level = level; spftree->family = family; spftree->tree_id = tree_id; - return; + spftree->hopcount_metric = hopcount_metric; +} + +static void isis_spf_loop(struct isis_spftree *spftree, + uint8_t *root_sysid) +{ + struct isis_vertex *vertex; + uint8_t lsp_id[ISIS_SYS_ID_LEN + 2]; + struct isis_lsp *lsp; + + while (isis_vertex_queue_count(&spftree->tents)) { + vertex = isis_vertex_queue_pop(&spftree->tents); + +#ifdef EXTREME_DEBUG + zlog_debug( + "ISIS-Spf: get TENT node %s %s depth %d dist %d to PATHS", + print_sys_hostname(vertex->N.id), + vtype2string(vertex->type), vertex->depth, vertex->d_N); +#endif /* EXTREME_DEBUG */ + + add_to_paths(spftree, vertex); + if (VTYPE_IS(vertex->type)) { + memcpy(lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1); + LSP_FRAGMENT(lsp_id) = 0; + lsp = lsp_search(lsp_id, spftree->area->lspdb[spftree->level - 1]); + if (lsp && lsp->hdr.rem_lifetime != 0) { + isis_spf_process_lsp(spftree, lsp, vertex->d_N, + vertex->depth, root_sysid, + vertex); + } else { + zlog_warn("ISIS-Spf: No LSP found for %s", + rawlspid_print(lsp_id)); + } + } + } +} + +struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, + uint8_t *sysid, + struct isis_spftree *spftree) +{ + if (!spftree) + spftree = isis_spftree_new(area); + + init_spt(spftree, ISIS_MT_IPV4_UNICAST, ISIS_LEVEL2, + AF_INET, SPFTREE_IPV4, true); + if (!memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN)) { + /* If we are running locally, initialize with information from adjacencies */ + struct isis_vertex *root = isis_spf_add_root(spftree, sysid); + isis_spf_preload_tent(spftree, sysid, root); + } else { + isis_vertex_queue_insert(&spftree->tents, isis_vertex_new( + sysid, VTYPE_NONPSEUDO_TE_IS)); + } + + isis_spf_loop(spftree, sysid); + + return spftree; } static int isis_run_spf(struct isis_area *area, int level, @@ -1292,11 +1363,8 @@ static int isis_run_spf(struct isis_area *area, int level, uint8_t *sysid, struct timeval *nowtv) { int retval = ISIS_OK; - struct isis_vertex *vertex; struct isis_vertex *root_vertex; struct isis_spftree *spftree = area->spftree[tree_id][level - 1]; - uint8_t lsp_id[ISIS_SYS_ID_LEN + 2]; - struct isis_lsp *lsp; struct timeval time_now; unsigned long long start_time, end_time; uint16_t mtid = 0; @@ -1330,7 +1398,7 @@ static int isis_run_spf(struct isis_area *area, int level, /* * C.2.5 Step 0 */ - init_spt(spftree, mtid, level, family, tree_id); + init_spt(spftree, mtid, level, family, tree_id, false); /* a) */ root_vertex = isis_spf_add_root(spftree, sysid); /* b) */ @@ -1350,32 +1418,7 @@ static int isis_run_spf(struct isis_area *area, int level, print_sys_hostname(sysid)); } - while (isis_vertex_queue_count(&spftree->tents)) { - vertex = isis_vertex_queue_pop(&spftree->tents); - -#ifdef EXTREME_DEBUG - zlog_debug( - "ISIS-Spf: get TENT node %s %s depth %d dist %d to PATHS", - print_sys_hostname(vertex->N.id), - vtype2string(vertex->type), vertex->depth, vertex->d_N); -#endif /* EXTREME_DEBUG */ - - add_to_paths(spftree, vertex); - if (VTYPE_IS(vertex->type)) { - memcpy(lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1); - LSP_FRAGMENT(lsp_id) = 0; - lsp = lsp_search(lsp_id, area->lspdb[level - 1]); - if (lsp && lsp->hdr.rem_lifetime != 0) { - isis_spf_process_lsp(spftree, lsp, vertex->d_N, - vertex->depth, sysid, - vertex); - } else { - zlog_warn("ISIS-Spf: No LSP found for %s", - rawlspid_print(lsp_id)); - } - } - } - + isis_spf_loop(spftree, sysid); out: spftree->runcount++; spftree->last_run_timestamp = time(NULL); @@ -1446,6 +1489,8 @@ static int isis_run_spf_cb(struct thread *thread) for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF); + fabricd_run_spf(area); + return retval; } @@ -1666,6 +1711,13 @@ DEFUN (show_isis_topology, } } + if (fabricd_spftree(area)) { + vty_out(vty, + "IS-IS paths to level-2 routers with hop-by-hop metric\n"); + isis_print_paths(vty, &fabricd_spftree(area)->paths, isis->sysid); + vty_out(vty, "\n"); + } + vty_out(vty, "\n"); } diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h index 9a73ca8783..0532bd5465 100644 --- a/isisd/isis_spf.h +++ b/isisd/isis_spf.h @@ -37,4 +37,8 @@ void spftree_area_adj_del(struct isis_area *area, struct isis_adjacency *adj); int isis_spf_schedule(struct isis_area *area, int level); void isis_spf_cmds_init(void); void isis_spf_print(struct isis_spftree *spftree, struct vty *vty); +struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, + uint8_t *sysid, + struct isis_spftree *spftree); + #endif /* _ZEBRA_ISIS_SPF_H */ diff --git a/isisd/isisd.c b/isisd/isisd.c index e8efa391e0..cc5463ffe7 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -159,7 +159,7 @@ struct isis_area *isis_area_create(const char *area_tag) area->isis = isis; if (fabricd) - area->fabricd = fabricd_new(); + area->fabricd = fabricd_new(area); QOBJ_REG(area, isis_area); return area; @@ -216,6 +216,9 @@ int isis_area_destroy(struct vty *vty, const char *area_tag) QOBJ_UNREG(area); + if (fabricd) + fabricd_finish(area->fabricd); + if (area->circuit_list) { for (ALL_LIST_ELEMENTS(area->circuit_list, node, nnode, circuit)) { From cbd8e49e3ead018b67f630752a37c44fae0697cc Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 10 May 2018 18:52:17 +0200 Subject: [PATCH 14/84] isisd: move spf datastructures to a header, to share with fabricd By moving the spf datastructures to a header, fabricd can access the results of the spf run for flooding optimization or fabric locality calculation. While this was deemed a sensible choice in this case, when compared with the option of adding a lot of OpenFabric specific code to isis_spf.c, the datastructures should still not be accessed randomly all over the code base. To make this more clear, the new header was called isis_spf_private.h (Think of a friend class) Signed-off-by: Christian Franke --- isisd/isis_spf.c | 312 ++-------------------------------- isisd/isis_spf_private.h | 351 +++++++++++++++++++++++++++++++++++++++ isisd/subdir.am | 1 + 3 files changed, 364 insertions(+), 300 deletions(-) create mode 100644 isisd/isis_spf_private.h diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 1e2052b652..79b3103237 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -31,14 +31,10 @@ #include "command.h" #include "memory.h" #include "prefix.h" -#include "hash.h" #include "if.h" #include "table.h" #include "spf_backoff.h" -#include "jhash.h" -#include "skiplist.h" #include "srcdest_table.h" -#include "lib_errors.h" #include "isis_constants.h" #include "isis_common.h" @@ -57,257 +53,10 @@ #include "isis_mt.h" #include "isis_tlvs.h" #include "fabricd.h" +#include "isis_spf_private.h" DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_RUN, "ISIS SPF Run Info"); -enum vertextype { - VTYPE_PSEUDO_IS = 1, - VTYPE_PSEUDO_TE_IS, - VTYPE_NONPSEUDO_IS, - VTYPE_NONPSEUDO_TE_IS, - VTYPE_ES, - VTYPE_IPREACH_INTERNAL, - VTYPE_IPREACH_EXTERNAL, - VTYPE_IPREACH_TE, - VTYPE_IP6REACH_INTERNAL, - VTYPE_IP6REACH_EXTERNAL -}; - -#define VTYPE_IS(t) ((t) >= VTYPE_PSEUDO_IS && (t) <= VTYPE_NONPSEUDO_TE_IS) -#define VTYPE_ES(t) ((t) == VTYPE_ES) -#define VTYPE_IP(t) ((t) >= VTYPE_IPREACH_INTERNAL && (t) <= VTYPE_IP6REACH_EXTERNAL) - -struct prefix_pair { - struct prefix dest; - struct prefix_ipv6 src; -}; - -/* - * Triple - */ -union isis_N { - uint8_t id[ISIS_SYS_ID_LEN + 1]; - struct prefix_pair ip; -}; -struct isis_vertex { - enum vertextype type; - union isis_N N; - uint32_t d_N; /* d(N) Distance from this IS */ - uint16_t depth; /* The depth in the imaginary tree */ - struct list *Adj_N; /* {Adj(N)} next hop or neighbor list */ - struct list *parents; /* list of parents for ECMP */ - uint64_t insert_counter; -}; - -/* Vertex Queue and associated functions */ - -struct isis_vertex_queue { - union { - struct skiplist *slist; - struct list *list; - } l; - struct hash *hash; - uint64_t insert_counter; -}; - -static unsigned isis_vertex_queue_hash_key(void *vp) -{ - struct isis_vertex *vertex = vp; - - if (VTYPE_IP(vertex->type)) { - uint32_t key; - - key = prefix_hash_key(&vertex->N.ip.dest); - key = jhash_1word(prefix_hash_key(&vertex->N.ip.src), key); - return key; - } - - return jhash(vertex->N.id, ISIS_SYS_ID_LEN + 1, 0x55aa5a5a); -} - -static int isis_vertex_queue_hash_cmp(const void *a, const void *b) -{ - const struct isis_vertex *va = a, *vb = b; - - if (va->type != vb->type) - return 0; - - if (VTYPE_IP(va->type)) { - if (prefix_cmp(&va->N.ip.dest, &vb->N.ip.dest)) - return 0; - - return prefix_cmp((struct prefix *)&va->N.ip.src, - (struct prefix *)&vb->N.ip.src) == 0; - } - - return memcmp(va->N.id, vb->N.id, ISIS_SYS_ID_LEN + 1) == 0; -} - -/* - * Compares vertizes for sorting in the TENT list. Returns true - * if candidate should be considered before current, false otherwise. - */ -static int isis_vertex_queue_tent_cmp(void *a, void *b) -{ - struct isis_vertex *va = a; - struct isis_vertex *vb = b; - - if (va->d_N < vb->d_N) - return -1; - - if (va->d_N > vb->d_N) - return 1; - - if (va->type < vb->type) - return -1; - - if (va->type > vb->type) - return 1; - - if (va->insert_counter < vb->insert_counter) - return -1; - - if (va->insert_counter > vb->insert_counter) - return 1; - - return 0; -} - -static struct skiplist *isis_vertex_queue_skiplist(void) -{ - return skiplist_new(0, isis_vertex_queue_tent_cmp, NULL); -} - -static void isis_vertex_queue_init(struct isis_vertex_queue *queue, - const char *name, bool ordered) -{ - if (ordered) { - queue->insert_counter = 1; - queue->l.slist = isis_vertex_queue_skiplist(); - } else { - queue->insert_counter = 0; - queue->l.list = list_new(); - } - queue->hash = hash_create(isis_vertex_queue_hash_key, - isis_vertex_queue_hash_cmp, name); -} - -static void isis_vertex_del(struct isis_vertex *vertex); - -static void isis_vertex_queue_clear(struct isis_vertex_queue *queue) -{ - hash_clean(queue->hash, NULL); - - if (queue->insert_counter) { - struct isis_vertex *vertex; - while (0 == skiplist_first(queue->l.slist, NULL, - (void **)&vertex)) { - isis_vertex_del(vertex); - skiplist_delete_first(queue->l.slist); - } - queue->insert_counter = 1; - } else { - queue->l.list->del = (void (*)(void *))isis_vertex_del; - list_delete_all_node(queue->l.list); - queue->l.list->del = NULL; - } -} - -static void isis_vertex_queue_free(struct isis_vertex_queue *queue) -{ - isis_vertex_queue_clear(queue); - - hash_free(queue->hash); - queue->hash = NULL; - - if (queue->insert_counter) { - skiplist_free(queue->l.slist); - queue->l.slist = NULL; - } else - list_delete_and_null(&queue->l.list); -} - -static unsigned int isis_vertex_queue_count(struct isis_vertex_queue *queue) -{ - return hashcount(queue->hash); -} - -static void isis_vertex_queue_append(struct isis_vertex_queue *queue, - struct isis_vertex *vertex) -{ - assert(!queue->insert_counter); - - listnode_add(queue->l.list, vertex); - - struct isis_vertex *inserted; - - inserted = hash_get(queue->hash, vertex, hash_alloc_intern); - assert(inserted == vertex); -} - -static void isis_vertex_queue_insert(struct isis_vertex_queue *queue, - struct isis_vertex *vertex) -{ - assert(queue->insert_counter); - vertex->insert_counter = queue->insert_counter++; - assert(queue->insert_counter != (uint64_t)-1); - - skiplist_insert(queue->l.slist, vertex, vertex); - - struct isis_vertex *inserted; - inserted = hash_get(queue->hash, vertex, hash_alloc_intern); - assert(inserted == vertex); -} - -static struct isis_vertex * -isis_vertex_queue_pop(struct isis_vertex_queue *queue) -{ - assert(queue->insert_counter); - - struct isis_vertex *rv; - - if (skiplist_first(queue->l.slist, NULL, (void **)&rv)) - return NULL; - - skiplist_delete_first(queue->l.slist); - hash_release(queue->hash, rv); - - return rv; -} - -static void isis_vertex_queue_delete(struct isis_vertex_queue *queue, - struct isis_vertex *vertex) -{ - assert(queue->insert_counter); - - skiplist_delete(queue->l.slist, vertex, vertex); - hash_release(queue->hash, vertex); -} - -#define ALL_QUEUE_ELEMENTS_RO(queue, node, data) \ - ALL_LIST_ELEMENTS_RO((queue)->l.list, node, data) - - -/* End of vertex queue definitions */ - -struct isis_spftree { - struct isis_vertex_queue paths; /* the SPT */ - struct isis_vertex_queue tents; /* TENT */ - struct route_table *route_table; - struct isis_area *area; /* back pointer to area */ - unsigned int runcount; /* number of runs since uptime */ - time_t last_run_timestamp; /* last run timestamp as wall time for display */ - time_t last_run_monotime; /* last run as monotime for scheduling */ - time_t last_run_duration; /* last run duration in msec */ - - uint16_t mtid; - int family; - int level; - enum spf_tree_id tree_id; - bool hopcount_metric; -}; - - /* * supports the given af ? */ @@ -430,20 +179,6 @@ static const char *vid2string(struct isis_vertex *vertex, char *buff, int size) return "UNKNOWN"; } -static void isis_vertex_id_init(struct isis_vertex *vertex, union isis_N *n, - enum vertextype vtype) -{ - vertex->type = vtype; - - if (VTYPE_IS(vtype) || VTYPE_ES(vtype)) { - memcpy(vertex->N.id, n->id, ISIS_SYS_ID_LEN + 1); - } else if (VTYPE_IP(vtype)) { - memcpy(&vertex->N.ip, &n->ip, sizeof(n->ip)); - } else { - flog_err(LIB_ERR_DEVELOPMENT, "Unknown Vertex Type"); - } -} - static struct isis_vertex *isis_vertex_new(union isis_N *n, enum vertextype vtype) { @@ -459,17 +194,6 @@ static struct isis_vertex *isis_vertex_new(union isis_N *n, return vertex; } -static void isis_vertex_del(struct isis_vertex *vertex) -{ - list_delete_and_null(&vertex->Adj_N); - list_delete_and_null(&vertex->parents); - - memset(vertex, 0, sizeof(struct isis_vertex)); - XFREE(MTYPE_ISIS_VERTEX, vertex); - - return; -} - static void isis_vertex_adj_del(struct isis_vertex *vertex, struct isis_adjacency *adj) { @@ -626,16 +350,6 @@ static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree, return vertex; } -static struct isis_vertex *isis_find_vertex(struct isis_vertex_queue *queue, - union isis_N *n, - enum vertextype vtype) -{ - struct isis_vertex querier; - - isis_vertex_id_init(&querier, n, vtype); - return hash_lookup(queue->hash, &querier); -} - /* * Add a vertex to TENT sorted by cost and by vertextype on tie break situation */ @@ -1305,7 +1019,6 @@ static void isis_spf_loop(struct isis_spftree *spftree, uint8_t *root_sysid) { struct isis_vertex *vertex; - uint8_t lsp_id[ISIS_SYS_ID_LEN + 2]; struct isis_lsp *lsp; while (isis_vertex_queue_count(&spftree->tents)) { @@ -1319,19 +1032,18 @@ static void isis_spf_loop(struct isis_spftree *spftree, #endif /* EXTREME_DEBUG */ add_to_paths(spftree, vertex); - if (VTYPE_IS(vertex->type)) { - memcpy(lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1); - LSP_FRAGMENT(lsp_id) = 0; - lsp = lsp_search(lsp_id, spftree->area->lspdb[spftree->level - 1]); - if (lsp && lsp->hdr.rem_lifetime != 0) { - isis_spf_process_lsp(spftree, lsp, vertex->d_N, - vertex->depth, root_sysid, - vertex); - } else { - zlog_warn("ISIS-Spf: No LSP found for %s", - rawlspid_print(lsp_id)); - } + if (!VTYPE_IS(vertex->type)) + continue; + + lsp = lsp_for_vertex(spftree, vertex); + if (!lsp) { + zlog_warn("ISIS-Spf: No LSP found for %s", + rawlspid_print(vertex->N.id)); /* FIXME */ + continue; } + + isis_spf_process_lsp(spftree, lsp, vertex->d_N, vertex->depth, + root_sysid, vertex); } } diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h new file mode 100644 index 0000000000..3cd6a005be --- /dev/null +++ b/isisd/isis_spf_private.h @@ -0,0 +1,351 @@ +/* + * IS-IS Rout(e)ing protocol - isis_spf_private.h + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * Copyright (C) 2017 Christian Franke + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public Licenseas published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef ISIS_SPF_PRIVATE_H +#define ISIS_SPF_PRIVATE_H + +#include "hash.h" +#include "jhash.h" +#include "skiplist.h" +#include "lib_errors.h" + +enum vertextype { + VTYPE_PSEUDO_IS = 1, + VTYPE_PSEUDO_TE_IS, + VTYPE_NONPSEUDO_IS, + VTYPE_NONPSEUDO_TE_IS, + VTYPE_ES, + VTYPE_IPREACH_INTERNAL, + VTYPE_IPREACH_EXTERNAL, + VTYPE_IPREACH_TE, + VTYPE_IP6REACH_INTERNAL, + VTYPE_IP6REACH_EXTERNAL +}; + +#define VTYPE_IS(t) ((t) >= VTYPE_PSEUDO_IS && (t) <= VTYPE_NONPSEUDO_TE_IS) +#define VTYPE_ES(t) ((t) == VTYPE_ES) +#define VTYPE_IP(t) ((t) >= VTYPE_IPREACH_INTERNAL && (t) <= VTYPE_IP6REACH_EXTERNAL) + +struct prefix_pair { + struct prefix dest; + struct prefix_ipv6 src; +}; + +/* + * Triple + */ +union isis_N { + uint8_t id[ISIS_SYS_ID_LEN + 1]; + struct prefix_pair ip; +}; +struct isis_vertex { + enum vertextype type; + union isis_N N; + uint32_t d_N; /* d(N) Distance from this IS */ + uint16_t depth; /* The depth in the imaginary tree */ + struct list *Adj_N; /* {Adj(N)} next hop or neighbor list */ + struct list *parents; /* list of parents for ECMP */ + uint64_t insert_counter; +}; + +/* Vertex Queue and associated functions */ + +struct isis_vertex_queue { + union { + struct skiplist *slist; + struct list *list; + } l; + struct hash *hash; + uint64_t insert_counter; +}; + +__attribute__((__unused__)) +static unsigned isis_vertex_queue_hash_key(void *vp) +{ + struct isis_vertex *vertex = vp; + + if (VTYPE_IP(vertex->type)) { + uint32_t key; + + key = prefix_hash_key(&vertex->N.ip.dest); + key = jhash_1word(prefix_hash_key(&vertex->N.ip.src), key); + return key; + } + + return jhash(vertex->N.id, ISIS_SYS_ID_LEN + 1, 0x55aa5a5a); +} + +__attribute__((__unused__)) +static int isis_vertex_queue_hash_cmp(const void *a, const void *b) +{ + const struct isis_vertex *va = a, *vb = b; + + if (va->type != vb->type) + return 0; + + if (VTYPE_IP(va->type)) { + if (prefix_cmp(&va->N.ip.dest, &vb->N.ip.dest)) + return 0; + + return prefix_cmp((struct prefix *)&va->N.ip.src, + (struct prefix *)&vb->N.ip.src) == 0; + } + + return memcmp(va->N.id, vb->N.id, ISIS_SYS_ID_LEN + 1) == 0; +} + +/* + * Compares vertizes for sorting in the TENT list. Returns true + * if candidate should be considered before current, false otherwise. + */ +__attribute__((__unused__)) +static int isis_vertex_queue_tent_cmp(void *a, void *b) +{ + struct isis_vertex *va = a; + struct isis_vertex *vb = b; + + if (va->d_N < vb->d_N) + return -1; + + if (va->d_N > vb->d_N) + return 1; + + if (va->type < vb->type) + return -1; + + if (va->type > vb->type) + return 1; + + if (va->insert_counter < vb->insert_counter) + return -1; + + if (va->insert_counter > vb->insert_counter) + return 1; + + return 0; +} + +__attribute__((__unused__)) +static struct skiplist *isis_vertex_queue_skiplist(void) +{ + return skiplist_new(0, isis_vertex_queue_tent_cmp, NULL); +} + +__attribute__((__unused__)) +static void isis_vertex_queue_init(struct isis_vertex_queue *queue, + const char *name, bool ordered) +{ + if (ordered) { + queue->insert_counter = 1; + queue->l.slist = isis_vertex_queue_skiplist(); + } else { + queue->insert_counter = 0; + queue->l.list = list_new(); + } + queue->hash = hash_create(isis_vertex_queue_hash_key, + isis_vertex_queue_hash_cmp, name); +} + +__attribute__((__unused__)) +static void isis_vertex_del(struct isis_vertex *vertex) +{ + list_delete_and_null(&vertex->Adj_N); + list_delete_and_null(&vertex->parents); + + memset(vertex, 0, sizeof(struct isis_vertex)); + XFREE(MTYPE_ISIS_VERTEX, vertex); +} + +__attribute__((__unused__)) +static void isis_vertex_queue_clear(struct isis_vertex_queue *queue) +{ + hash_clean(queue->hash, NULL); + + if (queue->insert_counter) { + struct isis_vertex *vertex; + while (0 == skiplist_first(queue->l.slist, NULL, + (void **)&vertex)) { + isis_vertex_del(vertex); + skiplist_delete_first(queue->l.slist); + } + queue->insert_counter = 1; + } else { + queue->l.list->del = (void (*)(void *))isis_vertex_del; + list_delete_all_node(queue->l.list); + queue->l.list->del = NULL; + } +} + +__attribute__((__unused__)) +static void isis_vertex_queue_free(struct isis_vertex_queue *queue) +{ + isis_vertex_queue_clear(queue); + + hash_free(queue->hash); + queue->hash = NULL; + + if (queue->insert_counter) { + skiplist_free(queue->l.slist); + queue->l.slist = NULL; + } else + list_delete_and_null(&queue->l.list); +} + +__attribute__((__unused__)) +static unsigned int isis_vertex_queue_count(struct isis_vertex_queue *queue) +{ + return hashcount(queue->hash); +} + +__attribute__((__unused__)) +static void isis_vertex_queue_append(struct isis_vertex_queue *queue, + struct isis_vertex *vertex) +{ + assert(!queue->insert_counter); + + listnode_add(queue->l.list, vertex); + + struct isis_vertex *inserted; + + inserted = hash_get(queue->hash, vertex, hash_alloc_intern); + assert(inserted == vertex); +} + +__attribute__((__unused__)) +static struct isis_vertex *isis_vertex_queue_last(struct isis_vertex_queue *queue) +{ + assert(!queue->insert_counter); + + return listgetdata(listtail(queue->l.list)); +} + +__attribute__((__unused__)) +static void isis_vertex_queue_insert(struct isis_vertex_queue *queue, + struct isis_vertex *vertex) +{ + assert(queue->insert_counter); + vertex->insert_counter = queue->insert_counter++; + assert(queue->insert_counter != (uint64_t)-1); + + skiplist_insert(queue->l.slist, vertex, vertex); + + struct isis_vertex *inserted; + inserted = hash_get(queue->hash, vertex, hash_alloc_intern); + assert(inserted == vertex); +} + +__attribute__((__unused__)) +static struct isis_vertex * +isis_vertex_queue_pop(struct isis_vertex_queue *queue) +{ + assert(queue->insert_counter); + + struct isis_vertex *rv; + + if (skiplist_first(queue->l.slist, NULL, (void **)&rv)) + return NULL; + + skiplist_delete_first(queue->l.slist); + hash_release(queue->hash, rv); + + return rv; +} + +__attribute__((__unused__)) +static void isis_vertex_queue_delete(struct isis_vertex_queue *queue, + struct isis_vertex *vertex) +{ + assert(queue->insert_counter); + + skiplist_delete(queue->l.slist, vertex, vertex); + hash_release(queue->hash, vertex); +} + +#define ALL_QUEUE_ELEMENTS_RO(queue, node, data) \ + ALL_LIST_ELEMENTS_RO((queue)->l.list, node, data) + +/* End of vertex queue definitions */ + +struct isis_spftree { + struct isis_vertex_queue paths; /* the SPT */ + struct isis_vertex_queue tents; /* TENT */ + struct route_table *route_table; + struct isis_area *area; /* back pointer to area */ + unsigned int runcount; /* number of runs since uptime */ + time_t last_run_timestamp; /* last run timestamp as wall time for display */ + time_t last_run_monotime; /* last run as monotime for scheduling */ + time_t last_run_duration; /* last run duration in msec */ + + uint16_t mtid; + int family; + int level; + enum spf_tree_id tree_id; + bool hopcount_metric; +}; + +__attribute__((__unused__)) +static void isis_vertex_id_init(struct isis_vertex *vertex, union isis_N *n, + enum vertextype vtype) +{ + vertex->type = vtype; + + if (VTYPE_IS(vtype) || VTYPE_ES(vtype)) { + memcpy(vertex->N.id, n->id, ISIS_SYS_ID_LEN + 1); + } else if (VTYPE_IP(vtype)) { + memcpy(&vertex->N.ip, &n->ip, sizeof(n->ip)); + } else { + flog_err(LIB_ERR_DEVELOPMENT, "Unknown Vertex Type"); + } +} + +__attribute__((__unused__)) +static struct isis_vertex *isis_find_vertex(struct isis_vertex_queue *queue, + union isis_N *n, + enum vertextype vtype) +{ + struct isis_vertex querier; + + isis_vertex_id_init(&querier, n, vtype); + return hash_lookup(queue->hash, &querier); +} + +__attribute__((__unused__)) +static struct isis_lsp *lsp_for_vertex(struct isis_spftree *spftree, + struct isis_vertex *vertex) +{ + uint8_t lsp_id[ISIS_SYS_ID_LEN + 2]; + + assert(VTYPE_IS(vertex->type)); + + memcpy(lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1); + LSP_FRAGMENT(lsp_id) = 0; + + dict_t *lspdb = spftree->area->lspdb[spftree->level - 1]; + struct isis_lsp *lsp = lsp_search(lsp_id, lspdb); + + if (lsp && lsp->hdr.rem_lifetime != 0) + return lsp; + + return NULL; +} + +#endif diff --git a/isisd/subdir.am b/isisd/subdir.am index cd8bfdbddc..792b0df6c2 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -37,6 +37,7 @@ noinst_HEADERS += \ isisd/isis_route.h \ isisd/isis_routemap.h \ isisd/isis_spf.h \ + isisd/isis_spf_private.h \ isisd/isis_te.h \ isisd/isis_tlvs.h \ isisd/isis_vty_common.h \ From 41a145f18d621e24a1c4b8ab17683d14d9190fd3 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 6 Apr 2018 13:49:38 +0200 Subject: [PATCH 15/84] fabricd: Add support for TLV 150 (Spine-Leaf-Extension) To flood the tier calculated by the fabric locality detection, OpenFabric makes use of TLV 150, defined in draft-shen-isis-spine-leaf-ext-06, so add support for that TLV. Signed-off-by: Christian Franke --- isisd/isis_tlvs.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++ isisd/isis_tlvs.h | 17 ++++++ 2 files changed, 170 insertions(+) diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index a433fcdb41..44beb45e43 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -1329,6 +1329,126 @@ static int unpack_tlv_dynamic_hostname(enum isis_tlv_context context, return 0; } +/* Functions related to TLV 150 Spine-Leaf-Extension */ + +static struct isis_spine_leaf *copy_tlv_spine_leaf( + const struct isis_spine_leaf *spine_leaf) +{ + if (!spine_leaf) + return NULL; + + struct isis_spine_leaf *rv = XMALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + memcpy(rv, spine_leaf, sizeof(*rv)); + + return rv; +} + +static void format_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf, + struct sbuf *buf, int indent) +{ + if (!spine_leaf) + return; + + sbuf_push(buf, indent, "Spine-Leaf-Extension:\n"); + if (spine_leaf->has_tier) { + if (spine_leaf->tier == ISIS_TIER_UNDEFINED) { + sbuf_push(buf, indent, " Tier: undefined\n"); + } else { + sbuf_push(buf, indent, " Tier: %" PRIu8 "\n", + spine_leaf->tier); + } + } + + sbuf_push(buf, indent, " Flags:%s%s%s\n", + spine_leaf->is_leaf ? " LEAF" : "", + spine_leaf->is_spine ? " SPINE" : "", + spine_leaf->is_backup ? " BACKUP" : ""); + +} + +static void free_tlv_spine_leaf(struct isis_spine_leaf *spine_leaf) +{ + XFREE(MTYPE_ISIS_TLV, spine_leaf); +} + +#define ISIS_SPINE_LEAF_FLAG_TIER 0x08 +#define ISIS_SPINE_LEAF_FLAG_BACKUP 0x04 +#define ISIS_SPINE_LEAF_FLAG_SPINE 0x02 +#define ISIS_SPINE_LEAF_FLAG_LEAF 0x01 + +static int pack_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf, + struct stream *s) +{ + if (!spine_leaf) + return 0; + + uint8_t tlv_len = 2; + + if (STREAM_WRITEABLE(s) < (unsigned)(2 + tlv_len)) + return 1; + + stream_putc(s, ISIS_TLV_SPINE_LEAF_EXT); + stream_putc(s, tlv_len); + + uint16_t spine_leaf_flags = 0; + + if (spine_leaf->has_tier) { + spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_TIER; + spine_leaf_flags |= spine_leaf->tier << 12; + } + + if (spine_leaf->is_leaf) + spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_LEAF; + + if (spine_leaf->is_spine) + spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_SPINE; + + if (spine_leaf->is_backup) + spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_BACKUP; + + stream_putw(s, spine_leaf_flags); + + return 0; +} + +static int unpack_tlv_spine_leaf(enum isis_tlv_context context, + uint8_t tlv_type, uint8_t tlv_len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + + sbuf_push(log, indent, "Unpacking Spine Leaf Extension TLV...\n"); + if (tlv_len < 2) { + sbuf_push(log, indent, "WARNING: Unexepected TLV size\n"); + stream_forward_getp(s, tlv_len); + return 0; + } + + if (tlvs->spine_leaf) { + sbuf_push(log, indent, + "WARNING: Spine Leaf Extension TLV present multiple times.\n"); + stream_forward_getp(s, tlv_len); + return 0; + } + + tlvs->spine_leaf = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->spine_leaf)); + + uint16_t spine_leaf_flags = stream_getw(s); + + if (spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_TIER) { + tlvs->spine_leaf->has_tier = true; + tlvs->spine_leaf->tier = spine_leaf_flags >> 12; + } + + tlvs->spine_leaf->is_leaf = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_LEAF; + tlvs->spine_leaf->is_spine = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_SPINE; + tlvs->spine_leaf->is_backup = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_BACKUP; + + stream_forward_getp(s, tlv_len - 2); + return 0; +} + /* Functions related to TLV 240 P2P Three-Way Adjacency */ const char *isis_threeway_state_name(enum isis_threeway_state state) @@ -2187,6 +2307,8 @@ struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs) rv->threeway_adj = copy_tlv_threeway_adj(tlvs->threeway_adj); + rv->spine_leaf = copy_tlv_spine_leaf(tlvs->spine_leaf); + return rv; } @@ -2250,6 +2372,8 @@ static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, int indent) &tlvs->mt_ipv6_reach, buf, indent); format_tlv_threeway_adj(tlvs->threeway_adj, buf, indent); + + format_tlv_spine_leaf(tlvs->spine_leaf, buf, indent); } const char *isis_format_tlvs(struct isis_tlvs *tlvs) @@ -2301,6 +2425,7 @@ void isis_free_tlvs(struct isis_tlvs *tlvs) free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH, &tlvs->mt_ipv6_reach); free_tlv_threeway_adj(tlvs->threeway_adj); + free_tlv_spine_leaf(tlvs->spine_leaf); XFREE(MTYPE_ISIS_TLV, tlvs); } @@ -2480,6 +2605,14 @@ static int pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream, copy_tlv_threeway_adj(tlvs->threeway_adj); } + rv = pack_tlv_spine_leaf(tlvs->spine_leaf, stream); + if (rv) + return rv; + if (fragment_tlvs) { + fragment_tlvs->spine_leaf = + copy_tlv_spine_leaf(tlvs->spine_leaf); + } + for (size_t pack_idx = 0; pack_idx < array_size(pack_order); pack_idx++) { rv = handle_pack_entry(&pack_order[pack_idx], tlvs, stream, @@ -2679,6 +2812,7 @@ ITEM_TLV_OPS(ipv4_address, "TLV 132 IPv4 Interface Address"); TLV_OPS(te_router_id, "TLV 134 TE Router ID"); ITEM_TLV_OPS(extended_ip_reach, "TLV 135 Extended IP Reachability"); TLV_OPS(dynamic_hostname, "TLV 137 Dynamic Hostname"); +TLV_OPS(spine_leaf, "TLV 150 Spine Leaf Extensions"); ITEM_TLV_OPS(mt_router_info, "TLV 229 MT Router Information"); TLV_OPS(threeway_adj, "TLV 240 P2P Three-Way Adjacency"); ITEM_TLV_OPS(ipv6_address, "TLV 232 IPv6 Interface Address"); @@ -2703,6 +2837,7 @@ static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = { [ISIS_TLV_EXTENDED_IP_REACH] = &tlv_extended_ip_reach_ops, [ISIS_TLV_MT_IP_REACH] = &tlv_extended_ip_reach_ops, [ISIS_TLV_DYNAMIC_HOSTNAME] = &tlv_dynamic_hostname_ops, + [ISIS_TLV_SPINE_LEAF_EXT] = &tlv_spine_leaf_ops, [ISIS_TLV_MT_ROUTER_INFO] = &tlv_mt_router_info_ops, [ISIS_TLV_THREE_WAY_ADJ] = &tlv_threeway_adj_ops, [ISIS_TLV_IPV6_ADDRESS] = &tlv_ipv6_address_ops, @@ -3239,6 +3374,24 @@ void isis_tlvs_add_threeway_adj(struct isis_tlvs *tlvs, } } +void isis_tlvs_add_spine_leaf(struct isis_tlvs *tlvs, uint8_t tier, + bool has_tier, bool is_leaf, bool is_spine, + bool is_backup) +{ + assert(!tlvs->spine_leaf); + + tlvs->spine_leaf = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->spine_leaf)); + + if (has_tier) { + tlvs->spine_leaf->tier = tier; + } + + tlvs->spine_leaf->has_tier = has_tier; + tlvs->spine_leaf->is_leaf = is_leaf; + tlvs->spine_leaf->is_spine = is_spine; + tlvs->spine_leaf->is_backup = is_backup; +} + struct isis_mt_router_info * isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid) { diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index bd1fa3e676..451321ea98 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -103,6 +103,17 @@ struct isis_protocols_supported { uint8_t *protocols; }; +#define ISIS_TIER_UNDEFINED 15 + +struct isis_spine_leaf { + uint8_t tier; + + bool has_tier; + bool is_leaf; + bool is_spine; + bool is_backup; +}; + enum isis_threeway_state { ISIS_THREEWAY_DOWN = 2, ISIS_THREEWAY_INITIALIZING = 1, @@ -205,6 +216,7 @@ struct isis_tlvs { struct isis_item_list ipv6_reach; struct isis_mt_item_list mt_ipv6_reach; struct isis_threeway_adj *threeway_adj; + struct isis_spine_leaf *spine_leaf; }; struct isis_subtlvs { @@ -236,6 +248,7 @@ enum isis_tlv_type { ISIS_TLV_TE_ROUTER_ID = 134, ISIS_TLV_EXTENDED_IP_REACH = 135, ISIS_TLV_DYNAMIC_HOSTNAME = 137, + ISIS_TLV_SPINE_LEAF_EXT = 150, ISIS_TLV_MT_REACH = 222, ISIS_TLV_MT_ROUTER_INFO = 229, ISIS_TLV_IPV6_ADDRESS = 232, @@ -331,6 +344,10 @@ void isis_tlvs_add_threeway_adj(struct isis_tlvs *tlvs, const uint8_t *neighbor_id, uint32_t neighbor_circuit_id); +void isis_tlvs_add_spine_leaf(struct isis_tlvs *tlvs, uint8_t tier, + bool has_tier, bool is_leaf, bool is_spine, + bool is_backup); + struct isis_mt_router_info * isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid); #endif From 92ed0cdef522c148250186cd3fbdf61cc9789d77 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 10 May 2018 19:05:40 +0200 Subject: [PATCH 16/84] fabricd: allow to configure tier-level advertisement While OpenFabric calculates most tier numbers automatically by the fabric locality calculation algorithm, that algorithm requires two systems to be manually configured as tier 0, so it has reference points. Also, completely manual configuration is possible. To support this, introduce appropriate CLI commands and flood the configured information. Signed-off-by: Christian Franke --- isisd/fabricd.c | 56 ++++++++++++++++++++++++++++++++++++++++ isisd/fabricd.h | 4 +++ isisd/isis_lsp.c | 8 ++++++ isisd/isis_vty_fabricd.c | 32 ++++++++++++++++++++++- isisd/isisd.c | 9 +++++++ 5 files changed, 108 insertions(+), 1 deletion(-) diff --git a/isisd/fabricd.c b/isisd/fabricd.c index 853b672075..11d1ff695e 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -27,6 +27,8 @@ #include "isisd/isis_misc.h" #include "isisd/isis_adjacency.h" #include "isisd/isis_spf.h" +#include "isisd/isis_tlvs.h" +#include "isisd/isis_lsp.h" DEFINE_MTYPE_STATIC(ISISD, FABRICD_STATE, "ISIS OpenFabric") @@ -42,20 +44,27 @@ enum fabricd_sync_state { }; struct fabricd { + struct isis_area *area; + enum fabricd_sync_state initial_sync_state; time_t initial_sync_start; struct isis_circuit *initial_sync_circuit; struct thread *initial_sync_timeout; struct isis_spftree *spftree; + + uint8_t tier; + uint8_t tier_config; }; struct fabricd *fabricd_new(struct isis_area *area) { struct fabricd *rv = XCALLOC(MTYPE_FABRICD_STATE, sizeof(*rv)); + rv->area = area; rv->initial_sync_state = FABRICD_SYNC_PENDING; rv->spftree = isis_spftree_new(area); + rv->tier = rv->tier_config = ISIS_TIER_UNDEFINED; return rv; }; @@ -147,6 +156,16 @@ void fabricd_initial_sync_finish(struct isis_area *area) f->initial_sync_timeout = NULL; } +static void fabricd_set_tier(struct fabricd *f, uint8_t tier) +{ + if (f->tier == tier) + return; + + f->tier = tier; + + lsp_regenerate_schedule(f->area, ISIS_LEVEL2, 0); +} + void fabricd_run_spf(struct isis_area *area) { struct fabricd *f = area->fabricd; @@ -166,3 +185,40 @@ struct isis_spftree *fabricd_spftree(struct isis_area *area) return f->spftree; } + +void fabricd_configure_tier(struct isis_area *area, uint8_t tier) +{ + struct fabricd *f = area->fabricd; + + if (!f || f->tier_config == tier) + return; + + f->tier_config = tier; + fabricd_set_tier(f, tier); +} + +uint8_t fabricd_tier(struct isis_area *area) +{ + struct fabricd *f = area->fabricd; + + if (!f) + return ISIS_TIER_UNDEFINED; + + return f->tier; +} + +int fabricd_write_settings(struct isis_area *area, struct vty *vty) +{ + struct fabricd *f = area->fabricd; + int written = 0; + + if (!f) + return written; + + if (f->tier_config != ISIS_TIER_UNDEFINED) { + vty_out(vty, " fabric-tier %" PRIu8 "\n", f->tier_config); + written++; + } + + return written; +} diff --git a/isisd/fabricd.h b/isisd/fabricd.h index 1707adf36e..a6dc979729 100644 --- a/isisd/fabricd.h +++ b/isisd/fabricd.h @@ -27,6 +27,7 @@ struct fabricd; struct isis_circuit; struct isis_area; struct isis_spftree; +struct vty; struct fabricd *fabricd_new(struct isis_area *area); void fabricd_finish(struct fabricd *f); @@ -36,5 +37,8 @@ struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area); void fabricd_initial_sync_finish(struct isis_area *area); void fabricd_run_spf(struct isis_area *area); struct isis_spftree *fabricd_spftree(struct isis_area *area); +void fabricd_configure_tier(struct isis_area *area, uint8_t tier); +uint8_t fabricd_tier(struct isis_area *area); +int fabricd_write_settings(struct isis_area *area, struct vty *vty); #endif diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 90bcece15d..f8ebce2efd 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -929,6 +929,14 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) lsp_debug("ISIS (%s): Adding circuit specific information.", area->area_tag); + if (fabricd) { + lsp_debug( + "ISIS (%s): Adding tier %" PRIu8 " spine-leaf-extension tlv.", + area->area_tag, fabricd_tier(area)); + isis_tlvs_add_spine_leaf(lsp->tlvs, fabricd_tier(area), true, + false, false, false); + } + struct isis_circuit *circuit; for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { if (!circuit->interface) diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c index 22876301c3..5ef3af0f19 100644 --- a/isisd/isis_vty_fabricd.c +++ b/isisd/isis_vty_fabricd.c @@ -25,8 +25,38 @@ #include "isisd.h" #include "isis_vty_common.h" +#include "fabricd.h" +#include "isis_tlvs.h" + +DEFUN (fabric_tier, + fabric_tier_cmd, + "fabric-tier (0-14)", + "Statically configure the tier to advertise\n" + "Tier to advertise\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + uint8_t tier = atoi(argv[1]->arg); + + fabricd_configure_tier(area, tier); + return CMD_SUCCESS; +} + +DEFUN (no_fabric_tier, + no_fabric_tier_cmd, + "no fabric-tier [(0-14)]", + NO_STR + "Statically configure the tier to advertise\n" + "Tier to advertise\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + fabricd_configure_tier(area, ISIS_TIER_UNDEFINED); + return CMD_SUCCESS; +} void isis_vty_daemon_init(void) { - return; + install_element(ROUTER_NODE, &fabric_tier_cmd); + install_element(ROUTER_NODE, &no_fabric_tier_cmd); } diff --git a/isisd/isisd.c b/isisd/isisd.c index cc5463ffe7..84f44b1648 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -1299,6 +1299,14 @@ DEFUN (show_isis_summary, vty_out(vty, "Area %s:\n", area->area_tag ? area->area_tag : "null"); + if (fabricd) { + uint8_t tier = fabricd_tier(area); + if (tier == ISIS_TIER_UNDEFINED) + vty_out(vty, " Tier: undefined\n"); + else + vty_out(vty, " Tier: %" PRIu8 "\n", tier); + } + if (listcount(area->area_addrs) > 0) { struct area_addr *area_addr; for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node2, @@ -2133,6 +2141,7 @@ int isis_config_write(struct vty *vty) } write += area_write_mt_settings(area, vty); + write += fabricd_write_settings(area, vty); } isis_mpls_te_config_write_router(vty); } From 75e0ec941509a8a38589fa61b139e85b6ccc48a5 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 10 May 2018 17:52:18 +0200 Subject: [PATCH 17/84] fabricd: implement fabric locality detection algorithm If an OpenFabric router doesn't have its tier number configured manually, try to execute the fabric locality calculation algorithm whenever we have run spf. Signed-off-by: Christian Franke --- isisd/fabricd.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/isisd/fabricd.c b/isisd/fabricd.c index 11d1ff695e..5effcd83df 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -55,6 +55,9 @@ struct fabricd { uint8_t tier; uint8_t tier_config; + uint8_t tier_pending; + struct thread *tier_calculation_timer; + struct thread *tier_set_timer; }; struct fabricd *fabricd_new(struct isis_area *area) @@ -73,6 +76,12 @@ void fabricd_finish(struct fabricd *f) if (f->initial_sync_timeout) thread_cancel(f->initial_sync_timeout); + if (f->tier_calculation_timer) + thread_cancel(f->tier_calculation_timer); + + if (f->tier_set_timer) + thread_cancel(f->tier_set_timer); + isis_spftree_del(f->spftree); } @@ -156,13 +165,132 @@ void fabricd_initial_sync_finish(struct isis_area *area) f->initial_sync_timeout = NULL; } +static void fabricd_bump_tier_calculation_timer(struct fabricd *f); +static void fabricd_set_tier(struct fabricd *f, uint8_t tier); + +static uint8_t fabricd_calculate_fabric_tier(struct isis_area *area) +{ + struct isis_spftree *local_tree = fabricd_spftree(area); + struct listnode *node; + + struct isis_vertex *furthest_t0 = NULL, + *second_furthest_t0 = NULL; + + struct isis_vertex *v; + + for (ALL_QUEUE_ELEMENTS_RO(&local_tree->paths, node, v)) { + struct isis_lsp *lsp = lsp_for_vertex(local_tree, v); + + if (!lsp || !lsp->tlvs + || !lsp->tlvs->spine_leaf + || !lsp->tlvs->spine_leaf->has_tier + || lsp->tlvs->spine_leaf->tier != 0) + continue; + + second_furthest_t0 = furthest_t0; + furthest_t0 = v; + } + + if (!second_furthest_t0) { + zlog_info("OpenFabric: Could not find two T0 routers"); + return ISIS_TIER_UNDEFINED; + } + + zlog_info("OpenFabric: Found %s as furthest t0 from local system, dist == %" + PRIu32, rawlspid_print(furthest_t0->N.id), furthest_t0->d_N); + + struct isis_spftree *remote_tree = + isis_run_hopcount_spf(area, furthest_t0->N.id, NULL); + + struct isis_vertex *furthest_from_remote = + isis_vertex_queue_last(&remote_tree->paths); + + if (!furthest_from_remote) { + zlog_info("OpenFabric: Found no furthest node in remote spf"); + isis_spftree_del(remote_tree); + return ISIS_TIER_UNDEFINED; + } else { + zlog_info("OpenFabric: Found %s as furthest from remote dist == %" + PRIu32, rawlspid_print(furthest_from_remote->N.id), + furthest_from_remote->d_N); + } + + int64_t tier = furthest_from_remote->d_N - furthest_t0->d_N; + isis_spftree_del(remote_tree); + + if (tier < 0 || tier >= ISIS_TIER_UNDEFINED) { + zlog_info("OpenFabric: Calculated tier %" PRId64 " seems implausible", + tier); + return ISIS_TIER_UNDEFINED; + } + + zlog_info("OpenFabric: Calculated %" PRId64 " as tier", tier); + return tier; +} + +static int fabricd_tier_set_timer(struct thread *thread) +{ + struct fabricd *f = THREAD_ARG(thread); + f->tier_set_timer = NULL; + + fabricd_set_tier(f, f->tier_pending); + return 0; +} + +static int fabricd_tier_calculation_cb(struct thread *thread) +{ + struct fabricd *f = THREAD_ARG(thread); + uint8_t tier = ISIS_TIER_UNDEFINED; + f->tier_calculation_timer = NULL; + + tier = fabricd_calculate_fabric_tier(f->area); + if (tier == ISIS_TIER_UNDEFINED) + return 0; + + zlog_info("OpenFabric: Got tier %" PRIu8 " from algorithm. Arming timer.", + tier); + f->tier_pending = tier; + thread_add_timer(master, fabricd_tier_set_timer, f, + f->area->lsp_gen_interval[ISIS_LEVEL2 - 1], + &f->tier_set_timer); + + return 0; +} + +static void fabricd_bump_tier_calculation_timer(struct fabricd *f) +{ + /* Cancel timer if we already know our tier */ + if (f->tier != ISIS_TIER_UNDEFINED + || f->tier_set_timer) { + if (f->tier_calculation_timer) { + thread_cancel(f->tier_calculation_timer); + f->tier_calculation_timer = NULL; + } + return; + } + + /* If we need to calculate the tier, wait some + * time for the topology to settle before running + * the calculation */ + if (f->tier_calculation_timer) { + thread_cancel(f->tier_calculation_timer); + f->tier_calculation_timer = NULL; + } + + thread_add_timer(master, fabricd_tier_calculation_cb, f, + 2 * f->area->lsp_gen_interval[ISIS_LEVEL2 - 1], + &f->tier_calculation_timer); +} + static void fabricd_set_tier(struct fabricd *f, uint8_t tier) { if (f->tier == tier) return; + zlog_info("OpenFabric: Set own tier to %" PRIu8, tier); f->tier = tier; + fabricd_bump_tier_calculation_timer(f); lsp_regenerate_schedule(f->area, ISIS_LEVEL2, 0); } @@ -174,6 +302,7 @@ void fabricd_run_spf(struct isis_area *area) return; isis_run_hopcount_spf(area, isis->sysid, f->spftree); + fabricd_bump_tier_calculation_timer(f); } struct isis_spftree *fabricd_spftree(struct isis_area *area) From a191178d0a500a4f016d22235bed13790fd6968e Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Wed, 23 May 2018 15:37:45 +0200 Subject: [PATCH 18/84] fabricd: implement asymmetric metric for tier 0 as per Section 6.2 To avoid passing of traffic via leaf nodes in the fabric, OpenFabric specifies that all links towards tier 0 nodes should be advertised with a very high metric. Signed-off-by: Christian Franke --- isisd/isis_lsp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index f8ebce2efd..b7e0b4b553 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -1100,9 +1100,16 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) */ subtlv_len = 0; + uint32_t neighbor_metric; + if (fabricd_tier(area) == 0) { + neighbor_metric = 0xffe; + } else { + neighbor_metric = metric; + } + tlvs_add_mt_p2p(lsp->tlvs, circuit, - ne_id, metric, subtlvs, - subtlv_len); + ne_id, neighbor_metric, + subtlvs, subtlv_len); } } else { lsp_debug( From 9b39405f00e17ef84a7a4659e37967728cbae283 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 10 May 2018 17:37:05 +0200 Subject: [PATCH 19/84] fabricd: reimplement LSP transmission logic Before this commit, isisd/fabricd maintained a bitfield for each LSP to track the SRM bit for each circuit, which specifies whether an LSP needs to be sent on that circuit. Every second, it would scan over all LSPs in `lsp_tick` and queue them up for transmission accordingly. This design has two drawbacks: a) it scales poorly b) it adds unacceptable latency to the update process: each router takes a random amount of time between 0 and 1 seconds to forward an update. In a network with a diamter of 10, it might already take 10 seconds for an update to traverse the network. To mitigate this, a new design was chosen. Instead of tracking SRM in a bitfield, have one tx_queue per circuit and declare that an LSP is in that queue if and only if it would have SRM set for that circuit. This way, we can track SRM similarly as we did before, however, on insertion into the LSP queue, we can add a timer for (re)transmission, alleviating the need for a periodic scan with LSP tick and reducing the latency for forwarding of updates. Signed-off-by: Christian Franke --- isisd/fabricd.c | 1 + isisd/isis_adjacency.c | 4 +- isisd/isis_circuit.c | 105 +++--------- isisd/isis_circuit.h | 16 +- isisd/isis_lsp.c | 99 ++++------- isisd/isis_lsp.h | 4 +- isisd/isis_lsp_hash.c | 89 ---------- isisd/isis_pdu.c | 77 ++++----- isisd/isis_pdu.h | 4 +- isisd/isis_tx_queue.c | 182 +++++++++++++++++++++ isisd/{isis_lsp_hash.h => isis_tx_queue.h} | 39 +++-- isisd/subdir.am | 4 +- 12 files changed, 312 insertions(+), 312 deletions(-) delete mode 100644 isisd/isis_lsp_hash.c create mode 100644 isisd/isis_tx_queue.c rename isisd/{isis_lsp_hash.h => isis_tx_queue.h} (51%) diff --git a/isisd/fabricd.c b/isisd/fabricd.c index 5effcd83df..fe8731c8d0 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -29,6 +29,7 @@ #include "isisd/isis_spf.h" #include "isisd/isis_tlvs.h" #include "isisd/isis_lsp.h" +#include "isisd/isis_tx_queue.h" DEFINE_MTYPE_STATIC(ISISD, FABRICD_STATE, "ISIS OpenFabric") diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 56dbc25829..a41d6ff815 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -268,7 +268,7 @@ void isis_adj_state_change(struct isis_adjacency *adj, circuit->upadjcount[level - 1]--; if (circuit->upadjcount[level - 1] == 0) - isis_circuit_lsp_queue_clean(circuit); + isis_tx_queue_clean(circuit->tx_queue); isis_event_adjacency_state_change(adj, new_state); @@ -324,7 +324,7 @@ void isis_adj_state_change(struct isis_adjacency *adj, adj->circuit->u.p2p.neighbor = NULL; circuit->upadjcount[level - 1]--; if (circuit->upadjcount[level - 1] == 0) - isis_circuit_lsp_queue_clean(circuit); + isis_tx_queue_clean(circuit->tx_queue); isis_event_adjacency_state_change(adj, new_state); diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 7ecc71023f..817a44bafe 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -45,7 +45,6 @@ #include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" #include "isisd/isis_lsp.h" -#include "isisd/isis_lsp_hash.h" #include "isisd/isis_pdu.h" #include "isisd/isis_network.h" #include "isisd/isis_misc.h" @@ -58,6 +57,7 @@ #include "isisd/isis_te.h" #include "isisd/isis_mt.h" #include "isisd/isis_errors.h" +#include "isisd/isis_tx_queue.h" DEFINE_QOBJ_TYPE(isis_circuit) @@ -495,29 +495,29 @@ static void isis_circuit_update_all_srmflags(struct isis_circuit *circuit, { struct isis_area *area; struct isis_lsp *lsp; - dnode_t *dnode, *dnode_next; + dnode_t *dnode; int level; assert(circuit); area = circuit->area; assert(area); for (level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { - if (level & circuit->is_type) { - if (area->lspdb[level - 1] - && dict_count(area->lspdb[level - 1]) > 0) { - for (dnode = dict_first(area->lspdb[level - 1]); - dnode != NULL; dnode = dnode_next) { - dnode_next = dict_next( - area->lspdb[level - 1], dnode); - lsp = dnode_get(dnode); - if (is_set) { - ISIS_SET_FLAG(lsp->SRMflags, - circuit); - } else { - ISIS_CLEAR_FLAG(lsp->SRMflags, - circuit); - } - } + if (!(level & circuit->is_type)) + continue; + + if (!area->lspdb[level - 1] + || !dict_count(area->lspdb[level - 1])) + continue; + + for (dnode = dict_first(area->lspdb[level - 1]); + dnode != NULL; + dnode = dict_next(area->lspdb[level - 1], dnode)) { + lsp = dnode_get(dnode); + if (is_set) { + isis_tx_queue_add(circuit->tx_queue, lsp, + TX_LSP_NORMAL); + } else { + isis_tx_queue_del(circuit->tx_queue, lsp); } } } @@ -672,10 +672,7 @@ int isis_circuit_up(struct isis_circuit *circuit) isis_circuit_prepare(circuit); - circuit->lsp_queue = list_new(); - circuit->lsp_hash = isis_lsp_hash_new(); - circuit->lsp_queue_last_push[0] = circuit->lsp_queue_last_push[1] = - monotime(NULL); + circuit->tx_queue = isis_tx_queue_new(circuit, send_lsp); return ISIS_OK; } @@ -743,13 +740,9 @@ void isis_circuit_down(struct isis_circuit *circuit) THREAD_OFF(circuit->t_send_lsp); THREAD_OFF(circuit->t_read); - if (circuit->lsp_queue) { - list_delete_and_null(&circuit->lsp_queue); - } - - if (circuit->lsp_hash) { - isis_lsp_hash_free(circuit->lsp_hash); - circuit->lsp_hash = NULL; + if (circuit->tx_queue) { + isis_tx_queue_free(circuit->tx_queue); + circuit->tx_queue = NULL; } /* send one gratuitous hello to spead up convergence */ @@ -1346,57 +1339,3 @@ void isis_circuit_init() install_node(&interface_node, isis_interface_config_write); if_cmd_init(); } - -void isis_circuit_schedule_lsp_send(struct isis_circuit *circuit) -{ - if (circuit->t_send_lsp) - return; - circuit->t_send_lsp = - thread_add_event(master, send_lsp, circuit, 0, NULL); -} - -void isis_circuit_queue_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp) -{ - if (isis_lsp_hash_lookup(circuit->lsp_hash, lsp)) - return; - - listnode_add(circuit->lsp_queue, lsp); - isis_lsp_hash_add(circuit->lsp_hash, lsp); - isis_circuit_schedule_lsp_send(circuit); -} - -void isis_circuit_lsp_queue_clean(struct isis_circuit *circuit) -{ - if (!circuit->lsp_queue) - return; - - list_delete_all_node(circuit->lsp_queue); - isis_lsp_hash_clean(circuit->lsp_hash); -} - -void isis_circuit_cancel_queued_lsp(struct isis_circuit *circuit, - struct isis_lsp *lsp) -{ - if (!circuit->lsp_queue) - return; - - listnode_delete(circuit->lsp_queue, lsp); - isis_lsp_hash_release(circuit->lsp_hash, lsp); -} - -struct isis_lsp *isis_circuit_lsp_queue_pop(struct isis_circuit *circuit) -{ - if (!circuit->lsp_queue) - return NULL; - - struct listnode *node = listhead(circuit->lsp_queue); - if (!node) - return NULL; - - struct isis_lsp *rv = listgetdata(node); - - list_delete_node(circuit->lsp_queue, node); - isis_lsp_hash_release(circuit->lsp_hash, rv); - - return rv; -} diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index c014e4a52e..ea68767fe0 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -80,14 +80,8 @@ struct isis_circuit { struct thread *t_send_csnp[2]; struct thread *t_send_psnp[2]; struct thread *t_send_lsp; - struct list *lsp_queue; /* LSPs to be txed (both levels) */ - struct isis_lsp_hash *lsp_hash; /* Hashtable synchronized with lsp_queue */ - time_t lsp_queue_last_push[2]; /* timestamp used to enforce transmit - * interval; - * for scalability, use one timestamp per - * circuit, instead of one per lsp per - * circuit - */ + struct isis_tx_queue *tx_queue; + /* there is no real point in two streams, just for programming kicker */ int (*rx)(struct isis_circuit *circuit, uint8_t *ssnpa); struct stream *rcv_stream; /* Stream for receiving */ @@ -196,10 +190,4 @@ ferr_r isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit, int isis_circuit_mt_enabled_set(struct isis_circuit *circuit, uint16_t mtid, bool enabled); -void isis_circuit_schedule_lsp_send(struct isis_circuit *circuit); -void isis_circuit_queue_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp); -void isis_circuit_lsp_queue_clean(struct isis_circuit *circuit); -void isis_circuit_cancel_queued_lsp(struct isis_circuit *circuit, - struct isis_lsp *lsp); -struct isis_lsp *isis_circuit_lsp_queue_pop(struct isis_circuit *circuit); #endif /* _ZEBRA_ISIS_CIRCUIT_H */ diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index b7e0b4b553..2598572248 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -57,6 +57,7 @@ #include "isisd/isis_mt.h" #include "isisd/isis_tlvs.h" #include "isisd/fabricd.h" +#include "isisd/isis_tx_queue.h" static int lsp_l1_refresh(struct thread *thread); static int lsp_l2_refresh(struct thread *thread); @@ -118,10 +119,9 @@ static void lsp_destroy(struct isis_lsp *lsp) return; for (ALL_LIST_ELEMENTS_RO(lsp->area->circuit_list, cnode, circuit)) - isis_circuit_cancel_queued_lsp(circuit, lsp); + isis_tx_queue_del(circuit->tx_queue, lsp); ISIS_FLAGS_CLEAR_ALL(lsp->SSNflags); - ISIS_FLAGS_CLEAR_ALL(lsp->SRMflags); lsp_clear_data(lsp); @@ -366,7 +366,7 @@ static void lsp_purge(struct isis_lsp *lsp, int level) lsp->age_out = lsp->area->max_lsp_lifetime[level - 1]; lsp_pack_pdu(lsp); - lsp_set_all_srmflags(lsp); + lsp_flood(lsp, NULL); } /* @@ -1208,7 +1208,7 @@ int lsp_generate(struct isis_area *area, int level) /* time to calculate our checksum */ lsp_seqno_update(newlsp); newlsp->last_generated = time(NULL); - lsp_set_all_srmflags(newlsp); + lsp_flood(newlsp, NULL); refresh_time = lsp_refresh_time(newlsp, rem_lifetime); @@ -1239,7 +1239,7 @@ int lsp_generate(struct isis_area *area, int level) } /* - * Search own LSPs, update holding time and set SRM + * Search own LSPs, update holding time and flood */ static int lsp_regenerate(struct isis_area *area, int level) { @@ -1271,7 +1271,7 @@ static int lsp_regenerate(struct isis_area *area, int level) rem_lifetime = lsp_rem_lifetime(area, level); lsp->hdr.rem_lifetime = rem_lifetime; lsp->last_generated = time(NULL); - lsp_set_all_srmflags(lsp); + lsp_flood(lsp, NULL); for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) { frag->hdr.lsp_bits = lsp_bits_generate( level, area->overload_bit, area->attached_bit); @@ -1281,7 +1281,7 @@ static int lsp_regenerate(struct isis_area *area, int level) */ frag->hdr.rem_lifetime = rem_lifetime; frag->age_out = ZERO_AGE_LIFETIME; - lsp_set_all_srmflags(frag); + lsp_flood(frag, NULL); } lsp_seqno_update(lsp); @@ -1581,7 +1581,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) lsp_pack_pdu(lsp); lsp->own_lsp = 1; lsp_insert(lsp, lspdb); - lsp_set_all_srmflags(lsp); + lsp_flood(lsp, NULL); refresh_time = lsp_refresh_time(lsp, rem_lifetime); THREAD_TIMER_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); @@ -1640,7 +1640,7 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) lsp_build_pseudo(lsp, circuit, level); lsp_inc_seqno(lsp, 0); lsp->last_generated = time(NULL); - lsp_set_all_srmflags(lsp); + lsp_flood(lsp, NULL); refresh_time = lsp_refresh_time(lsp, rem_lifetime); if (level == IS_LEVEL_1) @@ -1816,23 +1816,16 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level) /* * Walk through LSPs for an area * - set remaining lifetime - * - set LSPs with SRMflag set for sending */ int lsp_tick(struct thread *thread) { struct isis_area *area; - struct isis_circuit *circuit; struct isis_lsp *lsp; - struct list *lsp_list; - struct listnode *lspnode, *cnode; dnode_t *dnode, *dnode_next; int level; uint16_t rem_lifetime; - time_t now = monotime(NULL); bool fabricd_sync_incomplete = false; - lsp_list = list_new(); - area = THREAD_ARG(thread); assert(area); area->t_tick = NULL; @@ -1841,8 +1834,7 @@ int lsp_tick(struct thread *thread) struct isis_circuit *fabricd_init_c = fabricd_initial_sync_circuit(area); /* - * Build a list of LSPs with (any) SRMflag set - * and removed the ones that have aged out + * Remove LSPs which have aged out */ for (level = 0; level < ISIS_LEVELS; level++) { if (area->lspdb[level] && dict_count(area->lspdb[level]) > 0) { @@ -1873,7 +1865,7 @@ int lsp_tick(struct thread *thread) */ if (rem_lifetime == 1 && lsp->hdr.seqno != 0) { /* 7.3.16.4 a) set SRM flags on all */ - lsp_set_all_srmflags(lsp); + lsp_flood(lsp, NULL); /* 7.3.16.4 b) retain only the header * FIXME */ /* 7.3.16.4 c) record the time to purge @@ -1897,56 +1889,22 @@ int lsp_tick(struct thread *thread) lsp = NULL; dict_delete_free(area->lspdb[level], dnode); - } else if (flags_any_set(lsp->SRMflags)) - listnode_add(lsp_list, lsp); + } if (fabricd_init_c) { fabricd_sync_incomplete |= ISIS_CHECK_FLAG(lsp->SSNflags, fabricd_init_c); - fabricd_sync_incomplete |= - ISIS_CHECK_FLAG(lsp->SRMflags, - fabricd_init_c); } } - - /* - * Send LSPs on circuits indicated by the SRMflags - */ - if (listcount(lsp_list) > 0) { - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, - cnode, circuit)) { - if (!circuit->lsp_queue) - continue; - - if (now - circuit->lsp_queue_last_push[level] - < MIN_LSP_RETRANS_INTERVAL) { - continue; - } - - circuit->lsp_queue_last_push[level] = now; - - for (ALL_LIST_ELEMENTS_RO( - lsp_list, lspnode, lsp)) { - if (circuit->upadjcount - [lsp->level - 1] - && ISIS_CHECK_FLAG( - lsp->SRMflags, - circuit)) { - isis_circuit_queue_lsp( - circuit, lsp); - } - } - } - list_delete_all_node(lsp_list); - } } } - if (fabricd_init_c && !fabricd_sync_incomplete) + if (fabricd_init_c + && !fabricd_sync_incomplete + && !isis_tx_queue_len(fabricd_init_c->tx_queue)) { fabricd_initial_sync_finish(area); - - list_delete_and_null(&lsp_list); + } return ISIS_OK; } @@ -1986,24 +1944,35 @@ void lsp_purge_non_exist(int level, struct isis_lsp_hdr *hdr, lsp_pack_pdu(lsp); lsp_insert(lsp, area->lspdb[lsp->level - 1]); - lsp_set_all_srmflags(lsp); + lsp_flood(lsp, NULL); return; } -void lsp_set_all_srmflags(struct isis_lsp *lsp) +void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set) { struct listnode *node; struct isis_circuit *circuit; assert(lsp); - ISIS_FLAGS_CLEAR_ALL(lsp->SRMflags); + if (!lsp->area) + return; - if (lsp->area) { - struct list *circuit_list = lsp->area->circuit_list; - for (ALL_LIST_ELEMENTS_RO(circuit_list, node, circuit)) { - ISIS_SET_FLAG(lsp->SRMflags, circuit); + struct list *circuit_list = lsp->area->circuit_list; + for (ALL_LIST_ELEMENTS_RO(circuit_list, node, circuit)) { + if (set) { + isis_tx_queue_add(circuit->tx_queue, lsp, + TX_LSP_NORMAL); + } else { + isis_tx_queue_del(circuit->tx_queue, lsp); } } } + +void lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit) +{ + lsp_set_all_srmflags(lsp); + if (circuit) + isis_tx_queue_del(circuit->tx_queue, lsp); +} diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index d531cb1576..4e6379447c 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -37,7 +37,6 @@ struct isis_lsp { struct list *frags; struct isis_lsp *zero_lsp; } lspu; - uint32_t SRMflags[ISIS_MAX_CIRCUITS]; uint32_t SSNflags[ISIS_MAX_CIRCUITS]; int level; /* L1 or L2? */ int scheduled; /* scheduled for sending */ @@ -100,6 +99,7 @@ void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost); void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost); int lsp_print_all(struct vty *vty, dict_t *lspdb, char detail, char dynhost); /* sets SRMflags for all active circuits of an lsp */ -void lsp_set_all_srmflags(struct isis_lsp *lsp); +void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set); +void lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit); #endif /* ISIS_LSP */ diff --git a/isisd/isis_lsp_hash.c b/isisd/isis_lsp_hash.c deleted file mode 100644 index c521f42b3c..0000000000 --- a/isisd/isis_lsp_hash.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * IS-IS Rout(e)ing protocol - LSP Hash - * - * Copyright (C) 2017 Christian Franke - * - * This file is part of FreeRangeRouting (FRR) - * - * FRR is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * FRR is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include - -#include "hash.h" -#include "jhash.h" - -#include "isisd/isis_memory.h" -#include "isisd/isis_flags.h" -#include "dict.h" -#include "isisd/isis_circuit.h" -#include "isisd/isis_lsp.h" -#include "isisd/isis_lsp_hash.h" - -DEFINE_MTYPE_STATIC(ISISD, LSP_HASH, "ISIS LSP Hash") - -struct isis_lsp_hash { - struct hash *h; -}; - -static unsigned lsp_hash_key(void *lp) -{ - struct isis_lsp *lsp = lp; - - return jhash(lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2, 0x55aa5a5a); -} - -static int lsp_hash_cmp(const void *a, const void *b) -{ - const struct isis_lsp *la = a, *lb = b; - - return 0 == memcmp(la->hdr.lsp_id, lb->hdr.lsp_id, ISIS_SYS_ID_LEN + 2); -} - -struct isis_lsp_hash *isis_lsp_hash_new(void) -{ - struct isis_lsp_hash *rv = XCALLOC(MTYPE_LSP_HASH, sizeof(*rv)); - - rv->h = hash_create(lsp_hash_key, lsp_hash_cmp, NULL); - return rv; -} - -void isis_lsp_hash_clean(struct isis_lsp_hash *ih) -{ - hash_clean(ih->h, NULL); -} - -void isis_lsp_hash_free(struct isis_lsp_hash *ih) -{ - isis_lsp_hash_clean(ih); - hash_free(ih->h); -} - -struct isis_lsp *isis_lsp_hash_lookup(struct isis_lsp_hash *ih, - struct isis_lsp *lsp) -{ - return hash_lookup(ih->h, lsp); -} - -void isis_lsp_hash_add(struct isis_lsp_hash *ih, struct isis_lsp *lsp) -{ - struct isis_lsp *inserted; - inserted = hash_get(ih->h, lsp, hash_alloc_intern); - assert(inserted == lsp); -} - -void isis_lsp_hash_release(struct isis_lsp_hash *ih, struct isis_lsp *lsp) -{ - hash_release(ih->h, lsp); -} diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 8a5db3c6f9..c1523a268d 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -57,6 +57,7 @@ #include "isisd/isis_tlvs.h" #include "isisd/isis_errors.h" #include "isisd/fabricd.h" +#include "isisd/isis_tx_queue.h" static int ack_lsp(struct isis_lsp_hdr *hdr, struct isis_circuit *circuit, int level) @@ -707,7 +708,7 @@ out: * Section 7.3.15.1 - Action on receipt of a link state PDU */ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, - const uint8_t *ssnpa) + const uint8_t *ssnpa, uint8_t max_area_addrs) { int level = (pdu_type == L1_LINK_STATE) ? ISIS_LEVEL1 : ISIS_LEVEL2; @@ -906,7 +907,7 @@ dontcheckadj: lsp_confusion); tlvs = NULL; /* ii */ - lsp_set_all_srmflags(lsp); + lsp_flood(lsp, NULL); /* v */ ISIS_FLAGS_CLEAR_ALL( lsp->SSNflags); /* FIXME: @@ -920,9 +921,10 @@ dontcheckadj: * Otherwise, don't reflood * through incoming circuit as usual */ if (!lsp_confusion) { - /* iii */ - ISIS_CLEAR_FLAG(lsp->SRMflags, - circuit); + isis_tx_queue_del( + circuit->tx_queue, + lsp); + /* iv */ if (circuit->circ_type != CIRCUIT_T_BROADCAST) @@ -933,7 +935,8 @@ dontcheckadj: } /* 7.3.16.4 b) 2) */ else if (comp == LSP_EQUAL) { /* i */ - ISIS_CLEAR_FLAG(lsp->SRMflags, circuit); + isis_tx_queue_del(circuit->tx_queue, + lsp); /* ii */ if (circuit->circ_type != CIRCUIT_T_BROADCAST) @@ -941,16 +944,18 @@ dontcheckadj: circuit); } /* 7.3.16.4 b) 3) */ else { - ISIS_SET_FLAG(lsp->SRMflags, circuit); + isis_tx_queue_add(circuit->tx_queue, + lsp, TX_LSP_NORMAL); ISIS_CLEAR_FLAG(lsp->SSNflags, circuit); } } else if (lsp->hdr.rem_lifetime != 0) { /* our own LSP -> 7.3.16.4 c) */ if (comp == LSP_NEWER) { lsp_inc_seqno(lsp, hdr.seqno); - lsp_set_all_srmflags(lsp); + lsp_flood(lsp, NULL); } else { - ISIS_SET_FLAG(lsp->SRMflags, circuit); + isis_tx_queue_add(circuit->tx_queue, + lsp, TX_LSP_NORMAL); ISIS_CLEAR_FLAG(lsp->SSNflags, circuit); } if (isis->debugs & DEBUG_UPDATE_PACKETS) @@ -992,7 +997,7 @@ dontcheckadj: } /* If the received LSP is older or equal, * resend the LSP which will act as ACK */ - lsp_set_all_srmflags(lsp); + lsp_flood(lsp, NULL); } else { /* 7.3.15.1 e) - This lsp originated on another system */ @@ -1030,10 +1035,7 @@ dontcheckadj: circuit->area, level, false); tlvs = NULL; } - /* ii */ - lsp_set_all_srmflags(lsp); - /* iii */ - ISIS_CLEAR_FLAG(lsp->SRMflags, circuit); + lsp_flood(lsp, circuit); /* iv */ if (circuit->circ_type != CIRCUIT_T_BROADCAST) @@ -1042,7 +1044,7 @@ dontcheckadj: } /* 7.3.15.1 e) 2) LSP equal to the one in db */ else if (comp == LSP_EQUAL) { - ISIS_CLEAR_FLAG(lsp->SRMflags, circuit); + isis_tx_queue_del(circuit->tx_queue, lsp); lsp_update(lsp, &hdr, tlvs, circuit->rcv_stream, circuit->area, level, false); tlvs = NULL; @@ -1051,7 +1053,8 @@ dontcheckadj: } /* 7.3.15.1 e) 3) LSP older than the one in db */ else { - ISIS_SET_FLAG(lsp->SRMflags, circuit); + isis_tx_queue_add(circuit->tx_queue, lsp, + TX_LSP_NORMAL); ISIS_CLEAR_FLAG(lsp->SSNflags, circuit); } } @@ -1228,25 +1231,27 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, if (cmp == LSP_EQUAL) { /* if (circuit->circ_type != * CIRCUIT_T_BROADCAST) */ - ISIS_CLEAR_FLAG(lsp->SRMflags, circuit); + isis_tx_queue_del(circuit->tx_queue, lsp); } /* 7.3.15.2 b) 3) if it is older, clear SSN and set SRM */ else if (cmp == LSP_OLDER) { ISIS_CLEAR_FLAG(lsp->SSNflags, circuit); - ISIS_SET_FLAG(lsp->SRMflags, circuit); + isis_tx_queue_add(circuit->tx_queue, lsp, + TX_LSP_NORMAL); } /* 7.3.15.2 b) 4) if it is newer, set SSN and clear SRM on p2p */ else { if (own_lsp) { lsp_inc_seqno(lsp, entry->seqno); - ISIS_SET_FLAG(lsp->SRMflags, circuit); + isis_tx_queue_add(circuit->tx_queue, lsp, + TX_LSP_NORMAL); } else { ISIS_SET_FLAG(lsp->SSNflags, circuit); /* if (circuit->circ_type != * CIRCUIT_T_BROADCAST) */ - ISIS_CLEAR_FLAG(lsp->SRMflags, circuit); + isis_tx_queue_del(circuit->tx_queue, lsp); } } } else { @@ -1278,7 +1283,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, entry->checksum, lsp0, level); lsp_insert(lsp, circuit->area->lspdb[level - 1]); - ISIS_FLAGS_CLEAR_ALL(lsp->SRMflags); + + lsp_set_all_srmflags(lsp, false); ISIS_SET_FLAG(lsp->SSNflags, circuit); } } @@ -1310,8 +1316,10 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, } /* on remaining LSPs we set SRM (neighbor knew not of) */ - for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) - ISIS_SET_FLAG(lsp->SRMflags, circuit); + for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) { + isis_tx_queue_add(circuit->tx_queue, lsp, TX_LSP_NORMAL); + } + /* lets free it */ list_delete_and_null(&lsp_list); } @@ -1451,7 +1459,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) break; case L1_LINK_STATE: case L2_LINK_STATE: - retval = process_lsp(pdu_type, circuit, ssnpa); + retval = process_lsp(pdu_type, circuit, ssnpa, max_area_addrs); break; case L1_COMPLETE_SEQ_NUM: case L2_COMPLETE_SEQ_NUM: @@ -2102,25 +2110,12 @@ int send_l2_psnp(struct thread *thread) /* * ISO 10589 - 7.3.14.3 */ -int send_lsp(struct thread *thread) +void send_lsp(void *arg, struct isis_lsp *lsp, enum isis_tx_type tx_type) { - struct isis_circuit *circuit; - struct isis_lsp *lsp; + struct isis_circuit *circuit = arg; int clear_srm = 1; int retval = ISIS_OK; - circuit = THREAD_ARG(thread); - assert(circuit); - circuit->t_send_lsp = NULL; - - lsp = isis_circuit_lsp_queue_pop(circuit); - if (!lsp) - return ISIS_OK; - - if (!list_isempty(circuit->lsp_queue)) { - isis_circuit_schedule_lsp_send(circuit); - } - if (circuit->state != C_STATE_UP || circuit->is_passive == 1) goto out; @@ -2197,8 +2192,6 @@ out: * to clear * the fag. */ - ISIS_CLEAR_FLAG(lsp->SRMflags, circuit); + isis_tx_queue_del(circuit->tx_queue, lsp); } - - return retval; } diff --git a/isisd/isis_pdu.h b/isisd/isis_pdu.h index c69bfedeae..ea19de2591 100644 --- a/isisd/isis_pdu.h +++ b/isisd/isis_pdu.h @@ -24,6 +24,8 @@ #ifndef _ZEBRA_ISIS_PDU_H #define _ZEBRA_ISIS_PDU_H +#include "isisd/isis_tx_queue.h" + #ifdef __SUNPRO_C #pragma pack(1) #endif @@ -212,7 +214,7 @@ int send_l1_csnp(struct thread *thread); int send_l2_csnp(struct thread *thread); int send_l1_psnp(struct thread *thread); int send_l2_psnp(struct thread *thread); -int send_lsp(struct thread *thread); +void send_lsp(void *arg, struct isis_lsp *lsp, enum isis_tx_type tx_type); void fill_fixed_hdr(uint8_t pdu_type, struct stream *stream); int send_hello(struct isis_circuit *circuit, int level); int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa); diff --git a/isisd/isis_tx_queue.c b/isisd/isis_tx_queue.c new file mode 100644 index 0000000000..32427628ad --- /dev/null +++ b/isisd/isis_tx_queue.c @@ -0,0 +1,182 @@ +/* + * IS-IS Rout(e)ing protocol - LSP TX Queuing logic + * + * Copyright (C) 2018 Christian Franke + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "hash.h" +#include "jhash.h" + +#include "isisd/isisd.h" +#include "isisd/isis_memory.h" +#include "isisd/isis_flags.h" +#include "dict.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_tx_queue.h" + +DEFINE_MTYPE_STATIC(ISISD, TX_QUEUE, "ISIS TX Queue") +DEFINE_MTYPE_STATIC(ISISD, TX_QUEUE_ENTRY, "ISIS TX Queue Entry") + +struct isis_tx_queue { + void *arg; + void (*send_event)(void *arg, struct isis_lsp *, enum isis_tx_type); + struct hash *hash; +}; + +struct isis_tx_queue_entry { + struct isis_lsp *lsp; + enum isis_tx_type type; + struct thread *retry; + struct isis_tx_queue *queue; +}; + +static unsigned tx_queue_hash_key(void *p) +{ + struct isis_tx_queue_entry *e = p; + + uint32_t id_key = jhash(e->lsp->hdr.lsp_id, + ISIS_SYS_ID_LEN + 2, 0x55aa5a5a); + + return jhash_1word(e->lsp->level, id_key); +} + +static int tx_queue_hash_cmp(const void *a, const void *b) +{ + const struct isis_tx_queue_entry *ea = a, *eb = b; + + if (ea->lsp->level != eb->lsp->level) + return 0; + + if (memcmp(ea->lsp->hdr.lsp_id, eb->lsp->hdr.lsp_id, + ISIS_SYS_ID_LEN + 2)) + return 0; + + return 1; +} + +struct isis_tx_queue *isis_tx_queue_new(void *arg, + void(*send_event)(void *arg, + struct isis_lsp *, + enum isis_tx_type)) +{ + struct isis_tx_queue *rv = XCALLOC(MTYPE_TX_QUEUE, sizeof(*rv)); + + rv->arg = arg; + rv->send_event = send_event; + + rv->hash = hash_create(tx_queue_hash_key, tx_queue_hash_cmp, NULL); + return rv; +} + +static void tx_queue_element_free(void *element) +{ + struct isis_tx_queue_entry *e = element; + + if (e->retry) + thread_cancel(e->retry); + + XFREE(MTYPE_TX_QUEUE_ENTRY, e); +} + +void isis_tx_queue_free(struct isis_tx_queue *queue) +{ + hash_clean(queue->hash, tx_queue_element_free); + hash_free(queue->hash); + XFREE(MTYPE_TX_QUEUE, queue); +} + +static struct isis_tx_queue_entry *tx_queue_find(struct isis_tx_queue *queue, + struct isis_lsp *lsp) +{ + struct isis_tx_queue_entry e = { + .lsp = lsp + }; + + return hash_lookup(queue->hash, &e); +} + +static int tx_queue_send_event(struct thread *thread) +{ + struct isis_tx_queue_entry *e = THREAD_ARG(thread); + struct isis_tx_queue *queue = e->queue; + + e->retry = NULL; + thread_add_timer(master, tx_queue_send_event, e, 5, &e->retry); + + queue->send_event(queue->arg, e->lsp, e->type); + /* Don't access e here anymore, send_event might have destroyed it */ + + return 0; +} + +void isis_tx_queue_add(struct isis_tx_queue *queue, + struct isis_lsp *lsp, + enum isis_tx_type type) +{ + if (!queue) + return; + + struct isis_tx_queue_entry *e = tx_queue_find(queue, lsp); + if (!e) { + e = XCALLOC(MTYPE_TX_QUEUE_ENTRY, sizeof(*e)); + e->lsp = lsp; + e->queue = queue; + + struct isis_tx_queue_entry *inserted; + inserted = hash_get(queue->hash, e, hash_alloc_intern); + assert(inserted == e); + } + + e->type = type; + + if (e->retry) + thread_cancel(e->retry); + thread_add_event(master, tx_queue_send_event, e, 0, &e->retry); +} + +void isis_tx_queue_del(struct isis_tx_queue *queue, struct isis_lsp *lsp) +{ + if (!queue) + return; + + struct isis_tx_queue_entry *e = tx_queue_find(queue, lsp); + if (!e) + return; + + if (e->retry) + thread_cancel(e->retry); + + hash_release(queue->hash, e); + XFREE(MTYPE_TX_QUEUE_ENTRY, e); +} + +unsigned long isis_tx_queue_len(struct isis_tx_queue *queue) +{ + if (!queue) + return 0; + + return hashcount(queue->hash); +} + +void isis_tx_queue_clean(struct isis_tx_queue *queue) +{ + hash_clean(queue->hash, tx_queue_element_free); +} diff --git a/isisd/isis_lsp_hash.h b/isisd/isis_tx_queue.h similarity index 51% rename from isisd/isis_lsp_hash.h rename to isisd/isis_tx_queue.h index b50aa09dc1..ddecdf1e4f 100644 --- a/isisd/isis_lsp_hash.h +++ b/isisd/isis_tx_queue.h @@ -1,7 +1,7 @@ /* - * IS-IS Rout(e)ing protocol - LSP Hash + * IS-IS Rout(e)ing protocol - LSP TX Queuing logic * - * Copyright (C) 2017 Christian Franke + * Copyright (C) 2018 Christian Franke * * This file is part of FreeRangeRouting (FRR) * @@ -19,16 +19,31 @@ * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ISIS_LSP_HASH_H -#define ISIS_LSP_HASH_H +#ifndef ISIS_TX_QUEUE_H +#define ISIS_TX_QUEUE_H -struct isis_lsp_hash; +enum isis_tx_type { + TX_LSP_NORMAL = 0, + TX_LSP_CIRCUIT_SCOPED +}; + +struct isis_tx_queue; + +struct isis_tx_queue *isis_tx_queue_new(void *arg, + void(*send_event)(void *arg, + struct isis_lsp *, + enum isis_tx_type)); + +void isis_tx_queue_free(struct isis_tx_queue *queue); + +void isis_tx_queue_add(struct isis_tx_queue *queue, + struct isis_lsp *lsp, + enum isis_tx_type type); + +void isis_tx_queue_del(struct isis_tx_queue *queue, struct isis_lsp *lsp); + +unsigned long isis_tx_queue_len(struct isis_tx_queue *queue); + +void isis_tx_queue_clean(struct isis_tx_queue *queue); -struct isis_lsp_hash *isis_lsp_hash_new(void); -void isis_lsp_hash_clean(struct isis_lsp_hash *ih); -void isis_lsp_hash_free(struct isis_lsp_hash *ih); -struct isis_lsp *isis_lsp_hash_lookup(struct isis_lsp_hash *ih, - struct isis_lsp *lsp); -void isis_lsp_hash_add(struct isis_lsp_hash *ih, struct isis_lsp *lsp); -void isis_lsp_hash_release(struct isis_lsp_hash *ih, struct isis_lsp *lsp); #endif diff --git a/isisd/subdir.am b/isisd/subdir.am index 792b0df6c2..a45b9ca47c 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -27,7 +27,6 @@ noinst_HEADERS += \ isisd/isis_events.h \ isisd/isis_flags.h \ isisd/isis_lsp.h \ - isisd/isis_lsp_hash.h \ isisd/isis_memory.h \ isisd/isis_misc.h \ isisd/isis_mt.h \ @@ -40,6 +39,7 @@ noinst_HEADERS += \ isisd/isis_spf_private.h \ isisd/isis_te.h \ isisd/isis_tlvs.h \ + isisd/isis_tx_queue.h \ isisd/isis_vty_common.h \ isisd/isis_zebra.h \ isisd/isisd.h \ @@ -58,7 +58,6 @@ LIBISIS_SOURCES = \ isisd/isis_events.c \ isisd/isis_flags.c \ isisd/isis_lsp.c \ - isisd/isis_lsp_hash.c \ isisd/isis_memory.c \ isisd/isis_misc.c \ isisd/isis_mt.c \ @@ -69,6 +68,7 @@ LIBISIS_SOURCES = \ isisd/isis_spf.c \ isisd/isis_te.c \ isisd/isis_tlvs.c \ + isisd/isis_tx_queue.c \ isisd/isis_vty_common.c \ isisd/isis_zebra.c \ isisd/isisd.c \ From 1cbd5b37b7c0fc7bb1a075f2f232f5251ef1a106 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 9 Aug 2018 22:07:20 +0200 Subject: [PATCH 20/84] fabricd: support transmission/reception of circuit-scoped LSPs OpenFabric makes use of flooding scope LSPs to reduce the amount of reflooding caused by the update process. Implement transmission and reception of such PDUs. Signed-off-by: Christian Franke --- isisd/isis_pdu.c | 49 +++++++++++++++++++++++++++++++++++++++++------- isisd/isis_pdu.h | 2 ++ 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index c1523a268d..ce050a0c93 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -710,12 +710,35 @@ out: static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, const uint8_t *ssnpa, uint8_t max_area_addrs) { - int level = (pdu_type == L1_LINK_STATE) ? ISIS_LEVEL1 : ISIS_LEVEL2; + int level; + bool circuit_scoped; + + if (pdu_type == FS_LINK_STATE) { + if (!fabricd) + return ISIS_ERROR; + if (max_area_addrs != L2_CIRCUIT_FLOODING_SCOPE) + return ISIS_ERROR; + level = ISIS_LEVEL2; + circuit_scoped = true; + + /* The stream is used verbatim for sending out new LSPDUs. + * So make sure we store it as an L2 LSPDU internally. + * (compare for the reverse in `send_lsp`) */ + stream_putc_at(circuit->rcv_stream, 4, L2_LINK_STATE); + stream_putc_at(circuit->rcv_stream, 7, 0); + } else { + if (pdu_type == L1_LINK_STATE) + level = ISIS_LEVEL1; + else + level = ISIS_LEVEL2; + circuit_scoped = false; + } if (isis->debugs & DEBUG_UPDATE_PACKETS) { zlog_debug( - "ISIS-Upd (%s): Rcvd L%d LSP on %s, cirType %s, cirID %u", - circuit->area->area_tag, level, + "ISIS-Upd (%s): Rcvd %sL%d LSP on %s, cirType %s, cirID %u", + circuit->area->area_tag, + circuit_scoped ? "Circuit scoped " : "", level, circuit->interface->name, circuit_t2string(circuit->is_type), circuit->circuit_id); @@ -907,7 +930,8 @@ dontcheckadj: lsp_confusion); tlvs = NULL; /* ii */ - lsp_flood(lsp, NULL); + if (!circuit_scoped) + lsp_flood(lsp, NULL); /* v */ ISIS_FLAGS_CLEAR_ALL( lsp->SSNflags); /* FIXME: @@ -952,7 +976,8 @@ dontcheckadj: /* our own LSP -> 7.3.16.4 c) */ if (comp == LSP_NEWER) { lsp_inc_seqno(lsp, hdr.seqno); - lsp_flood(lsp, NULL); + if (!circuit_scoped) + lsp_flood(lsp, NULL); } else { isis_tx_queue_add(circuit->tx_queue, lsp, TX_LSP_NORMAL); @@ -1035,7 +1060,8 @@ dontcheckadj: circuit->area, level, false); tlvs = NULL; } - lsp_flood(lsp, circuit); + if (!circuit_scoped) + lsp_flood(lsp, circuit); /* iv */ if (circuit->circ_type != CIRCUIT_T_BROADCAST) @@ -1342,6 +1368,7 @@ static int pdu_size(uint8_t pdu_type, uint8_t *size) break; case L1_LINK_STATE: case L2_LINK_STATE: + case FS_LINK_STATE: *size = ISIS_LSP_HDR_LEN; break; case L1_COMPLETE_SEQ_NUM: @@ -1442,7 +1469,9 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) } /* either 3 or 0 */ - if (max_area_addrs != 0 && max_area_addrs != isis->max_area_addrs) { + if (pdu_type != FS_LINK_STATE /* FS PDU doesn't contain max area addr field */ + && max_area_addrs != 0 + && max_area_addrs != isis->max_area_addrs) { flog_err( ISIS_ERR_PACKET, "maximumAreaAddressesMismatch: maximumAreaAdresses in a received PDU %" PRIu8 @@ -1459,6 +1488,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) break; case L1_LINK_STATE: case L2_LINK_STATE: + case FS_LINK_STATE: retval = process_lsp(pdu_type, circuit, ssnpa, max_area_addrs); break; case L1_COMPLETE_SEQ_NUM: @@ -2155,6 +2185,11 @@ void send_lsp(void *arg, struct isis_lsp *lsp, enum isis_tx_type tx_type) /* copy our lsp to the send buffer */ stream_copy(circuit->snd_stream, lsp->pdu); + if (tx_type == TX_LSP_CIRCUIT_SCOPED) { + stream_putc_at(circuit->snd_stream, 4, FS_LINK_STATE); + stream_putc_at(circuit->snd_stream, 7, L2_CIRCUIT_FLOODING_SCOPE); + } + if (isis->debugs & DEBUG_UPDATE_PACKETS) { zlog_debug("ISIS-Upd (%s): Sending L%d LSP %s, seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 diff --git a/isisd/isis_pdu.h b/isisd/isis_pdu.h index ea19de2591..3d2420eb03 100644 --- a/isisd/isis_pdu.h +++ b/isisd/isis_pdu.h @@ -127,6 +127,8 @@ struct isis_p2p_hello_hdr { #define L1_LINK_STATE 18 #define L2_LINK_STATE 20 +#define FS_LINK_STATE 10 +#define L2_CIRCUIT_FLOODING_SCOPE 2 struct isis_lsp_hdr { uint16_t pdu_len; uint16_t rem_lifetime; From 686afe9f0700b561ea493d2041f5cf16b8d92538 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 10 May 2018 20:02:04 +0200 Subject: [PATCH 21/84] fabricd: add field with first and second nexthop to SPF paths OpenFabric requires knowledge of the first two hops on each path calculated by spf to implement its flooding optimization. Extend the hopcount-spf to build such a datastructure. Signed-off-by: Christian Franke --- isisd/isis_spf.c | 41 +++++++++++++++++++++++++--- isisd/isis_spf_private.h | 6 ++++ tests/isisd/test_isis_vertex_queue.c | 14 ++++++---- 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 79b3103237..225ef22ec1 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -179,7 +179,8 @@ static const char *vid2string(struct isis_vertex *vertex, char *buff, int size) return "UNKNOWN"; } -static struct isis_vertex *isis_vertex_new(union isis_N *n, +static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree, + union isis_N *n, enum vertextype vtype) { struct isis_vertex *vertex; @@ -191,6 +192,12 @@ static struct isis_vertex *isis_vertex_new(union isis_N *n, vertex->Adj_N = list_new(); vertex->parents = list_new(); + if (spftree->hopcount_metric) { + vertex->firsthops = hash_create(isis_vertex_queue_hash_key, + isis_vertex_queue_hash_cmp, + NULL); + } + return vertex; } @@ -334,7 +341,7 @@ static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree, zlog_warn("ISIS-Spf: could not find own l%d LSP!", spftree->level); - vertex = isis_vertex_new(&n, + vertex = isis_vertex_new(spftree, &n, spftree->area->oldmetric ? VTYPE_NONPSEUDO_IS : VTYPE_NONPSEUDO_TE_IS); @@ -350,6 +357,26 @@ static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree, return vertex; } +static void vertex_add_parent_firsthop(struct hash_backet *backet, void *arg) +{ + struct isis_vertex *vertex = arg; + struct isis_vertex *hop = backet->data; + + hash_get(vertex->firsthops, hop, hash_alloc_intern); +} + +static void vertex_update_firsthops(struct isis_vertex *vertex, + struct isis_vertex *parent) +{ + if (vertex->d_N <= 2) + hash_get(vertex->firsthops, vertex, hash_alloc_intern); + + if (vertex->d_N < 2 || !parent) + return; + + hash_iterate(parent->firsthops, vertex_add_parent_firsthop, vertex); +} + /* * Add a vertex to TENT sorted by cost and by vertextype on tie break situation */ @@ -368,7 +395,7 @@ static struct isis_vertex *isis_spf_add2tent(struct isis_spftree *spftree, assert(isis_find_vertex(&spftree->paths, id, vtype) == NULL); assert(isis_find_vertex(&spftree->tents, id, vtype) == NULL); - vertex = isis_vertex_new(id, vtype); + vertex = isis_vertex_new(spftree, id, vtype); vertex->d_N = cost; vertex->depth = depth; @@ -376,6 +403,9 @@ static struct isis_vertex *isis_spf_add2tent(struct isis_spftree *spftree, listnode_add(vertex->parents, parent); } + if (spftree->hopcount_metric) + vertex_update_firsthops(vertex, parent); + if (parent && parent->Adj_N && listcount(parent->Adj_N) > 0) { for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node, parent_adj)) listnode_add(vertex->Adj_N, parent_adj); @@ -497,6 +527,8 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype, if (listnode_lookup(vertex->Adj_N, parent_adj) == NULL) listnode_add(vertex->Adj_N, parent_adj); + if (spftree->hopcount_metric) + vertex_update_firsthops(vertex, parent); /* 2) */ if (listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS) remove_excess_adjs(vertex->Adj_N); @@ -1062,7 +1094,8 @@ struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, isis_spf_preload_tent(spftree, sysid, root); } else { isis_vertex_queue_insert(&spftree->tents, isis_vertex_new( - sysid, VTYPE_NONPSEUDO_TE_IS)); + spftree, sysid, + VTYPE_NONPSEUDO_TE_IS)); } isis_spf_loop(spftree, sysid); diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h index 3cd6a005be..4478c7a997 100644 --- a/isisd/isis_spf_private.h +++ b/isisd/isis_spf_private.h @@ -64,6 +64,7 @@ struct isis_vertex { uint16_t depth; /* The depth in the imaginary tree */ struct list *Adj_N; /* {Adj(N)} next hop or neighbor list */ struct list *parents; /* list of parents for ECMP */ + struct hash *firsthops; /* first two hops to neighbor */ uint64_t insert_counter; }; @@ -170,6 +171,11 @@ static void isis_vertex_del(struct isis_vertex *vertex) { list_delete_and_null(&vertex->Adj_N); list_delete_and_null(&vertex->parents); + if (vertex->firsthops) { + hash_clean(vertex->firsthops, NULL); + hash_free(vertex->firsthops); + vertex->firsthops = NULL; + } memset(vertex, 0, sizeof(struct isis_vertex)); XFREE(MTYPE_ISIS_VERTEX, vertex); diff --git a/tests/isisd/test_isis_vertex_queue.c b/tests/isisd/test_isis_vertex_queue.c index 3e31b83351..96c215dcff 100644 --- a/tests/isisd/test_isis_vertex_queue.c +++ b/tests/isisd/test_isis_vertex_queue.c @@ -16,6 +16,8 @@ static size_t vertex_count; static void setup_test_vertices(void) { + struct isis_spftree t = { + }; union isis_N nid, nip = { .ip.dest.family = AF_UNSPEC }; @@ -25,33 +27,35 @@ static void setup_test_vertices(void) nip.ip.dest.family = AF_INET; nip.ip.dest.prefixlen = 24; inet_pton(AF_INET, "192.168.1.0", &nip.ip.dest.u.prefix4); - vertices[vertex_count] = isis_vertex_new(&nip, VTYPE_IPREACH_TE); + vertices[vertex_count] = isis_vertex_new(&t, &nip, VTYPE_IPREACH_TE); vertices[vertex_count]->d_N = 20; vertex_count++; nip.ip.dest.family = AF_INET; nip.ip.dest.prefixlen = 24; inet_pton(AF_INET, "192.168.2.0", &nip.ip.dest.u.prefix4); - vertices[vertex_count] = isis_vertex_new(&nip, VTYPE_IPREACH_TE); + vertices[vertex_count] = isis_vertex_new(&t, &nip, VTYPE_IPREACH_TE); vertices[vertex_count]->d_N = 20; vertex_count++; memset(nid.id, 0, sizeof(nid.id)); nid.id[6] = 1; - vertices[vertex_count] = isis_vertex_new(&nid, VTYPE_PSEUDO_TE_IS); + vertices[vertex_count] = isis_vertex_new(&t, &nid, + VTYPE_PSEUDO_TE_IS); vertices[vertex_count]->d_N = 15; vertex_count++; memset(nid.id, 0, sizeof(nid.id)); nid.id[5] = 2; - vertices[vertex_count] = isis_vertex_new(&nid, VTYPE_NONPSEUDO_TE_IS); + vertices[vertex_count] = isis_vertex_new(&t, &nid, + VTYPE_NONPSEUDO_TE_IS); vertices[vertex_count]->d_N = 15; vertex_count++; nip.ip.dest.family = AF_INET; nip.ip.dest.prefixlen = 24; inet_pton(AF_INET, "192.168.3.0", &nip.ip.dest.u.prefix4); - vertices[vertex_count] = isis_vertex_new(&nip, VTYPE_IPREACH_TE); + vertices[vertex_count] = isis_vertex_new(&t, &nip, VTYPE_IPREACH_TE); vertices[vertex_count]->d_N = 20; vertex_count++; }; From 1f5be499f0c5727b89e2544c834955a631600412 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 10 May 2018 13:10:59 +0200 Subject: [PATCH 22/84] fabricd: build a list of neighbors and neighbors neighbors OpenFabric uses a list of neighbors and neighbors neighbors to calculate a set of designated reflooders. While the draft prescribes that these lists should be built whenever an LSP needs to be flooded, this implementation opted to build them only when we ran an spf, given that they will only change when the topology changes. Signed-off-by: Christian Franke --- isisd/fabricd.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/isisd/fabricd.c b/isisd/fabricd.c index fe8731c8d0..25252a0564 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -29,9 +29,11 @@ #include "isisd/isis_spf.h" #include "isisd/isis_tlvs.h" #include "isisd/isis_lsp.h" +#include "isisd/isis_spf_private.h" #include "isisd/isis_tx_queue.h" DEFINE_MTYPE_STATIC(ISISD, FABRICD_STATE, "ISIS OpenFabric") +DEFINE_MTYPE_STATIC(ISISD, FABRICD_NEIGHBOR, "ISIS OpenFabric Neighbor Entry") /* Tracks initial synchronization as per section 2.4 * @@ -53,6 +55,8 @@ struct fabricd { struct thread *initial_sync_timeout; struct isis_spftree *spftree; + struct skiplist *neighbors; + struct hash *neighbors_neighbors; uint8_t tier; uint8_t tier_config; @@ -61,13 +65,99 @@ struct fabricd { struct thread *tier_set_timer; }; +/* Code related to maintaining the neighbor lists */ + +struct neighbor_entry { + struct isis_vertex *vertex; + bool present; +}; + +static struct neighbor_entry *neighbor_entry_new(struct isis_vertex *vertex) +{ + struct neighbor_entry *rv = XMALLOC(MTYPE_FABRICD_NEIGHBOR, sizeof(*rv)); + + rv->vertex = vertex; + return rv; +} + +static void neighbor_entry_del(struct neighbor_entry *neighbor) +{ + XFREE(MTYPE_FABRICD_NEIGHBOR, neighbor); +} + +static void neighbor_entry_del_void(void *arg) +{ + neighbor_entry_del((struct neighbor_entry *)arg); +} + +static void neighbor_lists_clear(struct fabricd *f) +{ + while (!skiplist_empty(f->neighbors)) + skiplist_delete_first(f->neighbors); + + hash_clean(f->neighbors_neighbors, neighbor_entry_del_void); +} + +static unsigned neighbor_entry_hash_key(void *np) +{ + struct neighbor_entry *n = np; + + return jhash(n->vertex->N.id, ISIS_SYS_ID_LEN, 0x55aa5a5a); +} + +static int neighbor_entry_hash_cmp(const void *a, const void *b) +{ + const struct neighbor_entry *na = a, *nb = b; + + return memcmp(na->vertex->N.id, nb->vertex->N.id, ISIS_SYS_ID_LEN) == 0; +} + +static int neighbor_entry_list_cmp(void *a, void *b) +{ + struct neighbor_entry *na = a, *nb = b; + + return -memcmp(na->vertex->N.id, nb->vertex->N.id, ISIS_SYS_ID_LEN); +} + +static void neighbor_lists_update(struct fabricd *f) +{ + neighbor_lists_clear(f); + + struct listnode *node; + struct isis_vertex *v; + + for (ALL_QUEUE_ELEMENTS_RO(&f->spftree->paths, node, v)) { + if (!v->d_N || !VTYPE_IS(v->type)) + continue; + + if (v->d_N > 2) + break; + + struct neighbor_entry *n = neighbor_entry_new(v); + if (v->d_N == 1) { + skiplist_insert(f->neighbors, n, n); + } else { + struct neighbor_entry *inserted; + inserted = hash_get(f->neighbors_neighbors, n, hash_alloc_intern); + assert(inserted == n); + } + } +} + struct fabricd *fabricd_new(struct isis_area *area) { struct fabricd *rv = XCALLOC(MTYPE_FABRICD_STATE, sizeof(*rv)); rv->area = area; rv->initial_sync_state = FABRICD_SYNC_PENDING; + rv->spftree = isis_spftree_new(area); + rv->neighbors = skiplist_new(0, neighbor_entry_list_cmp, + neighbor_entry_del_void); + rv->neighbors_neighbors = hash_create(neighbor_entry_hash_key, + neighbor_entry_hash_cmp, + "Fabricd Neighbors"); + rv->tier = rv->tier_config = ISIS_TIER_UNDEFINED; return rv; }; @@ -84,6 +174,9 @@ void fabricd_finish(struct fabricd *f) thread_cancel(f->tier_set_timer); isis_spftree_del(f->spftree); + neighbor_lists_clear(f); + skiplist_free(f->neighbors); + hash_free(f->neighbors_neighbors); } static int fabricd_initial_sync_timeout(struct thread *thread) @@ -303,6 +396,7 @@ void fabricd_run_spf(struct isis_area *area) return; isis_run_hopcount_spf(area, isis->sysid, f->spftree); + neighbor_lists_update(f); fabricd_bump_tier_calculation_timer(f); } From 9d224819339c0c3e1485eac09daf6178ae94cfb5 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 10 May 2018 17:40:04 +0200 Subject: [PATCH 23/84] fabricd: implement flooding optimization Regular IS-IS will flood any LSP updates out to all circuits except the one where it was received on. This is done in `lsp_flood`. Change `lsp_flood` for fabricd to use the optimized flooding algorithm instead. Signed-off-by: Christian Franke --- isisd/fabricd.c | 156 +++++++++++++++++++++++++++++++++++++++ isisd/fabricd.h | 2 + isisd/isis_lsp.c | 10 ++- isisd/isis_spf_private.h | 2 +- 4 files changed, 166 insertions(+), 4 deletions(-) diff --git a/isisd/fabricd.c b/isisd/fabricd.c index 25252a0564..95718f3bdd 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -119,6 +119,45 @@ static int neighbor_entry_list_cmp(void *a, void *b) return -memcmp(na->vertex->N.id, nb->vertex->N.id, ISIS_SYS_ID_LEN); } +static struct neighbor_entry *neighbor_entry_lookup_list(struct skiplist *list, + const uint8_t *id) +{ + struct isis_vertex querier; + isis_vertex_id_init(&querier, id, VTYPE_NONPSEUDO_TE_IS); + + struct neighbor_entry n = { + .vertex = &querier + }; + + struct neighbor_entry *rv; + + if (skiplist_search(list, &n, (void**)&rv)) + return NULL; + + if (!rv->present) + return NULL; + + return rv; +} + +static struct neighbor_entry *neighbor_entry_lookup_hash(struct hash *hash, + const uint8_t *id) +{ + struct isis_vertex querier; + isis_vertex_id_init(&querier, id, VTYPE_NONPSEUDO_TE_IS); + + struct neighbor_entry n = { + .vertex = &querier + }; + + struct neighbor_entry *rv = hash_lookup(hash, &n); + + if (!rv || !rv->present) + return NULL; + + return rv; +} + static void neighbor_lists_update(struct fabricd *f) { neighbor_lists_clear(f); @@ -446,3 +485,120 @@ int fabricd_write_settings(struct isis_area *area, struct vty *vty) return written; } + +static void move_to_dnr(struct isis_lsp *lsp, struct neighbor_entry *n) +{ + struct isis_adjacency *adj = listnode_head(n->vertex->Adj_N); + + n->present = false; + if (adj) { + isis_tx_queue_add(adj->circuit->tx_queue, lsp, + TX_LSP_CIRCUIT_SCOPED); + } +} + +static void move_to_rf(struct isis_lsp *lsp, struct neighbor_entry *n) +{ + struct isis_adjacency *adj = listnode_head(n->vertex->Adj_N); + + n->present = false; + if (adj) { + isis_tx_queue_add(adj->circuit->tx_queue, lsp, + TX_LSP_NORMAL); + } +} + +static void mark_neighbor_as_present(struct hash_backet *backet, void *arg) +{ + struct neighbor_entry *n = backet->data; + + n->present = true; +} + +static void handle_firsthops(struct hash_backet *backet, void *arg) +{ + struct isis_lsp *lsp = arg; + struct fabricd *f = lsp->area->fabricd; + struct isis_vertex *vertex = backet->data; + + struct neighbor_entry *n; + + n = neighbor_entry_lookup_list(f->neighbors, vertex->N.id); + if (n) + n->present = false; + + n = neighbor_entry_lookup_hash(f->neighbors_neighbors, vertex->N.id); + if (n) + n->present = false; +} + +void fabricd_lsp_flood(struct isis_lsp *lsp) +{ + struct fabricd *f = lsp->area->fabricd; + assert(f); + + void *cursor = NULL; + struct neighbor_entry *n; + + /* Mark all elements in NL as present and move T0s into DNR */ + while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor)) { + n->present = true; + + struct isis_lsp *lsp = lsp_for_vertex(f->spftree, n->vertex); + if (!lsp || !lsp->tlvs || !lsp->tlvs->spine_leaf) + continue; + + if (!lsp->tlvs->spine_leaf->has_tier + || lsp->tlvs->spine_leaf->tier != 0) + continue; + + move_to_dnr(lsp, n); + } + + /* Mark all elements in NN as present */ + hash_iterate(f->neighbors_neighbors, mark_neighbor_as_present, NULL); + + struct isis_vertex *originator = isis_find_vertex(&f->spftree->paths, + lsp->hdr.lsp_id, + VTYPE_NONPSEUDO_TE_IS); + + /* Remove all IS from NL and NN in the shortest path + * to the IS that originated the LSP */ + if (originator) + hash_iterate(originator->firsthops, handle_firsthops, lsp); + + /* Iterate over all remaining IS in NL */ + cursor = NULL; + while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor)) { + if (!n->present) + continue; + + struct isis_lsp *nlsp = lsp_for_vertex(f->spftree, n->vertex); + if (!nlsp || !nlsp->tlvs) { + move_to_dnr(lsp, n); + continue; + } + + /* For all neighbors of the NL IS check whether they are present + * in NN. If yes, remove from NN and set need_reflood. */ + bool need_reflood = false; + struct isis_extended_reach *er; + for (er = (struct isis_extended_reach *)nlsp->tlvs->extended_reach.head; + er; er = er->next) { + struct neighbor_entry *nn; + + nn = neighbor_entry_lookup_hash(f->neighbors_neighbors, + er->id); + + if (nn) { + nn->present = false; + need_reflood = true; + } + } + + if (need_reflood) + move_to_rf(lsp, n); + else + move_to_dnr(lsp, n); + } +} diff --git a/isisd/fabricd.h b/isisd/fabricd.h index a6dc979729..da07c5d814 100644 --- a/isisd/fabricd.h +++ b/isisd/fabricd.h @@ -27,6 +27,7 @@ struct fabricd; struct isis_circuit; struct isis_area; struct isis_spftree; +struct isis_lsp; struct vty; struct fabricd *fabricd_new(struct isis_area *area); @@ -40,5 +41,6 @@ struct isis_spftree *fabricd_spftree(struct isis_area *area); void fabricd_configure_tier(struct isis_area *area, uint8_t tier); uint8_t fabricd_tier(struct isis_area *area); int fabricd_write_settings(struct isis_area *area, struct vty *vty); +void fabricd_lsp_flood(struct isis_lsp *lsp); #endif diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 2598572248..dc38737923 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -1972,7 +1972,11 @@ void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set) void lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit) { - lsp_set_all_srmflags(lsp); - if (circuit) - isis_tx_queue_del(circuit->tx_queue, lsp); + if (!fabricd) { + lsp_set_all_srmflags(lsp, true); + if (circuit) + isis_tx_queue_del(circuit->tx_queue, lsp); + } else { + fabricd_lsp_flood(lsp); + } } diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h index 4478c7a997..2c6514d470 100644 --- a/isisd/isis_spf_private.h +++ b/isisd/isis_spf_private.h @@ -309,7 +309,7 @@ struct isis_spftree { }; __attribute__((__unused__)) -static void isis_vertex_id_init(struct isis_vertex *vertex, union isis_N *n, +static void isis_vertex_id_init(struct isis_vertex *vertex, const union isis_N *n, enum vertextype vtype) { vertex->type = vtype; From d4cff91a064b593235cf7a510e2383c8443f1fed Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Wed, 25 Jul 2018 11:01:59 +0200 Subject: [PATCH 24/84] fabricd: add flooding algorithm debugging Add a command `debug openfabric flooding` to allow verification of correct operation of the OpenFabric flooding optimization algorithm. Signed-off-by: Christian Franke --- isisd/fabricd.c | 62 ++++++++++++++++++++++++++++++++++++++-- isisd/isis_spf.c | 3 +- isisd/isis_spf.h | 1 - isisd/isis_spf_private.h | 3 ++ isisd/isis_vty_fabricd.c | 32 +++++++++++++++++++++ isisd/isisd.c | 6 ++++ isisd/isisd.h | 1 + 7 files changed, 103 insertions(+), 5 deletions(-) diff --git a/isisd/fabricd.c b/isisd/fabricd.c index 95718f3bdd..88ecdf2a7b 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -491,6 +491,13 @@ static void move_to_dnr(struct isis_lsp *lsp, struct neighbor_entry *n) struct isis_adjacency *adj = listnode_head(n->vertex->Adj_N); n->present = false; + + if (isis->debugs & DEBUG_FABRICD_FLOODING) { + char buff[PREFIX2STR_BUFFER]; + zlog_debug("OpenFabric: Adding %s to DNR", + vid2string(n->vertex, buff, sizeof(buff))); + } + if (adj) { isis_tx_queue_add(adj->circuit->tx_queue, lsp, TX_LSP_CIRCUIT_SCOPED); @@ -502,6 +509,13 @@ static void move_to_rf(struct isis_lsp *lsp, struct neighbor_entry *n) struct isis_adjacency *adj = listnode_head(n->vertex->Adj_N); n->present = false; + + if (isis->debugs & DEBUG_FABRICD_FLOODING) { + char buff[PREFIX2STR_BUFFER]; + zlog_debug("OpenFabric: Adding %s to RF", + vid2string(n->vertex, buff, sizeof(buff))); + } + if (adj) { isis_tx_queue_add(adj->circuit->tx_queue, lsp, TX_LSP_NORMAL); @@ -524,12 +538,24 @@ static void handle_firsthops(struct hash_backet *backet, void *arg) struct neighbor_entry *n; n = neighbor_entry_lookup_list(f->neighbors, vertex->N.id); - if (n) + if (n) { + if (isis->debugs & DEBUG_FABRICD_FLOODING) { + char buff[PREFIX2STR_BUFFER]; + zlog_debug("Removing %s from NL as its in the reverse path", + vid2string(vertex, buff, sizeof(buff))); + } n->present = false; + } n = neighbor_entry_lookup_hash(f->neighbors_neighbors, vertex->N.id); - if (n) + if (n) { + if (isis->debugs & DEBUG_FABRICD_FLOODING) { + char buff[PREFIX2STR_BUFFER]; + zlog_debug("Removing %s from NN as its in the reverse path", + vid2string(vertex, buff, sizeof(buff))); + } n->present = false; + } } void fabricd_lsp_flood(struct isis_lsp *lsp) @@ -540,6 +566,11 @@ void fabricd_lsp_flood(struct isis_lsp *lsp) void *cursor = NULL; struct neighbor_entry *n; + if (isis->debugs & DEBUG_FABRICD_FLOODING) { + zlog_debug("OpenFabric: Flooding LSP %s", + rawlspid_print(lsp->hdr.lsp_id)); + } + /* Mark all elements in NL as present and move T0s into DNR */ while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor)) { n->present = true; @@ -552,6 +583,11 @@ void fabricd_lsp_flood(struct isis_lsp *lsp) || lsp->tlvs->spine_leaf->tier != 0) continue; + if (isis->debugs & DEBUG_FABRICD_FLOODING) { + zlog_debug("Moving %s to DNR because it's T0", + rawlspid_print(lsp->hdr.lsp_id)); + } + move_to_dnr(lsp, n); } @@ -575,10 +611,22 @@ void fabricd_lsp_flood(struct isis_lsp *lsp) struct isis_lsp *nlsp = lsp_for_vertex(f->spftree, n->vertex); if (!nlsp || !nlsp->tlvs) { + if (isis->debugs & DEBUG_FABRICD_FLOODING) { + char buff[PREFIX2STR_BUFFER]; + zlog_debug("Moving %s to DNR as it has no LSP", + vid2string(n->vertex, buff, sizeof(buff))); + } + move_to_dnr(lsp, n); continue; } + if (isis->debugs & DEBUG_FABRICD_FLOODING) { + char buff[PREFIX2STR_BUFFER]; + zlog_debug("Considering %s from NL...", + vid2string(n->vertex, buff, sizeof(buff))); + } + /* For all neighbors of the NL IS check whether they are present * in NN. If yes, remove from NN and set need_reflood. */ bool need_reflood = false; @@ -591,6 +639,12 @@ void fabricd_lsp_flood(struct isis_lsp *lsp) er->id); if (nn) { + if (isis->debugs & DEBUG_FABRICD_FLOODING) { + char buff[PREFIX2STR_BUFFER]; + zlog_debug("Found neighbor %s in NN, removing it from NN and setting reflood.", + vid2string(nn->vertex, buff, sizeof(buff))); + } + nn->present = false; need_reflood = true; } @@ -601,4 +655,8 @@ void fabricd_lsp_flood(struct isis_lsp *lsp) else move_to_dnr(lsp, n); } + + if (isis->debugs & DEBUG_FABRICD_FLOODING) { + zlog_debug("OpenFabric: Flooding algorithm complete."); + } } diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 225ef22ec1..25e2d43cea 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -162,8 +162,7 @@ static const char *vtype2string(enum vertextype vtype) return NULL; /* Not reached */ } -#define VID2STR_BUFFER SRCDEST2STR_BUFFER -static const char *vid2string(struct isis_vertex *vertex, char *buff, int size) +const char *vid2string(struct isis_vertex *vertex, char *buff, int size) { if (VTYPE_IS(vertex->type) || VTYPE_ES(vertex->type)) { return print_sys_hostname(vertex->N.id); diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h index 0532bd5465..f4db98cfed 100644 --- a/isisd/isis_spf.h +++ b/isisd/isis_spf.h @@ -40,5 +40,4 @@ void isis_spf_print(struct isis_spftree *spftree, struct vty *vty); struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, uint8_t *sysid, struct isis_spftree *spftree); - #endif /* _ZEBRA_ISIS_SPF_H */ diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h index 2c6514d470..2e33367e21 100644 --- a/isisd/isis_spf_private.h +++ b/isisd/isis_spf_private.h @@ -354,4 +354,7 @@ static struct isis_lsp *lsp_for_vertex(struct isis_spftree *spftree, return NULL; } +#define VID2STR_BUFFER SRCDEST2STR_BUFFER +const char *vid2string(struct isis_vertex *vertex, char *buff, int size); + #endif diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c index 5ef3af0f19..95ebe0de81 100644 --- a/isisd/isis_vty_fabricd.c +++ b/isisd/isis_vty_fabricd.c @@ -55,8 +55,40 @@ DEFUN (no_fabric_tier, return CMD_SUCCESS; } +DEFUN (debug_fabric_flooding, + debug_fabric_flooding_cmd, + "debug openfabric flooding", + DEBUG_STR + PROTO_HELP + "Flooding optimization algorithm\n") +{ + isis->debugs |= DEBUG_FABRICD_FLOODING; + print_debug(vty, DEBUG_FABRICD_FLOODING, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_fabric_flooding, + no_debug_fabric_flooding_cmd, + "no debug openfabric flooding", + NO_STR + UNDEBUG_STR + PROTO_HELP + "Flooding optimization algorithm\n") +{ + isis->debugs &= ~DEBUG_FABRICD_FLOODING; + print_debug(vty, DEBUG_FABRICD_FLOODING, 0); + + return CMD_SUCCESS; +} + + void isis_vty_daemon_init(void) { install_element(ROUTER_NODE, &fabric_tier_cmd); install_element(ROUTER_NODE, &no_fabric_tier_cmd); + install_element(ENABLE_NODE, &debug_fabric_flooding_cmd); + install_element(ENABLE_NODE, &no_debug_fabric_flooding_cmd); + install_element(CONFIG_NODE, &debug_fabric_flooding_cmd); + install_element(CONFIG_NODE, &no_debug_fabric_flooding_cmd); } diff --git a/isisd/isisd.c b/isisd/isisd.c index 84f44b1648..640bd69ce4 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -744,6 +744,8 @@ void print_debug(struct vty *vty, int flags, int onoff) vty_out(vty, "IS-IS LSP generation debugging is %s\n", onoffs); if (flags & DEBUG_LSP_SCHED) vty_out(vty, "IS-IS LSP scheduling debugging is %s\n", onoffs); + if (flags & DEBUG_FABRICD_FLOODING) + vty_out(vty, "OpenFabric Flooding debugging is %s\n", onoffs); } DEFUN_NOSH (show_debugging, @@ -825,6 +827,10 @@ static int config_write_debug(struct vty *vty) vty_out(vty, "debug " PROTO_NAME " lsp-sched\n"); write++; } + if (flags & DEBUG_FABRICD_FLOODING) { + vty_out(vty, "debug " PROTO_NAME " flooding\n"); + write++; + } write += spf_backoff_write_config(vty); return write; diff --git a/isisd/isisd.h b/isisd/isisd.h index 0e75b870a5..cc5def8f56 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -209,6 +209,7 @@ extern struct thread_master *master; #define DEBUG_PACKET_DUMP (1<<12) #define DEBUG_LSP_GEN (1<<13) #define DEBUG_LSP_SCHED (1<<14) +#define DEBUG_FABRICD_FLOODING (1<<15) #define lsp_debug(...) \ do { \ From bd507085e0559ae6c8c6076c2948fdd6c66e4d1f Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 25 May 2018 13:26:27 +0200 Subject: [PATCH 25/84] isisd: add support for Prefix-SID subtlv Extend isisd's TLV parser to support the Prefix-SID subtlv as per draft-ietf-isis-segment-routing-extensions-19 Signed-off-by: Christian Franke --- isisd/isis_tlvs.c | 182 ++++++++++++++++++++++++++++++++++++++++++++-- isisd/isis_tlvs.h | 30 +++++++- 2 files changed, 201 insertions(+), 11 deletions(-) diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index 44beb45e43..966350e30f 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -107,6 +107,111 @@ static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX]; /* Prototypes */ static void append_item(struct isis_item_list *dest, struct isis_item *item); +/* Functions for Sub-TLV 3 SR Prefix-SID */ + +static struct isis_item *copy_item_prefix_sid(struct isis_item *i) +{ + struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i; + struct isis_prefix_sid *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv)); + + rv->flags = sid->flags; + rv->algorithm = sid->algorithm; + rv->value = sid->value; + return (struct isis_item *)rv; +} + +static void format_item_prefix_sid(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i; + + sbuf_push(buf, indent, "SR Prefix-SID:\n"); + sbuf_push(buf, indent, " Flags:%s%s%s%s%s%s\n", + sid->flags & ISIS_PREFIX_SID_READVERTISED ? " READVERTISED" : "", + sid->flags & ISIS_PREFIX_SID_NODE ? " NODE" : "", + sid->flags & ISIS_PREFIX_SID_NO_PHP ? " NO_PHP" : "", + sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL ? " EXPLICIT-NULL" : "", + sid->flags & ISIS_PREFIX_SID_VALUE ? " VALUE" : "", + sid->flags & ISIS_PREFIX_SID_LOCAL ? " LOCAL" : ""); + sbuf_push(buf, indent, " Algorithm: %" PRIu8 "\n", sid->algorithm); + if (sid->flags & ISIS_PREFIX_SID_VALUE) { + sbuf_push(buf, indent, "Label: %" PRIu32 "\n", sid->value); + } else { + sbuf_push(buf, indent, "Index: %" PRIu32 "\n", sid->value); + } +} + +static void free_item_prefix_sid(struct isis_item *i) +{ + XFREE(MTYPE_ISIS_SUBTLV, i); +} + +static int pack_item_prefix_sid(struct isis_item *i, struct stream *s) +{ + struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i; + + uint8_t size = (sid->flags & ISIS_PREFIX_SID_VALUE) ? 5 : 6; + + if (STREAM_WRITEABLE(s) < size) + return 1; + + stream_putc(s, sid->flags); + stream_putc(s, sid->algorithm); + + if (sid->flags & ISIS_PREFIX_SID_VALUE) { + stream_put3(s, sid->value); + } else { + stream_putl(s, sid->value); + } + + return 0; +} + +static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s, + struct sbuf *log, void *dest, int indent) +{ + struct isis_subtlvs *subtlvs = dest; + struct isis_prefix_sid sid = { + }; + + sbuf_push(log, indent, "Unpacking SR Prefix-SID...\n"); + + if (len < 5) { + sbuf_push(log, indent, + "Not enough data left. (expected 5 or more bytes, got %" PRIu8 ")\n", + len); + return 1; + } + + sid.flags = stream_getc(s); + if ((sid.flags & ISIS_PREFIX_SID_VALUE) + != (sid.flags & ISIS_PREFIX_SID_LOCAL)) { + sbuf_push(log, indent, "Flags inplausible: Local Flag needs to match Value Flag\n"); + return 0; + } + + sid.algorithm = stream_getc(s); + + uint8_t expected_size = (sid.flags & ISIS_PREFIX_SID_VALUE) ? 5 : 6; + if (len != expected_size) { + sbuf_push(log, indent, + "TLV size differs from expected size. " + "(expected %u but got %" PRIu8 ")\n", + expected_size, len); + return 1; + } + + if (sid.flags & ISIS_PREFIX_SID_VALUE) { + sid.value = stream_get3(s); + } else { + sid.value = stream_getl(s); + } + + format_item_prefix_sid(mtid, (struct isis_item *)&sid, log, indent + 2); + append_item(&subtlvs->prefix_sids, copy_item_prefix_sid((struct isis_item *)&sid)); + return 0; +} + /* Functions for Sub-TVL ??? IPv6 Source Prefix */ static struct prefix_ipv6 *copy_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p) @@ -198,14 +303,36 @@ static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context, memcpy(subtlvs->source_prefix, &p, sizeof(p)); return 0; } +static void init_item_list(struct isis_item_list *items); +static struct isis_item *copy_item(enum isis_tlv_context context, + enum isis_tlv_type type, + struct isis_item *item); +static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type, + struct isis_item_list *src, struct isis_item_list *dest); +static void format_items_(uint16_t mtid, enum isis_tlv_context context, + enum isis_tlv_type type, struct isis_item_list *items, + struct sbuf *buf, int indent); +#define format_items(...) format_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__) +static void free_items(enum isis_tlv_context context, enum isis_tlv_type type, + struct isis_item_list *items); +static int pack_items_(uint16_t mtid, enum isis_tlv_context context, + enum isis_tlv_type type, struct isis_item_list *items, + struct stream *s, struct isis_tlvs **fragment_tlvs, + struct pack_order_entry *pe, + struct isis_tlvs *(*new_fragment)(struct list *l), + struct list *new_fragment_arg); +#define pack_items(...) pack_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__) /* Functions related to subtlvs */ -static struct isis_subtlvs *isis_alloc_subtlvs(void) +static struct isis_subtlvs *isis_alloc_subtlvs(enum isis_tlv_context context) { struct isis_subtlvs *result; result = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*result)); + result->context = context; + + init_item_list(&result->prefix_sids); return result; } @@ -217,6 +344,11 @@ static struct isis_subtlvs *copy_subtlvs(struct isis_subtlvs *subtlvs) struct isis_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv)); + rv->context = subtlvs->context; + + copy_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID, + &subtlvs->prefix_sids, &rv->prefix_sids); + rv->source_prefix = copy_subtlv_ipv6_source_prefix(subtlvs->source_prefix); return rv; @@ -225,6 +357,9 @@ static struct isis_subtlvs *copy_subtlvs(struct isis_subtlvs *subtlvs) static void format_subtlvs(struct isis_subtlvs *subtlvs, struct sbuf *buf, int indent) { + format_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID, + &subtlvs->prefix_sids, buf, indent); + format_subtlv_ipv6_source_prefix(subtlvs->source_prefix, buf, indent); } @@ -233,6 +368,9 @@ static void isis_free_subtlvs(struct isis_subtlvs *subtlvs) if (!subtlvs) return; + free_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID, + &subtlvs->prefix_sids); + XFREE(MTYPE_ISIS_SUBTLV, subtlvs->source_prefix); XFREE(MTYPE_ISIS_SUBTLV, subtlvs); @@ -248,6 +386,11 @@ static int pack_subtlvs(struct isis_subtlvs *subtlvs, struct stream *s) stream_putc(s, 0); /* Put 0 as subtlvs length, filled in later */ + rv = pack_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID, + &subtlvs->prefix_sids, s, NULL, NULL, NULL, NULL); + if (rv) + return rv; + rv = pack_subtlv_ipv6_source_prefix(subtlvs->source_prefix, s); if (rv) return rv; @@ -1135,6 +1278,7 @@ static void free_item_extended_ip_reach(struct isis_item *i) { struct isis_extended_ip_reach *item = (struct isis_extended_ip_reach *)i; + isis_free_subtlvs(item->subtlvs); XFREE(MTYPE_ISIS_TLV, item); } @@ -1149,11 +1293,16 @@ static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s) control = r->down ? ISIS_EXTENDED_IP_REACH_DOWN : 0; control |= r->prefix.prefixlen; + control |= r->subtlvs ? ISIS_EXTENDED_IP_REACH_SUBTLV : 0; + stream_putc(s, control); if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen)) return 1; stream_put(s, &r->prefix.prefix.s_addr, PSIZE(r->prefix.prefixlen)); + + if (r->subtlvs) + return pack_subtlvs(r->subtlvs, s); return 0; } @@ -1235,9 +1384,12 @@ static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len, len - 6 - PSIZE(rv->prefix.prefixlen)); goto out; } - sbuf_push(log, indent, "Skipping %" PRIu8 " bytes of subvls", - subtlv_len); - stream_forward_getp(s, subtlv_len); + + rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH); + if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IP_REACH, subtlv_len, s, + log, rv->subtlvs, indent + 4)) { + goto out; + } } append_item(items, (struct isis_item *)rv); @@ -1712,7 +1864,7 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s, goto out; } - rv->subtlvs = isis_alloc_subtlvs(); + rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH); if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH, subtlv_len, s, log, rv->subtlvs, indent + 4)) { goto out; @@ -1890,7 +2042,6 @@ static void format_items_(uint16_t mtid, enum isis_tlv_context context, for (i = items->head; i; i = i->next) format_item(mtid, context, type, i, buf, indent); } -#define format_items(...) format_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__) static void free_item(enum isis_tlv_context tlv_context, enum isis_tlv_type tlv_type, struct isis_item *item) @@ -1996,6 +2147,14 @@ top: break; } + /* Multiple prefix-sids don't go into one TLV, so always break */ + if (type == ISIS_SUBTLV_PREFIX_SID + && (context == ISIS_CONTEXT_SUBTLV_IP_REACH + || context == ISIS_CONTEXT_SUBTLV_IPV6_REACH)) { + item = item->next; + break; + } + if (len > 255) { if (!last_len) /* strange, not a single item fit */ return 1; @@ -2800,6 +2959,9 @@ int isis_unpack_tlvs(size_t avail_len, struct stream *stream, .name = _desc_, .unpack = unpack_subtlv_##_name_, \ } +#define ITEM_SUBTLV_OPS(_name_, _desc_) \ + ITEM_TLV_OPS(_name_, _desc_) + ITEM_TLV_OPS(area_address, "TLV 1 Area Addresses"); ITEM_TLV_OPS(oldstyle_reach, "TLV 2 IS Reachability"); ITEM_TLV_OPS(lan_neighbor, "TLV 6 LAN Neighbors"); @@ -2818,6 +2980,7 @@ TLV_OPS(threeway_adj, "TLV 240 P2P Three-Way Adjacency"); ITEM_TLV_OPS(ipv6_address, "TLV 232 IPv6 Interface Address"); ITEM_TLV_OPS(ipv6_reach, "TLV 236 IPv6 Reachability"); +ITEM_SUBTLV_OPS(prefix_sid, "Sub-TLV 3 SR Prefix-SID"); SUBTLV_OPS(ipv6_source_prefix, "Sub-TLV 22 IPv6 Source Prefix"); static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = { @@ -2845,8 +3008,11 @@ static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = { [ISIS_TLV_MT_IPV6_REACH] = &tlv_ipv6_reach_ops, }, [ISIS_CONTEXT_SUBTLV_NE_REACH] = {}, - [ISIS_CONTEXT_SUBTLV_IP_REACH] = {}, + [ISIS_CONTEXT_SUBTLV_IP_REACH] = { + [ISIS_SUBTLV_PREFIX_SID] = &tlv_prefix_sid_ops, + }, [ISIS_CONTEXT_SUBTLV_IPV6_REACH] = { + [ISIS_SUBTLV_PREFIX_SID] = &tlv_prefix_sid_ops, [ISIS_SUBTLV_IPV6_SOURCE_PREFIX] = &subtlv_ipv6_source_prefix_ops, } }; @@ -3318,7 +3484,7 @@ void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid, mtid); struct isis_ipv6_reach *r = (struct isis_ipv6_reach*)last_item(l); - r->subtlvs = isis_alloc_subtlvs(); + r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH); r->subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*src)); memcpy(r->subtlvs->source_prefix, src, sizeof(*src)); } diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index 451321ea98..7ebb648cc6 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -83,6 +83,8 @@ struct isis_extended_ip_reach { uint32_t metric; bool down; struct prefix_ipv4 prefix; + + struct isis_subtlvs *subtlvs; }; struct isis_ipv6_reach; @@ -219,9 +221,21 @@ struct isis_tlvs { struct isis_spine_leaf *spine_leaf; }; -struct isis_subtlvs { - /* draft-baker-ipv6-isis-dst-src-routing-06 */ - struct prefix_ipv6 *source_prefix; +#define ISIS_PREFIX_SID_READVERTISED 0x80 +#define ISIS_PREFIX_SID_NODE 0x40 +#define ISIS_PREFIX_SID_NO_PHP 0x20 +#define ISIS_PREFIX_SID_EXPLICIT_NULL 0x10 +#define ISIS_PREFIX_SID_VALUE 0x08 +#define ISIS_PREFIX_SID_LOCAL 0x04 + +struct isis_prefix_sid; +struct isis_prefix_sid { + struct isis_prefix_sid *next; + + uint8_t flags; + uint8_t algorithm; + + uint32_t value; }; enum isis_tlv_context { @@ -232,6 +246,15 @@ enum isis_tlv_context { ISIS_CONTEXT_MAX }; +struct isis_subtlvs { + enum isis_tlv_context context; + + /* draft-baker-ipv6-isis-dst-src-routing-06 */ + struct prefix_ipv6 *source_prefix; + /* draft-ietf-isis-segment-routing-extensions-16 */ + struct isis_item_list prefix_sids; +}; + enum isis_tlv_type { ISIS_TLV_AREA_ADDRESSES = 1, ISIS_TLV_OLDSTYLE_REACH = 2, @@ -258,6 +281,7 @@ enum isis_tlv_type { ISIS_TLV_THREE_WAY_ADJ = 240, ISIS_TLV_MAX = 256, + ISIS_SUBTLV_PREFIX_SID = 3, ISIS_SUBTLV_IPV6_SOURCE_PREFIX = 22 }; From 9d33cf3886c6451291d019577ae6e6f1c9dbbf2a Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 25 May 2018 14:31:59 +0200 Subject: [PATCH 26/84] tests/isisd: remove auth when fuzzing Our "deserialize, reserialize, check-equality" test fails when the fuzzer produces PDUs with incorrect cryptographic checksums. While the most realistic solution would be to validate the cryptographic checksums in the test program, that seems very silly, given that we don't want to fuzz our cryptographic auth. Given that, removing auth during fuzzing seems the next best solution. Signed-off-by: Christian Franke --- tests/isisd/test_fuzz_isis_tlv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c index 67a1593500..3a56f83f0a 100644 --- a/tests/isisd/test_fuzz_isis_tlv.c +++ b/tests/isisd/test_fuzz_isis_tlv.c @@ -114,7 +114,11 @@ static int test(FILE *input, FILE *output) const char *s_tlvs = isis_format_tlvs(tlvs); fprintf(output, "Unpacked TLVs:\n%s", s_tlvs); + struct isis_item *orig_auth = tlvs->isis_auth.head; + tlvs->isis_auth.head = NULL; + s_tlvs = isis_format_tlvs(tlvs); struct isis_tlvs *tlv_copy = isis_copy_tlvs(tlvs); + tlvs->isis_auth.head = orig_auth; isis_free_tlvs(tlvs); struct stream *s2 = stream_new(TEST_STREAM_SIZE); From 5f77d90188278673764eb63d3210b38dc42924b0 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 28 May 2018 15:36:15 +0200 Subject: [PATCH 27/84] isisd: add TLV 13 Purge Originator Identification Extend our parser to support the Purge Originator Identifaction TLV as per RFC 6232. Signed-off-by: Christian Franke --- isisd/isis_tlvs.c | 124 ++++++++++++++++++++++ isisd/isis_tlvs.h | 9 ++ tests/isisd/test_fuzz_isis_tlv_tests.h.gz | Bin 233035 -> 222030 bytes 3 files changed, 133 insertions(+) diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index 966350e30f..0efe52d0c1 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -1985,6 +1985,114 @@ static int unpack_item_auth(uint16_t mtid, uint8_t len, struct stream *s, return 0; } +/* Functions related to TLV 13 Purge Originator */ + +static struct isis_purge_originator *copy_tlv_purge_originator( + struct isis_purge_originator *poi) +{ + if (!poi) + return NULL; + + struct isis_purge_originator *rv; + + rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + rv->sender_set = poi->sender_set; + memcpy(rv->generator, poi->generator, sizeof(rv->generator)); + if (poi->sender_set) + memcpy(rv->sender, poi->sender, sizeof(rv->sender)); + return rv; +} + +static void format_tlv_purge_originator(struct isis_purge_originator *poi, + struct sbuf *buf, int indent) +{ + if (!poi) + return; + + sbuf_push(buf, indent, "Purge Originator Identification:\n"); + sbuf_push(buf, indent, " Generator: %s\n", + isis_format_id(poi->generator, sizeof(poi->generator))); + if (poi->sender_set) { + sbuf_push(buf, indent, " Received-From: %s\n", + isis_format_id(poi->sender, sizeof(poi->sender))); + } +} + +static void free_tlv_purge_originator(struct isis_purge_originator *poi) +{ + XFREE(MTYPE_ISIS_TLV, poi); +} + +static int pack_tlv_purge_originator(struct isis_purge_originator *poi, + struct stream *s) +{ + if (!poi) + return 0; + + uint8_t data_len = 1 + sizeof(poi->generator); + + if (poi->sender_set) + data_len += sizeof(poi->sender); + + if (STREAM_WRITEABLE(s) < (unsigned)(2 + data_len)) + return 1; + + stream_putc(s, ISIS_TLV_PURGE_ORIGINATOR); + stream_putc(s, data_len); + stream_putc(s, poi->sender_set ? 2 : 1); + stream_put(s, poi->generator, sizeof(poi->generator)); + if (poi->sender_set) + stream_put(s, poi->sender, sizeof(poi->sender)); + return 0; +} + +static int unpack_tlv_purge_originator(enum isis_tlv_context context, + uint8_t tlv_type, uint8_t tlv_len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + struct isis_purge_originator poi = {}; + + sbuf_push(log, indent, "Unpacking Purge Originator Identification TLV...\n"); + if (tlv_len < 7) { + sbuf_push(log, indent, "Not enough data left. (Expected at least 7 bytes, got %" + PRIu8 ")\n", tlv_len); + return 1; + } + + uint8_t number_of_ids = stream_getc(s); + + if (number_of_ids == 1) { + poi.sender_set = false; + } else if (number_of_ids == 2) { + poi.sender_set = true; + } else { + sbuf_push(log, indent, "Got invalid value for number of system IDs: %" + PRIu8 ")\n", number_of_ids); + return 1; + } + + if (tlv_len != 1 + 6 * number_of_ids) { + sbuf_push(log, indent, "Incorrect tlv len for number of IDs.\n"); + return 1; + } + + stream_get(poi.generator, s, sizeof(poi.generator)); + if (poi.sender_set) + stream_get(poi.sender, s, sizeof(poi.sender)); + + if (tlvs->purge_originator) { + sbuf_push(log, indent, + "WARNING: Purge originator present multiple times, ignoring.\n"); + return 0; + } + + tlvs->purge_originator = copy_tlv_purge_originator(&poi); + return 0; +} + + /* Functions relating to item TLVs */ static void init_item_list(struct isis_item_list *items) @@ -2410,6 +2518,9 @@ struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs) copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth, &rv->isis_auth); + rv->purge_originator = + copy_tlv_purge_originator(tlvs->purge_originator); + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES, &tlvs->area_addresses, &rv->area_addresses); @@ -2478,6 +2589,8 @@ static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, int indent) format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth, buf, indent); + format_tlv_purge_originator(tlvs->purge_originator, buf, indent); + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES, &tlvs->area_addresses, buf, indent); @@ -2553,6 +2666,7 @@ void isis_free_tlvs(struct isis_tlvs *tlvs) return; free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth); + free_tlv_purge_originator(tlvs->purge_originator); free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES, &tlvs->area_addresses); free_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO, @@ -2701,6 +2815,14 @@ static int pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream, return rv; } + rv = pack_tlv_purge_originator(tlvs->purge_originator, stream); + if (rv) + return rv; + if (fragment_tlvs) { + fragment_tlvs->purge_originator = + copy_tlv_purge_originator(tlvs->purge_originator); + } + rv = pack_tlv_protocols_supported(&tlvs->protocols_supported, stream); if (rv) return rv; @@ -2967,6 +3089,7 @@ ITEM_TLV_OPS(oldstyle_reach, "TLV 2 IS Reachability"); ITEM_TLV_OPS(lan_neighbor, "TLV 6 LAN Neighbors"); ITEM_TLV_OPS(lsp_entry, "TLV 9 LSP Entries"); ITEM_TLV_OPS(auth, "TLV 10 IS-IS Auth"); +TLV_OPS(purge_originator, "TLV 13 Purge Originator Identification"); ITEM_TLV_OPS(extended_reach, "TLV 22 Extended Reachability"); ITEM_TLV_OPS(oldstyle_ip_reach, "TLV 128/130 IP Reachability"); TLV_OPS(protocols_supported, "TLV 129 Protocols Supported"); @@ -2990,6 +3113,7 @@ static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = { [ISIS_TLV_LAN_NEIGHBORS] = &tlv_lan_neighbor_ops, [ISIS_TLV_LSP_ENTRY] = &tlv_lsp_entry_ops, [ISIS_TLV_AUTH] = &tlv_auth_ops, + [ISIS_TLV_PURGE_ORIGINATOR] = &tlv_purge_originator_ops, [ISIS_TLV_EXTENDED_REACH] = &tlv_extended_reach_ops, [ISIS_TLV_MT_REACH] = &tlv_extended_reach_ops, [ISIS_TLV_OLDSTYLE_IP_REACH] = &tlv_oldstyle_ip_reach_ops, diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index 7ebb648cc6..abdd03f02d 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -189,6 +189,13 @@ struct isis_item_list { unsigned int count; }; +struct isis_purge_originator { + bool sender_set; + + uint8_t generator[6]; + uint8_t sender[6]; +}; + RB_HEAD(isis_mt_item_list, isis_item_list); struct isis_item_list *isis_get_mt_items(struct isis_mt_item_list *m, @@ -198,6 +205,7 @@ struct isis_item_list *isis_lookup_mt_items(struct isis_mt_item_list *m, struct isis_tlvs { struct isis_item_list isis_auth; + struct isis_purge_originator *purge_originator; struct isis_item_list area_addresses; struct isis_item_list oldstyle_reach; struct isis_item_list lan_neighbor; @@ -262,6 +270,7 @@ enum isis_tlv_type { ISIS_TLV_PADDING = 8, ISIS_TLV_LSP_ENTRY = 9, ISIS_TLV_AUTH = 10, + ISIS_TLV_PURGE_ORIGINATOR = 13, ISIS_TLV_EXTENDED_REACH = 22, ISIS_TLV_OLDSTYLE_IP_REACH = 128, diff --git a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz index 4abbe81499be693f39a91cf6581bde569ebd3da8..4a89bda84eac865059ab1755ffddc96497bc9691 100644 GIT binary patch literal 222030 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

nwJx+!UejKR^rj99BJg^WRIT4jkzYmn3(8Tk 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 z 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 pjjh@tH;GzQEwTUr literal 233035 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%0A7fnOK5?P7^(ohE>f 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

B}=&JeUW*+YKS;BP8r)3L@@dM?@XZO>@?weJ??d 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 Date: Thu, 31 May 2018 15:14:26 +0200 Subject: [PATCH 28/84] isisd: add purge originator identification support Implement RFC 6232, optionally allowing to flood isisd's NET and hostname in purges it originates. Signed-off-by: Christian Franke --- doc/user/isisd.rst | 8 +++++++ isisd/isis_lsp.c | 48 +++++++++++++++++++++++++++-------------- isisd/isis_pdu.c | 3 ++- isisd/isis_tlvs.c | 17 +++++++++++++++ isisd/isis_tlvs.h | 4 ++++ isisd/isis_vty_common.c | 14 ++++++++++++ isisd/isisd.c | 4 ++++ isisd/isisd.h | 1 + 8 files changed, 82 insertions(+), 17 deletions(-) diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index 54f82f6832..ee681858d1 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -106,6 +106,14 @@ writing, *isisd* does not support multiple ISIS processes. Set overload bit to avoid any transit traffic. +.. index:: purge-originator +.. clicmd:: purge-originator + +.. index:: no purge-originator +.. clicmd:: no purge-originator + + Enable or disable :rfc:`6232` purge originator identification. + .. _isis-timer: ISIS Timer diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index dc38737923..e8777e9b53 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -353,7 +353,21 @@ void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno) isis_spf_schedule(lsp->area, lsp->level); } -static void lsp_purge(struct isis_lsp *lsp, int level) +static void lsp_purge_add_poi(struct isis_lsp *lsp, + const uint8_t *sender) +{ + if (!lsp->area->purge_originator) + return; + + /* add purge originator identification */ + if (!lsp->tlvs) + lsp->tlvs = isis_alloc_tlvs(); + isis_tlvs_set_purge_originator(lsp->tlvs, isis->sysid, sender); + isis_tlvs_set_dynamic_hostname(lsp->tlvs, cmd_hostname_get()); +} + +static void lsp_purge(struct isis_lsp *lsp, int level, + const uint8_t *sender) { /* reset stream */ lsp_clear_data(lsp); @@ -365,6 +379,8 @@ static void lsp_purge(struct isis_lsp *lsp, int level) lsp->level = level; lsp->age_out = lsp->area->max_lsp_lifetime[level - 1]; + lsp_purge_add_poi(lsp, sender); + lsp_pack_pdu(lsp); lsp_flood(lsp, NULL); } @@ -386,7 +402,7 @@ static void lsp_seqno_update(struct isis_lsp *lsp0) if (lsp->tlvs) lsp_inc_seqno(lsp, 0); else - lsp_purge(lsp, lsp0->level); + lsp_purge(lsp, lsp0->level, NULL); } return; @@ -426,7 +442,8 @@ static void lsp_update_data(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, lsp->tlvs = tlvs; - if (area->dynhostname && lsp->tlvs->hostname) { + if (area->dynhostname && lsp->tlvs->hostname + && lsp->hdr.rem_lifetime) { isis_dynhn_insert(lsp->hdr.lsp_id, lsp->tlvs->hostname, (lsp->hdr.lsp_bits & LSPBIT_IST) == IS_LEVEL_1_AND_2 @@ -463,10 +480,10 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, lsp->own_lsp = 0; } - lsp_update_data(lsp, hdr, tlvs, stream, area, level); if (confusion) { - lsp->hdr.rem_lifetime = hdr->rem_lifetime = 0; - put_lsp_hdr(lsp, NULL, true); + lsp_purge(lsp, level, NULL); + } else { + lsp_update_data(lsp, hdr, tlvs, stream, area, level); } if (LSP_FRAGMENT(lsp->hdr.lsp_id) && !lsp->lspu.zero_lsp) { @@ -1865,17 +1882,14 @@ int lsp_tick(struct thread *thread) */ if (rem_lifetime == 1 && lsp->hdr.seqno != 0) { /* 7.3.16.4 a) set SRM flags on all */ - lsp_flood(lsp, NULL); - /* 7.3.16.4 b) retain only the header - * FIXME */ + /* 7.3.16.4 b) retain only the header */ + if (lsp->area->purge_originator) + lsp_purge(lsp, lsp->level, NULL); + else + lsp_flood(lsp, NULL); /* 7.3.16.4 c) record the time to purge * FIXME */ - /* run/schedule spf */ - /* isis_spf_schedule is called inside - * lsp_destroy() below; - * so it is not needed here. */ - /* isis_spf_schedule (lsp->area, - * lsp->level); */ + isis_spf_schedule(lsp->area, lsp->level); } if (lsp->age_out == 0) { @@ -1917,7 +1931,7 @@ void lsp_purge_pseudo(uint8_t *id, struct isis_circuit *circuit, int level) if (!lsp) return; - lsp_purge(lsp, level); + lsp_purge(lsp, level, NULL); } /* @@ -1941,6 +1955,8 @@ void lsp_purge_non_exist(int level, struct isis_lsp_hdr *hdr, memcpy(&lsp->hdr, hdr, sizeof(lsp->hdr)); lsp->hdr.rem_lifetime = 0; + lsp_purge_add_poi(lsp, NULL); + lsp_pack_pdu(lsp); lsp_insert(lsp, area->lspdb[lsp->level - 1]); diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index ce050a0c93..8649c5a0c8 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -900,7 +900,8 @@ dontcheckadj: * but * wrong checksum, initiate a purge. */ if (lsp && (lsp->hdr.seqno == hdr.seqno) - && (lsp->hdr.checksum != hdr.checksum)) { + && (lsp->hdr.checksum != hdr.checksum) + && hdr.rem_lifetime) { zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08" PRIx32 " with confused checksum received.", circuit->area->area_tag, rawlspid_print(hdr.lsp_id), diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index 0efe52d0c1..b22460a0b5 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -3697,3 +3697,20 @@ isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid) return NULL; } + +void isis_tlvs_set_purge_originator(struct isis_tlvs *tlvs, + const uint8_t *generator, + const uint8_t *sender) +{ + assert(!tlvs->purge_originator); + + tlvs->purge_originator = XCALLOC(MTYPE_ISIS_TLV, + sizeof(*tlvs->purge_originator)); + memcpy(tlvs->purge_originator->generator, generator, + sizeof(tlvs->purge_originator->generator)); + if (sender) { + tlvs->purge_originator->sender_set = true; + memcpy(tlvs->purge_originator->sender, sender, + sizeof(tlvs->purge_originator->sender)); + } +} diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index abdd03f02d..4144809fa3 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -383,4 +383,8 @@ void isis_tlvs_add_spine_leaf(struct isis_tlvs *tlvs, uint8_t tier, struct isis_mt_router_info * isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid); + +void isis_tlvs_set_purge_originator(struct isis_tlvs *tlvs, + const uint8_t *generator, + const uint8_t *sender); #endif diff --git a/isisd/isis_vty_common.c b/isisd/isis_vty_common.c index dbe5beca6e..2b98a88b34 100644 --- a/isisd/isis_vty_common.c +++ b/isisd/isis_vty_common.c @@ -568,6 +568,18 @@ DEFUN (no_area_lsp_mtu, return isis_vty_lsp_mtu_set(vty, DEFAULT_LSP_MTU); } +DEFUN (area_purge_originator, + area_purge_originator_cmd, + "[no] purge-originator", + NO_STR + "Use the RFC 6232 purge-originator\n") +{ + VTY_DECLVAR_CONTEXT(isis_area, area); + + area->purge_originator = !!strcmp(argv[0]->text, "no"); + return CMD_SUCCESS; +} + int isis_vty_lsp_gen_interval_set(struct vty *vty, int level, uint16_t interval) { VTY_DECLVAR_CONTEXT(isis_area, area); @@ -924,6 +936,8 @@ void isis_vty_init(void) install_element(ROUTER_NODE, &area_lsp_mtu_cmd); install_element(ROUTER_NODE, &no_area_lsp_mtu_cmd); + install_element(ROUTER_NODE, &area_purge_originator_cmd); + install_element(ROUTER_NODE, &lsp_gen_interval_cmd); install_element(ROUTER_NODE, &no_lsp_gen_interval_cmd); diff --git a/isisd/isisd.c b/isisd/isisd.c index 640bd69ce4..e3ff3b8d93 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -2029,6 +2029,10 @@ int isis_config_write(struct vty *vty) vty_out(vty, " lsp-mtu %u\n", area->lsp_mtu); write++; } + if (area->purge_originator) { + vty_out(vty, " purge-originator\n"); + write++; + } /* Minimum SPF interval. */ if (area->min_spf_interval[0] diff --git a/isisd/isisd.h b/isisd/isisd.h index cc5def8f56..864021428a 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -148,6 +148,7 @@ struct isis_area { /* multi topology settings */ struct list *mt_settings; int ipv6_circuits; + bool purge_originator; /* Counters */ uint32_t circuit_state_changes; struct isis_redist redist_settings[REDIST_PROTOCOL_COUNT] From a2d41bb0ee1d18353f9cb58c9073f5805e0903ee Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Tue, 5 Jun 2018 20:50:59 +0200 Subject: [PATCH 29/84] fabricd: Remove processing for unneded PDUs and TLVs The OpenFabric draft prescribes that any IS-IS PDUs not needed for OpenFabric operation MUST be ignored. So this commit makes fabricd ignore any LAN IIHs and any L1 LSPs. Also the draft specifies that any reachabilities given as narrow-metric TLVs SHALL be ignored, so adhere to that too. Signed-off-by: Christian Franke --- isisd/isis_pdu.c | 6 ++++++ isisd/isis_spf.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 8649c5a0c8..d5e9704b02 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -1485,11 +1485,17 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) case L1_LAN_HELLO: case L2_LAN_HELLO: case P2P_HELLO: + if (fabricd && pdu_type != P2P_HELLO) + return ISIS_ERROR; retval = process_hello(pdu_type, circuit, ssnpa); break; case L1_LINK_STATE: case L2_LINK_STATE: case FS_LINK_STATE: + if (fabricd + && pdu_type != L2_LINK_STATE + && pdu_type != FS_LINK_STATE) + return ISIS_ERROR; retval = process_lsp(pdu_type, circuit, ssnpa, max_area_addrs); break; case L1_COMPLETE_SEQ_NUM: diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 25e2d43cea..02575a2f6a 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -607,6 +607,9 @@ lspfragloop: for (r = (struct isis_oldstyle_reach *) lsp->tlvs->oldstyle_reach.head; r; r = r->next) { + if (fabricd) + continue; + /* C.2.6 a) */ /* Two way connectivity */ if (!memcmp(r->id, root_sysid, ISIS_SYS_ID_LEN)) @@ -658,6 +661,9 @@ lspfragloop: &lsp->tlvs->oldstyle_ip_reach_ext}; for (unsigned int i = 0; i < array_size(reachs); i++) { + if (fabricd) + continue; + vtype = i ? VTYPE_IPREACH_EXTERNAL : VTYPE_IPREACH_INTERNAL; From df0ba689ebf4960812bb4c65ea1b44236627f6ba Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 7 Jun 2018 11:08:55 +0200 Subject: [PATCH 30/84] fabricd: trigger CSNP after flooding scope LSP has been received Have fabricd send out a CSNP whenever a circuit scoped LSP is received, and log a warning if the CSNP showed resynchronization was necessary. Signed-off-by: Christian Franke --- isisd/fabricd.c | 31 +++++++++++++++++++++++++++++++ isisd/fabricd.h | 2 ++ isisd/isis_pdu.c | 14 +++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/isisd/fabricd.c b/isisd/fabricd.c index 88ecdf2a7b..b52f2b554e 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -271,6 +271,16 @@ bool fabricd_initial_sync_is_in_progress(struct isis_area *area) return false; } +bool fabricd_initial_sync_is_complete(struct isis_area *area) +{ + struct fabricd *f = area->fabricd; + + if (!f) + return false; + + return f->initial_sync_state == FABRICD_SYNC_COMPLETE; +} + struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area) { struct fabricd *f = area->fabricd; @@ -660,3 +670,24 @@ void fabricd_lsp_flood(struct isis_lsp *lsp) zlog_debug("OpenFabric: Flooding algorithm complete."); } } + +void fabricd_trigger_csnp(struct isis_area *area) +{ + struct fabricd *f = area->fabricd; + + if (!f) + return; + + struct listnode *node; + struct isis_circuit *circuit; + + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { + if (!circuit->t_send_csnp[1]) + continue; + + thread_cancel(circuit->t_send_csnp[ISIS_LEVEL2 - 1]); + thread_add_timer_msec(master, send_l2_csnp, circuit, + isis_jitter(500, CSNP_JITTER), + &circuit->t_send_csnp[ISIS_LEVEL2 - 1]); + } +} diff --git a/isisd/fabricd.h b/isisd/fabricd.h index da07c5d814..f54c7bf89e 100644 --- a/isisd/fabricd.h +++ b/isisd/fabricd.h @@ -33,6 +33,7 @@ struct vty; struct fabricd *fabricd_new(struct isis_area *area); void fabricd_finish(struct fabricd *f); void fabricd_initial_sync_hello(struct isis_circuit *circuit); +bool fabricd_initial_sync_is_complete(struct isis_area *area); bool fabricd_initial_sync_is_in_progress(struct isis_area *area); struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area); void fabricd_initial_sync_finish(struct isis_area *area); @@ -42,5 +43,6 @@ void fabricd_configure_tier(struct isis_area *area, uint8_t tier); uint8_t fabricd_tier(struct isis_area *area); int fabricd_write_settings(struct isis_area *area, struct vty *vty); void fabricd_lsp_flood(struct isis_lsp *lsp); +void fabricd_trigger_csnp(struct isis_area *area); #endif diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index d5e9704b02..fc9ff5143f 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -1044,7 +1044,7 @@ dontcheckadj: if (!lsp0) { zlog_debug( "Got lsp frag, while zero lsp not in database"); - return ISIS_OK; + goto out; } } /* i */ @@ -1089,6 +1089,10 @@ dontcheckadj: retval = ISIS_OK; out: + if (circuit_scoped) { + fabricd_trigger_csnp(circuit->area); + } + isis_free_tlvs(tlvs); return retval; } @@ -1243,6 +1247,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, } } + bool resync_needed = false; + /* 7.3.15.2 b) Actions on LSP_ENTRIES reported */ for (struct isis_lsp_entry *entry = entry_head; entry; entry = entry->next) { @@ -1279,6 +1285,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, /* if (circuit->circ_type != * CIRCUIT_T_BROADCAST) */ isis_tx_queue_del(circuit->tx_queue, lsp); + resync_needed = true; } } } else { @@ -1313,6 +1320,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, lsp_set_all_srmflags(lsp, false); ISIS_SET_FLAG(lsp->SSNflags, circuit); + resync_needed = true; } } } @@ -1345,12 +1353,16 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, /* on remaining LSPs we set SRM (neighbor knew not of) */ for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) { isis_tx_queue_add(circuit->tx_queue, lsp, TX_LSP_NORMAL); + resync_needed = true; } /* lets free it */ list_delete_and_null(&lsp_list); } + if (fabricd_initial_sync_is_complete(circuit->area) && resync_needed) + zlog_warn("OpenFabric: Needed to resync LSPDB using CSNP!\n"); + retval = ISIS_OK; out: isis_free_tlvs(tlvs); From 103e4a718fb6ddc1b8dab00cf87ea91b44dedf9e Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 7 Jun 2018 12:58:21 +0200 Subject: [PATCH 31/84] zebra: add a ZEBRA_FLAG_ONLINK so that routes bypass the is-unnumbered check For OpenFabric operation, we need to be able to install routes via interfaces without any IPv4 addresses configured. Introduce a flag ZEBRA_FLAG_ONLINK which upper protocols can set on a route they send towards zebra, to force the nexthops to be considered onlink. Signed-off-by: Christian Franke --- lib/zebra.h | 1 + zebra/zebra_rib.c | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/zebra.h b/lib/zebra.h index b12f6616ba..7b7a42d908 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -414,6 +414,7 @@ extern const char *zserv_command_string(unsigned int command); #define ZEBRA_FLAG_FIB_OVERRIDE 0x200 #define ZEBRA_FLAG_EVPN_ROUTE 0x400 #define ZEBRA_FLAG_RR_USE_DISTANCE 0x800 +#define ZEBRA_FLAG_ONLINK 0x1000 /* ZEBRA_FLAG_BLACKHOLE was 0x04 */ /* ZEBRA_FLAG_REJECT was 0x80 */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index ab07549ec2..1088a97278 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -266,7 +266,8 @@ struct nexthop *route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re, There was a crash because ifp here was coming to be NULL */ if (ifp) if (connected_is_unnumbered(ifp) - || CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) { + || CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE) + || CHECK_FLAG(re->flags, ZEBRA_FLAG_ONLINK)) { SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); } @@ -303,8 +304,10 @@ struct nexthop *route_entry_nexthop_ipv6_ifindex_add(struct route_entry *re, nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX; nexthop->gate.ipv6 = *ipv6; nexthop->ifindex = ifindex; - if (CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) + if (CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE) + || CHECK_FLAG(re->flags, ZEBRA_FLAG_ONLINK)) { SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); + } route_entry_nexthop_add(re, nexthop); @@ -438,7 +441,8 @@ static int nexthop_active(afi_t afi, struct route_entry *re, */ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) { ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); - if (ifp && connected_is_unnumbered(ifp)) { + if ((ifp && connected_is_unnumbered(ifp)) + || CHECK_FLAG(re->flags, ZEBRA_FLAG_ONLINK)) { if (if_is_operative(ifp)) return 1; else From 414158882318898c970297d84d20935f4e4a7bb0 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 7 Jun 2018 12:58:42 +0200 Subject: [PATCH 32/84] fabricd: add support for completely unnumbered operation With this commit, fabricd can run without any IPv4 addresses configured except on loopback. There are two changes to achieve this: a) If a circuit has no IPv4 address configured, fabricd will resort to advertise the routers loopback IP in the OpenFabric hellos. b) All the routes from OpenFabric are sent with ZEBRA_FLAG_ONLINK set, so that zebra will install them into the fib without checking whether the nexthop is reachable Signed-off-by: Christian Franke --- isisd/fabricd.c | 24 ++++++++++++++++++++++++ isisd/fabricd.h | 1 + isisd/isis_pdu.c | 10 +++++++--- isisd/isis_zebra.c | 2 ++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/isisd/fabricd.c b/isisd/fabricd.c index b52f2b554e..7951efe5a1 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -691,3 +691,27 @@ void fabricd_trigger_csnp(struct isis_area *area) &circuit->t_send_csnp[ISIS_LEVEL2 - 1]); } } + +struct list *fabricd_ip_addrs(struct isis_circuit *circuit) +{ + if (circuit->ip_addrs && listcount(circuit->ip_addrs)) + return circuit->ip_addrs; + + if (!fabricd || !circuit->area || !circuit->area->circuit_list) + return NULL; + + struct listnode *node; + struct isis_circuit *c; + + for (ALL_LIST_ELEMENTS_RO(circuit->area->circuit_list, node, c)) { + if (c->circ_type != CIRCUIT_T_LOOPBACK) + continue; + + if (!c->ip_addrs || !listcount(c->ip_addrs)) + return NULL; + + return c->ip_addrs; + } + + return NULL; +} diff --git a/isisd/fabricd.h b/isisd/fabricd.h index f54c7bf89e..76c182f2d2 100644 --- a/isisd/fabricd.h +++ b/isisd/fabricd.h @@ -44,5 +44,6 @@ uint8_t fabricd_tier(struct isis_area *area); int fabricd_write_settings(struct isis_area *area, struct vty *vty); void fabricd_lsp_flood(struct isis_lsp *lsp); void fabricd_trigger_csnp(struct isis_area *area); +struct list *fabricd_ip_addrs(struct isis_circuit *circuit); #endif diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index fc9ff5143f..88575f5319 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -679,7 +679,7 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, goto out; } - iih.v4_usable = (circuit->ip_addrs && listcount(circuit->ip_addrs) + iih.v4_usable = (fabricd_ip_addrs(circuit) && iih.tlvs->ipv4_address.count); iih.v6_usable = (circuit->ipv6_link && listcount(circuit->ipv6_link) @@ -1689,8 +1689,12 @@ int send_hello(struct isis_circuit *circuit, int level) false, false); } - if (circuit->ip_router && circuit->ip_addrs) - isis_tlvs_add_ipv4_addresses(tlvs, circuit->ip_addrs); + if (circuit->ip_router) { + struct list *circuit_ip_addrs = fabricd_ip_addrs(circuit); + + if (circuit_ip_addrs) + isis_tlvs_add_ipv4_addresses(tlvs, circuit_ip_addrs); + } if (circuit->ipv6_router && circuit->ipv6_link) isis_tlvs_add_ipv6_addresses(tlvs, circuit->ipv6_link); diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 220f131b63..33d8a0f771 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -261,6 +261,8 @@ static void isis_zebra_route_add_route(struct prefix *prefix, return; memset(&api, 0, sizeof(api)); + if (fabricd) + api.flags |= ZEBRA_FLAG_ONLINK; api.vrf_id = VRF_DEFAULT; api.type = PROTO_TYPE; api.safi = SAFI_UNICAST; From f6ae63ca955e77aaf40ee1add30506a7a1bbcffa Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 9 Aug 2018 20:37:30 +0200 Subject: [PATCH 33/84] isisd: fix warnings by removing union isis_N and going to void * instead Signed-off-by: Christian Franke --- isisd/isis_spf.c | 10 +++---- isisd/isis_spf_private.h | 19 +++++++------ tests/isisd/test_isis_vertex_queue.c | 40 ++++++++++++++-------------- 3 files changed, 32 insertions(+), 37 deletions(-) diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 02575a2f6a..8f8cfb3078 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -179,14 +179,14 @@ const char *vid2string(struct isis_vertex *vertex, char *buff, int size) } static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree, - union isis_N *n, + void *id, enum vertextype vtype) { struct isis_vertex *vertex; vertex = XCALLOC(MTYPE_ISIS_VERTEX, sizeof(struct isis_vertex)); - isis_vertex_id_init(vertex, n, vtype); + isis_vertex_id_init(vertex, id, vtype); vertex->Adj_N = list_new(); vertex->parents = list_new(); @@ -330,17 +330,13 @@ static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree, #ifdef EXTREME_DEBUG char buff[VID2STR_BUFFER]; #endif /* EXTREME_DEBUG */ - union isis_N n; - - memcpy(n.id, sysid, ISIS_SYS_ID_LEN); - LSP_PSEUDO_ID(n.id) = 0; lsp = isis_root_system_lsp(spftree->area, spftree->level, sysid); if (lsp == NULL) zlog_warn("ISIS-Spf: could not find own l%d LSP!", spftree->level); - vertex = isis_vertex_new(spftree, &n, + vertex = isis_vertex_new(spftree, sysid, spftree->area->oldmetric ? VTYPE_NONPSEUDO_IS : VTYPE_NONPSEUDO_TE_IS); diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h index 2e33367e21..664ddc3f29 100644 --- a/isisd/isis_spf_private.h +++ b/isisd/isis_spf_private.h @@ -53,13 +53,12 @@ struct prefix_pair { /* * Triple */ -union isis_N { - uint8_t id[ISIS_SYS_ID_LEN + 1]; - struct prefix_pair ip; -}; struct isis_vertex { enum vertextype type; - union isis_N N; + union { + uint8_t id[ISIS_SYS_ID_LEN + 1]; + struct prefix_pair ip; + } N; uint32_t d_N; /* d(N) Distance from this IS */ uint16_t depth; /* The depth in the imaginary tree */ struct list *Adj_N; /* {Adj(N)} next hop or neighbor list */ @@ -309,15 +308,15 @@ struct isis_spftree { }; __attribute__((__unused__)) -static void isis_vertex_id_init(struct isis_vertex *vertex, const union isis_N *n, +static void isis_vertex_id_init(struct isis_vertex *vertex, const void *id, enum vertextype vtype) { vertex->type = vtype; if (VTYPE_IS(vtype) || VTYPE_ES(vtype)) { - memcpy(vertex->N.id, n->id, ISIS_SYS_ID_LEN + 1); + memcpy(vertex->N.id, id, ISIS_SYS_ID_LEN + 1); } else if (VTYPE_IP(vtype)) { - memcpy(&vertex->N.ip, &n->ip, sizeof(n->ip)); + memcpy(&vertex->N.ip, id, sizeof(vertex->N.ip)); } else { flog_err(LIB_ERR_DEVELOPMENT, "Unknown Vertex Type"); } @@ -325,12 +324,12 @@ static void isis_vertex_id_init(struct isis_vertex *vertex, const union isis_N * __attribute__((__unused__)) static struct isis_vertex *isis_find_vertex(struct isis_vertex_queue *queue, - union isis_N *n, + const void *id, enum vertextype vtype) { struct isis_vertex querier; - isis_vertex_id_init(&querier, n, vtype); + isis_vertex_id_init(&querier, id, vtype); return hash_lookup(queue->hash, &querier); } diff --git a/tests/isisd/test_isis_vertex_queue.c b/tests/isisd/test_isis_vertex_queue.c index 96c215dcff..869dd732eb 100644 --- a/tests/isisd/test_isis_vertex_queue.c +++ b/tests/isisd/test_isis_vertex_queue.c @@ -18,44 +18,44 @@ static void setup_test_vertices(void) { struct isis_spftree t = { }; - union isis_N nid, nip = { - .ip.dest.family = AF_UNSPEC + struct prefix_pair p = { }; + uint8_t node_id[7]; vertices = XMALLOC(MTYPE_TMP, sizeof(*vertices) * 16); - nip.ip.dest.family = AF_INET; - nip.ip.dest.prefixlen = 24; - inet_pton(AF_INET, "192.168.1.0", &nip.ip.dest.u.prefix4); - vertices[vertex_count] = isis_vertex_new(&t, &nip, VTYPE_IPREACH_TE); + p.dest.family = AF_INET; + p.dest.prefixlen = 24; + inet_pton(AF_INET, "192.168.1.0", &p.dest.u.prefix4); + vertices[vertex_count] = isis_vertex_new(&t, &p, VTYPE_IPREACH_TE); vertices[vertex_count]->d_N = 20; vertex_count++; - nip.ip.dest.family = AF_INET; - nip.ip.dest.prefixlen = 24; - inet_pton(AF_INET, "192.168.2.0", &nip.ip.dest.u.prefix4); - vertices[vertex_count] = isis_vertex_new(&t, &nip, VTYPE_IPREACH_TE); + p.dest.family = AF_INET; + p.dest.prefixlen = 24; + inet_pton(AF_INET, "192.168.2.0", &p.dest.u.prefix4); + vertices[vertex_count] = isis_vertex_new(&t, &p, VTYPE_IPREACH_TE); vertices[vertex_count]->d_N = 20; vertex_count++; - memset(nid.id, 0, sizeof(nid.id)); - nid.id[6] = 1; - vertices[vertex_count] = isis_vertex_new(&t, &nid, + memset(node_id, 0, sizeof(node_id)); + node_id[6] = 1; + vertices[vertex_count] = isis_vertex_new(&t, node_id, VTYPE_PSEUDO_TE_IS); vertices[vertex_count]->d_N = 15; vertex_count++; - memset(nid.id, 0, sizeof(nid.id)); - nid.id[5] = 2; - vertices[vertex_count] = isis_vertex_new(&t, &nid, + memset(node_id, 0, sizeof(node_id)); + node_id[5] = 2; + vertices[vertex_count] = isis_vertex_new(&t, node_id, VTYPE_NONPSEUDO_TE_IS); vertices[vertex_count]->d_N = 15; vertex_count++; - nip.ip.dest.family = AF_INET; - nip.ip.dest.prefixlen = 24; - inet_pton(AF_INET, "192.168.3.0", &nip.ip.dest.u.prefix4); - vertices[vertex_count] = isis_vertex_new(&t, &nip, VTYPE_IPREACH_TE); + p.dest.family = AF_INET; + p.dest.prefixlen = 24; + inet_pton(AF_INET, "192.168.3.0", &p.dest.u.prefix4); + vertices[vertex_count] = isis_vertex_new(&t, &p, VTYPE_IPREACH_TE); vertices[vertex_count]->d_N = 20; vertex_count++; }; From f3c7b99d05647fa5cda851917c12f3350a326e3c Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 20 Aug 2018 22:29:29 +0200 Subject: [PATCH 34/84] doc: add documentation for fabricd Signed-off-by: Christian Franke --- doc/manpages/conf.py | 1 + doc/manpages/fabricd.rst | 38 ++++ doc/manpages/index.rst | 1 + doc/manpages/subdir.am | 1 + doc/user/fabricd.rst | 404 ++++++++++++++++++++++++++++++++++++++ doc/user/index.rst | 1 + doc/user/installation.rst | 4 + doc/user/setup.rst | 3 + doc/user/subdir.am | 1 + 9 files changed, 454 insertions(+) create mode 100644 doc/manpages/fabricd.rst create mode 100644 doc/user/fabricd.rst diff --git a/doc/manpages/conf.py b/doc/manpages/conf.py index e540d236ea..f57bc1d278 100644 --- a/doc/manpages/conf.py +++ b/doc/manpages/conf.py @@ -333,6 +333,7 @@ man_pages = [ ('vtysh', 'vtysh', 'an integrated shell for FRRouting.', [], 1), ('frr', 'frr', 'a systemd interaction script', [], 1), ('bfdd', 'bfdd', fwfrr.format("a bfd"), [], 8), + ('fabricd', 'fabricd', fwfrr.format("an OpenFabric "), [], 8), ] # -- Options for Texinfo output ------------------------------------------- diff --git a/doc/manpages/fabricd.rst b/doc/manpages/fabricd.rst new file mode 100644 index 0000000000..c14c07661e --- /dev/null +++ b/doc/manpages/fabricd.rst @@ -0,0 +1,38 @@ +******* +FABRICD +******* + +.. include:: defines.rst +.. |DAEMON| replace:: fabricd + +SYNOPSIS +======== +|DAEMON| |synopsis-options-hv| + +|DAEMON| |synopsis-options| + +DESCRIPTION +=========== +|DAEMON| is a routing component that works with the FRRouting routing engine. + +OPTIONS +======= +OPTIONS available for the |DAEMON| command: + +.. include:: common-options.rst + +FILES +===== + +|INSTALL_PREFIX_SBIN|/|DAEMON| + The default location of the |DAEMON| binary. + +|INSTALL_PREFIX_ETC|/|DAEMON|.conf + The default location of the |DAEMON| config file. + +$(PWD)/|DAEMON|.log + If the |DAEMON| process is configured to output logs to a file, then you + will find this file in the directory where you started |DAEMON|. + +.. include:: epilogue.rst + diff --git a/doc/manpages/index.rst b/doc/manpages/index.rst index c62835c770..053555c4e4 100644 --- a/doc/manpages/index.rst +++ b/doc/manpages/index.rst @@ -10,6 +10,7 @@ bgpd eigrpd isisd + fabricd ldpd nhrpd ospf6d diff --git a/doc/manpages/subdir.am b/doc/manpages/subdir.am index 24f47fc7a5..20efd523fc 100644 --- a/doc/manpages/subdir.am +++ b/doc/manpages/subdir.am @@ -9,6 +9,7 @@ man_RSTFILES = \ doc/manpages/defines.rst \ doc/manpages/eigrpd.rst \ doc/manpages/epilogue.rst \ + doc/manpages/fabricd.rst \ doc/manpages/frr.rst \ doc/manpages/index.rst \ doc/manpages/isisd.rst \ diff --git a/doc/user/fabricd.rst b/doc/user/fabricd.rst new file mode 100644 index 0000000000..cf0f937c13 --- /dev/null +++ b/doc/user/fabricd.rst @@ -0,0 +1,404 @@ +.. _fabricd: + +********** +OpenFabric +********** + +OpenFabric, specified in :t:`draft-white-openfabric-06.txt`, is a routing +protocol derived from IS-IS, providing link-state routing with efficient +flooding for topologies like spine-leaf networks. + +FRR implements OpenFabric in a daemon called *fabricd* + +.. _configuring-fabricd: + +Configuring fabricd +=================== + +There are no *fabricd* specific options. Common options can be specified +(:ref:`common-invocation-options`) to *fabricd*. *fabricd* needs to acquire +interface information from *zebra* in order to function. Therefore *zebra* must +be running before invoking *fabricd*. Also, if *zebra* is restarted then *fabricd* +must be too. + +Like other daemons, *fabricd* configuration is done in an OpenFabric specific +configuration file :file:`fabricd.conf`. + +.. _openfabric-router: + +OpenFabric router +================= + +To enable the OpenFabric routing protocol, an OpenFabric router needs to be created +in the configuration: + +.. index:: router openfabric WORD +.. clicmd:: router openfabric WORD + +.. index:: no router openfabric WORD +.. clicmd:: no router openfabric WORD + + Enable or disable the OpenFabric process by specifying the OpenFabric domain with + 'WORD'. + +.. index:: net XX.XXXX. ... .XXX.XX +.. clicmd:: net XX.XXXX. ... .XXX.XX + +.. index:: no net XX.XXXX. ... .XXX.XX +.. clicmd:: no net XX.XXXX. ... .XXX.XX + + Set/Unset network entity title (NET) provided in ISO format. + +.. index:: domain-password [clear | md5] +.. clicmd:: domain-password [clear | md5] + +.. index:: no domain-password +.. clicmd:: no domain-password + + Configure the authentication password for a domain, as clear text or md5 one. + +.. index:: log-adjacency-changes +.. clicmd:: log-adjacency-changes + +.. index:: no log-adjacency-changes +.. clicmd:: no log-adjacency-changes + + Log changes in adjacency state. + +.. index:: set-overload-bit +.. clicmd:: set-overload-bit + +.. index:: no set-overload-bit +.. clicmd:: no set-overload-bit + + Set overload bit to avoid any transit traffic. + +.. index:: purge-originator +.. clicmd:: purge-originator + +.. index:: no purge-originator +.. clicmd:: no purge-originator + + Enable or disable :rfc:`6232` purge originator identification. + +.. index:: fabric-tier (0-14) +.. clicmd:: fabric-tier (0-14) + +.. index:: no fabric-tier +.. clicmd:: no fabric-tier + + Configure a static tier number to advertise as location in the fabric + +.. _openfabric-timer: + +OpenFabric Timer +================ + +.. index:: lsp-gen-interval (1-120) +.. clicmd:: lsp-gen-interval (1-120) + +.. index:: no lsp-gen-interval +.. clicmd:: no lsp-gen-interval + + Set minimum interval in seconds between regenerating same LSP. + +.. index:: lsp-refresh-interval (1-65235) +.. clicmd:: lsp-refresh-interval (1-65235) + +.. index:: no lsp-refresh-interval +.. clicmd:: no lsp-refresh-interval + + Set LSP refresh interval in seconds. + +.. index:: max-lsp-lifetime (360-65535) +.. clicmd:: max-lsp-lifetime (360-65535) + +.. index:: no max-lsp-lifetime +.. clicmd:: no max-lsp-lifetime + + Set LSP maximum LSP lifetime in seconds. + +.. index:: spf-interval (1-120) +.. clicmd:: spf-interval (1-120) + +.. index:: no spf-interval +.. clicmd:: no spf-interval + + Set minimum interval between consecutive SPF calculations in seconds. + +.. _openfabric-interface: + +OpenFabric interface +==================== + +.. index:: ip router openfabric WORD +.. clicmd:: ip router openfabric WORD + +.. index:: no ip router openfabric WORD +.. clicmd:: no ip router openfabric WORD + +.. _ip-router-openfabric-word: + + Activate OpenFabric on this interface. Note that the name + of OpenFabric instance must be the same as the one used to configure the + routing process (see command :clicmd:`router openfabric WORD`). + +.. index:: openfabric csnp-interval (1-600) +.. clicmd:: openfabric csnp-interval (1-600) + +.. index:: no openfabric csnp-interval +.. clicmd:: no openfabric csnp-interval + + Set CSNP interval in seconds. + +.. index:: openfabric hello-interval (1-600) +.. clicmd:: openfabric hello-interval (1-600) + +.. index:: no openfabric hello-interval +.. clicmd:: no openfabric hello-interval + + Set Hello interval in seconds. + +.. index:: openfabric hello-multiplier (2-100) +.. clicmd:: openfabric hello-multiplier (2-100) + +.. index:: no openfabric hello-multiplier +.. clicmd:: no openfabric hello-multiplier + + Set multiplier for Hello holding time. + +.. index:: openfabric metric (0-16777215) +.. clicmd:: openfabric metric (0-16777215) + +.. index:: no openfabric metric +.. clicmd:: no openfabric metric + + Set interface metric value. + +.. index:: openfabric passive +.. clicmd:: openfabric passive + +.. index:: no openfabric passive +.. clicmd:: no openfabric passive + + Configure the passive mode for this interface. + +.. index:: openfabric password [clear | md5] +.. clicmd:: openfabric password [clear | md5] + +.. index:: no openfabric password +.. clicmd:: no openfabric password + + Configure the authentication password (clear or encoded text) for the + interface. + +.. index:: openfabric psnp-interval (1-120) +.. clicmd:: openfabric psnp-interval (1-120) + +.. index:: no openfabric psnp-interval +.. clicmd:: no openfabric psnp-interval + + Set PSNP interval in seconds. + +.. _showing-openfabric-information: + +Showing OpenFabric information +============================== + +.. index:: show openfabric summary +.. clicmd:: show openfabric summary + + Show summary information about OpenFabric. + +.. index:: show openfabric hostname +.. clicmd:: show openfabric hostname + + Show which hostnames are associated with which OpenFabric system ids. + +.. index:: show openfabric interface +.. clicmd:: show openfabric interface + +.. index:: show openfabric interface detail +.. clicmd:: show openfabric interface detail + +.. index:: show openfabric interface +.. clicmd:: show openfabric interface + + Show state and configuration of specified OpenFabric interface, or all interfaces + if no interface is given with or without details. + +.. index:: show openfabric neighbor +.. clicmd:: show openfabric neighbor + +.. index:: show openfabric neighbor +.. clicmd:: show openfabric neighbor + +.. index:: show openfabric neighbor detail +.. clicmd:: show openfabric neighbor detail + + Show state and information of specified OpenFabric neighbor, or all neighbors if + no system id is given with or without details. + +.. index:: show openfabric database +.. clicmd:: show openfabric database + +.. index:: show openfabric database [detail] +.. clicmd:: show openfabric database [detail] + +.. index:: show openfabric database [detail] +.. clicmd:: show openfabric database [detail] + +.. index:: show openfabric database detail +.. clicmd:: show openfabric database detail + + Show the OpenFabric database globally, for a specific LSP id without or with + details. + +.. index:: show openfabric topology +.. clicmd:: show openfabric topology + + Show calculated OpenFabric paths and associated topology information. + +.. _debugging-openfabric: + +Debugging OpenFabric +==================== + +.. index:: debug openfabric adj-packets +.. clicmd:: debug openfabric adj-packets + +.. index:: no debug openfabric adj-packets +.. clicmd:: no debug openfabric adj-packets + +OpenFabric Adjacency related packets. + +.. index:: debug openfabric checksum-errors +.. clicmd:: debug openfabric checksum-errors + +.. index:: no debug openfabric checksum-errors +.. clicmd:: no debug openfabric checksum-errors + +OpenFabric LSP checksum errors. + +.. index:: debug openfabric events +.. clicmd:: debug openfabric events + +.. index:: no debug openfabric events +.. clicmd:: no debug openfabric events + +OpenFabric Events. + +.. index:: debug openfabric local-updates +.. clicmd:: debug openfabric local-updates + +.. index:: no debug openfabric local-updates +.. clicmd:: no debug openfabric local-updates + +OpenFabric local update packets. + +.. index:: debug openfabric lsp-gen +.. clicmd:: debug openfabric lsp-gen + +.. index:: no debug openfabric lsp-gen +.. clicmd:: no debug openfabric lsp-gen + +Generation of own LSPs. + +.. index:: debug openfabric lsp-sched +.. clicmd:: debug openfabric lsp-sched + +.. index:: no debug openfabric lsp-sched +.. clicmd:: no debug openfabric lsp-sched + +Debug scheduling of generation of own LSPs. + +.. index:: debug openfabric packet-dump +.. clicmd:: debug openfabric packet-dump + +.. index:: no debug openfabric packet-dump +.. clicmd:: no debug openfabric packet-dump + +OpenFabric packet dump. + +.. index:: debug openfabric protocol-errors +.. clicmd:: debug openfabric protocol-errors + +.. index:: no debug openfabric protocol-errors +.. clicmd:: no debug openfabric protocol-errors + +OpenFabric LSP protocol errors. + +.. index:: debug openfabric route-events +.. clicmd:: debug openfabric route-events + +.. index:: no debug openfabric route-events +.. clicmd:: no debug openfabric route-events + +OpenFabric Route related events. + +.. index:: debug openfabric snp-packets +.. clicmd:: debug openfabric snp-packets + +.. index:: no debug openfabric snp-packets +.. clicmd:: no debug openfabric snp-packets + +OpenFabric CSNP/PSNP packets. + +.. index:: debug openfabric spf-events +.. clicmd:: debug openfabric spf-events + +.. index:: debug openfabric spf-statistics +.. clicmd:: debug openfabric spf-statistics + +.. index:: debug openfabric spf-triggers +.. clicmd:: debug openfabric spf-triggers + +.. index:: no debug openfabric spf-events +.. clicmd:: no debug openfabric spf-events + +.. index:: no debug openfabric spf-statistics +.. clicmd:: no debug openfabric spf-statistics + +.. index:: no debug openfabric spf-triggers +.. clicmd:: no debug openfabric spf-triggers + +OpenFabric Shortest Path First Events, Timing and Statistic Data and triggering +events. + +.. index:: debug openfabric update-packets +.. clicmd:: debug openfabric update-packets + +.. index:: no debug openfabric update-packets +.. clicmd:: no debug openfabric update-packets + +Update related packets. + +.. index:: show debugging openfabric +.. clicmd:: show debugging openfabric + + Print which OpenFabric debug levels are active. + +OpenFabric configuration example +================================ + +A simple example: + +.. code-block:: frr + + ! + interface lo + ip address 192.0.2.1/32 + ip router openfabric 1 + ipv6 address 2001:db8::1/128 + ipv6 router openfabric 1 + ! + interface eth0 + ip router openfabric 1 + ipv6 router openfabric 1 + ! + interface eth1 + ip router openfabric 1 + ipv6 router openfabric 1 + ! + router openfabric 1 + net 49.0000.0000.0001.00 diff --git a/doc/user/index.rst b/doc/user/index.rst index 5818551343..8190415bf4 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -42,6 +42,7 @@ Protocols bfd bgp babeld + fabricd ldpd eigrpd isisd diff --git a/doc/user/installation.rst b/doc/user/installation.rst index 3da5a6cd30..da431916ae 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -139,6 +139,10 @@ options from the list below. Do not build isisd. +.. option:: --disable-fabricd + + Do not build fabricd. + .. option:: --enable-isis-topology Enable IS-IS topology generator. diff --git a/doc/user/setup.rst b/doc/user/setup.rst index 68ce14982b..8a76a61e78 100644 --- a/doc/user/setup.rst +++ b/doc/user/setup.rst @@ -32,6 +32,7 @@ systemd. The file initially looks like this: staticd=no pbrd=no bfdd=no + fabricd=no To enable a particular daemon, simply change the corresponding 'no' to 'yes'. Subsequent service restarts should start the daemon. @@ -68,6 +69,7 @@ This file has several parts. Here is an example: staticd_options=" --daemon -A 127.0.0.1" pbrd_options=" --daemon -A 127.0.0.1" bfdd_options=" --daemon -A 127.0.0.1" + fabricd_options=" --daemon -A 127.0.0.1" # The list of daemons to watch is automatically generated by the init script. watchfrr_enable=yes @@ -139,6 +141,7 @@ add the following entries to :file:`/etc/services`. ldpd 2612/tcp # LDPd vty eigprd 2613/tcp # EIGRPd vty bfdd 2617/tcp # bfdd vty + fabricd 2618/tcp # fabricd vty If you use a FreeBSD newer than 2.2.8, the above entries are already added to diff --git a/doc/user/subdir.am b/doc/user/subdir.am index 6e51eed9d1..53d36052ac 100644 --- a/doc/user/subdir.am +++ b/doc/user/subdir.am @@ -10,6 +10,7 @@ user_RSTFILES = \ doc/user/bugs.rst \ doc/user/conf.py \ doc/user/eigrpd.rst \ + doc/user/fabricd.rst \ doc/user/filter.rst \ doc/user/glossary.rst \ doc/user/index.rst \ From 2acc231024c17385c1e878531c4852eb8775b3ee Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Wed, 5 Sep 2018 17:04:35 -0400 Subject: [PATCH 35/84] zebra: Fix RB-Tree storage comparison function for v6 The RB-Tree used to store rmac information was not properly handling the v6 address family. Modify the code to allow this handling. Cleans up this error message: zebra[2231]: host_rb_entry_compare: Unexpected family type: 10 That is being seen, This fixes some connectivity issues being seen. Signed-off-by: Donald Sharp --- zebra/zebra_vxlan.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 28adc37b43..5dcbe97166 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -207,6 +207,9 @@ static int host_rb_entry_compare(const struct host_rb_entry *hle1, return 1; return 0; + } else if (hle1->p.family == AF_INET6) { + return memcmp(&hle1->p.u.prefix6, &hle2->p.u.prefix6, + IPV6_MAX_BYTELEN); } else { zlog_warn("%s: Unexpected family type: %d", __PRETTY_FUNCTION__, hle1->p.family); From 98ea5be8b32ecb42829fbd1834a0c664e045b4f4 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Wed, 5 Sep 2018 20:47:07 -0400 Subject: [PATCH 36/84] lib: Add missing smux.h to `make distrib` results Signed-off-by: Donald Sharp --- lib/subdir.am | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/subdir.am b/lib/subdir.am index ef6c8f8e55..d3bd441a80 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -152,6 +152,7 @@ pkginclude_HEADERS += \ lib/sha256.h \ lib/sigevent.h \ lib/skiplist.h \ + lib/smux.h \ lib/sockopt.h \ lib/sockunion.h \ lib/spf_backoff.h \ From c3568c4d1a080bc0510894374e16c157be28e8e4 Mon Sep 17 00:00:00 2001 From: Thibaut Collet Date: Thu, 6 Sep 2018 07:48:12 +0200 Subject: [PATCH 37/84] zebra/lib: code cleaning Remove useless parenthesis and explicit cast. Remove redundant code. Signed-off-by: Thibaut Collet --- lib/vrf.c | 8 ++------ zebra/interface.c | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/vrf.c b/lib/vrf.c index 696ae3f48c..3ab830c556 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -493,18 +493,14 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *), "vrf_init: failed to create the default VRF!"); exit(1); } - if (vrf_is_backend_netns()) - strlcpy(default_vrf->data.l.netns_name, - VRF_DEFAULT_NAME, NS_NAMSIZ); - if (vrf_is_backend_netns()) { struct ns *ns; strlcpy(default_vrf->data.l.netns_name, VRF_DEFAULT_NAME, NS_NAMSIZ); ns = ns_lookup(ns_get_default_id()); - ns->vrf_ctxt = (void *)default_vrf; - default_vrf->ns_ctxt = (void *)ns; + ns->vrf_ctxt = default_vrf; + default_vrf->ns_ctxt = ns; } /* Enable the default VRF. */ diff --git a/zebra/interface.c b/zebra/interface.c index bb87728b5d..48adfd5a3d 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -733,7 +733,7 @@ void if_delete_update(struct interface *ifp) * occur with this implementation whereas it is not possible with * vrf-lite). */ - if ((ifp->vrf_id) && !vrf_is_backend_netns()) + if (ifp->vrf_id && !vrf_is_backend_netns()) if_handle_vrf_change(ifp, VRF_DEFAULT); /* Reset some zebra interface params to default values. */ From 6e23e5e9e16fb16aafb4ef05ceb3514a02147a21 Mon Sep 17 00:00:00 2001 From: Biswajit Sadhu Date: Thu, 6 Sep 2018 02:51:40 -0700 Subject: [PATCH 38/84] lib: Ensure FRR detects running of the second instance of a FRR daemon, doesnot allow it to run. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Solution : The following procedures would be performed : 1. Verify if the pid file for each daemon is present or not. If the file is not present, that means the daemon is getting instantiated for the first time. So let it go ahead. If the file is present proceed to point ‘2’. 2. Try fetching the properties of the pid file. 3. If it has RW lock, that means one instance of this the daemon is already running. So stop moving ahead and do exit() else let it go ahead.
Please note all above procedure happen at the initial state of daemon’s instantiation, much before it starts any session with other process/allocates resources etc.. and this verification do not have any impact of any operations done later, if the verification succeeds. Signed-off-by: bisdhdh sadhub@vmware.com --- lib/libfrr.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/libfrr.c b/lib/libfrr.c index 821c57f37b..115c70f7c9 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -262,12 +262,39 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, static struct frr_daemon_info *di = NULL; +static void frr_guard_daemon(void) +{ + int fd; + struct flock lock; + + const char *path = di->pid_file; + fd = open(path, O_RDWR); + if (fd != -1) { + memset(&lock, 0, sizeof(lock)); + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + if (fcntl(fd, F_GETLK, &lock) < 0) { + flog_err_sys(LIB_ERR_SYSTEM_CALL, + "Could not do F_GETLK pid_file %s (%s), exiting", + path, safe_strerror(errno)); + exit(1); + } else if (lock.l_type == F_WRLCK) { + flog_err_sys(LIB_ERR_SYSTEM_CALL, + "Process %d has a write lock on file %s already! Error : ( %s)", + lock.l_pid, path, safe_strerror(errno)); + exit(1); + } + close(fd); + } +} + void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) { di = daemon; /* basename(), opencoded. */ char *p = strrchr(argv[0], '/'); + di->progname = p ? p + 1 : argv[0]; umask(0027); @@ -589,6 +616,9 @@ struct thread_master *frr_init(void) zprivs_init(di->privs); + /* Guard to prevent a second instance of this daemon */ + frr_guard_daemon(); + master = thread_master_create(NULL); signal_init(master, di->n_signals, di->signals); From 34815ea334c8a892b006c2524707ff6e98b95fc7 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 6 Sep 2018 18:26:04 -0400 Subject: [PATCH 39/84] zebra: Modify nexthop checks to report inactive a bit more Debugging inactive nexthops in zebra can be quite difficult and non-obvious what has gone wrong. Add detailed rib debugs for the cases where we decide that a nexthop is inactive so that we can more easily debug a reason for the failure. Signed-off-by: Donald Sharp --- zebra/zebra_rib.c | 55 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 49d2f26943..0739db1027 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -428,8 +428,12 @@ static int nexthop_active(afi_t afi, struct route_entry *re, /* Skip nexthops that have been filtered out due to route-map */ /* The nexthops are specific to this route and so the same */ /* nexthop for a different route may not have this flag set */ - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED)) + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: Nexthop Filtered", + __PRETTY_FUNCTION__); return 0; + } /* * Check to see if we should trust the passed in information @@ -441,10 +445,21 @@ static int nexthop_active(afi_t afi, struct route_entry *re, if (ifp && connected_is_unnumbered(ifp)) { if (if_is_operative(ifp)) return 1; - else + else { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Onlink and interface %s is not operative", + __PRETTY_FUNCTION__, ifp->name); return 0; - } else + } + } else { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Interface %s is not unnumbered", + __PRETTY_FUNCTION__, + ifp ? ifp->name : "Unknown"); return 0; + } } /* Make lookup prefix. */ @@ -466,8 +481,12 @@ static int nexthop_active(afi_t afi, struct route_entry *re, } /* Lookup table. */ table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id); - if (!table) + if (!table) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: Table not found", + __PRETTY_FUNCTION__); return 0; + } rn = route_node_match(table, (struct prefix *)&p); while (rn) { @@ -480,15 +499,25 @@ static int nexthop_active(afi_t afi, struct route_entry *re, */ if (top && rn == top) if (((afi == AFI_IP) && (rn->p.prefixlen != 32)) - || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) + || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Matched against ourself and prefix length is not max bit length", + __PRETTY_FUNCTION__); return 0; + } /* Pick up selected route. */ /* However, do not resolve over default route unless explicitly * allowed. */ if (is_default_prefix(&rn->p) - && !rnh_resolve_via_default(p.family)) + && !rnh_resolve_via_default(p.family)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t:%s: Resolved against default route", + __PRETTY_FUNCTION__); return 0; + } dest = rib_dest_from_rnode(rn); if (dest && dest->selected_fib @@ -540,6 +569,9 @@ static int nexthop_active(afi_t afi, struct route_entry *re, } if (resolved && set) re->nexthop_mtu = match->mtu; + if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: Recursion failed to find", + __PRETTY_FUNCTION__); return resolved; } else if (re->type == ZEBRA_ROUTE_STATIC) { resolved = 0; @@ -558,6 +590,11 @@ static int nexthop_active(afi_t afi, struct route_entry *re, } if (resolved && set) re->nexthop_mtu = match->mtu; + + if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Static route unable to resolve", + __PRETTY_FUNCTION__); return resolved; } else { return 0; @@ -900,8 +937,12 @@ static unsigned nexthop_active_check(struct route_node *rn, default: break; } - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: Unable to find a active nexthop", + __PRETTY_FUNCTION__); return 0; + } /* XXX: What exactly do those checks do? Do we support * e.g. IPv4 routes with IPv6 nexthops or vice versa? */ From aa1bac3039e800d7c972387c5cfb890b42592d29 Mon Sep 17 00:00:00 2001 From: "F. Aragon" Date: Fri, 7 Sep 2018 11:20:45 +0200 Subject: [PATCH 40/84] lib: array index check (Coverity 1473088) Signed-off-by: F. Aragon --- lib/ferr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ferr.c b/lib/ferr.c index 35d0fe4ff4..fb5719d4de 100644 --- a/lib/ferr.c +++ b/lib/ferr.c @@ -148,7 +148,7 @@ void log_ref_display(struct vty *vty, uint32_t code, bool json) snprintf(pbuf, sizeof(pbuf), "\nError %"PRIu32" - %s", ref->code, ref->title); memset(ubuf, '=', strlen(pbuf)); - ubuf[strlen(pbuf) - 1] = '\0'; + ubuf[strlen(pbuf)] = '\0'; vty_out(vty, "%s\n%s\n", pbuf, ubuf); vty_out(vty, "Description:\n%s\n\n", ref->description); From e60b527e66e18acd5b8dc625324628bef35fa778 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 7 Sep 2018 07:01:19 -0400 Subject: [PATCH 41/84] staticd: Fix 'show debug static" command 'show debugging' is returning a Command incomplete error message as that it is being sent to staticd and staticd has no knowledge of it, fix this. Signed-off-by: Donald Sharp --- staticd/static_vty.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/staticd/static_vty.c b/staticd/static_vty.c index 771d8d1de3..b323612d7e 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -1390,6 +1390,18 @@ DEFPY(ipv6_route_vrf, table_str); } +DEFUN_NOSH (show_debugging_staticd, + show_debugging_staticd_cmd, + "show debugging [static]", + SHOW_STR + DEBUG_STR + "Static Information\n") +{ + vty_out(vty, "Static debugging status\n"); + + return CMD_SUCCESS; +} + void static_vty_init(void) { install_element(CONFIG_NODE, &ip_mroute_dist_cmd); @@ -1408,6 +1420,8 @@ void static_vty_init(void) install_element(CONFIG_NODE, &ipv6_route_cmd); install_element(VRF_NODE, &ipv6_route_vrf_cmd); + install_element(VIEW_NODE, &show_debugging_staticd_cmd); + static_list = list_new(); static_list->cmp = (int (*)(void *, void *))static_list_compare; static_list->del = (void (*)(void *))static_list_delete; From aaf8c96fa90b264eb4f081ccbb22e4b1cffc6cab Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 7 Sep 2018 07:05:35 -0400 Subject: [PATCH 42/84] sharpd: Fix missing 'show debug' command The sharp daemon was not properly handling 'show debug' commands. Fix. Signed-off-by: Donald Sharp --- sharpd/sharp_vty.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 956da9d4ed..e9ca18c5df 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -185,6 +185,18 @@ DEFPY (remove_routes, return CMD_SUCCESS; } +DEFUN_NOSH (show_debugging_sharpd, + show_debugging_sharpd_cmd, + "show debugging [sharp]", + SHOW_STR + DEBUG_STR + "Sharp Information\n") +{ + vty_out(vty, "Sharp debugging status\n"); + + return CMD_SUCCESS; +} + void sharp_vty_init(void) { install_element(ENABLE_NODE, &install_routes_cmd); @@ -192,5 +204,8 @@ void sharp_vty_init(void) install_element(ENABLE_NODE, &vrf_label_cmd); install_element(ENABLE_NODE, &watch_nexthop_v6_cmd); install_element(ENABLE_NODE, &watch_nexthop_v4_cmd); + + install_element(VIEW_NODE, &show_debugging_sharpd_cmd); + return; } From 7b90f00cceb71d0df28073b778d969ec09065199 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 7 Sep 2018 12:55:03 -0400 Subject: [PATCH 43/84] lib: Cleanup include of link.h We need link.h for the HAVE_DLINFO_LINKMAP, so include it if we need it for that code path. Signed-off-by: Donald Sharp --- lib/memory_vty.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/memory_vty.c b/lib/memory_vty.c index 73a18529a2..5fd9c3b900 100644 --- a/lib/memory_vty.c +++ b/lib/memory_vty.c @@ -28,7 +28,9 @@ #include #endif #include +#ifdef HAVE_LINK_H #include +#endif #include "log.h" #include "memory.h" From afc9534f6727f9298da4881d2e1c8279f6ca315f Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 7 Sep 2018 13:11:02 -0400 Subject: [PATCH 44/84] lib: Detect if pthread_condattr_setclock is available Auto-detect if pthread_condattr_setclock is available and if it is not allow the code to compile around the issue. Signed-off-by: Donald Sharp --- configure.ac | 7 +++++++ lib/frr_pthread.h | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/configure.ac b/configure.ac index 09a6f364fb..f1ffd43f5b 100755 --- a/configure.ac +++ b/configure.ac @@ -313,6 +313,13 @@ AX_PTHREAD([ AC_MSG_FAILURE([This FRR version needs pthreads]) ]) +AC_SEARCH_LIBS([pthread_condattr_setclock], [], + [frr_cv_pthread_condattr_setclock=yes], + [frr_cv_pthread_condattr_setclock=no]) +if test "$frr_cv_pthread_condattr_setclock" = yes; then + AC_DEFINE(HAVE_PTHREAD_CONDATTR_SETCLOCK, 1, [Have pthread.h pthread_condattr_setclock]) +fi + dnl -------------- dnl Check programs dnl -------------- diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h index cc4fc74337..732e2925fe 100644 --- a/lib/frr_pthread.h +++ b/lib/frr_pthread.h @@ -234,4 +234,8 @@ void frr_pthread_yield(void); */ uint32_t frr_pthread_get_id(void); +#ifndef HAVE_PTHREAD_CONDATTR_SETCLOCK +#define pthread_condattr_setclock(A, B) +#endif + #endif /* _FRR_PTHREAD_H */ From 8860ffdc093f19a6b0fe1abd43ff782d938a2d2d Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 7 Sep 2018 13:50:57 -0400 Subject: [PATCH 45/84] vtysh: Add code to isolate append_history The append_history function in lib readline appears to not be universally available across all of the esoteric platforms we may want to compile on. As such provide a way to gracefully do nothing. Signed-off-by: Donald Sharp --- configure.ac | 4 ++++ vtysh/vtysh_main.c | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/configure.ac b/configure.ac index f1ffd43f5b..055cdda957 100755 --- a/configure.ac +++ b/configure.ac @@ -1056,6 +1056,10 @@ dnl [TODO] on Linux, and in [TODO] on Solaris. if test $ac_cv_lib_readline_rl_completion_matches = no; then AC_DEFINE(rl_completion_matches,completion_matches,Old readline) fi + AC_SEARCH_LIBS([append_history], [readline], [frr_cv_append_history=yes], [frr_cv_append_history=no]) + if test "$frr_cv_append_history" = yes; then + AC_DEFINE(HAVE_APPEND_HISTORY, 1, [Have history.h append_history]) + fi ;; esac AC_SUBST(LIBREADLINE) diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 86fa62f474..7e979f2c8f 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -30,6 +30,16 @@ #include #include +/* + * The append_history function only appears in newer versions + * of the readline library it appears like. Since we don't + * need this just silently ignore the code on these + * ancient platforms. + */ +#if !defined HAVE_APPEND_HISTORY +#define append_history(A, B) +#endif + #include #include "getopt.h" #include "command.h" From 7c70dc57e03e573827040fd1c1408a5923c526f1 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 7 Sep 2018 14:30:24 -0400 Subject: [PATCH 46/84] vtysh: Actually make the new_completion function match The new_completion function was not declared the same way the rl_attempted_completion_function pointer was. The only difference was a 'const char *' -vs- 'char *' So convert it over. Signed-off-by: Donald Sharp --- vtysh/vtysh.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index c249115fd3..bcae4407cb 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1112,7 +1112,7 @@ static char *command_generator(const char *text, int state) return NULL; } -static char **new_completion(char *text, int start, int end) +static char **new_completion(const char *text, int start, int end) { char **matches; @@ -3386,8 +3386,7 @@ void vtysh_readline_init(void) rl_initialize(); rl_bind_key('?', (rl_command_func_t *)vtysh_rl_describe); rl_completion_entry_function = vtysh_completion_entry_function; - rl_attempted_completion_function = - (rl_completion_func_t *)new_completion; + rl_attempted_completion_function = new_completion; } char *vtysh_prompt(void) From 3be6e41147632ee1bc2105f4c6e4149df6c26519 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 8 Sep 2018 18:25:45 +0200 Subject: [PATCH 47/84] isisd: silence SA warnings As suggested by Renato Westphal. Signed-off-by: David Lamparter --- isisd/isis_spf.c | 5 +---- isisd/isis_spf_private.h | 7 +++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 8f8cfb3078..6a7528623c 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -650,16 +650,13 @@ lspfragloop: } } - if (!pseudo_lsp && spftree->family == AF_INET + if (!fabricd && !pseudo_lsp && spftree->family == AF_INET && spftree->mtid == ISIS_MT_IPV4_UNICAST) { struct isis_item_list *reachs[] = { &lsp->tlvs->oldstyle_ip_reach, &lsp->tlvs->oldstyle_ip_reach_ext}; for (unsigned int i = 0; i < array_size(reachs); i++) { - if (fabricd) - continue; - vtype = i ? VTYPE_IPREACH_EXTERNAL : VTYPE_IPREACH_INTERNAL; diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h index 664ddc3f29..af552e0ed1 100644 --- a/isisd/isis_spf_private.h +++ b/isisd/isis_spf_private.h @@ -238,9 +238,12 @@ static void isis_vertex_queue_append(struct isis_vertex_queue *queue, __attribute__((__unused__)) static struct isis_vertex *isis_vertex_queue_last(struct isis_vertex_queue *queue) { - assert(!queue->insert_counter); + struct listnode *tail; - return listgetdata(listtail(queue->l.list)); + assert(!queue->insert_counter); + tail = listtail(queue->l.list); + assert(tail); + return listgetdata(tail); } __attribute__((__unused__)) From a43ad4fef85196592e59665fa9b69eddb30592e0 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 8 Sep 2018 19:25:00 +0200 Subject: [PATCH 48/84] lib, ldpd: fix SA warnings from TAILQ oddness Add a TAILQ_POP_FIRST so Clang understands it's the same item that is getting removed from the list. Signed-off-by: David Lamparter --- ldpd/lde.c | 4 +--- lib/imsg-buffer.c | 19 ++++++++++--------- lib/imsg.c | 3 +-- lib/queue.h | 13 +++++++++++++ 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/ldpd/lde.c b/ldpd/lde.c index 03b62b482b..8104398886 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -1620,10 +1620,8 @@ lde_address_list_free(struct lde_nbr *ln) { struct lde_addr *lde_addr; - while ((lde_addr = TAILQ_FIRST(&ln->addr_list)) != NULL) { - TAILQ_REMOVE(&ln->addr_list, lde_addr, entry); + while ((lde_addr = TAILQ_POP_FIRST(&ln->addr_list, entry)) != NULL) free(lde_addr); - } } static void zclient_sync_init(unsigned short instance) diff --git a/lib/imsg-buffer.c b/lib/imsg-buffer.c index b83f1f76f2..c2f4052b8f 100644 --- a/lib/imsg-buffer.c +++ b/lib/imsg-buffer.c @@ -21,9 +21,9 @@ #include "queue.h" #include "imsg.h" -int ibuf_realloc(struct ibuf *, size_t); -void ibuf_enqueue(struct msgbuf *, struct ibuf *); -void ibuf_dequeue(struct msgbuf *, struct ibuf *); +static int ibuf_realloc(struct ibuf *, size_t); +static void ibuf_enqueue(struct msgbuf *, struct ibuf *); +static void ibuf_dequeue(struct msgbuf *, struct ibuf *); struct ibuf *ibuf_open(size_t len) { @@ -57,7 +57,7 @@ struct ibuf *ibuf_dynamic(size_t len, size_t max) return (buf); } -int ibuf_realloc(struct ibuf *buf, size_t len) +static int ibuf_realloc(struct ibuf *buf, size_t len) { uint8_t *b; @@ -183,6 +183,8 @@ void msgbuf_drain(struct msgbuf *msgbuf, size_t n) next = TAILQ_NEXT(buf, entry); if (buf->rpos + n >= buf->wpos) { n -= buf->wpos - buf->rpos; + + TAILQ_REMOVE(&msgbuf->bufs, buf, entry); ibuf_dequeue(msgbuf, buf); } else { buf->rpos += n; @@ -195,7 +197,7 @@ void msgbuf_clear(struct msgbuf *msgbuf) { struct ibuf *buf; - while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) + while ((buf = TAILQ_POP_FIRST(&msgbuf->bufs, entry)) != NULL) ibuf_dequeue(msgbuf, buf); } @@ -266,16 +268,15 @@ again: return (1); } -void ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) +static void ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) { TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); msgbuf->queued++; } -void ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) +static void ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) { - TAILQ_REMOVE(&msgbuf->bufs, buf, entry); - + /* TAILQ_REMOVE done by caller */ if (buf->fd != -1) close(buf->fd); diff --git a/lib/imsg.c b/lib/imsg.c index 5424140720..935d137727 100644 --- a/lib/imsg.c +++ b/lib/imsg.c @@ -299,11 +299,10 @@ int imsg_get_fd(struct imsgbuf *ibuf) int fd; struct imsg_fd *ifd; - if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL) + if ((ifd = TAILQ_POP_FIRST(&ibuf->fds, entry)) == NULL) return (-1); fd = ifd->fd; - TAILQ_REMOVE(&ibuf->fds, ifd, entry); free(ifd); return (fd); diff --git a/lib/queue.h b/lib/queue.h index 04fbeee700..11e28b4c91 100644 --- a/lib/queue.h +++ b/lib/queue.h @@ -72,4 +72,17 @@ #include "freebsd-queue.h" #endif /* defined(__OpenBSD__) && !defined(STAILQ_HEAD) */ +#ifndef TAILQ_POP_FIRST +#define TAILQ_POP_FIRST(head, field) \ + ({ typeof((head)->tqh_first) _elm = TAILQ_FIRST(head); \ + if (_elm) { \ + if ((TAILQ_NEXT((_elm), field)) != NULL) \ + TAILQ_NEXT((_elm), field)->field.tqe_prev = \ + &TAILQ_FIRST(head); \ + else \ + (head)->tqh_last = &TAILQ_FIRST(head); \ + TAILQ_FIRST(head) = TAILQ_NEXT((_elm), field); \ + }; _elm; }) +#endif + #endif /* _FRR_QUEUE_H */ From f70247febe1e61bb77f9ba318a7dea55491d0b84 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 8 Sep 2018 19:47:05 +0200 Subject: [PATCH 49/84] lib: fix SA warning in skiplist code Clang was thinking the random level could be negative. (And, no, I couldn't figure that out by reading its output... trial and error this was.) Signed-off-by: David Lamparter --- lib/skiplist.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/skiplist.c b/lib/skiplist.c index a36bf47139..585cf859e5 100644 --- a/lib/skiplist.c +++ b/lib/skiplist.c @@ -202,6 +202,7 @@ int skiplist_insert(register struct skiplist *l, register void *key, } k = randomLevel(); + assert(k >= 0); if (k > l->level) { k = ++l->level; update[k] = l->header; From 4f4060f6abbfa004e0eb63b7c447776cc74c8d66 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 8 Sep 2018 20:16:59 +0200 Subject: [PATCH 50/84] *: fix clang-6 SA warnings I don't see these in CI, but my local clang-6 does emit warnings for these. Signed-off-by: David Lamparter --- lib/command.c | 1 + lib/csv.c | 2 ++ ospf6d/ospf6_asbr.c | 3 ++- ospf6d/ospf6_route.c | 2 +- ospfd/ospf_snmp.c | 1 - staticd/static_vty.c | 4 ++-- zebra/zebra_fpm_protobuf.c | 1 + 7 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/command.c b/lib/command.c index 1df6442107..3cc6806a88 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1197,6 +1197,7 @@ static int handle_pipe_action(struct vty *vty, const char *cmd_in, /* retrieve action */ token = strsep(&working, " "); + assert(token); /* match result to known actions */ if (strmatch(token, "include")) { diff --git a/lib/csv.c b/lib/csv.c index ce84783aa6..2752974df5 100644 --- a/lib/csv.c +++ b/lib/csv.c @@ -563,6 +563,8 @@ void csv_decode(csv_t *csv, char *inbuf) csv_record_t *rec; buf = (inbuf) ? inbuf : csv->buf; + assert(buf); + pos = strpbrk(buf, "\n"); while (pos != NULL) { rec = calloc(1, sizeof(csv_record_t)); diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 5af88defeb..dc7a3f6d45 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -732,7 +732,8 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, ? 1 : 2, buf, listcount(route->paths), - listcount(route->nh_list)); + route->nh_list ? + listcount(route->nh_list) : 0); } if (listcount(route->paths)) { diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index a099eead49..021e825ae3 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -732,7 +732,7 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route, route->next = next; if (node->info == next) { - assert(next->rnode == node); + assert(next && next->rnode == node); node->info = route; UNSET_FLAG(next->flag, OSPF6_ROUTE_BEST); SET_FLAG(route->flag, OSPF6_ROUTE_BEST); diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c index 19d2e6a952..755634a2f1 100644 --- a/ospfd/ospf_snmp.c +++ b/ospfd/ospf_snmp.c @@ -995,7 +995,6 @@ static struct ospf_lsa *ospfLsdbLookup(struct variable *v, oid *name, if (len <= 0) type_next = 1; else { - len = 1; type_next = 0; *type = *offset; } diff --git a/staticd/static_vty.c b/staticd/static_vty.c index b323612d7e..f697969a72 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -88,8 +88,8 @@ static struct list *static_list; static int static_list_compare_helper(const char *s1, const char *s2) { - /* Are Both NULL */ - if (s1 == s2) + /* extra (!s1 && !s2) to keep SA happy */ + if (s1 == s2 || (!s1 && !s2)) return 0; if (!s1 && s2) diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c index ebd632270c..be0f6a23be 100644 --- a/zebra/zebra_fpm_protobuf.c +++ b/zebra/zebra_fpm_protobuf.c @@ -129,6 +129,7 @@ static inline int add_nexthop(qpb_allocator_t *allocator, Fpm__AddRoute *msg, } // TODO: Use src. + (void)src; return 1; } From e10cfdaf51fd5662309eda11ff37dfac1d94abdf Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 8 Sep 2018 20:18:30 +0200 Subject: [PATCH 51/84] bfdd: fix garbage "port" string bfd_recv_ipv4() is getting an uninitialized buffer passed in as port, and then checks it without clearing it first. Thus we can end up leaving garbage data in it. Signed-off-by: David Lamparter --- bfdd/bfd_packet.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index 4bdfb314e2..8acb9438c5 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -248,6 +248,8 @@ ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, struct iovec iov[1]; uint8_t cmsgbuf[255]; + port[0] = '\0'; + /* Prepare the recvmsg params. */ iov[0].iov_base = msgbuf; iov[0].iov_len = msgbuflen; From 84d837b75d3dc3e4640dd311fcb22bfa62065f34 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 15 Aug 2018 23:35:51 +0200 Subject: [PATCH 52/84] build: non-recursive bgpd & rfp Note: no more --with-rfp-path on configure - badly messing with the build system like this really isn't how to do a conditional external dependency. Signed-off-by: David Lamparter --- Makefile.am | 10 +- bgpd/.gitignore | 2 +- bgpd/Makefile | 10 ++ bgpd/Makefile.am | 140 ------------------- bgpd/rfp-example/librfp/Makefile | 10 ++ bgpd/rfp-example/librfp/Makefile.am | 40 ------ bgpd/rfp-example/librfp/subdir.am | 16 +++ bgpd/rfp-example/rfptest/Makefile | 10 ++ bgpd/rfp-example/rfptest/Makefile.am | 52 ------- bgpd/rfp-example/rfptest/subdir.am | 20 +++ bgpd/subdir.am | 197 +++++++++++++++++++++++++++ configure.ac | 31 ----- vtysh/Makefile.am | 23 +--- 13 files changed, 276 insertions(+), 285 deletions(-) create mode 100644 bgpd/Makefile delete mode 100644 bgpd/Makefile.am create mode 100644 bgpd/rfp-example/librfp/Makefile delete mode 100644 bgpd/rfp-example/librfp/Makefile.am create mode 100644 bgpd/rfp-example/librfp/subdir.am create mode 100644 bgpd/rfp-example/rfptest/Makefile delete mode 100644 bgpd/rfp-example/rfptest/Makefile.am create mode 100644 bgpd/rfp-example/rfptest/subdir.am create mode 100644 bgpd/subdir.am diff --git a/Makefile.am b/Makefile.am index b9003b8358..080206855e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -106,6 +106,9 @@ include tools/subdir.am include debianpkg/subdir.am include solaris/subdir.am +include bgpd/subdir.am +include bgpd/rfp-example/librfp/subdir.am +include bgpd/rfp-example/rfptest/subdir.am include ripd/subdir.am include ripngd/subdir.am include ospfd/subdir.am @@ -122,15 +125,12 @@ include pbrd/subdir.am include staticd/subdir.am include bfdd/subdir.am -SUBDIRS = . @LIBRFP@ @RFPTEST@ \ - @BGPD@ \ +SUBDIRS = . \ @VTYSH@ \ tests -DIST_SUBDIRS = . bgpd \ +DIST_SUBDIRS = . \ vtysh tests \ - bgpd/rfp-example/librfp \ - bgpd/rfp-example/rfptest \ # end if PKGSRC diff --git a/bgpd/.gitignore b/bgpd/.gitignore index 105be22995..7e2e6293a5 100644 --- a/bgpd/.gitignore +++ b/bgpd/.gitignore @@ -1,4 +1,4 @@ -Makefile +!Makefile Makefile.in *.o bgpd diff --git a/bgpd/Makefile b/bgpd/Makefile new file mode 100644 index 0000000000..b8664a8e23 --- /dev/null +++ b/bgpd/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. bgpd/bgpd +%: ALWAYS + @$(MAKE) -s -C .. bgpd/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am deleted file mode 100644 index b6b125f752..0000000000 --- a/bgpd/Makefile.am +++ /dev/null @@ -1,140 +0,0 @@ -## Process this file with automake to produce Makefile.in. -AUTOMAKE_OPTIONS = subdir-objects - -include ../common.am - -if ENABLE_BGP_VNC -#o file to keep linker happy -BGP_VNC_RFP_LIB=rfapi/rfapi_descriptor_rfp_utils.o @top_builddir@/$(LIBRFP)/librfp.a -BGP_VNC_RFP_INC=-I@top_srcdir@/$(RFPINC) -BGP_VNC_RFP_HD=\ - @top_srcdir@/$(RFPINC)/rfp.h -BGP_VNC_RFP_LD_FLAGS_FILE=@top_srcdir@/$(LIBRFP)/rfp_ld_flags -BGP_VNC_RFP_LD_FLAGS=`if [ -e "$(BGP_VNC_RFP_LD_FLAGS_FILE)" ] ; then cat "$(BGP_VNC_RFP_LD_FLAGS_FILE)" ; fi ` - -#BGP_VNC_RFAPI_SRCDIR=rfapi -BGP_VNC_RFAPI_SRCDIR= -BGP_VNC_RFAPI_INC=-Irfapi -BGP_VNC_RFAPI_SRC=rfapi/bgp_rfapi_cfg.c \ - rfapi/rfapi_import.c \ - rfapi/rfapi.c \ - rfapi/rfapi_ap.c \ - rfapi/rfapi_descriptor_rfp_utils.c \ - rfapi/rfapi_encap_tlv.c \ - rfapi/rfapi_nve_addr.c \ - rfapi/rfapi_monitor.c \ - rfapi/rfapi_rib.c \ - rfapi/rfapi_vty.c \ - rfapi/vnc_debug.c \ - rfapi/vnc_export_bgp.c \ - rfapi/vnc_export_table.c \ - rfapi/vnc_import_bgp.c \ - rfapi/vnc_zebra.c -BGP_VNC_RFAPI_HD=rfapi/bgp_rfapi_cfg.h \ - rfapi/rfapi_import.h \ - rfapi/rfapi.h \ - rfapi/rfapi_ap.h \ - rfapi/rfapi_backend.h \ - rfapi/rfapi_descriptor_rfp_utils.h \ - rfapi/rfapi_encap_tlv.h \ - rfapi/rfapi_nve_addr.h \ - rfapi/rfapi_monitor.h \ - rfapi/rfapi_private.h \ - rfapi/rfapi_rib.h \ - rfapi/rfapi_vty.h \ - rfapi/vnc_debug.h \ - rfapi/vnc_export_bgp.h \ - rfapi/vnc_export_table.h \ - rfapi/vnc_import_bgp.h \ - rfapi/vnc_zebra.h \ - rfapi/vnc_export_bgp_p.h \ - rfapi/vnc_import_bgp_p.h \ - bgp_vnc_types.h $(BGP_VNC_RFP_HD) - -else -BGP_VNC_RFAPI_INC= -BGP_VNC_RFAPI_SRC= -BGP_VNC_RFAPI_HD= -BGP_VNC_RFP_LIB= -BGP_VNC_RFP_INC= -BGP_VNC_RFP_HD= -BGP_VNC_RFP_LD_FLAGS= -endif - -AM_CPPFLAGS += -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ - $(BGP_VNC_RFAPI_INC) $(BGP_VNC_RFP_INC) -DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -INSTALL_SDATA=@INSTALL@ -m 600 - -noinst_LIBRARIES = libbgp.a -module_LTLIBRARIES = -sbin_PROGRAMS = bgpd -bin_PROGRAMS = bgp_btoa - -BUILT_SOURCES = - -libbgp_a_SOURCES = \ - bgp_memory.c \ - bgpd.c bgp_fsm.c bgp_aspath.c bgp_community.c bgp_attr.c \ - bgp_debug.c bgp_route.c bgp_zebra.c bgp_open.c bgp_routemap.c \ - bgp_packet.c bgp_network.c bgp_filter.c bgp_regex.c bgp_clist.c \ - bgp_dump.c bgp_ecommunity.c bgp_lcommunity.c \ - bgp_mplsvpn.c bgp_nexthop.c \ - bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c bgp_mpath.c \ - bgp_nht.c bgp_updgrp.c bgp_updgrp_packet.c bgp_updgrp_adv.c bgp_bfd.c \ - bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) bgp_attr_evpn.c \ - bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c bgp_label.c bgp_rd.c \ - bgp_keepalives.c bgp_io.c bgp_flowspec.c bgp_flowspec_util.c \ - bgp_flowspec_vty.c bgp_labelpool.c bgp_pbr.c bgp_errors.c - -noinst_HEADERS = \ - bgp_memory.h \ - bgp_aspath.h bgp_attr.h bgp_community.h bgp_debug.h bgp_fsm.h \ - bgp_network.h bgp_open.h bgp_packet.h bgp_regex.h bgp_route.h \ - bgpd.h bgp_filter.h bgp_clist.h bgp_dump.h bgp_zebra.h \ - bgp_ecommunity.h bgp_lcommunity.h \ - bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \ - bgp_advertise.h bgp_vty.h bgp_mpath.h bgp_nht.h \ - bgp_updgrp.h bgp_bfd.h bgp_encap_tlv.h bgp_encap_types.h \ - $(BGP_VNC_RFAPI_HD) bgp_attr_evpn.h bgp_evpn.h bgp_evpn_vty.h \ - bgp_vpn.h bgp_label.h bgp_rd.h bgp_evpn_private.h bgp_keepalives.h \ - bgp_io.h bgp_flowspec.h bgp_flowspec_private.h bgp_flowspec_util.h \ - bgp_labelpool.h bgp_pbr.h bgp_errors.h - -bgpd_SOURCES = bgp_main.c -bgpd_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libfrr.la @LIBCAP@ @LIBM@ -bgpd_LDFLAGS = $(BGP_VNC_RFP_LD_FLAGS) - -bgp_btoa_SOURCES = bgp_btoa.c -bgp_btoa_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libfrr.la @LIBCAP@ @LIBM@ -bgp_btoa_LDFLAGS = $(BGP_VNC_RFP_LD_FLAGS) - -if SNMP -module_LTLIBRARIES += bgpd_snmp.la -endif - -bgpd_snmp_la_SOURCES = bgp_snmp.c -bgpd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99 -bgpd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -bgpd_snmp_la_LIBADD = ../lib/libfrrsnmp.la - -if RPKI -module_LTLIBRARIES += bgpd_rpki.la -BUILT_SOURCES += bgp_rpki_clippy.c -endif - -bgpd_rpki_la_SOURCES = bgp_rpki.c -bgpd_rpki_la_CFLAGS = $(WERROR) $(RTRLIB_CFLAGS) -bgpd_rpki_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -bgpd_rpki_la_LIBADD = $(RTRLIB_LIBS) - -examplesdir = $(exampledir) -dist_examples_DATA = bgpd.conf.sample bgpd.conf.sample2 \ - bgpd.conf.vnc.sample - -bgp_vty.o: bgp_vty_clippy.c -bgp_route.o: bgp_route_clippy.c -bgp_debug.o: bgp_debug_clippy.c - -EXTRA_DIST = BGP4-MIB.txt - diff --git a/bgpd/rfp-example/librfp/Makefile b/bgpd/rfp-example/librfp/Makefile new file mode 100644 index 0000000000..8deb93d745 --- /dev/null +++ b/bgpd/rfp-example/librfp/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C ../../.. bgpd/rfp-example/librfp/librfp.a +%: ALWAYS + @$(MAKE) -s -C ../../.. bgpd/rfp-example/librfp/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/bgpd/rfp-example/librfp/Makefile.am b/bgpd/rfp-example/librfp/Makefile.am deleted file mode 100644 index fc66a40f00..0000000000 --- a/bgpd/rfp-example/librfp/Makefile.am +++ /dev/null @@ -1,40 +0,0 @@ -# -# This file has been modified by LabN Consulting, L.L.C. -# -# -## Process this file with automake to produce Makefile.in. - -if ENABLE_BGP_VNC -BGP_VNC_RFAPI_INC=-I$(top_srcdir)/bgpd/rfapi -BGP_VNC_RFP_LIBDIR=. -BGP_VNC_RFP_INCDIR=$(BGP_VNC_RFP_LIBDIR) -BGP_VNC_RFP_LIB=librfp.a -BGP_VNC_RFP_INC=-I$(BGP_VNC_RFP_INCDIR) - -librfp_a_SOURCES = \ - rfp_example.c - -librfp_a_INCLUDES = \ - rfp.h \ - rfp_internal.h - -else -BGP_VNC_RFAPI_INC= -BGP_VNC_RFAPI_SRC= -BGP_VNC_RFP_LIB= -BGP_VNC_RFP_INC= -endif - -AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib \ - -I$(top_builddir) -I$(top_builddir)/lib \ - $(BGP_VNC_RFAPI_INC) $(BGP_VNC_RFP_INC) -DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -INSTALL_SDATA=@INSTALL@ -m 600 - -AM_CFLAGS = $(PICFLAGS) -AM_LDFLAGS = $(PILDFLAGS) - -noinst_LIBRARIES = $(BGP_VNC_RFP_LIB) - -noinst_HEADERS = \ - $(librfp_a_INCLUDES) diff --git a/bgpd/rfp-example/librfp/subdir.am b/bgpd/rfp-example/librfp/subdir.am new file mode 100644 index 0000000000..2800f5aa36 --- /dev/null +++ b/bgpd/rfp-example/librfp/subdir.am @@ -0,0 +1,16 @@ +# +# librfp +# + +if ENABLE_BGP_VNC +noinst_LIBRARIES += bgpd/rfp-example/librfp/librfp.a +endif + +bgpd_rfp_example_librfp_librfp_a_SOURCES = \ + bgpd/rfp-example/librfp/rfp_example.c \ + # end + +noinst_HEADERS += \ + bgpd/rfp-example/librfp/rfp.h \ + bgpd/rfp-example/librfp/rfp_internal.h \ + # end diff --git a/bgpd/rfp-example/rfptest/Makefile b/bgpd/rfp-example/rfptest/Makefile new file mode 100644 index 0000000000..659a9ceb17 --- /dev/null +++ b/bgpd/rfp-example/rfptest/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C ../../.. bgpd/rfp-example/rfptest/rfptest +%: ALWAYS + @$(MAKE) -s -C ../../.. bgpd/rfp-example/rfptest/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/bgpd/rfp-example/rfptest/Makefile.am b/bgpd/rfp-example/rfptest/Makefile.am deleted file mode 100644 index f5db852dbb..0000000000 --- a/bgpd/rfp-example/rfptest/Makefile.am +++ /dev/null @@ -1,52 +0,0 @@ -# -# This file has been modified by LabN Consulting, L.L.C. -# -# -## Process this file with automake to produce Makefile.in. - -if ENABLE_BGP_VNC -BGP_VNC_RFAPI_INC=-I$(top_srcdir)/bgpd/rfapi -BGP_VNC_RFP_LIBDIR=../librfp -BGP_VNC_RFP_INCDIR=$(BGP_VNC_RFP_LIBDIR) -BGP_VNC_RFP_LIB=$(BGP_VNC_RFP_LIBDIR)/librfp.a -BGP_VNC_RFP_INC=-I$(BGP_VNC_RFP_INCDIR) - -rfptest_SOURCES = \ - rfptest.c - -rfptest_INCLUDES = \ - rfptest.h - - -RFPTEST_BIN = rfptest - -else -BGP_VNC_RFAPI_INC= -BGP_VNC_RFAPI_SRC= -BGP_VNC_RFP_LIB= -BGP_VNC_RFP_INC= -RFPTEST_BIN= -endif - -AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib \ - -I$(top_builddir) -I$(top_builddir)/lib \ - $(BGP_VNC_RFAPI_INC) $(BGP_VNC_RFP_INC) - -DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -INSTALL_SDATA=@INSTALL@ -m 600 - - -AM_CFLAGS = $(PICFLAGS) -AM_LDFLAGS = $(PILDFLAGS) - - -noinst_HEADERS = \ - $(rfptest_INCLUDES) - -noinst_LIBRARIES = -sbin_PROGRAMS = $(RFPTEST_BIN) - -examplesdir = $(exampledir) - -rfptest_LDADD = $(top_builddir)/lib/libfrr.la $(BGP_VNC_RFP_LIB) -dist_examples_DATA = diff --git a/bgpd/rfp-example/rfptest/subdir.am b/bgpd/rfp-example/rfptest/subdir.am new file mode 100644 index 0000000000..e1975e6b05 --- /dev/null +++ b/bgpd/rfp-example/rfptest/subdir.am @@ -0,0 +1,20 @@ +# +# libtest +# + +if ENABLE_BGP_VNC +sbin_PROGRAMS += bgpd/rfp-example/rfptest/rfptest +endif + +bgpd_rfp_example_rfptest_rfptest_CFLAGS = -I$(top_srcdir)/bgpd/rfapi +bgpd_rfp_example_rfptest_rfptest_SOURCES = \ + bgpd/rfp-example/rfptest/rfptest.c \ + # end +noinst_HEADERS += \ + bgpd/rfp-example/rfptest/rfptest.h \ + # end + +bgpd_rfp_example_rfptest_rfptest_LDADD = \ + lib/libfrr.la \ + bgpd/rfp-example/librfp/librfp.a \ + # end diff --git a/bgpd/subdir.am b/bgpd/subdir.am new file mode 100644 index 0000000000..e24516acd8 --- /dev/null +++ b/bgpd/subdir.am @@ -0,0 +1,197 @@ +# +# bgpd +# + +if BGPD +noinst_LIBRARIES += bgpd/libbgp.a +sbin_PROGRAMS += bgpd/bgpd +noinst_PROGRAMS += bgpd/bgp_btoa +dist_examples_DATA += \ + bgpd/bgpd.conf.sample \ + bgpd/bgpd.conf.sample2 \ + bgpd/bgpd.conf.vnc.sample \ + # end +if SNMP +module_LTLIBRARIES += bgpd/bgpd_snmp.la +endif +if RPKI +module_LTLIBRARIES += bgpd/bgpd_rpki.la +endif +endif + +bgpd_libbgp_a_SOURCES = \ + bgpd/bgp_advertise.c \ + bgpd/bgp_aspath.c \ + bgpd/bgp_attr.c \ + bgpd/bgp_attr_evpn.c \ + bgpd/bgp_bfd.c \ + bgpd/bgp_clist.c \ + bgpd/bgp_community.c \ + bgpd/bgp_damp.c \ + bgpd/bgp_debug.c \ + bgpd/bgp_dump.c \ + bgpd/bgp_ecommunity.c \ + bgpd/bgp_encap_tlv.c \ + bgpd/bgp_errors.c \ + bgpd/bgp_evpn.c \ + bgpd/bgp_evpn_vty.c \ + bgpd/bgp_filter.c \ + bgpd/bgp_flowspec.c \ + bgpd/bgp_flowspec_util.c \ + bgpd/bgp_flowspec_vty.c \ + bgpd/bgp_fsm.c \ + bgpd/bgp_io.c \ + bgpd/bgp_keepalives.c \ + bgpd/bgp_label.c \ + bgpd/bgp_labelpool.c \ + bgpd/bgp_lcommunity.c \ + bgpd/bgp_memory.c \ + bgpd/bgp_mpath.c \ + bgpd/bgp_mplsvpn.c \ + bgpd/bgp_network.c \ + bgpd/bgp_nexthop.c \ + bgpd/bgp_nht.c \ + bgpd/bgp_open.c \ + bgpd/bgp_packet.c \ + bgpd/bgp_pbr.c \ + bgpd/bgp_rd.c \ + bgpd/bgp_regex.c \ + bgpd/bgp_route.c \ + bgpd/bgp_routemap.c \ + bgpd/bgp_table.c \ + bgpd/bgp_updgrp.c \ + bgpd/bgp_updgrp_adv.c \ + bgpd/bgp_updgrp_packet.c \ + bgpd/bgp_vpn.c \ + bgpd/bgp_vty.c \ + bgpd/bgp_zebra.c \ + bgpd/bgpd.c \ + # end + +if ENABLE_BGP_VNC +bgpd_libbgp_a_SOURCES += \ + bgpd/rfapi/bgp_rfapi_cfg.c \ + bgpd/rfapi/rfapi_import.c \ + bgpd/rfapi/rfapi.c \ + bgpd/rfapi/rfapi_ap.c \ + bgpd/rfapi/rfapi_descriptor_rfp_utils.c \ + bgpd/rfapi/rfapi_encap_tlv.c \ + bgpd/rfapi/rfapi_nve_addr.c \ + bgpd/rfapi/rfapi_monitor.c \ + bgpd/rfapi/rfapi_rib.c \ + bgpd/rfapi/rfapi_vty.c \ + bgpd/rfapi/vnc_debug.c \ + bgpd/rfapi/vnc_export_bgp.c \ + bgpd/rfapi/vnc_export_table.c \ + bgpd/rfapi/vnc_import_bgp.c \ + bgpd/rfapi/vnc_zebra.c \ + # end +endif + +noinst_HEADERS += \ + bgpd/bgp_advertise.h \ + bgpd/bgp_aspath.h \ + bgpd/bgp_attr.h \ + bgpd/bgp_attr_evpn.h \ + bgpd/bgp_bfd.h \ + bgpd/bgp_clist.h \ + bgpd/bgp_community.h \ + bgpd/bgp_damp.h \ + bgpd/bgp_debug.h \ + bgpd/bgp_dump.h \ + bgpd/bgp_ecommunity.h \ + bgpd/bgp_encap_tlv.h \ + bgpd/bgp_encap_types.h \ + bgpd/bgp_errors.h \ + bgpd/bgp_evpn.h \ + bgpd/bgp_evpn_private.h \ + bgpd/bgp_evpn_vty.h \ + bgpd/bgp_filter.h \ + bgpd/bgp_flowspec.h \ + bgpd/bgp_flowspec_private.h \ + bgpd/bgp_flowspec_util.h \ + bgpd/bgp_fsm.h \ + bgpd/bgp_io.h \ + bgpd/bgp_keepalives.h \ + bgpd/bgp_label.h \ + bgpd/bgp_labelpool.h \ + bgpd/bgp_lcommunity.h \ + bgpd/bgp_memory.h \ + bgpd/bgp_mpath.h \ + bgpd/bgp_mplsvpn.h \ + bgpd/bgp_network.h \ + bgpd/bgp_nexthop.h \ + bgpd/bgp_nht.h \ + bgpd/bgp_open.h \ + bgpd/bgp_packet.h \ + bgpd/bgp_pbr.h \ + bgpd/bgp_rd.h \ + bgpd/bgp_regex.h \ + bgpd/bgp_route.h \ + bgpd/bgp_table.h \ + bgpd/bgp_updgrp.h \ + bgpd/bgp_vpn.h \ + bgpd/bgp_vty.h \ + bgpd/bgp_zebra.h \ + bgpd/bgpd.h \ + \ + bgpd/rfapi/bgp_rfapi_cfg.h \ + bgpd/rfapi/rfapi_import.h \ + bgpd/rfapi/rfapi.h \ + bgpd/rfapi/rfapi_ap.h \ + bgpd/rfapi/rfapi_backend.h \ + bgpd/rfapi/rfapi_descriptor_rfp_utils.h \ + bgpd/rfapi/rfapi_encap_tlv.h \ + bgpd/rfapi/rfapi_nve_addr.h \ + bgpd/rfapi/rfapi_monitor.h \ + bgpd/rfapi/rfapi_private.h \ + bgpd/rfapi/rfapi_rib.h \ + bgpd/rfapi/rfapi_vty.h \ + bgpd/rfapi/vnc_debug.h \ + bgpd/rfapi/vnc_export_bgp.h \ + bgpd/rfapi/vnc_export_table.h \ + bgpd/rfapi/vnc_import_bgp.h \ + bgpd/rfapi/vnc_zebra.h \ + bgpd/rfapi/vnc_export_bgp_p.h \ + bgpd/rfapi/vnc_import_bgp_p.h \ + bgpd/bgp_vnc_types.h \ + # end + +bgpd_bgpd_SOURCES = bgpd/bgp_main.c +bgpd_bgp_btoa_SOURCES = bgpd/bgp_btoa.c + +if ENABLE_BGP_VNC +bgpd_bgpd_SOURCES += bgpd/rfapi/rfapi_descriptor_rfp_utils.c +bgpd_bgpd_CFLAGS = -Irfapi -I@top_srcdir@/$(RFPINC) + +bgpd_bgp_btoa_SOURCES += bgpd/rfapi/rfapi_descriptor_rfp_utils.c +bgpd_bgp_btoa_CFLAGS = -Irfapi -I@top_srcdir@/$(RFPINC) + +RFPLDADD = bgpd/rfp-example/librfp/librfp.a +else +RFPLDADD = +endif +bgpd_bgpd_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la @LIBCAP@ @LIBM@ +bgpd_bgp_btoa_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la @LIBCAP@ @LIBM@ + +bgpd_bgpd_snmp_la_SOURCES = bgpd/bgp_snmp.c +bgpd_bgpd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99 +bgpd_bgpd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +bgpd_bgpd_snmp_la_LIBADD = lib/libfrrsnmp.la + +bgpd_bgpd_rpki_la_SOURCES = bgpd/bgp_rpki.c +bgpd_bgpd_rpki_la_CFLAGS = $(WERROR) $(RTRLIB_CFLAGS) +bgpd_bgpd_rpki_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +bgpd_bgpd_rpki_la_LIBADD = $(RTRLIB_LIBS) + +bgpd/bgp_vty_clippy.c: $(CLIPPY_DEPS) +bgpd/bgp_vty.$(OBJEXT): bgpd/bgp_vty_clippy.c +bgpd/bgp_route_clippy.c: $(CLIPPY_DEPS) +bgpd/bgp_route.$(OBJEXT): bgpd/bgp_route_clippy.c +bgpd/bgp_debug_clippy.c: $(CLIPPY_DEPS) +bgpd/bgp_debug.$(OBJEXT): bgpd/bgp_debug_clippy.c +bgpd/bgp_rpki_clippy.c: $(CLIPPY_DEPS) +$(AUTOMAKE_DUMMY)bgpd/bgpd_bgpd_rpki_la-bgp_rpki.lo: bgpd/bgp_rpki_clippy.c +$(AUTOMAKE_DUMMY)bgpd/bgpd_rpki_la-bgp_rpki.lo: bgpd/bgp_rpki_clippy.c + +EXTRA_DIST += bgpd/BGP4-MIB.txt diff --git a/configure.ac b/configure.ac index 4ab6b89d3e..837a4f2252 100755 --- a/configure.ac +++ b/configure.ac @@ -394,8 +394,6 @@ AC_ARG_ENABLE(bgp-announce, AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement])) AC_ARG_ENABLE(bgp-vnc, AS_HELP_STRING([--disable-bgp-vnc],[turn off BGP VNC support])) -AC_ARG_WITH(rfp-path, - AS_HELP_STRING([--with-rfp-path[=DIR]],[path to replaced stub RFP used with BGP VNC])) AC_ARG_ENABLE(snmp, AS_HELP_STRING([--enable-snmp], [enable SNMP support for agentx])) AC_ARG_ENABLE(zeromq, @@ -1451,31 +1449,11 @@ else AC_DEFINE(DISABLE_BGP_ANNOUNCE,0,Disable BGP installation to zebra) fi -if test "${with_rfp_path}" = "yes" || test x"${with_rfp_path}" = x""; then - with_rfp_path="bgpd/rfp-example" -fi -if test "${with_rfp_path}" != "no"; then - VNC_RFP_PATH="${with_rfp_path}" - AC_SUBST(VNC_RFP_PATH) -fi - if test "${enable_bgp_vnc}" != "no";then AC_DEFINE(ENABLE_BGP_VNC,1,Enable BGP VNC support) - RFPTEST="${with_rfp_path}/rfptest" - LIBRFP="${with_rfp_path}/librfp" - RFPINC="${with_rfp_path}/librfp" -else - RFPTEST= - LIBRFP= - RFPINC="bgpd/rfp-example/librfp" fi -# set AM_CONDITIONAL([ENABLE_BGP_VNC], [test x${enable_bgp_vnc} != xno]) -AC_SUBST(RFPTEST) -AC_SUBST(LIBRFP) -AC_SUBST(RFPINC) -AC_SUBST(BGPD) AC_SUBST(SOLARIS) AC_SUBST(VTYSH) AC_SUBST(CURSES) @@ -2031,11 +2009,8 @@ AC_MSG_RESULT($ac_cv_htonl_works) AC_CONFIG_FILES([Makefile],[sed -e 's/^#AUTODERP# //' -i Makefile]) AC_CONFIG_FILES([ - bgpd/Makefile vtysh/Makefile tests/Makefile - bgpd/rfp-example/rfptest/Makefile - bgpd/rfp-example/librfp/Makefile redhat/frr.spec solaris/Makefile debianpkg/changelog @@ -2047,12 +2022,6 @@ AC_CONFIG_FILES([ pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh pkgsrc/eigrpd.sh]) -if test "${enable_bgp_vnc}" != "no"; then - if test "${with_rfp_path}" != "bgpd/rfp-example" ; then - AC_CONFIG_FILES([${with_rfp_path}/rfptest/Makefile ${with_rfp_path}/librfp/Makefile]) - fi -fi - AC_CONFIG_FILES([vtysh/extract.pl],[chmod +x vtysh/extract.pl]) AC_CONFIG_COMMANDS([lib/route_types.h], [ diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 350b6fedea..b8e4e3987c 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -2,21 +2,6 @@ include ../common.am -if ENABLE_BGP_VNC -BGP_VNC_RFP_SRCDIR = @top_srcdir@/@LIBRFP@ -BGP_VNC_RFP_INCDIR = -I$(BGP_VNC_RFP_SRCDIR) -BGP_VNC_RFP_SRC = $(BGP_VNC_RFP_SRCDIR)/*.c -BGP_VNC_RFAPI_SRCDIR = @top_srcdir@/bgpd/rfapi -BGP_VNC_RFAPI_INCDIR = -I$(BGP_VNC_RFAPI_SRCDIR) -I$(top_srcdir)/bgpd -BGP_VNC_RFAPI_SRC = $(BGP_VNC_RFAPI_SRCDIR)/*.c -else -BGP_VNC_RFP_INCDIR = -BGP_VNC_RFP_SRCDIR = -BGP_VNC_RFP_SRC = -BGP_VNC_RFAPI_INCDIR = -BGP_VNC_RFAPI_SRCDIR = -BGP_VNC_RFAPI_SRC = -endif AM_CPPFLAGS += -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ $(BGP_VNC_RFAPI_INCDIR) $(BGP_VNC_RFP_INCDIR) DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" @@ -54,6 +39,12 @@ vtysh_scan += $(top_srcdir)/bgpd/bgp_route.c vtysh_scan += $(top_srcdir)/bgpd/bgp_routemap.c vtysh_scan += $(top_srcdir)/bgpd/bgp_vty.c vtysh_scan += $(top_srcdir)/bgpd/bgp_flowspec_vty.c +if ENABLE_BGP_VNC +vtysh_scan += $(top_srcdir)/bgpd/rfapi/bgp_rfapi_cfg.c +vtysh_scan += $(top_srcdir)/bgpd/rfapi/rfapi.c +vtysh_scan += $(top_srcdir)/bgpd/rfapi/rfapi_vty.c +vtysh_scan += $(top_srcdir)/bgpd/rfapi/vnc_debug.c +endif endif if RPKI @@ -174,7 +165,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/zebra/zebra_mpls_vty.c \ $(top_srcdir)/zebra/zebra_pw.c \ $(top_srcdir)/watchfrr/watchfrr_vty.c \ - $(BGP_VNC_RFAPI_SRC) $(BGP_VNC_RFP_SRC) + # end vtysh_cmd.c: $(vtysh_cmd_FILES) extract.pl ./extract.pl $(vtysh_cmd_FILES) > vtysh_cmd.c From 8b7668eccecac31a0347c75be11580f7cf66bf06 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 15 Aug 2018 23:55:22 +0200 Subject: [PATCH 53/84] build: non-recursive vtysh Signed-off-by: David Lamparter --- Makefile.am | 8 ++--- configure.ac | 2 -- vtysh/.gitignore | 2 +- vtysh/Makefile | 10 +++++++ vtysh/extract.pl.in | 4 +-- vtysh/{Makefile.am => subdir.am} | 51 +++++++++++++++++++------------- 6 files changed, 47 insertions(+), 30 deletions(-) create mode 100644 vtysh/Makefile rename vtysh/{Makefile.am => subdir.am} (84%) diff --git a/Makefile.am b/Makefile.am index 080206855e..96d4a34409 100644 --- a/Makefile.am +++ b/Makefile.am @@ -125,12 +125,13 @@ include pbrd/subdir.am include staticd/subdir.am include bfdd/subdir.am +include vtysh/subdir.am + SUBDIRS = . \ - @VTYSH@ \ tests DIST_SUBDIRS = . \ - vtysh tests \ + tests \ # end if PKGSRC @@ -169,9 +170,6 @@ EXTRA_DIST += \ snapcraft/helpers \ snapcraft/snap \ \ - vtysh/Makefile.am \ - vtysh/Makefile.in \ - \ doc/Makefile \ doc/developer/Makefile \ doc/manpages/Makefile \ diff --git a/configure.ac b/configure.ac index 837a4f2252..45a571b4c1 100755 --- a/configure.ac +++ b/configure.ac @@ -1455,7 +1455,6 @@ fi AM_CONDITIONAL([ENABLE_BGP_VNC], [test x${enable_bgp_vnc} != xno]) AC_SUBST(SOLARIS) -AC_SUBST(VTYSH) AC_SUBST(CURSES) AC_CHECK_LIB(crypt, crypt, [], [AC_CHECK_LIB(crypto, DES_crypt)]) @@ -2009,7 +2008,6 @@ AC_MSG_RESULT($ac_cv_htonl_works) AC_CONFIG_FILES([Makefile],[sed -e 's/^#AUTODERP# //' -i Makefile]) AC_CONFIG_FILES([ - vtysh/Makefile tests/Makefile redhat/frr.spec solaris/Makefile diff --git a/vtysh/.gitignore b/vtysh/.gitignore index 5856eac253..e63d1e2dee 100644 --- a/vtysh/.gitignore +++ b/vtysh/.gitignore @@ -1,4 +1,4 @@ -Makefile +!Makefile Makefile.in *.o vtysh diff --git a/vtysh/Makefile b/vtysh/Makefile new file mode 100644 index 0000000000..07e093b0f5 --- /dev/null +++ b/vtysh/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. vtysh/vtysh +%: ALWAYS + @$(MAKE) -s -C .. vtysh/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 690e9a12c6..0f68e58d62 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -29,7 +29,7 @@ print <; close (FH); diff --git a/vtysh/Makefile.am b/vtysh/subdir.am similarity index 84% rename from vtysh/Makefile.am rename to vtysh/subdir.am index b8e4e3987c..ca288ea9e4 100644 --- a/vtysh/Makefile.am +++ b/vtysh/subdir.am @@ -1,25 +1,31 @@ -## Process this file with Automake to create Makefile.in +# +# vtysh +# -include ../common.am +if VTYSH +bin_PROGRAMS += vtysh/vtysh +dist_examples_DATA += vtysh/vtysh.conf.sample +endif -AM_CPPFLAGS += -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ - $(BGP_VNC_RFAPI_INCDIR) $(BGP_VNC_RFP_INCDIR) -DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" +vtysh_vtysh_SOURCES = \ + vtysh/vtysh_main.c \ + vtysh/vtysh.c \ + vtysh/vtysh_user.c \ + vtysh/vtysh_config.c \ + # end +nodist_vtysh_vtysh_SOURCES = \ + vtysh/vtysh_cmd.c \ + # end +CLEANFILES += vtysh/vtysh_cmd.c -LIBS = @LIBS@ @CURSES@ @LIBPAM@ +noinst_HEADERS += \ + vtysh/vtysh.h \ + vtysh/vtysh_user.h \ + # end -bin_PROGRAMS = vtysh +vtysh_vtysh_LDADD = lib/libfrr.la @LIBCAP@ @LIBREADLINE@ @LIBS@ @CURSES@ @LIBPAM@ -vtysh_SOURCES = vtysh_main.c vtysh.c vtysh_user.c vtysh_config.c -nodist_vtysh_SOURCES = vtysh_cmd.c -CLEANFILES = vtysh_cmd.c -noinst_HEADERS = vtysh.h vtysh_user.h -vtysh_LDADD = ../lib/libfrr.la @LIBCAP@ @LIBREADLINE@ - -examplesdir = $(exampledir) -dist_examples_DATA = vtysh.conf.sample - -EXTRA_DIST = extract.pl +EXTRA_DIST += vtysh/extract.pl vtysh_scan = @@ -147,7 +153,7 @@ if BFDD vtysh_scan += $(top_srcdir)/bfdd/bfdd_vty.c endif -vtysh_cmd_FILES = $(vtysh_scan) \ +vtysh_vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/lib/keychain.c $(top_srcdir)/lib/routemap.c \ $(top_srcdir)/lib/filter.c $(top_srcdir)/lib/plist.c \ $(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \ @@ -167,5 +173,10 @@ vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/watchfrr/watchfrr_vty.c \ # end -vtysh_cmd.c: $(vtysh_cmd_FILES) extract.pl - ./extract.pl $(vtysh_cmd_FILES) > vtysh_cmd.c +AM_V_EXTRACT = $(am__v_EXTRACT_$(V)) +am__v_EXTRACT_ = $(am__v_EXTRACT_$(AM_DEFAULT_VERBOSITY)) +am__v_EXTRACT_0 = @echo " EXTRACT " $@; +am__v_EXTRACT_1 = + +vtysh/vtysh_cmd.c: $(vtysh_vtysh_cmd_FILES) vtysh/extract.pl + $(AM_V_EXTRACT) vtysh/extract.pl $(vtysh_vtysh_cmd_FILES) > vtysh/vtysh_cmd.c From a1286a3245d638fe9c56339642a7e1fbf183c946 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 16 Aug 2018 00:26:39 +0200 Subject: [PATCH 54/84] build: non-recursive tests May SUBDIRS rest in pieces... er, peace. Signed-off-by: David Lamparter --- Makefile.am | 8 +- configure.ac | 1 - tests/.gitignore | 2 +- tests/Makefile | 10 ++ tests/Makefile.am | 235 ------------------------------- tests/lib/cli/test_cli.c | 2 +- tests/ospf6d/test_lsdb.c | 2 +- tests/subdir.am | 295 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 309 insertions(+), 246 deletions(-) create mode 100644 tests/Makefile delete mode 100644 tests/Makefile.am create mode 100644 tests/subdir.am diff --git a/Makefile.am b/Makefile.am index 96d4a34409..6decbe64fc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -126,13 +126,7 @@ include staticd/subdir.am include bfdd/subdir.am include vtysh/subdir.am - -SUBDIRS = . \ - tests - -DIST_SUBDIRS = . \ - tests \ - # end +include tests/subdir.am if PKGSRC rcdir=@pkgsrcrcdir@ diff --git a/configure.ac b/configure.ac index 45a571b4c1..32aa178379 100755 --- a/configure.ac +++ b/configure.ac @@ -2008,7 +2008,6 @@ AC_MSG_RESULT($ac_cv_htonl_works) AC_CONFIG_FILES([Makefile],[sed -e 's/^#AUTODERP# //' -i Makefile]) AC_CONFIG_FILES([ - tests/Makefile redhat/frr.spec solaris/Makefile debianpkg/changelog diff --git a/tests/.gitignore b/tests/.gitignore index c8368b39b6..5d414ef3d7 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,4 +1,4 @@ -Makefile +!Makefile Makefile.in *.o tags diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000000..dd4594febe --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. check +%: ALWAYS + @$(MAKE) -s -C .. tests/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/tests/Makefile.am b/tests/Makefile.am deleted file mode 100644 index a7dec67348..0000000000 --- a/tests/Makefile.am +++ /dev/null @@ -1,235 +0,0 @@ -include ../common.am - -PYTHON ?= python - -AUTOMAKE_OPTIONS = subdir-objects -AM_CPPFLAGS += \ - -I.. \ - -I$(top_srcdir) \ - -I$(top_srcdir)/lib \ - -I$(top_builddir)/lib \ - -I$(top_srcdir)/tests/helpers/c \ - -I$(top_builddir)/tests/helpers/c \ - -O -DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" - -if BGPD -TESTS_BGPD = \ - bgpd/test_aspath \ - bgpd/test_capability \ - bgpd/test_packet \ - bgpd/test_peer_attr \ - bgpd/test_ecommunity \ - bgpd/test_mp_attr \ - bgpd/test_mpath \ - bgpd/test_bgp_table -else -TESTS_BGPD = -endif - -if ISISD -if SOLARIS -TESTS_ISISD = -else -TESTS_ISISD = \ - isisd/test_fuzz_isis_tlv \ - isisd/test_isis_vertex_queue \ - # end -endif -else -TESTS_ISISD = -endif - -if OSPF6D -TESTS_OSPF6D = \ - ospf6d/test_lsdb \ - # end -else -TESTS_OSPF6D = -endif - -if ENABLE_BGP_VNC -BGP_VNC_RFP_LIB=@top_builddir@/$(LIBRFP)/librfp.a -else -BGP_VNC_RFP_LIB = -endif - -lib/cli/test_cli.o: lib/cli/test_cli_clippy.c -ospf6d/test_lsdb.o: ospf6d/test_lsdb_clippy.c - -check_PROGRAMS = \ - lib/test_buffer \ - lib/test_checksum \ - lib/test_heavy_thread \ - lib/test_heavy_wq \ - lib/test_heavy \ - lib/test_memory \ - lib/test_nexthop_iter \ - lib/test_privs \ - lib/test_ringbuf \ - lib/test_srcdest_table \ - lib/test_segv \ - lib/test_sig \ - lib/test_stream \ - lib/test_table \ - lib/test_timer_correctness \ - lib/test_timer_performance \ - lib/test_ttable \ - lib/test_zlog \ - lib/test_graph \ - lib/cli/test_cli \ - lib/cli/test_commands \ - $(TESTS_BGPD) \ - $(TESTS_ISISD) \ - $(TESTS_OSPF6D) \ - # end - -if ZEROMQ -check_PROGRAMS += \ - lib/test_zmq \ - # end -endif - -../vtysh/vtysh_cmd.c: - $(MAKE) -C ../vtysh vtysh_cmd.c - -lib/cli/test_commands_defun.c: ../vtysh/vtysh_cmd.c - sed \ - -e 's/"vtysh\.h"/"tests.h"/' \ - -e 's/vtysh_init_cmd/test_init_cmd/' \ - -e 's/VTYSH_[A-Z][A-Z_0-9]*/0/g' \ - < ../vtysh/vtysh_cmd.c \ - > "$@" - -isisd/test_fuzz_isis_tlv_tests.h: $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz - gzip -d < $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz > "$@" - -noinst_HEADERS = \ - ./helpers/c/prng.h \ - ./helpers/c/tests.h \ - ./lib/cli/common_cli.h - -lib_test_buffer_SOURCES = lib/test_buffer.c -lib_test_checksum_SOURCES = lib/test_checksum.c -lib_test_heavy_thread_SOURCES = lib/test_heavy_thread.c helpers/c/main.c -lib_test_heavy_wq_SOURCES = lib/test_heavy_wq.c helpers/c/main.c -lib_test_heavy_SOURCES = lib/test_heavy.c helpers/c/main.c -lib_test_memory_SOURCES = lib/test_memory.c -lib_test_nexthop_iter_SOURCES = lib/test_nexthop_iter.c helpers/c/prng.c -lib_test_privs_SOURCES = lib/test_privs.c -lib_test_ringbuf_SOURCES = lib/test_ringbuf.c -lib_test_srcdest_table_SOURCES = lib/test_srcdest_table.c \ - helpers/c/prng.c -lib_test_segv_SOURCES = lib/test_segv.c -lib_test_sig_SOURCES = lib/test_sig.c -lib_test_stream_SOURCES = lib/test_stream.c -lib_test_table_SOURCES = lib/test_table.c -lib_test_timer_correctness_SOURCES = lib/test_timer_correctness.c \ - helpers/c/prng.c -lib_test_timer_performance_SOURCES = lib/test_timer_performance.c \ - helpers/c/prng.c -lib_test_ttable_SOURCES = lib/test_ttable.c -lib_test_zlog_SOURCES = lib/test_zlog.c -lib_test_graph_SOURCES = lib/test_graph.c -lib_test_zmq_SOURCES = lib/test_zmq.c -lib_test_zmq_CFLAGS = $(AM_CFLAGS) $(ZEROMQ_CFLAGS) -lib_cli_test_cli_SOURCES = lib/cli/test_cli.c lib/cli/common_cli.c -lib_cli_test_commands_SOURCES = lib/cli/test_commands_defun.c \ - lib/cli/test_commands.c \ - helpers/c/prng.c -bgpd_test_aspath_SOURCES = bgpd/test_aspath.c -bgpd_test_capability_SOURCES = bgpd/test_capability.c -bgpd_test_packet_SOURCES = bgpd/test_packet.c -bgpd_test_peer_attr_SOURCES = bgpd/test_peer_attr.c -bgpd_test_ecommunity_SOURCES = bgpd/test_ecommunity.c -bgpd_test_mp_attr_SOURCES = bgpd/test_mp_attr.c -bgpd_test_mpath_SOURCES = bgpd/test_mpath.c -bgpd_test_bgp_table_SOURCES = bgpd/test_bgp_table.c -isisd_test_fuzz_isis_tlv_SOURCES = isisd/test_fuzz_isis_tlv.c -nodist_isisd_test_fuzz_isis_tlv_SOURCES = isisd/test_fuzz_isis_tlv_tests.h -BUILT_SOURCES=isisd/test_fuzz_isis_tlv_tests.h -CLEANFILES=isisd/test_fuzz_isis_tlv_tests.h -isisd_test_fuzz_isis_tlv_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/tests/isisd -isisd_test_isis_vertex_queue_SOURCES = isisd/test_isis_vertex_queue.c - -ospf6d_test_lsdb_SOURCES = ospf6d/test_lsdb.c lib/cli/common_cli.c - -ALL_TESTS_LDADD = ../lib/libfrr.la @LIBCAP@ -BGP_TEST_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) $(ALL_TESTS_LDADD) -lm -ISISD_TEST_LDADD = ../isisd/libisis.a $(ALL_TESTS_LDADD) -OSPF6_TEST_LDADD = ../ospf6d/libospf6.a $(ALL_TESTS_LDADD) - -lib_test_buffer_LDADD = $(ALL_TESTS_LDADD) -lib_test_checksum_LDADD = $(ALL_TESTS_LDADD) -lib_test_heavy_thread_LDADD = $(ALL_TESTS_LDADD) -lm -lib_test_heavy_wq_LDADD = $(ALL_TESTS_LDADD) -lm -lib_test_heavy_LDADD = $(ALL_TESTS_LDADD) -lm -lib_test_memory_LDADD = $(ALL_TESTS_LDADD) -lib_test_nexthop_iter_LDADD = $(ALL_TESTS_LDADD) -lib_test_privs_LDADD = $(ALL_TESTS_LDADD) -lib_test_ringbuf_LDADD = $(ALL_TESTS_LDADD) -lib_test_srcdest_table_LDADD = $(ALL_TESTS_LDADD) -lib_test_segv_LDADD = $(ALL_TESTS_LDADD) -lib_test_sig_LDADD = $(ALL_TESTS_LDADD) -lib_test_stream_LDADD = $(ALL_TESTS_LDADD) -lib_test_table_LDADD = $(ALL_TESTS_LDADD) -lm -lib_test_timer_correctness_LDADD = $(ALL_TESTS_LDADD) -lib_test_timer_performance_LDADD = $(ALL_TESTS_LDADD) -lib_test_ttable_LDADD = $(ALL_TESTS_LDADD) -lib_test_zlog_LDADD = $(ALL_TESTS_LDADD) -lib_test_graph_LDADD = $(ALL_TESTS_LDADD) -lib_test_zmq_LDADD = ../lib/libfrrzmq.la $(ALL_TESTS_LDADD) $(ZEROMQ_LIBS) -lib_cli_test_cli_LDADD = $(ALL_TESTS_LDADD) -lib_cli_test_commands_LDADD = $(ALL_TESTS_LDADD) -bgpd_test_aspath_LDADD = $(BGP_TEST_LDADD) -bgpd_test_capability_LDADD = $(BGP_TEST_LDADD) -bgpd_test_packet_LDADD = $(BGP_TEST_LDADD) -bgpd_test_peer_attr_LDADD = $(BGP_TEST_LDADD) -bgpd_test_ecommunity_LDADD = $(BGP_TEST_LDADD) -bgpd_test_mp_attr_LDADD = $(BGP_TEST_LDADD) -bgpd_test_mpath_LDADD = $(BGP_TEST_LDADD) -bgpd_test_bgp_table_LDADD = $(BGP_TEST_LDADD) -isisd_test_fuzz_isis_tlv_LDADD = $(ISISD_TEST_LDADD) -isisd_test_isis_vertex_queue_LDADD = $(ISISD_TEST_LDADD) -ospf6d_test_lsdb_LDADD = $(OSPF6_TEST_LDADD) - -EXTRA_DIST = \ - runtests.py \ - bgpd/test_aspath.py \ - bgpd/test_capability.py \ - bgpd/test_ecommunity.py \ - bgpd/test_mp_attr.py \ - bgpd/test_mpath.py \ - bgpd/test_peer_attr.py \ - helpers/python/frrsix.py \ - helpers/python/frrtest.py \ - isisd/test_fuzz_isis_tlv.py \ - isisd/test_fuzz_isis_tlv_tests.h.gz \ - isisd/test_isis_vertex_queue.py \ - lib/cli/test_commands.in \ - lib/cli/test_commands.py \ - lib/cli/test_commands.refout \ - lib/cli/test_cli.in \ - lib/cli/test_cli.py \ - lib/cli/test_cli.refout \ - lib/test_nexthop_iter.py \ - lib/test_ringbuf.py \ - lib/test_srcdest_table.py \ - lib/test_stream.py \ - lib/test_stream.refout \ - lib/test_table.py \ - lib/test_timer_correctness.py \ - lib/test_ttable.py \ - lib/test_ttable.refout \ - lib/test_zlog.py \ - lib/test_graph.py \ - lib/test_graph.refout \ - ospf6d/test_lsdb.py \ - ospf6d/test_lsdb.in \ - ospf6d/test_lsdb.refout \ - # end - -.PHONY: tests.xml -tests.xml: $(check_PROGRAMS) - $(PYTHON) $(srcdir)/runtests.py --junitxml=$@ -v $(srcdir) -check: tests.xml diff --git a/tests/lib/cli/test_cli.c b/tests/lib/cli/test_cli.c index 4cc15ba23f..8f062d8b5e 100644 --- a/tests/lib/cli/test_cli.c +++ b/tests/lib/cli/test_cli.c @@ -41,7 +41,7 @@ DUMMY_DEFUN(cmd13, "alt a X:X::X:X"); DUMMY_DEFUN(cmd14, "pat g { foo A.B.C.D$foo|foo|bar X:X::X:X$bar| baz } [final]"); -#include "lib/cli/test_cli_clippy.c" +#include "tests/lib/cli/test_cli_clippy.c" DEFPY(magic_test, magic_test_cmd, "magic (0-100) {ipv4net A.B.C.D/M|X:X::X:X$ipv6}", diff --git a/tests/ospf6d/test_lsdb.c b/tests/ospf6d/test_lsdb.c index ec0835c719..24821febe6 100644 --- a/tests/ospf6d/test_lsdb.c +++ b/tests/ospf6d/test_lsdb.c @@ -29,7 +29,7 @@ #include "ospf6d/ospf6_lsdb.h" #include "tests/lib/cli/common_cli.h" -#include "ospf6d/test_lsdb_clippy.c" +#include "tests/ospf6d/test_lsdb_clippy.c" static struct ospf6_lsdb *lsdb; diff --git a/tests/subdir.am b/tests/subdir.am new file mode 100644 index 0000000000..cffc316927 --- /dev/null +++ b/tests/subdir.am @@ -0,0 +1,295 @@ +# +# tests +# + +PYTHON ?= python + +if BGPD +TESTS_BGPD = \ + tests/bgpd/test_aspath \ + tests/bgpd/test_capability \ + tests/bgpd/test_packet \ + tests/bgpd/test_peer_attr \ + tests/bgpd/test_ecommunity \ + tests/bgpd/test_mp_attr \ + tests/bgpd/test_mpath \ + tests/bgpd/test_bgp_table +else +TESTS_BGPD = +endif + +if ISISD +if SOLARIS +TESTS_ISISD = +else +TESTS_ISISD = \ + tests/isisd/test_fuzz_isis_tlv \ + tests/isisd/test_isis_vertex_queue \ + # end +endif +else +TESTS_ISISD = +endif + +if OSPF6D +TESTS_OSPF6D = \ + tests/ospf6d/test_lsdb \ + # end +else +TESTS_OSPF6D = +endif + +tests/lib/cli/tests_lib_cli_test_cli-test_cli.$(OBJEXT): tests/lib/cli/test_cli_clippy.c +tests/lib/cli/test_cli-test_cli.$(OBJEXT): tests/lib/cli/test_cli_clippy.c +tests/ospf6d/tests_ospf6d_test_lsdb-test_lsdb.$(OBJEXT): tests/ospf6d/test_lsdb_clippy.c +tests/ospf6d/test_lsdb-test_lsdb.$(OBJEXT): tests/ospf6d/test_lsdb_clippy.c + +check_PROGRAMS = \ + tests/lib/test_buffer \ + tests/lib/test_checksum \ + tests/lib/test_heavy_thread \ + tests/lib/test_heavy_wq \ + tests/lib/test_heavy \ + tests/lib/test_memory \ + tests/lib/test_nexthop_iter \ + tests/lib/test_privs \ + tests/lib/test_ringbuf \ + tests/lib/test_srcdest_table \ + tests/lib/test_segv \ + tests/lib/test_sig \ + tests/lib/test_stream \ + tests/lib/test_table \ + tests/lib/test_timer_correctness \ + tests/lib/test_timer_performance \ + tests/lib/test_ttable \ + tests/lib/test_zlog \ + tests/lib/test_graph \ + tests/lib/cli/test_cli \ + tests/lib/cli/test_commands \ + $(TESTS_BGPD) \ + $(TESTS_ISISD) \ + $(TESTS_OSPF6D) \ + # end + +if ZEROMQ +check_PROGRAMS += \ + tests/lib/test_zmq \ + # end +endif + +tests/lib/cli/test_commands_defun.c: vtysh/vtysh_cmd.c + sed \ + -e 's%"vtysh/vtysh\.h"%"tests/helpers/c/tests.h"%' \ + -e 's/vtysh_init_cmd/test_init_cmd/' \ + -e 's/VTYSH_[A-Z][A-Z_0-9]*/0/g' \ + < vtysh/vtysh_cmd.c \ + > "$@" + +tests/isisd/test_fuzz_isis_tlv_tests.h: $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz + gzip -d < $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz > "$@" +CLEANFILES += tests/isisd/test_fuzz_isis_tlv_tests.h + +tests/isisd/tests_isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.$(OBJEXT): \ + tests/isisd/test_fuzz_isis_tlv_tests.h +tests/isisd/test_fuzz_isis_tlv-test_fuzz_isis_tlv.$(OBJEXT): \ + tests/isisd/test_fuzz_isis_tlv_tests.h + +noinst_HEADERS += \ + tests/helpers/c/prng.h \ + tests/helpers/c/tests.h \ + tests/lib/cli/common_cli.h \ + # end + +# +# *sigh* - there is no way to get CPPFLAGS or CFLAGS for a group of files :( +# + +TESTS_CPPFLAGS = $(AM_CPPFLAGS) \ + -I$(top_srcdir)/tests/helpers/c \ + -I$(top_builddir)/tests/helpers/c \ + # end +TESTS_CFLAGS = @ASAN_FLAGS@ @TSAN_FLAGS@ @MSAN_FLAGS@ +# note no -Werror + +ALL_TESTS_LDADD = lib/libfrr.la @LIBCAP@ +BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) -lm +ISISD_TEST_LDADD = isisd/libisis.a $(ALL_TESTS_LDADD) +OSPF6_TEST_LDADD = ospf6d/libospf6.a $(ALL_TESTS_LDADD) + +tests_bgpd_test_aspath_CFLAGS = $(TESTS_CFLAGS) +tests_bgpd_test_aspath_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_bgpd_test_aspath_LDADD = $(BGP_TEST_LDADD) +tests_bgpd_test_aspath_SOURCES = tests/bgpd/test_aspath.c +tests_bgpd_test_bgp_table_CFLAGS = $(TESTS_CFLAGS) +tests_bgpd_test_bgp_table_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_bgpd_test_bgp_table_LDADD = $(BGP_TEST_LDADD) +tests_bgpd_test_bgp_table_SOURCES = tests/bgpd/test_bgp_table.c +tests_bgpd_test_capability_CFLAGS = $(TESTS_CFLAGS) +tests_bgpd_test_capability_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_bgpd_test_capability_LDADD = $(BGP_TEST_LDADD) +tests_bgpd_test_capability_SOURCES = tests/bgpd/test_capability.c +tests_bgpd_test_ecommunity_CFLAGS = $(TESTS_CFLAGS) +tests_bgpd_test_ecommunity_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_bgpd_test_ecommunity_LDADD = $(BGP_TEST_LDADD) +tests_bgpd_test_ecommunity_SOURCES = tests/bgpd/test_ecommunity.c +tests_bgpd_test_mp_attr_CFLAGS = $(TESTS_CFLAGS) +tests_bgpd_test_mp_attr_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_bgpd_test_mp_attr_LDADD = $(BGP_TEST_LDADD) +tests_bgpd_test_mp_attr_SOURCES = tests/bgpd/test_mp_attr.c +tests_bgpd_test_mpath_CFLAGS = $(TESTS_CFLAGS) +tests_bgpd_test_mpath_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_bgpd_test_mpath_LDADD = $(BGP_TEST_LDADD) +tests_bgpd_test_mpath_SOURCES = tests/bgpd/test_mpath.c +tests_bgpd_test_packet_CFLAGS = $(TESTS_CFLAGS) +tests_bgpd_test_packet_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_bgpd_test_packet_LDADD = $(BGP_TEST_LDADD) +tests_bgpd_test_packet_SOURCES = tests/bgpd/test_packet.c +tests_bgpd_test_peer_attr_CFLAGS = $(TESTS_CFLAGS) +tests_bgpd_test_peer_attr_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_bgpd_test_peer_attr_LDADD = $(BGP_TEST_LDADD) +tests_bgpd_test_peer_attr_SOURCES = tests/bgpd/test_peer_attr.c + +tests_isisd_test_fuzz_isis_tlv_CFLAGS = $(TESTS_CFLAGS) -I$(top_builddir)/tests/isisd +tests_isisd_test_fuzz_isis_tlv_CPPFLAGS = $(TESTS_CPPFLAGS) -I$(top_builddir)/tests/isisd +tests_isisd_test_fuzz_isis_tlv_LDADD = $(ISISD_TEST_LDADD) +tests_isisd_test_fuzz_isis_tlv_SOURCES = tests/isisd/test_fuzz_isis_tlv.c +nodist_tests_isisd_test_fuzz_isis_tlv_SOURCES = tests/isisd/test_fuzz_isis_tlv_tests.h +tests_isisd_test_isis_vertex_queue_CFLAGS = $(TESTS_CFLAGS) +tests_isisd_test_isis_vertex_queue_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_isisd_test_isis_vertex_queue_LDADD = $(ISISD_TEST_LDADD) +tests_isisd_test_isis_vertex_queue_SOURCES = tests/isisd/test_isis_vertex_queue.c + +tests_lib_cli_test_cli_CFLAGS = $(TESTS_CFLAGS) +tests_lib_cli_test_cli_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_cli_test_cli_LDADD = $(ALL_TESTS_LDADD) +tests_lib_cli_test_cli_SOURCES = tests/lib/cli/test_cli.c tests/lib/cli/common_cli.c +tests_lib_cli_test_commands_CFLAGS = $(TESTS_CFLAGS) +tests_lib_cli_test_commands_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_cli_test_commands_LDADD = $(ALL_TESTS_LDADD) +tests_lib_cli_test_commands_SOURCES = tests/lib/cli/test_commands_defun.c tests/lib/cli/test_commands.c tests/helpers/c/prng.c +tests_lib_test_buffer_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_buffer_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_buffer_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_buffer_SOURCES = tests/lib/test_buffer.c +tests_lib_test_checksum_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_checksum_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_checksum_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_checksum_SOURCES = tests/lib/test_checksum.c +tests_lib_test_graph_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_graph_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_graph_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_graph_SOURCES = tests/lib/test_graph.c +tests_lib_test_heavy_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_heavy_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_heavy_LDADD = $(ALL_TESTS_LDADD) -lm +tests_lib_test_heavy_SOURCES = tests/lib/test_heavy.c tests/helpers/c/main.c +tests_lib_test_heavy_thread_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_heavy_thread_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_heavy_thread_LDADD = $(ALL_TESTS_LDADD) -lm +tests_lib_test_heavy_thread_SOURCES = tests/lib/test_heavy_thread.c tests/helpers/c/main.c +tests_lib_test_heavy_wq_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_heavy_wq_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_heavy_wq_LDADD = $(ALL_TESTS_LDADD) -lm +tests_lib_test_heavy_wq_SOURCES = tests/lib/test_heavy_wq.c tests/helpers/c/main.c +tests_lib_test_memory_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_memory_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_memory_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_memory_SOURCES = tests/lib/test_memory.c +tests_lib_test_nexthop_iter_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_nexthop_iter_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_nexthop_iter_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_nexthop_iter_SOURCES = tests/lib/test_nexthop_iter.c tests/helpers/c/prng.c +tests_lib_test_privs_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_privs_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_privs_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_privs_SOURCES = tests/lib/test_privs.c +tests_lib_test_ringbuf_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_ringbuf_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_ringbuf_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_ringbuf_SOURCES = tests/lib/test_ringbuf.c +tests_lib_test_segv_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_segv_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_segv_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_segv_SOURCES = tests/lib/test_segv.c +tests_lib_test_sig_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_sig_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_sig_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_sig_SOURCES = tests/lib/test_sig.c +tests_lib_test_srcdest_table_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_srcdest_table_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_srcdest_table_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_srcdest_table_SOURCES = tests/lib/test_srcdest_table.c tests/helpers/c/prng.c +tests_lib_test_stream_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_stream_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_stream_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_stream_SOURCES = tests/lib/test_stream.c +tests_lib_test_table_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_table_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_table_LDADD = $(ALL_TESTS_LDADD) -lm +tests_lib_test_table_SOURCES = tests/lib/test_table.c +tests_lib_test_timer_correctness_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_timer_correctness_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_timer_correctness_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_timer_correctness_SOURCES = tests/lib/test_timer_correctness.c tests/helpers/c/prng.c +tests_lib_test_timer_performance_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_timer_performance_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_timer_performance_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_timer_performance_SOURCES = tests/lib/test_timer_performance.c tests/helpers/c/prng.c +tests_lib_test_ttable_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_ttable_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_ttable_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_ttable_SOURCES = tests/lib/test_ttable.c +tests_lib_test_zlog_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_zlog_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_zlog_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_zlog_SOURCES = tests/lib/test_zlog.c +tests_lib_test_zmq_CFLAGS = $(TESTS_CFLAGS) $(ZEROMQ_CFLAGS) +tests_lib_test_zmq_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_zmq_LDADD = lib/libfrrzmq.la $(ALL_TESTS_LDADD) $(ZEROMQ_LIBS) +tests_lib_test_zmq_SOURCES = tests/lib/test_zmq.c + +tests_ospf6d_test_lsdb_CFLAGS = $(TESTS_CFLAGS) +tests_ospf6d_test_lsdb_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_ospf6d_test_lsdb_LDADD = $(OSPF6_TEST_LDADD) +tests_ospf6d_test_lsdb_SOURCES = tests/ospf6d/test_lsdb.c tests/lib/cli/common_cli.c + +EXTRA_DIST += \ + tests/runtests.py \ + tests/bgpd/test_aspath.py \ + tests/bgpd/test_capability.py \ + tests/bgpd/test_ecommunity.py \ + tests/bgpd/test_mp_attr.py \ + tests/bgpd/test_mpath.py \ + tests/bgpd/test_peer_attr.py \ + tests/helpers/python/frrsix.py \ + tests/helpers/python/frrtest.py \ + tests/isisd/test_fuzz_isis_tlv.py \ + tests/isisd/test_fuzz_isis_tlv_tests.h.gz \ + tests/isisd/test_isis_vertex_queue.py \ + tests/lib/cli/test_commands.in \ + tests/lib/cli/test_commands.py \ + tests/lib/cli/test_commands.refout \ + tests/lib/cli/test_cli.in \ + tests/lib/cli/test_cli.py \ + tests/lib/cli/test_cli.refout \ + tests/lib/test_nexthop_iter.py \ + tests/lib/test_ringbuf.py \ + tests/lib/test_srcdest_table.py \ + tests/lib/test_stream.py \ + tests/lib/test_stream.refout \ + tests/lib/test_table.py \ + tests/lib/test_timer_correctness.py \ + tests/lib/test_ttable.py \ + tests/lib/test_ttable.refout \ + tests/lib/test_zlog.py \ + tests/lib/test_graph.py \ + tests/lib/test_graph.refout \ + tests/ospf6d/test_lsdb.py \ + tests/ospf6d/test_lsdb.in \ + tests/ospf6d/test_lsdb.refout \ + # end + +.PHONY: tests/tests.xml +tests/tests.xml: $(check_PROGRAMS) + ( cd tests; $(PYTHON) ../$(srcdir)/tests/runtests.py --junitxml=tests.xml -v ../$(srcdir)/tests; ) +check: tests/tests.xml From f8948c1e02cc78051429c70d9e4a7c590aff88ba Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 18 Aug 2018 20:08:46 +0200 Subject: [PATCH 55/84] build: include helper Makefiles in dist Need these to have "make" work in subdirectories. Signed-off-by: David Lamparter --- Makefile.am | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Makefile.am b/Makefile.am index 6decbe64fc..67c3540e11 100644 --- a/Makefile.am +++ b/Makefile.am @@ -164,10 +164,35 @@ EXTRA_DIST += \ snapcraft/helpers \ snapcraft/snap \ \ + babeld/Makefile \ + bgpd/Makefile \ + bgpd/rfp-example/librfp/Makefile \ + bgpd/rfp-example/rfptest/Makefile \ doc/Makefile \ doc/developer/Makefile \ doc/manpages/Makefile \ doc/user/Makefile \ + eigrpd/Makefile \ + fpm/Makefile \ + isisd/Makefile \ + ldpd/Makefile \ + lib/Makefile \ + nhrpd/Makefile \ + ospf6d/Makefile \ + ospfclient/Makefile \ + ospfd/Makefile \ + pbrd/Makefile \ + pimd/Makefile \ + ports/Makefile \ + qpb/Makefile \ + ripd/Makefile \ + ripngd/Makefile \ + staticd/Makefile \ + tests/Makefile \ + tools/Makefile \ + vtysh/Makefile \ + watchfrr/Makefile \ + zebra/Makefile \ # end ACLOCAL_AMFLAGS = -I m4 From aad24c5ba61c7219bad009d849ad84e7f057813f Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 18 Aug 2018 05:34:59 +0200 Subject: [PATCH 56/84] build: remove common.am Fold things into where they make sense. Signed-off-by: David Lamparter --- Makefile.am | 13 +++++---- common.am | 61 ------------------------------------------- doc/developer/cli.rst | 2 -- lib/subdir.am | 20 ++++++++++++++ qpb/subdir.am | 36 +++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 68 deletions(-) delete mode 100644 common.am diff --git a/Makefile.am b/Makefile.am index 67c3540e11..fc1beaeeae 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,10 +1,15 @@ ## Process this file with automake to produce Makefile.in. AUTOMAKE_OPTIONS = subdir-objects 1.12 -include common.am +ACLOCAL_AMFLAGS = -I m4 -AM_CPPFLAGS += -I$(top_srcdir) -I$(top_srcdir)/include -I$(top_srcdir)/lib \ - -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/lib +AM_CFLAGS = \ + @ASAN_FLAGS@ @TSAN_FLAGS@ @MSAN_FLAGS@ \ + $(WERROR) +AM_CPPFLAGS = \ + @ASAN_FLAGS@ @TSAN_FLAGS@ @MSAN_FLAGS@ \ + -I$(top_srcdir) -I$(top_srcdir)/include -I$(top_srcdir)/lib \ + -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -DCONFDATE=$(CONFDATE) LIBCAP = @LIBCAP@ @@ -195,8 +200,6 @@ EXTRA_DIST += \ zebra/Makefile \ # end -ACLOCAL_AMFLAGS = -I m4 - noinst_HEADERS += defaults.h indent: diff --git a/common.am b/common.am deleted file mode 100644 index 9c0c547811..0000000000 --- a/common.am +++ /dev/null @@ -1,61 +0,0 @@ -# -# Automake fragment intended to be shared by Makefile.am files in the -# tree. When used, should be included at the very top of the file. -# -AM_CPPFLAGS = @ASAN_FLAGS@ @TSAN_FLAGS@ @MSAN_FLAGS@ -AM_CFLAGS = @ASAN_FLAGS@ @TSAN_FLAGS@ @MSAN_FLAGS@ $(WERROR) - -AM_V_CLIPPY = $(am__v_CLIPPY_$(V)) -am__v_CLIPPY_ = $(am__v_CLIPPY_$(AM_DEFAULT_VERBOSITY)) -am__v_CLIPPY_0 = @echo " CLIPPY " $@; -am__v_CLIPPY_1 = - -CLIPPY_DEPS = $(HOSTTOOLS)lib/clippy $(top_srcdir)/python/clidef.py - -SUFFIXES = _clippy.c .proto .pb-c.c .pb-c.h .pb.h -.c_clippy.c: - @{ test -x $(top_builddir)/$(HOSTTOOLS)lib/clippy || $(MAKE) -C $(top_builddir)/$(HOSTTOOLS) lib/clippy; } - $(AM_V_CLIPPY) $(top_builddir)/$(HOSTTOOLS)lib/clippy $(top_srcdir)/python/clidef.py -o $@ $< - -## automake's "ylwrap" is a great piece of GNU software... not. -.l.c: - $(AM_V_LEX)$(am__skiplex) $(LEXCOMPILE) $< -.y.c: - $(AM_V_YACC)$(am__skipyacc) $(YACCCOMPILE) $< - - -if HAVE_PROTOBUF - -# Uncomment to use an non-system version of libprotobuf-c. -# -# Q_PROTOBUF_C_CLIENT_INCLUDES = -I$(top_srcdir)/third-party/protobuf-c/src -# Q_PROTOBUF_C_CLIENT_LDOPTS = $(top_builddir)/third-party/protobuf-c/src/libprotobuf-c.la - -Q_PROTOBUF_C_CLIENT_INCLUDES= -Q_PROTOBUF_C_CLIENT_LDOPTS=-lprotobuf-c - -Q_PROTOC=protoc -Q_PROTOC_C=protoc-c - -# Rules -.proto.pb.h: - $(Q_PROTOC) -I$(top_srcdir) --cpp_out=$(top_srcdir) $(top_srcdir)/$^ - -AM_V_PROTOC_C = $(am__v_PROTOC_C_$(V)) -am__v_PROTOC_C_ = $(am__v_PROTOC_C_$(AM_DEFAULT_VERBOSITY)) -am__v_PROTOC_C_0 = @echo " PROTOC_C" $@; -am__v_PROTOC_C_1 = - -.proto.pb-c.c: - $(AM_V_PROTOC_C)$(Q_PROTOC_C) -I$(top_srcdir) --c_out=$(top_srcdir) $(top_srcdir)/$^ -.pb-c.c.pb-c.h: - @/bin/true - -# -# Information about how to link to various libraries. -# -Q_FRR_PB_CLIENT_LDOPTS = $(top_srcdir)/qpb/libfrr_pb.la $(Q_PROTOBUF_C_CLIENT_LDOPTS) - -Q_FPM_PB_CLIENT_LDOPTS = $(top_srcdir)/fpm/libfrrfpm_pb.la $(Q_FRR_PB_CLIENT_LDOPTS) - -endif # HAVE_PROTOBUF diff --git a/doc/developer/cli.rst b/doc/developer/cli.rst index 20391c47bc..c0716a5c93 100644 --- a/doc/developer/cli.rst +++ b/doc/developer/cli.rst @@ -450,8 +450,6 @@ is no ordering requirement) .. code-block:: make - include ../common.am - # ... # if linked into a LTLIBRARY (.la/.so): diff --git a/lib/subdir.am b/lib/subdir.am index d3bd441a80..ef0cefe7a5 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -253,6 +253,26 @@ lib_clippy_SOURCES = \ lib/vector.c \ # end +# (global) clippy rules for all directories + +AM_V_CLIPPY = $(am__v_CLIPPY_$(V)) +am__v_CLIPPY_ = $(am__v_CLIPPY_$(AM_DEFAULT_VERBOSITY)) +am__v_CLIPPY_0 = @echo " CLIPPY " $@; +am__v_CLIPPY_1 = + +CLIPPY_DEPS = $(HOSTTOOLS)lib/clippy $(top_srcdir)/python/clidef.py + +SUFFIXES = _clippy.c .proto .pb-c.c .pb-c.h .pb.h +.c_clippy.c: + @{ test -x $(top_builddir)/$(HOSTTOOLS)lib/clippy || \ + $(MAKE) -C $(top_builddir)/$(HOSTTOOLS) lib/clippy; } + $(AM_V_CLIPPY) $(top_builddir)/$(HOSTTOOLS)lib/clippy $(top_srcdir)/python/clidef.py -o $@ $< + +## automake's "ylwrap" is a great piece of GNU software... not. +.l.c: + $(AM_V_LEX)$(am__skiplex) $(LEXCOMPILE) $< +.y.c: + $(AM_V_YACC)$(am__skipyacc) $(YACCCOMPILE) $< # # generated sources & extra foo diff --git a/qpb/subdir.am b/qpb/subdir.am index 3c006fd221..0ed50c01be 100644 --- a/qpb/subdir.am +++ b/qpb/subdir.am @@ -23,3 +23,39 @@ CLEANFILES += \ endif EXTRA_DIST += qpb/qpb.proto + +if HAVE_PROTOBUF + +# Uncomment to use an non-system version of libprotobuf-c. +# +# Q_PROTOBUF_C_CLIENT_INCLUDES = -I$(top_srcdir)/third-party/protobuf-c/src +# Q_PROTOBUF_C_CLIENT_LDOPTS = $(top_builddir)/third-party/protobuf-c/src/libprotobuf-c.la + +Q_PROTOBUF_C_CLIENT_INCLUDES= +Q_PROTOBUF_C_CLIENT_LDOPTS=-lprotobuf-c + +Q_PROTOC=protoc +Q_PROTOC_C=protoc-c + +# Rules +.proto.pb.h: + $(Q_PROTOC) -I$(top_srcdir) --cpp_out=$(top_srcdir) $(top_srcdir)/$^ + +AM_V_PROTOC_C = $(am__v_PROTOC_C_$(V)) +am__v_PROTOC_C_ = $(am__v_PROTOC_C_$(AM_DEFAULT_VERBOSITY)) +am__v_PROTOC_C_0 = @echo " PROTOC_C" $@; +am__v_PROTOC_C_1 = + +.proto.pb-c.c: + $(AM_V_PROTOC_C)$(Q_PROTOC_C) -I$(top_srcdir) --c_out=$(top_srcdir) $(top_srcdir)/$^ +.pb-c.c.pb-c.h: + @/bin/true + +# +# Information about how to link to various libraries. +# +Q_FRR_PB_CLIENT_LDOPTS = $(top_srcdir)/qpb/libfrr_pb.la $(Q_PROTOBUF_C_CLIENT_LDOPTS) + +Q_FPM_PB_CLIENT_LDOPTS = $(top_srcdir)/fpm/libfrrfpm_pb.la $(Q_FRR_PB_CLIENT_LDOPTS) + +endif # HAVE_PROTOBUF From 74dc19a2f5f72865b5f7da86453b881ae161e857 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 18 Aug 2018 06:05:07 +0200 Subject: [PATCH 57/84] build: move vtysh & manpage listings to subdir.am Since we're now building through one large Makefile, we can easily put things with their daemons and crossreference nicely. Signed-off-by: David Lamparter --- Makefile.am | 1 + babeld/subdir.am | 5 ++ bfdd/subdir.am | 2 + bgpd/bgp_rpki.c | 2 + bgpd/subdir.am | 28 ++++++++ doc/manpages/subdir.am | 73 -------------------- eigrpd/subdir.am | 6 ++ isisd/subdir.am | 10 +++ ldpd/subdir.am | 2 + lib/subdir.am | 16 +++++ nhrpd/subdir.am | 2 + ospf6d/subdir.am | 18 +++++ ospfclient/subdir.am | 1 + ospfd/subdir.am | 11 +++ pbrd/subdir.am | 5 ++ pimd/subdir.am | 3 + ripd/subdir.am | 9 +++ ripngd/subdir.am | 8 +++ sharpd/subdir.am | 2 + staticd/subdir.am | 2 + vtysh/subdir.am | 151 +---------------------------------------- watchfrr/subdir.am | 2 + zebra/subdir.am | 17 +++++ 23 files changed, 155 insertions(+), 221 deletions(-) diff --git a/Makefile.am b/Makefile.am index fc1beaeeae..aae89a90d7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -88,6 +88,7 @@ pkginclude_HEADERS = nodist_pkginclude_HEADERS = dist_examples_DATA = man_MANS = +vtysh_scan = ## libtool, the self-made GNU scourge ## ... this should fix relinking diff --git a/babeld/subdir.am b/babeld/subdir.am index 6f91f73930..e1f2cb0a00 100644 --- a/babeld/subdir.am +++ b/babeld/subdir.am @@ -6,6 +6,11 @@ if BABELD noinst_LIBRARIES += babeld/libbabel.a sbin_PROGRAMS += babeld/babeld dist_examples_DATA += babeld/babeld.conf.sample +vtysh_scan += \ + $(top_srcdir)/babeld/babel_interface.c \ + $(top_srcdir)/babeld/babel_zebra.c \ + $(top_srcdir)/babeld/babeld.c \ + # end endif babeld_libbabel_a_SOURCES = \ diff --git a/bfdd/subdir.am b/bfdd/subdir.am index 86923f5cec..7447519b21 100644 --- a/bfdd/subdir.am +++ b/bfdd/subdir.am @@ -6,6 +6,8 @@ if BFDD noinst_LIBRARIES += bfdd/libbfd.a sbin_PROGRAMS += bfdd/bfdd dist_examples_DATA += bfdd/bfdd.conf.sample +vtysh_scan += $(top_srcdir)/bfdd/bfdd_vty.c +rstman8_DATA += $(MANBUILD)/bfdd.8 endif bfdd_libbfd_a_SOURCES = \ diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 82b268c31d..2e0bb1ae62 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -49,6 +49,7 @@ #include "bgpd/bgp_route.h" #include "lib/network.h" #include "lib/thread.h" +#ifndef VTYSH_EXTRACT_PL #include "rtrlib/rtrlib.h" #include "rtrlib/rtr_mgr.h" #include "rtrlib/lib/ip.h" @@ -56,6 +57,7 @@ #if defined(FOUND_SSH) #include "rtrlib/transport/ssh/ssh_transport.h" #endif +#endif #include "hook.h" #include "libfrr.h" #include "version.h" diff --git a/bgpd/subdir.am b/bgpd/subdir.am index e24516acd8..7bb8b8ed8d 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -11,12 +11,40 @@ dist_examples_DATA += \ bgpd/bgpd.conf.sample2 \ bgpd/bgpd.conf.vnc.sample \ # end +vtysh_scan += \ + $(top_srcdir)/bgpd/bgp_bfd.c \ + $(top_srcdir)/bgpd/bgp_debug.c \ + $(top_srcdir)/bgpd/bgp_dump.c \ + $(top_srcdir)/bgpd/bgp_evpn_vty.c \ + $(top_srcdir)/bgpd/bgp_filter.c \ + $(top_srcdir)/bgpd/bgp_mplsvpn.c \ + $(top_srcdir)/bgpd/bgp_nexthop.c \ + $(top_srcdir)/bgpd/bgp_route.c \ + $(top_srcdir)/bgpd/bgp_routemap.c \ + $(top_srcdir)/bgpd/bgp_vty.c \ + $(top_srcdir)/bgpd/bgp_flowspec_vty.c \ + # end + +# can be loaded as DSO - always include for vtysh +if RPKI +vtysh_scan += $(top_srcdir)/bgpd/bgp_rpki.c +endif + +if ENABLE_BGP_VNC +vtysh_scan += \ + $(top_srcdir)/bgpd/rfapi/bgp_rfapi_cfg.c \ + $(top_srcdir)/bgpd/rfapi/rfapi.c \ + $(top_srcdir)/bgpd/rfapi/rfapi_vty.c \ + $(top_srcdir)/bgpd/rfapi/vnc_debug.c \ + # end +endif if SNMP module_LTLIBRARIES += bgpd/bgpd_snmp.la endif if RPKI module_LTLIBRARIES += bgpd/bgpd_rpki.la endif +rstman8_DATA += $(MANBUILD)/bgpd.8 endif bgpd_libbgp_a_SOURCES = \ diff --git a/doc/manpages/subdir.am b/doc/manpages/subdir.am index 20efd523fc..0ce9161db6 100644 --- a/doc/manpages/subdir.am +++ b/doc/manpages/subdir.am @@ -49,79 +49,6 @@ rstman8_DATA = rstman1_DATA += $(MANBUILD)/frr.1 -if PIMD -rstman8_DATA += $(MANBUILD)/pimd.8 -rstman8_DATA += $(MANBUILD)/mtracebis.8 -endif - -if PBRD -rstman8_DATA += $(MANBUILD)/pbrd.8 -endif - -if BGPD -rstman8_DATA += $(MANBUILD)/bgpd.8 -endif - -if ISISD -rstman8_DATA += $(MANBUILD)/isisd.8 -endif - -if OSPF6D -rstman8_DATA += $(MANBUILD)/ospf6d.8 -endif - -if OSPFCLIENT -rstman8_DATA += $(MANBUILD)/ospfclient.8 -endif - -if OSPFD -rstman8_DATA += $(MANBUILD)/ospfd.8 -endif - -if LDPD -rstman8_DATA += $(MANBUILD)/ldpd.8 -endif - -if RIPD -rstman8_DATA += $(MANBUILD)/ripd.8 -endif - -if RIPNGD -rstman8_DATA += $(MANBUILD)/ripngd.8 -endif - -if NHRPD -rstman8_DATA += $(MANBUILD)/nhrpd.8 -endif - -if VTYSH -rstman1_DATA += $(MANBUILD)/vtysh.1 -endif - -if WATCHFRR -rstman8_DATA += $(MANBUILD)/watchfrr.8 -endif - -if ZEBRA -rstman8_DATA += $(MANBUILD)/zebra.8 -endif - -if EIGRPD -rstman8_DATA += $(MANBUILD)/eigrpd.8 -endif - -if SHARPD -rstman8_DATA += $(MANBUILD)/sharpd.8 -endif - -if STATICD -rstman8_DATA += $(MANBUILD)/staticd.8 -endif - -if BFDD -rstman8_DATA += $(MANBUILD)/bfdd.8 -endif - # dependency $(rstman8_DATA) $(rstman1_DATA): $(MANBUILD)/man.stamp diff --git a/eigrpd/subdir.am b/eigrpd/subdir.am index 2635d555d9..75b77feee6 100644 --- a/eigrpd/subdir.am +++ b/eigrpd/subdir.am @@ -6,6 +6,12 @@ if EIGRPD noinst_LIBRARIES += eigrpd/libeigrp.a sbin_PROGRAMS += eigrpd/eigrpd dist_examples_DATA += eigrpd/eigrpd.conf.sample +vtysh_scan += \ + $(top_srcdir)/eigrpd/eigrp_dump.c \ + $(top_srcdir)/eigrpd/eigrp_vty.c \ + # end +# $(top_srcdir)/eigrpd/eigrp_routemap.c +rstman8_DATA += $(MANBUILD)/eigrpd.8 endif eigrpd_libeigrp_a_SOURCES = \ diff --git a/isisd/subdir.am b/isisd/subdir.am index a45b9ca47c..855cd9dc54 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -6,6 +6,16 @@ if ISISD noinst_LIBRARIES += isisd/libisis.a sbin_PROGRAMS += isisd/isisd dist_examples_DATA += isisd/isisd.conf.sample +vtysh_scan += \ + $(top_srcdir)/isisd/isis_redist.c \ + $(top_srcdir)/isisd/isis_spf.c \ + $(top_srcdir)/isisd/isis_te.c \ + $(top_srcdir)/isisd/isis_vty_common.c \ + $(top_srcdir)/isisd/isis_vty_fabricd.c \ + $(top_srcdir)/isisd/isis_vty_isisd.c \ + $(top_srcdir)/isisd/isisd.c \ + # end +rstman8_DATA += $(MANBUILD)/isisd.8 endif if FABRICD diff --git a/ldpd/subdir.am b/ldpd/subdir.am index 2d87be0cda..b42f401f25 100644 --- a/ldpd/subdir.am +++ b/ldpd/subdir.am @@ -6,6 +6,8 @@ if LDPD noinst_LIBRARIES += ldpd/libldp.a sbin_PROGRAMS += ldpd/ldpd dist_examples_DATA += ldpd/ldpd.conf.sample +vtysh_scan += $(top_srcdir)/ldpd/ldp_vty_cmds.c +rstman8_DATA += $(MANBUILD)/ldpd.8 endif ldpd_libldp_a_SOURCES = \ diff --git a/lib/subdir.am b/lib/subdir.am index ef0cefe7a5..09deb9bc1b 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -84,6 +84,22 @@ lib_libfrr_la_SOURCES = \ lib/logicalrouter.c \ # end +vtysh_scan += \ + $(top_srcdir)/lib/distribute.c \ + $(top_srcdir)/lib/filter.c \ + $(top_srcdir)/lib/if.c \ + $(top_srcdir)/lib/if_rmap.c \ + $(top_srcdir)/lib/keychain.c \ + $(top_srcdir)/lib/logicalrouter.c \ + $(top_srcdir)/lib/nexthop_group.c \ + $(top_srcdir)/lib/plist.c \ + $(top_srcdir)/lib/routemap.c \ + $(top_srcdir)/lib/vrf.c \ + $(top_srcdir)/lib/vty.c \ + # end +# can be loaded as DSO - always include for vtysh +vtysh_scan += $(top_srcdir)/lib/agentx.c + lib/plist_clippy.c: $(CLIPPY_DEPS) lib/plist.lo: lib/plist_clippy.c lib/nexthop_group_clippy.c: $(CLIPPY_DEPS) diff --git a/nhrpd/subdir.am b/nhrpd/subdir.am index d66e968224..f7575971e9 100644 --- a/nhrpd/subdir.am +++ b/nhrpd/subdir.am @@ -4,6 +4,8 @@ if NHRPD sbin_PROGRAMS += nhrpd/nhrpd +vtysh_scan += $(top_srcdir)/nhrpd/nhrp_vty.c +rstman8_DATA += $(MANBUILD)/nhrpd.8 endif nhrpd_nhrpd_LDADD = lib/libfrr.la @LIBCAP@ @CARES_LIBS@ diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index 8a6c4a5ccf..5338e1ea37 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -6,9 +6,27 @@ if OSPF6D noinst_LIBRARIES += ospf6d/libospf6.a sbin_PROGRAMS += ospf6d/ospf6d dist_examples_DATA += ospf6d/ospf6d.conf.sample +vtysh_scan += \ + $(top_srcdir)/ospf6d/ospf6_abr.c \ + $(top_srcdir)/ospf6d/ospf6_asbr.c \ + $(top_srcdir)/ospf6d/ospf6_area.c \ + $(top_srcdir)/ospf6d/ospf6_bfd.c \ + $(top_srcdir)/ospf6d/ospf6_flood.c \ + $(top_srcdir)/ospf6d/ospf6_interface.c \ + $(top_srcdir)/ospf6d/ospf6_intra.c \ + $(top_srcdir)/ospf6d/ospf6_lsa.c \ + $(top_srcdir)/ospf6d/ospf6_message.c \ + $(top_srcdir)/ospf6d/ospf6_neighbor.c \ + $(top_srcdir)/ospf6d/ospf6_route.c \ + $(top_srcdir)/ospf6d/ospf6_spf.c \ + $(top_srcdir)/ospf6d/ospf6_top.c \ + $(top_srcdir)/ospf6d/ospf6_zebra.c \ + $(top_srcdir)/ospf6d/ospf6d.c \ + # end if SNMP module_LTLIBRARIES += ospf6d/ospf6d_snmp.la endif +rstman8_DATA += $(MANBUILD)/ospf6d.8 endif ospf6d_libospf6_a_SOURCES = \ diff --git a/ospfclient/subdir.am b/ospfclient/subdir.am index 834d4aaba7..d880f9fc70 100644 --- a/ospfclient/subdir.am +++ b/ospfclient/subdir.am @@ -5,6 +5,7 @@ if OSPFCLIENT lib_LTLIBRARIES += ospfclient/libfrrospfapiclient.la sbin_PROGRAMS += ospfclient/ospfclient +rstman8_DATA += $(MANBUILD)/ospfclient.8 endif ospfclient_libfrrospfapiclient_la_LDFLAGS = -version-info 0:0:0 diff --git a/ospfd/subdir.am b/ospfd/subdir.am index cd659a9bc9..2b42b5230b 100644 --- a/ospfd/subdir.am +++ b/ospfd/subdir.am @@ -6,9 +6,20 @@ if OSPFD noinst_LIBRARIES += ospfd/libfrrospf.a sbin_PROGRAMS += ospfd/ospfd dist_examples_DATA += ospfd/ospfd.conf.sample +vtysh_scan += \ + $(top_srcdir)/ospfd/ospf_bfd.c \ + $(top_srcdir)/ospfd/ospf_dump.c \ + $(top_srcdir)/ospfd/ospf_opaque.c \ + $(top_srcdir)/ospfd/ospf_ri.c \ + $(top_srcdir)/ospfd/ospf_routemap.c \ + $(top_srcdir)/ospfd/ospf_te.c \ + $(top_srcdir)/ospfd/ospf_sr.c \ + $(top_srcdir)/ospfd/ospf_vty.c \ + # end if SNMP module_LTLIBRARIES += ospfd/ospfd_snmp.la endif +rstman8_DATA += $(MANBUILD)/ospfd.8 endif ospfd_libfrrospf_a_SOURCES = \ diff --git a/pbrd/subdir.am b/pbrd/subdir.am index 42ab393218..49a07e1f60 100644 --- a/pbrd/subdir.am +++ b/pbrd/subdir.am @@ -6,6 +6,11 @@ if PBRD noinst_LIBRARIES += pbrd/libpbr.a sbin_PROGRAMS += pbrd/pbrd dist_examples_DATA += pbrd/pbrd.conf.sample +vtysh_scan += \ + $(top_srcdir)/pbrd/pbr_vty.c \ + $(top_srcdir)/pbrd/pbr_debug.c \ + # end +rstman8_DATA += $(MANBUILD)/pbrd.8 endif pbrd_libpbr_a_SOURCES = \ diff --git a/pimd/subdir.am b/pimd/subdir.am index 55d56ece97..00cabb99b0 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -8,6 +8,9 @@ sbin_PROGRAMS += pimd/pimd bin_PROGRAMS += pimd/mtracebis noinst_PROGRAMS += pimd/test_igmpv3_join dist_examples_DATA += pimd/pimd.conf.sample +vtysh_scan += $(top_srcdir)/pimd/pim_cmd.c +rstman8_DATA += $(MANBUILD)/pimd.8 +rstman8_DATA += $(MANBUILD)/mtracebis.8 endif pimd_libpim_a_SOURCES = \ diff --git a/ripd/subdir.am b/ripd/subdir.am index 612db1a7ab..f2c54c835e 100644 --- a/ripd/subdir.am +++ b/ripd/subdir.am @@ -6,9 +6,18 @@ if RIPD noinst_LIBRARIES += ripd/librip.a sbin_PROGRAMS += ripd/ripd dist_examples_DATA += ripd/ripd.conf.sample +vtysh_scan += \ + $(top_srcdir)/ripd/rip_debug.c \ + $(top_srcdir)/ripd/rip_interface.c \ + $(top_srcdir)/ripd/rip_offset.c \ + $(top_srcdir)/ripd/rip_zebra.c \ + $(top_srcdir)/ripd/ripd.c \ + # end + if SNMP module_LTLIBRARIES += ripd/ripd_snmp.la endif +rstman8_DATA += $(MANBUILD)/ripd.8 endif ripd_librip_a_SOURCES = \ diff --git a/ripngd/subdir.am b/ripngd/subdir.am index 1f7ff09d6e..0948b23342 100644 --- a/ripngd/subdir.am +++ b/ripngd/subdir.am @@ -5,6 +5,14 @@ if RIPNGD noinst_LIBRARIES += ripngd/libripng.a sbin_PROGRAMS += ripngd/ripngd +vtysh_scan += \ + $(top_srcdir)/ripngd/ripng_debug.c \ + $(top_srcdir)/ripngd/ripng_interface.c \ + $(top_srcdir)/ripngd/ripng_offset.c \ + $(top_srcdir)/ripngd/ripng_zebra.c \ + $(top_srcdir)/ripngd/ripngd.c \ + # end +rstman8_DATA += $(MANBUILD)/ripngd.8 endif ripngd_libripng_a_SOURCES = \ diff --git a/sharpd/subdir.am b/sharpd/subdir.am index 490a2ba787..ecc62af142 100644 --- a/sharpd/subdir.am +++ b/sharpd/subdir.am @@ -6,6 +6,8 @@ if SHARPD noinst_LIBRARIES += sharpd/libsharp.a sbin_PROGRAMS += sharpd/sharpd dist_examples_DATA += sharpd/sharpd.conf.sample +vtysh_scan += $(top_srcdir)/sharpd/sharp_vty.c +rstman8_DATA += $(MANBUILD)/sharpd.8 endif sharpd_libsharp_a_SOURCES = \ diff --git a/staticd/subdir.am b/staticd/subdir.am index 3b06a92e22..f1071545ab 100644 --- a/staticd/subdir.am +++ b/staticd/subdir.am @@ -6,6 +6,8 @@ if STATICD noinst_LIBRARIES += staticd/libstatic.a sbin_PROGRAMS += staticd/staticd dist_examples_DATA += staticd/staticd.conf.sample +vtysh_scan += $(top_srcdir)/staticd/static_vty.c +rstman8_DATA += $(MANBUILD)/staticd.8 endif staticd_libstatic_a_SOURCES = \ diff --git a/vtysh/subdir.am b/vtysh/subdir.am index ca288ea9e4..3d40f37d22 100644 --- a/vtysh/subdir.am +++ b/vtysh/subdir.am @@ -5,6 +5,7 @@ if VTYSH bin_PROGRAMS += vtysh/vtysh dist_examples_DATA += vtysh/vtysh.conf.sample +rstman1_DATA += $(MANBUILD)/vtysh.1 endif vtysh_vtysh_SOURCES = \ @@ -27,156 +28,10 @@ vtysh_vtysh_LDADD = lib/libfrr.la @LIBCAP@ @LIBREADLINE@ @LIBS@ @CURSES@ @LIBPAM EXTRA_DIST += vtysh/extract.pl -vtysh_scan = - -if PIMD -vtysh_scan += $(top_srcdir)/pimd/pim_cmd.c -endif - -if BGPD -vtysh_scan += $(top_srcdir)/bgpd/bgp_bfd.c -vtysh_scan += $(top_srcdir)/bgpd/bgp_debug.c -vtysh_scan += $(top_srcdir)/bgpd/bgp_dump.c -vtysh_scan += $(top_srcdir)/bgpd/bgp_evpn_vty.c -vtysh_scan += $(top_srcdir)/bgpd/bgp_filter.c -vtysh_scan += $(top_srcdir)/bgpd/bgp_mplsvpn.c -vtysh_scan += $(top_srcdir)/bgpd/bgp_nexthop.c -vtysh_scan += $(top_srcdir)/bgpd/bgp_route.c -vtysh_scan += $(top_srcdir)/bgpd/bgp_routemap.c -vtysh_scan += $(top_srcdir)/bgpd/bgp_vty.c -vtysh_scan += $(top_srcdir)/bgpd/bgp_flowspec_vty.c -if ENABLE_BGP_VNC -vtysh_scan += $(top_srcdir)/bgpd/rfapi/bgp_rfapi_cfg.c -vtysh_scan += $(top_srcdir)/bgpd/rfapi/rfapi.c -vtysh_scan += $(top_srcdir)/bgpd/rfapi/rfapi_vty.c -vtysh_scan += $(top_srcdir)/bgpd/rfapi/vnc_debug.c -endif -endif - -if RPKI -vtysh_scan += $(top_srcdir)/bgpd/bgp_rpki.c -endif - -if ISISD -vtysh_scan += $(top_srcdir)/isisd/isis_redist.c -vtysh_scan += $(top_srcdir)/isisd/isis_spf.c -vtysh_scan += $(top_srcdir)/isisd/isis_te.c -vtysh_scan += $(top_srcdir)/isisd/isis_vty_common.c -vtysh_scan += $(top_srcdir)/isisd/isis_vty_fabricd.c -vtysh_scan += $(top_srcdir)/isisd/isis_vty_isisd.c -vtysh_scan += $(top_srcdir)/isisd/isisd.c -endif - -if OSPFD -vtysh_scan += $(top_srcdir)/ospfd/ospf_bfd.c -vtysh_scan += $(top_srcdir)/ospfd/ospf_dump.c -vtysh_scan += $(top_srcdir)/ospfd/ospf_opaque.c -vtysh_scan += $(top_srcdir)/ospfd/ospf_ri.c -vtysh_scan += $(top_srcdir)/ospfd/ospf_routemap.c -vtysh_scan += $(top_srcdir)/ospfd/ospf_te.c -vtysh_scan += $(top_srcdir)/ospfd/ospf_sr.c -vtysh_scan += $(top_srcdir)/ospfd/ospf_vty.c -endif - -if OSPF6D -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_abr.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_asbr.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_area.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_bfd.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_flood.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_interface.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_intra.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_lsa.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_message.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_neighbor.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_route.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_spf.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_top.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6_zebra.c -vtysh_scan += $(top_srcdir)/ospf6d/ospf6d.c -endif - -if RIPD -vtysh_scan += $(top_srcdir)/ripd/rip_debug.c -vtysh_scan += $(top_srcdir)/ripd/rip_interface.c -vtysh_scan += $(top_srcdir)/ripd/rip_offset.c -vtysh_scan += $(top_srcdir)/ripd/rip_zebra.c -vtysh_scan += $(top_srcdir)/ripd/ripd.c -endif - -if RIPNGD -vtysh_scan += $(top_srcdir)/ripngd/ripng_debug.c -vtysh_scan += $(top_srcdir)/ripngd/ripng_interface.c -vtysh_scan += $(top_srcdir)/ripngd/ripng_offset.c -vtysh_scan += $(top_srcdir)/ripngd/ripng_zebra.c -vtysh_scan += $(top_srcdir)/ripngd/ripngd.c -endif - -if LDPD -vtysh_scan += $(top_srcdir)/ldpd/ldp_vty_cmds.c -endif - -if NHRPD -vtysh_scan += $(top_srcdir)/nhrpd/nhrp_vty.c -endif - -if EIGRPD -vtysh_scan += $(top_srcdir)/eigrpd/eigrp_dump.c -#vtysh_scan += $(top_srcdir)/eigrpd/eigrp_routemap.c -vtysh_scan += $(top_srcdir)/eigrpd/eigrp_vty.c -endif - -if BABELD -vtysh_scan += $(top_srcdir)/babeld/babel_interface.c -vtysh_scan += $(top_srcdir)/babeld/babel_zebra.c -vtysh_scan += $(top_srcdir)/babeld/babeld.c -endif - -if SHARPD -vtysh_scan += $(top_srcdir)/sharpd/sharp_vty.c -endif - -if SNMP -vtysh_scan += $(top_srcdir)/lib/agentx.c -endif - -if PBRD -vtysh_scan += $(top_srcdir)/pbrd/pbr_vty.c -vtysh_scan += $(top_srcdir)/pbrd/pbr_debug.c -endif - -if STATICD -vtysh_scan += $(top_srcdir)/staticd/static_vty.c -endif - -if BFDD -vtysh_scan += $(top_srcdir)/bfdd/bfdd_vty.c -endif - -vtysh_vtysh_cmd_FILES = $(vtysh_scan) \ - $(top_srcdir)/lib/keychain.c $(top_srcdir)/lib/routemap.c \ - $(top_srcdir)/lib/filter.c $(top_srcdir)/lib/plist.c \ - $(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \ - $(top_srcdir)/lib/vrf.c $(top_srcdir)/lib/if.c \ - $(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \ - $(top_srcdir)/lib/logicalrouter.c \ - $(top_srcdir)/lib/nexthop_group.c \ - $(top_srcdir)/zebra/interface.c \ - $(top_srcdir)/zebra/irdp_interface.c \ - $(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \ - $(top_srcdir)/zebra/zserv.c $(top_srcdir)/zebra/router-id.c \ - $(top_srcdir)/zebra/zebra_routemap.c \ - $(top_srcdir)/zebra/zebra_fpm.c \ - $(top_srcdir)/zebra/zebra_ptm.c \ - $(top_srcdir)/zebra/zebra_mpls_vty.c \ - $(top_srcdir)/zebra/zebra_pw.c \ - $(top_srcdir)/watchfrr/watchfrr_vty.c \ - # end - AM_V_EXTRACT = $(am__v_EXTRACT_$(V)) am__v_EXTRACT_ = $(am__v_EXTRACT_$(AM_DEFAULT_VERBOSITY)) am__v_EXTRACT_0 = @echo " EXTRACT " $@; am__v_EXTRACT_1 = -vtysh/vtysh_cmd.c: $(vtysh_vtysh_cmd_FILES) vtysh/extract.pl - $(AM_V_EXTRACT) vtysh/extract.pl $(vtysh_vtysh_cmd_FILES) > vtysh/vtysh_cmd.c +vtysh/vtysh_cmd.c: $(vtysh_scan) vtysh/extract.pl + $(AM_V_EXTRACT) vtysh/extract.pl $(vtysh_scan) > vtysh/vtysh_cmd.c diff --git a/watchfrr/subdir.am b/watchfrr/subdir.am index 931f11ef63..96df81d4a6 100644 --- a/watchfrr/subdir.am +++ b/watchfrr/subdir.am @@ -4,6 +4,8 @@ if WATCHFRR sbin_PROGRAMS += watchfrr/watchfrr +vtysh_scan += $(top_srcdir)/watchfrr/watchfrr_vty.c +rstman8_DATA += $(MANBUILD)/watchfrr.8 endif noinst_HEADERS += \ diff --git a/zebra/subdir.am b/zebra/subdir.am index 5dc3750315..91bd792b00 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -5,6 +5,22 @@ if ZEBRA sbin_PROGRAMS += zebra/zebra dist_examples_DATA += zebra/zebra.conf.sample +vtysh_scan += \ + $(top_srcdir)/zebra/debug.c \ + $(top_srcdir)/zebra/interface.c \ + $(top_srcdir)/zebra/router-id.c \ + $(top_srcdir)/zebra/rtadv.c \ + $(top_srcdir)/zebra/zebra_mpls_vty.c \ + $(top_srcdir)/zebra/zebra_ptm.c \ + $(top_srcdir)/zebra/zebra_pw.c \ + $(top_srcdir)/zebra/zebra_routemap.c \ + $(top_srcdir)/zebra/zebra_vty.c \ + $(top_srcdir)/zebra/zserv.c \ + # end + +# can be loaded as DSO - always include for vtysh +vtysh_scan += $(top_srcdir)/zebra/irdp_interface.c +vtysh_scan += $(top_srcdir)/zebra/zebra_fpm.c if IRDP module_LTLIBRARIES += zebra/zebra_irdp.la @@ -16,6 +32,7 @@ if FPM module_LTLIBRARIES += zebra/zebra_fpm.la endif +rstman8_DATA += $(MANBUILD)/zebra.8 ## endif ZEBRA endif From b37ccace1e456487c825024fdbbeddad3e027061 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 18 Aug 2018 05:00:54 +0200 Subject: [PATCH 58/84] tests: properly locate files in builddir test_cli.refout is written by configure into the build directory, thus we need a little special glue to find it correctly. Signed-off-by: David Lamparter --- tests/helpers/python/frrtest.py | 10 ++++++++-- tests/lib/cli/test_cli.py | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/helpers/python/frrtest.py b/tests/helpers/python/frrtest.py index da9e447fc0..60bee5c88c 100644 --- a/tests/helpers/python/frrtest.py +++ b/tests/helpers/python/frrtest.py @@ -176,8 +176,14 @@ class TestRefOut(object): basedir = os.path.dirname(inspect.getsourcefile(type(self))) program = os.path.join(basedir, self.program) - refin = program + '.in' - refout = program + '.refout' + if getattr(self, 'built_refin', False): + refin = binpath(program) + '.in' + else: + refin = program + '.in' + if getattr(self, 'built_refout', False): + refout = binpath(program) + '.refout' + else: + refout = program + '.refout' intext = '' if os.path.exists(refin): diff --git a/tests/lib/cli/test_cli.py b/tests/lib/cli/test_cli.py index e3c31c2d91..7371db283a 100644 --- a/tests/lib/cli/test_cli.py +++ b/tests/lib/cli/test_cli.py @@ -2,3 +2,4 @@ import frrtest class TestCli(frrtest.TestRefOut): program = './test_cli' + built_refout = True From 4007e3adf1208f2943c72ab11044b57a01b3813b Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sun, 2 Sep 2018 14:52:43 +0200 Subject: [PATCH 59/84] build: use -export-dynamic Signed-off-by: David Lamparter --- Makefile.am | 2 ++ configure.ac | 14 -------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/Makefile.am b/Makefile.am index aae89a90d7..b4d38e69af 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,6 +10,8 @@ AM_CPPFLAGS = \ @ASAN_FLAGS@ @TSAN_FLAGS@ @MSAN_FLAGS@ \ -I$(top_srcdir) -I$(top_srcdir)/include -I$(top_srcdir)/lib \ -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/lib +AM_LDFLAGS = \ + -export-dynamic DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -DCONFDATE=$(CONFDATE) LIBCAP = @LIBCAP@ diff --git a/configure.ac b/configure.ac index 521d27a682..2e8b953c0c 100755 --- a/configure.ac +++ b/configure.ac @@ -287,20 +287,6 @@ if test x"${enable_werror}" = x"yes" ; then fi AC_SUBST(WERROR) -dnl need link on this one, not compile -AC_LANG_PUSH(C) -ac_ld_flag_save="$LDFLAGS" -LDFLAGS="$LDFLAGS -rdynamic" -AC_MSG_CHECKING([[whether linker supports -rdynamic]]) -AC_LINK_IFELSE( - [AC_LANG_PROGRAM([[]])], - [AC_MSG_RESULT([yes])], - [ - LDFLAGS="$ac_ld_flag_save" - AC_MSG_RESULT([no]) - ]) -AC_LANG_POP(C) - dnl ---------- dnl Essentials dnl ---------- From 2b2f275ee2ea2c204f27b94d5db62d2a7da87189 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sun, 2 Sep 2018 15:15:17 +0200 Subject: [PATCH 60/84] build: clean up protobuf build integration We were linking all libs and binaries against libprotobuf-c if the option was enabled... that makes no sense at all. Signed-off-by: David Lamparter --- configure.ac | 49 ++++++++++++++++++------------------------------- fpm/subdir.am | 10 ++++++---- qpb/subdir.am | 39 ++++++++++++--------------------------- zebra/subdir.am | 3 ++- 4 files changed, 38 insertions(+), 63 deletions(-) diff --git a/configure.ac b/configure.ac index 2e8b953c0c..3657152644 100755 --- a/configure.ac +++ b/configure.ac @@ -616,27 +616,26 @@ AC_SUBST(PYTHON_LIBS) # Logic for protobuf support. # if test "$enable_protobuf" = "yes"; then - have_protobuf=yes + # Check for protoc & protoc-c - # Check for protoc-c - AC_CHECK_PROG([PROTOC_C], [protoc-c], [protoc-c], [/bin/false]) - if test "x$PROTOC_C" = "x/bin/false"; then - have_protobuf=no - else - found_protobuf_c=no - PKG_CHECK_MODULES([PROTOBUF_C], libprotobuf-c >= 0.14, - [found_protobuf_c=yes], - [AC_MSG_RESULT([pkg-config did not find libprotobuf-c])]) + # protoc is not required, it's only for a "be nice" helper target + AC_CHECK_PROGS([PROTOC], [protoc], [/bin/false]) - if test "x$found_protobuf_c" = "xyes"; then - LDFLAGS="$LDFLAGS $PROTOBUF_C_LIBS" - CFLAGS="$CFLAGS $PROTOBUF_C_CFLAGS" - else - AC_CHECK_HEADER([google/protobuf-c/protobuf-c.h], [], - [have_protobuf=no; AC_MSG_RESULT([Couldn't find google/protobuf-c.h])]) - fi - fi + AC_CHECK_PROGS([PROTOC_C], [protoc-c], [/bin/false]) + if test "$PROTOC_C" = "/bin/false"; then + AC_MSG_FAILURE([protobuf requested but protoc-c not found. Install protobuf-c.]) + fi + + PKG_CHECK_MODULES([PROTOBUF_C], [libprotobuf-c >= 0.14],, [ + AC_MSG_FAILURE([protobuf requested but libprotobuf-c not found. Install protobuf-c.]) + ]) + AC_CHECK_HEADER([google/protobuf-c/protobuf-c.h], [], [ + AC_MSG_FAILURE([protobuf requested but protobuf-c.h not found. Install protobuf-c.]) + ]) + + AC_DEFINE(HAVE_PROTOBUF,, protobuf) fi +AM_CONDITIONAL([HAVE_PROTOBUF], [test "x$enable_protobuf" = "xyes"]) # # Logic for old vpn commans support. @@ -645,18 +644,6 @@ if test "$enable_oldvpn_commands" = "yes"; then AC_DEFINE(KEEP_OLD_VPN_COMMANDS,, [Define for compiling with old vpn commands]) fi -# Fail if the user explicity enabled protobuf support and we couldn't -# find the compiler or libraries. -if test "x$have_protobuf" = "xno" && test "x$enable_protobuf" = "xyes"; then - AC_MSG_ERROR([Protobuf enabled explicitly but can't find libraries/tools]) -fi - -if test "x$have_protobuf" = "xyes"; then - AC_DEFINE(HAVE_PROTOBUF,, protobuf) -fi - -AM_CONDITIONAL([HAVE_PROTOBUF], [test "x$have_protobuf" = "xyes"]) - # # End of logic for protobuf support. # @@ -2057,7 +2044,7 @@ group to run as : ${enable_group} group for vty sockets : ${enable_vty_group} config file mask : ${enable_configfile_mask} log file mask : ${enable_logfile_mask} -zebra protobuf enabled : ${have_protobuf:-no} +zebra protobuf enabled : ${enable_protobuf:-no} The above user and group must have read/write access to the state file directory and to the config files in the config file directory." diff --git a/fpm/subdir.am b/fpm/subdir.am index 795535596b..05cec5a528 100644 --- a/fpm/subdir.am +++ b/fpm/subdir.am @@ -3,8 +3,7 @@ lib_LTLIBRARIES += fpm/libfrrfpm_pb.la endif fpm_libfrrfpm_pb_la_LDFLAGS = -version-info 0:0:0 -fpm_libfrrfpm_pb_la_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir) -I$(top_builddir)/lib \ - $(Q_PROTOBUF_C_CLIENT_INCLUDES) +fpm_libfrrfpm_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(PROTOBUF_C_CFLAGS) fpm_libfrrfpm_pb_la_SOURCES = \ fpm/fpm.h \ fpm/fpm_pb.h \ @@ -12,11 +11,14 @@ fpm_libfrrfpm_pb_la_SOURCES = \ # end if HAVE_PROTOBUF -nodist_fpm_libfrrfpm_pb_la_SOURCES = fpm/fpm.pb-c.c +nodist_fpm_libfrrfpm_pb_la_SOURCES = \ + fpm/fpm.pb-c.c \ + # end +endif + CLEANFILES += \ fpm/fpm.pb-c.c \ fpm/fpm.pb-c.h \ # end -endif EXTRA_DIST += fpm/fpm.proto diff --git a/qpb/subdir.am b/qpb/subdir.am index 0ed50c01be..75a733f8fc 100644 --- a/qpb/subdir.am +++ b/qpb/subdir.am @@ -2,44 +2,36 @@ if HAVE_PROTOBUF lib_LTLIBRARIES += qpb/libfrr_pb.la endif -qpb_libfrr_pb_la_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir) -I$(top_builddir)/lib \ - $(Q_PROTOBUF_C_CLIENT_INCLUDES) +qpb_libfrr_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(PROTOBUF_C_CFLAGS) +qpb_libfrr_pb_la_LIBADD = $(PROTOBUF_C_LIBS) qpb_libfrr_pb_la_LDFLAGS = -version-info 0:0:0 qpb_libfrr_pb_la_SOURCES = \ + qpb/qpb.c \ + qpb/qpb_allocator.c \ + # end +nodist_qpb_libfrr_pb_la_SOURCES = \ + qpb/qpb.pb-c.c \ + # end + +noinst_HEADERS += \ qpb/linear_allocator.h \ qpb/qpb.h \ - qpb/qpb.c \ qpb/qpb_allocator.h \ # end -if HAVE_PROTOBUF -qpb_libfrr_pb_la_SOURCES += qpb/qpb_allocator.c -nodist_qpb_libfrr_pb_la_SOURCES = qpb/qpb.pb-c.c CLEANFILES += \ qpb/qpb.pb-c.c \ qpb/qpb.pb-c.h \ # end -endif EXTRA_DIST += qpb/qpb.proto if HAVE_PROTOBUF -# Uncomment to use an non-system version of libprotobuf-c. -# -# Q_PROTOBUF_C_CLIENT_INCLUDES = -I$(top_srcdir)/third-party/protobuf-c/src -# Q_PROTOBUF_C_CLIENT_LDOPTS = $(top_builddir)/third-party/protobuf-c/src/libprotobuf-c.la - -Q_PROTOBUF_C_CLIENT_INCLUDES= -Q_PROTOBUF_C_CLIENT_LDOPTS=-lprotobuf-c - -Q_PROTOC=protoc -Q_PROTOC_C=protoc-c - # Rules .proto.pb.h: - $(Q_PROTOC) -I$(top_srcdir) --cpp_out=$(top_srcdir) $(top_srcdir)/$^ + $(PROTOC) -I$(top_srcdir) --cpp_out=$(top_srcdir) $(top_srcdir)/$^ AM_V_PROTOC_C = $(am__v_PROTOC_C_$(V)) am__v_PROTOC_C_ = $(am__v_PROTOC_C_$(AM_DEFAULT_VERBOSITY)) @@ -47,15 +39,8 @@ am__v_PROTOC_C_0 = @echo " PROTOC_C" $@; am__v_PROTOC_C_1 = .proto.pb-c.c: - $(AM_V_PROTOC_C)$(Q_PROTOC_C) -I$(top_srcdir) --c_out=$(top_srcdir) $(top_srcdir)/$^ + $(AM_V_PROTOC_C)$(PROTOC_C) -I$(top_srcdir) --c_out=$(top_srcdir) $(top_srcdir)/$^ .pb-c.c.pb-c.h: @/bin/true -# -# Information about how to link to various libraries. -# -Q_FRR_PB_CLIENT_LDOPTS = $(top_srcdir)/qpb/libfrr_pb.la $(Q_PROTOBUF_C_CLIENT_LDOPTS) - -Q_FPM_PB_CLIENT_LDOPTS = $(top_srcdir)/fpm/libfrrfpm_pb.la $(Q_FRR_PB_CLIENT_LDOPTS) - endif # HAVE_PROTOBUF diff --git a/zebra/subdir.am b/zebra/subdir.am index 91bd792b00..ac0abe348e 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -149,10 +149,11 @@ zebra_zebra_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic zebra_zebra_snmp_la_LIBADD = lib/libfrrsnmp.la zebra_zebra_fpm_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -zebra_zebra_fpm_la_LIBADD = $(Q_FPM_PB_CLIENT_LDOPTS) +zebra_zebra_fpm_la_LIBADD = zebra_zebra_fpm_la_SOURCES = zebra/zebra_fpm.c zebra_zebra_fpm_la_SOURCES += zebra/zebra_fpm_netlink.c if HAVE_PROTOBUF +zebra_zebra_fpm_la_LIBADD += fpm/libfrrfpm_pb.la qpb/libfrr_pb.la $(PROTOBUF_C_LIBS) zebra_zebra_fpm_la_SOURCES += zebra/zebra_fpm_protobuf.c if DEV_BUILD zebra_zebra_fpm_la_SOURCES += zebra/zebra_fpm_dt.c From 184792415987af06870c672ea2dd15d5b7fa06f5 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 27 Aug 2018 07:02:59 +0200 Subject: [PATCH 61/84] build: make pkgconfig configure output useful The variable name is not exactly the most helpful thing there. Signed-off-by: David Lamparter --- m4/pkg.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/m4/pkg.m4 b/m4/pkg.m4 index c5b26b52e6..a8dcd17c41 100644 --- a/m4/pkg.m4 +++ b/m4/pkg.m4 @@ -109,7 +109,7 @@ AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no -AC_MSG_CHECKING([for $1]) +AC_MSG_CHECKING([for $1 ($2)]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) From dbac691da646e624addc8f3ed5e744f9d3f8d69f Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sun, 2 Sep 2018 20:36:20 +0200 Subject: [PATCH 62/84] build: fix & clean up *SAN flags ASAN/MSAN/TSAN flags need to be in CFLAGS and LDFLAGS; the latter links the correct compiler-dependent library. Also, the configure switch was broken (--disable-... would enable the sanitizer.) Signed-off-by: David Lamparter --- Makefile.am | 10 ++++--- configure.ac | 74 +++++++++++++++++++++---------------------------- lib/subdir.am | 5 ++-- tests/subdir.am | 2 +- 4 files changed, 42 insertions(+), 49 deletions(-) diff --git a/Makefile.am b/Makefile.am index b4d38e69af..303881b077 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,14 +4,16 @@ AUTOMAKE_OPTIONS = subdir-objects 1.12 ACLOCAL_AMFLAGS = -I m4 AM_CFLAGS = \ - @ASAN_FLAGS@ @TSAN_FLAGS@ @MSAN_FLAGS@ \ - $(WERROR) + $(SAN_FLAGS) \ + $(WERROR) \ + # end AM_CPPFLAGS = \ - @ASAN_FLAGS@ @TSAN_FLAGS@ @MSAN_FLAGS@ \ -I$(top_srcdir) -I$(top_srcdir)/include -I$(top_srcdir)/lib \ -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/lib AM_LDFLAGS = \ - -export-dynamic + -export-dynamic \ + $(SAN_FLAGS) \ + # end DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -DCONFDATE=$(CONFDATE) LIBCAP = @LIBCAP@ diff --git a/configure.ac b/configure.ac index 3657152644..326794f2cc 100755 --- a/configure.ac +++ b/configure.ac @@ -189,46 +189,6 @@ CC="${CC% -std=c99}" AC_C_FLAG([-std=gnu11], [CC="$ac_cc"], [CC="$CC -std=gnu11"]) -dnl AddressSanitizer support -AC_ARG_ENABLE([address-sanitizer], AS_HELP_STRING([--enable-address-sanitizer], \ - [enabled AddressSanitizer support for detecting a wide variety of \ - memory allocation and deallocation errors]), \ - [AC_DEFINE(HAVE_ADDRESS_SANITIZER, 1, [enable AddressSanitizer]) - ASAN_FLAGS="-fsanitize=address" - SAN_CLIPPY_FLAGS="-fno-sanitize=all" - AC_SUBST([ASAN_FLAGS]) - AC_SUBST([SAN_CLIPPY_FLAGS]) - LIBS="-ldl $LIBS" - AC_TRY_COMPILE([],[const int i=0;],[AC_MSG_NOTICE([Address Sanitizer Enabled])], - [AC_MSG_ERROR([Address Sanitizer not available])]) - ]) - -dnl ThreadSanitizer support -AC_ARG_ENABLE([thread-sanitizer], AS_HELP_STRING([--enable-thread-sanitizer], \ - [enabled ThreadSanitizer support for detecting data races]), \ - [AC_DEFINE(HAVE_THREAD_SANITIZER, 1, [enable ThreadSanitizer]) - TSAN_FLAGS="-fsanitize=thread" - SAN_CLIPPY_FLAGS="-fno-sanitize=all" - AC_SUBST([TSAN_FLAGS]) - AC_SUBST([SAN_CLIPPY_FLAGS]) - LIBS="-ldl $LIBS" - AC_TRY_COMPILE([],[const int i=0;],[AC_MSG_NOTICE([Thread Sanitizer Enabled])], - [AC_MSG_ERROR([Thread Sanitizer not available])]) - ]) - -dnl MemorySanitizer support -AC_ARG_ENABLE([memory-sanitizer], AS_HELP_STRING([--enable-memory-sanitizer], \ - [enabled MemorySanitizer support for detecting uninitialized memory reads]), \ - [AC_DEFINE(HAVE_THREAD_SANITIZER, 1, [enable MemorySanitizer]) - MSAN_FLAGS="-fsanitize=memory -fPIE -pie" - SAN_CLIPPY_FLAGS="-fno-sanitize=all" - AC_SUBST([MSAN_FLAGS]) - AC_SUBST([SAN_CLIPPY_FLAGS]) - LIBS="-ldl $LIBS" - AC_TRY_COMPILE([],[const int i=0;],[AC_MSG_NOTICE([Memory Sanitizer Enabled])], - [AC_MSG_ERROR([Memory Sanitizer not available])]) - ]) - dnl if the user has specified any CFLAGS, override our settings if test "x${enable_gcov}" = "xyes"; then if test "z$orig_cflags" = "z"; then @@ -287,6 +247,30 @@ if test x"${enable_werror}" = x"yes" ; then fi AC_SUBST(WERROR) +SAN_FLAGS="" +if test "$enable_address_sanitizer" = "yes"; then + AC_C_FLAG([-fsanitize=address], [ + AC_MSG_ERROR([$CC does not support Address Sanitizer.]) + ], [ + SAN_FLAGS="$SAN_FLAGS -fsanitize=address" + ]) +fi +if test "$enable_thread_sanitizer" = "yes"; then + AC_C_FLAG([-fsanitize=thread], [ + AC_MSG_ERROR([$CC does not support Thread Sanitizer.]) + ], [ + SAN_FLAGS="$SAN_FLAGS -fsanitize=thread" + ]) +fi +if test "$enable_memory_sanitizer" = "yes"; then + AC_C_FLAG([-fsanitize=thread -fPIE -pie], [ + AC_MSG_ERROR([$CC does not support Thread Sanitizer.]) + ], [ + SAN_FLAGS="-fsanitize=memory -fPIE -pie" + ]) +fi +AC_SUBST([SAN_FLAGS]) + dnl ---------- dnl Essentials dnl ---------- @@ -453,6 +437,12 @@ AC_ARG_ENABLE([gcov], AS_HELP_STRING([--enable-gcov], [Add code coverage information])) AC_ARG_ENABLE(bfdd, AS_HELP_STRING([--disable-bfdd], [do not build bfdd])) +AC_ARG_ENABLE([address-sanitizer], + AS_HELP_STRING([--enable-address-sanitizer], [enable AddressSanitizer support for detecting a wide variety of memory allocation and deallocation errors])) +AC_ARG_ENABLE([thread-sanitizer], + AS_HELP_STRING([--enable-thread-sanitizer], [enable ThreadSanitizer support for detecting data races])) +AC_ARG_ENABLE([memory-sanitizer], + AS_HELP_STRING([--enable-memory-sanitizer], [enable MemorySanitizer support for detecting uninitialized memory reads])) AS_IF([test "${enable_clippy_only}" != "yes"], [ AC_CHECK_HEADERS(json-c/json.h) @@ -2032,9 +2022,9 @@ FRR version : ${PACKAGE_VERSION} host operating system : ${host_os} source code location : ${srcdir} compiler : ${CC} -compiler flags : ${CFLAGS} +compiler flags : ${CFLAGS} ${SAN_FLAGS} make : ${MAKE-make} -linker flags : ${LDFLAGS} ${LIBS} ${LIBCAP} ${LIBREADLINE} ${LIBM} +linker flags : ${LDFLAGS} ${SAN_FLAGS} ${LIBS} ${LIBCAP} ${LIBREADLINE} ${LIBM} state file directory : ${frr_statedir} config file directory : `eval echo \`echo ${sysconfdir}\`` example directory : `eval echo \`echo ${exampledir}\`` diff --git a/lib/subdir.am b/lib/subdir.am index 09deb9bc1b..499bb94920 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -254,9 +254,10 @@ lib_grammar_sandbox_SOURCES = \ lib_grammar_sandbox_LDADD = \ lib/libfrr.la -lib_clippy_CPPFLAGS = $(AM_CPPFLAGS) -D_GNU_SOURCE -DBUILDING_CLIPPY @SAN_CLIPPY_FLAGS@ -lib_clippy_CFLAGS = $(PYTHON_CFLAGS) @SAN_CLIPPY_FLAGS@ +lib_clippy_CPPFLAGS = $(AM_CPPFLAGS) -D_GNU_SOURCE -DBUILDING_CLIPPY +lib_clippy_CFLAGS = $(PYTHON_CFLAGS) lib_clippy_LDADD = $(PYTHON_LIBS) +lib_clippy_LDFLAGS = -export-dynamic lib_clippy_SOURCES = \ lib/clippy.c \ lib/command_graph.c \ diff --git a/tests/subdir.am b/tests/subdir.am index cffc316927..739a0e86fe 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -108,7 +108,7 @@ TESTS_CPPFLAGS = $(AM_CPPFLAGS) \ -I$(top_srcdir)/tests/helpers/c \ -I$(top_builddir)/tests/helpers/c \ # end -TESTS_CFLAGS = @ASAN_FLAGS@ @TSAN_FLAGS@ @MSAN_FLAGS@ +TESTS_CFLAGS = $(SAN_FLAGS) # note no -Werror ALL_TESTS_LDADD = lib/libfrr.la @LIBCAP@ From b45ac5f5c61f98b0aacf65b456547142193ae1fc Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 28 Aug 2018 10:57:13 +0200 Subject: [PATCH 63/84] *: fix config.h/zebra.h include order config.h (or, transitively, zebra.h) must be the first include file listed for autoconf things like _GNU_SOURCE and _POSIX_C_SOURCE to work correctly. Signed-off-by: David Lamparter --- babeld/babel_filter.c | 4 ++++ babeld/kernel.c | 4 ++++ babeld/neighbour.c | 4 ++++ babeld/net.c | 4 ++++ babeld/resend.c | 4 ++++ babeld/source.c | 4 ++++ babeld/util.c | 4 ++++ bgpd/bgp_flowspec.c | 4 ++-- bgpd/rfapi/rfapi.c | 3 --- bgpd/rfapi/rfapi_ap.c | 2 -- bgpd/rfapi/rfapi_descriptor_rfp_utils.c | 3 --- bgpd/rfapi/rfapi_import.c | 2 -- bgpd/rfapi/rfapi_monitor.c | 2 -- bgpd/rfapi/rfapi_rib.c | 2 -- bgpd/rfapi/rfapi_vty.c | 3 --- bgpd/rfp-example/librfp/rfp_example.c | 4 ++++ bgpd/rfp-example/rfptest/rfptest.c | 3 +++ eigrpd/eigrp_fsm.c | 2 +- ldpd/pfkey.c | 4 ++++ lib/csv.c | 5 +++++ lib/ferr.c | 4 ++++ lib/frrstr.c | 4 ++++ lib/grammar_sandbox.c | 4 ++++ lib/grammar_sandbox_main.c | 4 ++++ lib/hook.c | 4 ++++ lib/lib_errors.c | 4 ++++ lib/openbsd-tree.c | 4 ++++ lib/ptm_lib.c | 5 +++++ lib/strlcat.c | 6 ++++-- lib/strlcpy.c | 6 ++++-- nhrpd/linux.c | 4 ++++ nhrpd/netlink_arp.c | 4 ++++ nhrpd/netlink_gre.c | 4 ++++ nhrpd/nhrp_event.c | 4 ++++ nhrpd/nhrp_interface.c | 4 ++++ nhrpd/nhrp_main.c | 4 ++++ nhrpd/nhrp_packet.c | 4 ++++ nhrpd/nhrp_peer.c | 4 ++++ nhrpd/nhrp_route.c | 4 ++++ nhrpd/nhrp_shortcut.c | 4 ++++ nhrpd/resolver.c | 4 ++++ nhrpd/vici.c | 4 ++++ nhrpd/zbuf.c | 5 ++++- nhrpd/znl.c | 4 ++++ ospfd/ospf_sr.c | 4 ++++ pimd/mtracebis.c | 4 ++++ pimd/mtracebis_netlink.c | 4 ++++ pimd/mtracebis_routeget.c | 4 ++++ pimd/pim_igmp_stats.c | 4 ++++ qpb/qpb_allocator.c | 4 ++++ tests/isisd/test_fuzz_isis_tlv.c | 4 ++++ tests/test_lblmgr.c | 4 ++++ tools/permutations.c | 4 ++++ tools/start-stop-daemon.c | 5 +++++ 54 files changed, 185 insertions(+), 25 deletions(-) diff --git a/babeld/babel_filter.c b/babeld/babel_filter.c index ff5cca42a0..31778901a6 100644 --- a/babeld/babel_filter.c +++ b/babeld/babel_filter.c @@ -20,6 +20,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "babel_filter.h" #include "vty.h" #include "filter.h" diff --git a/babeld/kernel.c b/babeld/kernel.c index ba2b58131c..d4c962af3b 100644 --- a/babeld/kernel.c +++ b/babeld/kernel.c @@ -21,6 +21,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/babeld/neighbour.c b/babeld/neighbour.c index c1592fb18a..512b60e29a 100644 --- a/babeld/neighbour.c +++ b/babeld/neighbour.c @@ -20,6 +20,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/babeld/net.c b/babeld/net.c index ad9a6bad92..d1f6a44142 100644 --- a/babeld/net.c +++ b/babeld/net.c @@ -20,6 +20,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/babeld/resend.c b/babeld/resend.c index 1f21977442..8949075f67 100644 --- a/babeld/resend.c +++ b/babeld/resend.c @@ -20,6 +20,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/babeld/source.c b/babeld/source.c index d6dd848952..75bca06206 100644 --- a/babeld/source.c +++ b/babeld/source.c @@ -20,6 +20,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/babeld/util.c b/babeld/util.c index 4a3ecace0c..880cda2fce 100644 --- a/babeld/util.c +++ b/babeld/util.c @@ -21,6 +21,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index e29508bf36..c604135bfd 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -18,9 +18,9 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "math.h" - #include +#include + #include "prefix.h" #include "lib_errors.h" diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index a427608f67..51504bb0ad 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -18,9 +18,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include - #include "lib/zebra.h" #include "lib/prefix.h" #include "lib/agg_table.h" diff --git a/bgpd/rfapi/rfapi_ap.c b/bgpd/rfapi/rfapi_ap.c index 691e1e4ec8..c5fda15d33 100644 --- a/bgpd/rfapi/rfapi_ap.c +++ b/bgpd/rfapi/rfapi_ap.c @@ -18,8 +18,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include - #include "lib/zebra.h" #include "lib/prefix.h" #include "lib/agg_table.h" diff --git a/bgpd/rfapi/rfapi_descriptor_rfp_utils.c b/bgpd/rfapi/rfapi_descriptor_rfp_utils.c index 3217d34e77..ce5acf002c 100644 --- a/bgpd/rfapi/rfapi_descriptor_rfp_utils.c +++ b/bgpd/rfapi/rfapi_descriptor_rfp_utils.c @@ -18,9 +18,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include - #include "lib/zebra.h" #include "lib/prefix.h" #include "lib/table.h" diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index 4c506da686..4601718f12 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -23,8 +23,6 @@ * Purpose: Handle import of routes from BGP to RFAPI */ -#include - #include "lib/zebra.h" #include "lib/prefix.h" #include "lib/agg_table.h" diff --git a/bgpd/rfapi/rfapi_monitor.c b/bgpd/rfapi/rfapi_monitor.c index 59387240fa..f18c6bfe12 100644 --- a/bgpd/rfapi/rfapi_monitor.c +++ b/bgpd/rfapi/rfapi_monitor.c @@ -24,8 +24,6 @@ /* TBD remove unneeded includes */ -#include - #include "lib/zebra.h" #include "lib/prefix.h" #include "lib/agg_table.h" diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index 3ac217ff89..008da30118 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -23,8 +23,6 @@ * Purpose: maintain per-nve ribs and generate change lists */ -#include - #include "lib/zebra.h" #include "lib/prefix.h" #include "lib/agg_table.h" diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index b2767da8b2..cd751319eb 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -18,9 +18,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include - #include "lib/zebra.h" #include "lib/prefix.h" #include "lib/agg_table.h" diff --git a/bgpd/rfp-example/librfp/rfp_example.c b/bgpd/rfp-example/librfp/rfp_example.c index 75e57a029e..af3092232c 100644 --- a/bgpd/rfp-example/librfp/rfp_example.c +++ b/bgpd/rfp-example/librfp/rfp_example.c @@ -18,6 +18,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + /* stub rfp */ #include "rfp_internal.h" #include "bgpd/rfapi/rfapi.h" diff --git a/bgpd/rfp-example/rfptest/rfptest.c b/bgpd/rfp-example/rfptest/rfptest.c index 53e1c33cfb..48df6c0cc7 100644 --- a/bgpd/rfp-example/rfptest/rfptest.c +++ b/bgpd/rfp-example/rfptest/rfptest.c @@ -18,6 +18,9 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* dummy test program */ #include diff --git a/eigrpd/eigrp_fsm.c b/eigrpd/eigrp_fsm.c index eeefc51968..d291cab4c0 100644 --- a/eigrpd/eigrp_fsm.c +++ b/eigrpd/eigrp_fsm.c @@ -67,8 +67,8 @@ * 7- state not changed, usually by receiving not last reply */ -#include #include +#include #include "prefix.h" #include "table.h" diff --git a/ldpd/pfkey.c b/ldpd/pfkey.c index 906737217c..a719d0cbb7 100644 --- a/ldpd/pfkey.c +++ b/ldpd/pfkey.c @@ -17,6 +17,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #ifdef __OpenBSD__ #include #include diff --git a/lib/csv.c b/lib/csv.c index ce84783aa6..10e62731d1 100644 --- a/lib/csv.c +++ b/lib/csv.c @@ -17,6 +17,11 @@ * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/lib/ferr.c b/lib/ferr.c index fb5719d4de..afef196cec 100644 --- a/lib/ferr.c +++ b/lib/ferr.c @@ -14,6 +14,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/lib/frrstr.c b/lib/frrstr.c index 715e67b868..85d968182b 100644 --- a/lib/frrstr.c +++ b/lib/frrstr.c @@ -18,6 +18,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index 51e7a3987e..ef03e85217 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -23,6 +23,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "command.h" #include "memory_vty.h" #include "graph.h" diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index 264c7c48f0..c9c942f9bf 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -23,6 +23,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "command.h" #include "memory_vty.h" diff --git a/lib/hook.c b/lib/hook.c index 935064f4d2..4fe305f282 100644 --- a/lib/hook.c +++ b/lib/hook.c @@ -20,6 +20,10 @@ * DEALINGS IN THE SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "memory.h" #include "hook.h" diff --git a/lib/lib_errors.c b/lib/lib_errors.c index 332a5b1d45..2ddc571e68 100644 --- a/lib/lib_errors.c +++ b/lib/lib_errors.c @@ -18,6 +18,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "lib_errors.h" /* clang-format off */ diff --git a/lib/openbsd-tree.c b/lib/openbsd-tree.c index 35bfce3a89..e8d13339b6 100644 --- a/lib/openbsd-tree.c +++ b/lib/openbsd-tree.c @@ -41,6 +41,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include diff --git a/lib/ptm_lib.c b/lib/ptm_lib.c index 69fd61e2a0..7f868beda4 100644 --- a/lib/ptm_lib.c +++ b/lib/ptm_lib.c @@ -17,6 +17,11 @@ * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/lib/strlcat.c b/lib/strlcat.c index be211f82a8..39773d9ac8 100644 --- a/lib/strlcat.c +++ b/lib/strlcat.c @@ -20,11 +20,13 @@ /* adapted for Quagga from glibc patch submission originally from * Florian Weimer , 2016-05-18 */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include -#include "config.h" - #ifndef HAVE_STRLCAT #undef strlcat diff --git a/lib/strlcpy.c b/lib/strlcpy.c index b0c33ca7f4..71ee9f1a54 100644 --- a/lib/strlcpy.c +++ b/lib/strlcpy.c @@ -20,9 +20,11 @@ /* adapted for Quagga from glibc patch submission originally from * Florian Weimer , 2016-05-18 */ -#include - +#ifdef HAVE_CONFIG_H #include "config.h" +#endif + +#include #ifndef HAVE_STRLCPY #undef strlcpy diff --git a/nhrpd/linux.c b/nhrpd/linux.c index 46a327b59f..85e941e7ba 100644 --- a/nhrpd/linux.c +++ b/nhrpd/linux.c @@ -7,6 +7,10 @@ * (at your option) any later version. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/nhrpd/netlink_arp.c b/nhrpd/netlink_arp.c index af78b3d9ee..4c6827cb3d 100644 --- a/nhrpd/netlink_arp.c +++ b/nhrpd/netlink_arp.c @@ -7,6 +7,10 @@ * (at your option) any later version. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/nhrpd/netlink_gre.c b/nhrpd/netlink_gre.c index 75ecaa70c1..3fdfa9c313 100644 --- a/nhrpd/netlink_gre.c +++ b/nhrpd/netlink_gre.c @@ -7,6 +7,10 @@ * (at your option) any later version. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/nhrpd/nhrp_event.c b/nhrpd/nhrp_event.c index 7ca9731765..9301c2d515 100644 --- a/nhrpd/nhrp_event.c +++ b/nhrpd/nhrp_event.c @@ -7,6 +7,10 @@ * (at your option) any later version. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c index 3a42712748..ccca100db5 100644 --- a/nhrpd/nhrp_interface.c +++ b/nhrpd/nhrp_interface.c @@ -7,6 +7,10 @@ * (at your option) any later version. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include "zebra.h" #include "linklist.h" diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c index 737e70103e..f99e566399 100644 --- a/nhrpd/nhrp_main.c +++ b/nhrpd/nhrp_main.c @@ -7,6 +7,10 @@ * (at your option) any later version. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include "zebra.h" diff --git a/nhrpd/nhrp_packet.c b/nhrpd/nhrp_packet.c index e62ee1ef72..a983aa71bc 100644 --- a/nhrpd/nhrp_packet.c +++ b/nhrpd/nhrp_packet.c @@ -7,6 +7,10 @@ * (at your option) any later version. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include "nhrpd.h" #include "zbuf.h" diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c index 44271d68ac..e051830f85 100644 --- a/nhrpd/nhrp_peer.c +++ b/nhrpd/nhrp_peer.c @@ -7,6 +7,10 @@ * (at your option) any later version. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include "zebra.h" diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index 044529a5ca..e7b187f3b6 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -7,6 +7,10 @@ * (at your option) any later version. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "nhrpd.h" #include "table.h" #include "memory.h" diff --git a/nhrpd/nhrp_shortcut.c b/nhrpd/nhrp_shortcut.c index 9ed2517069..84053b4b5d 100644 --- a/nhrpd/nhrp_shortcut.c +++ b/nhrpd/nhrp_shortcut.c @@ -7,6 +7,10 @@ * (at your option) any later version. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "nhrpd.h" #include "table.h" #include "memory.h" diff --git a/nhrpd/resolver.c b/nhrpd/resolver.c index dfa5dc3df0..f017d974df 100644 --- a/nhrpd/resolver.c +++ b/nhrpd/resolver.c @@ -7,6 +7,10 @@ * (at your option) any later version. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include diff --git a/nhrpd/vici.c b/nhrpd/vici.c index 7cd703414a..c1a99685f3 100644 --- a/nhrpd/vici.c +++ b/nhrpd/vici.c @@ -7,6 +7,10 @@ * (at your option) any later version. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/nhrpd/zbuf.c b/nhrpd/zbuf.c index 6e7cad8aec..c662295083 100644 --- a/nhrpd/zbuf.c +++ b/nhrpd/zbuf.c @@ -7,7 +7,10 @@ * (at your option) any later version. */ -#define _GNU_SOURCE +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/nhrpd/znl.c b/nhrpd/znl.c index 01b2f433af..6030987a1b 100644 --- a/nhrpd/znl.c +++ b/nhrpd/znl.c @@ -7,6 +7,10 @@ * (at your option) any later version. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c index 62eca156f5..220de3234d 100644 --- a/ospfd/ospf_sr.c +++ b/ospfd/ospf_sr.c @@ -24,6 +24,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/pimd/mtracebis.c b/pimd/mtracebis.c index c0d95aeed9..65c495eff0 100644 --- a/pimd/mtracebis.c +++ b/pimd/mtracebis.c @@ -17,6 +17,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #ifdef __linux__ #include "pim_igmp_mtrace.h" diff --git a/pimd/mtracebis_netlink.c b/pimd/mtracebis_netlink.c index b4bf6bada3..30ee8f24ab 100644 --- a/pimd/mtracebis_netlink.c +++ b/pimd/mtracebis_netlink.c @@ -10,6 +10,10 @@ * */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #ifdef __linux__ #include diff --git a/pimd/mtracebis_routeget.c b/pimd/mtracebis_routeget.c index 8c1cd8d963..8d974403ac 100644 --- a/pimd/mtracebis_routeget.c +++ b/pimd/mtracebis_routeget.c @@ -17,6 +17,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #ifdef __linux__ #include diff --git a/pimd/pim_igmp_stats.c b/pimd/pim_igmp_stats.c index 428816e1f0..40851a4529 100644 --- a/pimd/pim_igmp_stats.c +++ b/pimd/pim_igmp_stats.c @@ -17,6 +17,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "pim_igmp_stats.h" void igmp_stats_init(struct igmp_stats *stats) diff --git a/qpb/qpb_allocator.c b/qpb/qpb_allocator.c index 7e5ba5b0ce..aca611ba19 100644 --- a/qpb/qpb_allocator.c +++ b/qpb/qpb_allocator.c @@ -22,6 +22,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "linear_allocator.h" #include "qpb_allocator.h" diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c index 3a56f83f0a..b75c1002d4 100644 --- a/tests/isisd/test_fuzz_isis_tlv.c +++ b/tests/isisd/test_fuzz_isis_tlv.c @@ -1,3 +1,7 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "test_fuzz_isis_tlv_tests.h" #include diff --git a/tests/test_lblmgr.c b/tests/test_lblmgr.c index c751c0b12d..652d7618a8 100644 --- a/tests/test_lblmgr.c +++ b/tests/test_lblmgr.c @@ -21,6 +21,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "lib/stream.h" #include "lib/zclient.h" diff --git a/tools/permutations.c b/tools/permutations.c index 80441753e7..c5109dc3e8 100644 --- a/tools/permutations.c +++ b/tools/permutations.c @@ -20,6 +20,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "command.h" #include "graph.h" #include "vector.h" diff --git a/tools/start-stop-daemon.c b/tools/start-stop-daemon.c index de58e0a20e..e1cce85557 100644 --- a/tools/start-stop-daemon.c +++ b/tools/start-stop-daemon.c @@ -25,12 +25,17 @@ * the whole automake/config.h dance. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #ifdef HAVE_LXC #define _GNU_SOURCE #include #endif /* HAVE_LXC */ #include +#undef VERSION #define VERSION "1.9.18" #define MIN_POLL_INTERVAL 20000 /*us*/ From e7c25325ccf55e0dc4b9a264fc3cf2f91e523d7d Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 18 Aug 2018 05:29:10 +0200 Subject: [PATCH 64/84] *: cleanup .gitignore files Signed-off-by: David Lamparter --- .gitignore | 136 ++++++++++++++++++++--------------------- bfdd/.gitignore | 1 - bgpd/.gitignore | 15 ----- doc/.gitignore | 6 -- doc/mpls/.gitignore | 5 -- eigrpd/.gitignore | 16 ----- fpm/.gitignore | 15 ----- init/.gitignore | 6 -- isisd/.gitignore | 13 ---- ldpd/.gitignore | 15 ----- lib/.gitignore | 39 ++++-------- m4/.gitignore | 12 ++-- nhrpd/.gitignore | 1 - ospf6d/.gitignore | 16 ----- ospfclient/.gitignore | 15 ----- ospfd/.gitignore | 15 ----- pbrd/.gitignore | 14 ----- pimd/.gitignore | 14 ----- pkgsrc/.gitignore | 7 --- ports/.gitignore | 6 -- ports/files/.gitignore | 6 -- ports/pkg/.gitignore | 6 -- qpb/.gitignore | 15 ----- redhat/.gitignore | 8 --- ripd/.gitignore | 15 ----- ripngd/.gitignore | 15 ----- sharpd/.gitignore | 15 ----- snapcraft/.gitignore | 1 - solaris/.gitignore | 3 - tests/.gitignore | 18 ------ tools/.gitignore | 8 --- vtysh/.gitignore | 13 ---- watchfrr/.gitignore | 15 ----- zebra/.gitignore | 12 ---- 34 files changed, 87 insertions(+), 430 deletions(-) delete mode 100644 doc/mpls/.gitignore delete mode 100644 fpm/.gitignore delete mode 100644 init/.gitignore delete mode 100644 ports/.gitignore delete mode 100644 ports/files/.gitignore delete mode 100644 ports/pkg/.gitignore delete mode 100644 qpb/.gitignore diff --git a/.gitignore b/.gitignore index c5fd0ced9b..8c62f05539 100644 --- a/.gitignore +++ b/.gitignore @@ -1,87 +1,87 @@ -compile -config.log -config.h -config.cache -config.status -config.guess -config.sub -ltmain.sh -stamp-h -stamp-h[0-9]* +### autoconf/automake root stuff + +/compile +/config.log +/config.h +/config.cache +/config.status +/config.guess +/config.sub +/ltmain.sh +/stamp-h +/stamp-h[0-9]* *-stamp -Makefile -INSTALL +/INSTALL +/depcomp +/missing +/install-sh +/mkinstalldirs +/ylwrap +/autom4te*.cache +/configure.lineno +/configure +/config.h.in +/confdefs.h +/conftest +/conftest.err +/aclocal.m4 +/libtool + +/Makefile +/Makefile.in + +### autoconf/automake subdir stuff + .deps -depcomp -missing -install-sh -mkinstalldirs -ylwrap -autom4te*.cache -configure.lineno -configure -config.h.in -confdefs.h -conftest -conftest.err -aclocal.m4 -Makefile.in -*.tar.gz -*.tar.gz.asc -*.tar.?z -.nfs* -libtool .libs -.arch-inventory -.arch-ids -{arch} -build -.msg -.rebase-* -*~ + +### build outputs + *.o +*.lo +*.a +*.la +*.so *.loT -m4/*.m4 -!m4/ax_sys_weak_alias.m4 -!m4/ax_compare_version.m4 -!m4/ax_prog_perl_modules.m4 -!m4/pkg.m4 -debian/autoreconf.after -debian/autoreconf.before -debian/files -debian/frr-dbg.debhelper.log -debian/frr-dbg.substvars -debian/frr-dbg/ -debian/frr-doc.debhelper.log -debian/frr-doc.substvars -debian/frr-doc/ -debian/frr.debhelper.log -debian/frr.postinst.debhelper -debian/frr.postrm.debhelper -debian/frr.prerm.debhelper -debian/frr.substvars -debian/frr/ -debian/tmp/ +*.pb.h +*.pb-c.h +*.pb-c.c +*_clippy.c + +### dist + +*.tar.?z +*.tar.?z.asc +*.tar.asc *.deb *.ddeb *.dsc *.changes -*.pyc + +### other garbage + +.nfs* +.arch-inventory +.arch-ids +{arch} +build +.cache +.msg +.rebase-* +*~ +*.bak *.swp +*.pyc +__pycache__ +*.patch +*.diff cscope.* -*.pb.h -*.pb-c.h -*.pb-c.c TAGS tags GTAGS GSYMS GRTAGS GPATH -*.la -*.lo compile_commands.json .dirstamp - -# clippy generated source -*_clippy.c +refix diff --git a/bfdd/.gitignore b/bfdd/.gitignore index e554d1b33f..2b020911fb 100644 --- a/bfdd/.gitignore +++ b/bfdd/.gitignore @@ -1,3 +1,2 @@ # ignore binary files -*.a bfdd diff --git a/bgpd/.gitignore b/bgpd/.gitignore index 7e2e6293a5..2e77195b90 100644 --- a/bgpd/.gitignore +++ b/bgpd/.gitignore @@ -1,18 +1,3 @@ -!Makefile -Makefile.in -*.o bgpd bgp_btoa bgpd.conf -tags -TAGS -.deps -.nfs* -*.lo -*.la -*.a -*.libs -.arch-inventory -.arch-ids -*~ -*.loT diff --git a/doc/.gitignore b/doc/.gitignore index d99a6a5b2d..fa2b508321 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -1,4 +1,3 @@ -!Makefile mdate-sh draft-zebra-00.txt *.pdf @@ -6,7 +5,6 @@ draft-zebra-00.txt frr.ps frr.dvi stamp-vti -.nfs* *.aux *.cp *.cps @@ -20,8 +18,4 @@ stamp-vti *.toc *.tp *.vr -.arch-inventory -.arch-ids -*~ -*.loT refix diff --git a/doc/mpls/.gitignore b/doc/mpls/.gitignore deleted file mode 100644 index b0a4a4619f..0000000000 --- a/doc/mpls/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.arch-ids -.arch-inventory -*~ -*.loT - diff --git a/eigrpd/.gitignore b/eigrpd/.gitignore index 5b72399e72..0303c6f0d4 100644 --- a/eigrpd/.gitignore +++ b/eigrpd/.gitignore @@ -1,18 +1,2 @@ -!Makefile -Makefile.in -*.o -*.a eigrpd eigrpd.conf -tags -TAGS -.deps -.nfs* -*.lo -*.la -*.libs -.arch-inventory -.arch-ids -*~ -*.loT - diff --git a/fpm/.gitignore b/fpm/.gitignore deleted file mode 100644 index 17e90443e9..0000000000 --- a/fpm/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -!Makefile -Makefile.in -*.o -tags -TAGS -.deps -.nfs* -*.lo -*.la -*.a -*.libs -.arch-inventory -.arch-ids -*~ -*.loT diff --git a/init/.gitignore b/init/.gitignore deleted file mode 100644 index 30b4bc99ec..0000000000 --- a/init/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -Makefile -Makefile.in -.nfs* -*~ -*.loT - diff --git a/isisd/.gitignore b/isisd/.gitignore index 865cc38253..b6184ca0f5 100644 --- a/isisd/.gitignore +++ b/isisd/.gitignore @@ -1,16 +1,3 @@ -!Makefile -Makefile.in -*.o isisd fabricd -.deps isisd.conf -.nfs* -*.lo -*.la -*.libs -.arch-inventory -.arch-ids -*~ -*.loT -*.a diff --git a/ldpd/.gitignore b/ldpd/.gitignore index a2f4b51698..ec8a5c4086 100644 --- a/ldpd/.gitignore +++ b/ldpd/.gitignore @@ -1,17 +1,2 @@ -!Makefile -Makefile.in -*.o ldpd ldpd.conf -tags -TAGS -.deps -.nfs* -*.lo -*.la -*.a -*.libs -.arch-inventory -.arch-ids -*~ -*.loT diff --git a/lib/.gitignore b/lib/.gitignore index 072146dbd5..6176b30f8d 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -1,26 +1,13 @@ -!Makefile -Makefile.in -*.o -*.lo -*.la -version.c -version.h -gitversion.h -gitversion.h.tmp -.deps -.nfs* -.libs -.arch-inventory -.arch-ids -*~ -*.loT -route_types.h -memtypes.h -command_lex.c -command_lex.h -command_parse.c -command_parse.h -refix -grammar_sandbox -clippy -defun_lex.c +/version.c +/version.h +/gitversion.h +/gitversion.h.tmp +/route_types.h +/memtypes.h +/command_lex.c +/command_lex.h +/command_parse.c +/command_parse.h +/grammar_sandbox +/clippy +/defun_lex.c diff --git a/m4/.gitignore b/m4/.gitignore index 798188b0b9..357e65588d 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -1,8 +1,8 @@ -Makefile -Makefile.in -.arch-inventory -.arch-ids -*~ -*.loT +*.m4 + +!ax_compare_version.m4 +!ax_prog_perl_modules.m4 !ax_pthread.m4 !ax_sys_weak_alias.m4 +!ax_sys_weak_alias.m4 +!pkg.m4 diff --git a/nhrpd/.gitignore b/nhrpd/.gitignore index 3f47381278..3d4d56d589 100644 --- a/nhrpd/.gitignore +++ b/nhrpd/.gitignore @@ -1,2 +1 @@ -!Makefile nhrpd diff --git a/ospf6d/.gitignore b/ospf6d/.gitignore index 744af2d5ce..e398b1ca58 100644 --- a/ospf6d/.gitignore +++ b/ospf6d/.gitignore @@ -1,18 +1,2 @@ -!Makefile -Makefile.in -*.o -*.patch ospf6d ospf6d.conf -tags -TAGS -.deps -.nfs* -*.lo -*.la -*.libs -.arch-inventory -.arch-ids -*~ -*.loT -*.a diff --git a/ospfclient/.gitignore b/ospfclient/.gitignore index 1740b04fbc..9be70451f0 100644 --- a/ospfclient/.gitignore +++ b/ospfclient/.gitignore @@ -1,16 +1 @@ -!Makefile -Makefile.in -*.o ospfclient -tags -TAGS -.deps -.nfs* -*.lo -*.la -*.libs -.arch-inventory -.arch-ids -*~ -*.loT -refix diff --git a/ospfd/.gitignore b/ospfd/.gitignore index 752c875a62..fc65db33ee 100644 --- a/ospfd/.gitignore +++ b/ospfd/.gitignore @@ -1,17 +1,2 @@ -!Makefile -Makefile.in -*.o ospfd ospfd.conf -tags -TAGS -.deps -.nfs* -*.lo -*.la -*.libs -.arch-inventory -.arch-ids -*~ -*.loT -*.a diff --git a/pbrd/.gitignore b/pbrd/.gitignore index ff95d88527..86622ea174 100644 --- a/pbrd/.gitignore +++ b/pbrd/.gitignore @@ -1,15 +1 @@ -!Makefile -Makefile.in -libpbr.a pbrd -tags -TAGS -.deps -*.o -*.lo -*.la -*.libs -.arch-inventory -.arch-ids -*~ -*.loT diff --git a/pimd/.gitignore b/pimd/.gitignore index 1f56cfaecd..b1780df758 100644 --- a/pimd/.gitignore +++ b/pimd/.gitignore @@ -1,17 +1,3 @@ -!Makefile -Makefile.in -libpim.a pimd mtracebis test_igmpv3_join -tags -TAGS -.deps -*.o -*.lo -*.la -*.libs -.arch-inventory -.arch-ids -*~ -*.loT diff --git a/pkgsrc/.gitignore b/pkgsrc/.gitignore index 63e9a66d8f..c97f963b3d 100644 --- a/pkgsrc/.gitignore +++ b/pkgsrc/.gitignore @@ -1,8 +1 @@ -Makefile -Makefile.in *.sh -.arch-inventory -.arch-ids -*~ -*.loT - diff --git a/ports/.gitignore b/ports/.gitignore deleted file mode 100644 index dd5bf7c67c..0000000000 --- a/ports/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.arch-inventory -.arch-ids - -*~ -*.loT - diff --git a/ports/files/.gitignore b/ports/files/.gitignore deleted file mode 100644 index dd5bf7c67c..0000000000 --- a/ports/files/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.arch-inventory -.arch-ids - -*~ -*.loT - diff --git a/ports/pkg/.gitignore b/ports/pkg/.gitignore deleted file mode 100644 index dd5bf7c67c..0000000000 --- a/ports/pkg/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.arch-inventory -.arch-ids - -*~ -*.loT - diff --git a/qpb/.gitignore b/qpb/.gitignore deleted file mode 100644 index 17e90443e9..0000000000 --- a/qpb/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -!Makefile -Makefile.in -*.o -tags -TAGS -.deps -.nfs* -*.lo -*.la -*.a -*.libs -.arch-inventory -.arch-ids -*~ -*.loT diff --git a/redhat/.gitignore b/redhat/.gitignore index a38f1c06e3..15804cea0f 100644 --- a/redhat/.gitignore +++ b/redhat/.gitignore @@ -1,10 +1,2 @@ zebra.spec frr.spec -Makefile -Makefile.in -.nfs* -.arch-inventory -.arch-ids -*~ -*.loT - diff --git a/ripd/.gitignore b/ripd/.gitignore index 177250ca61..f149501d61 100644 --- a/ripd/.gitignore +++ b/ripd/.gitignore @@ -1,17 +1,2 @@ -!Makefile -Makefile.in -*.o ripd ripd.conf -tags -TAGS -.deps -.nfs* -*.lo -*.la -*.libs -.arch-inventory -.arch-ids -*~ -*.loT -*.a diff --git a/ripngd/.gitignore b/ripngd/.gitignore index 213384d139..e6a8ee6be2 100644 --- a/ripngd/.gitignore +++ b/ripngd/.gitignore @@ -1,17 +1,2 @@ -!Makefile -Makefile.in -*.o ripngd ripngd.conf -tags -TAGS -.deps -.nfs* -*.lo -*.la -*.libs -.arch-inventory -.arch-ids -*~ -*.loT -*.a diff --git a/sharpd/.gitignore b/sharpd/.gitignore index cc33cfc188..91b9f2e902 100644 --- a/sharpd/.gitignore +++ b/sharpd/.gitignore @@ -1,17 +1,2 @@ -Makefile -Makefile.in -*.o -tags -TAGS -.deps -.nfs* -*.lo -*.la -*.a -*.libs -.arch-inventory -.arch-ids -*~ -*.loT sharpd sharpd.conf diff --git a/snapcraft/.gitignore b/snapcraft/.gitignore index ac7860290b..a4796fd730 100644 --- a/snapcraft/.gitignore +++ b/snapcraft/.gitignore @@ -3,4 +3,3 @@ parts prime stage frr*.snap -!*/Makefile diff --git a/solaris/.gitignore b/solaris/.gitignore index a249b61996..3f1a0385a4 100644 --- a/solaris/.gitignore +++ b/solaris/.gitignore @@ -17,6 +17,3 @@ depend.smf frr.init *.pkg *.pkg.gz -*~ -*.loT -*.a diff --git a/tests/.gitignore b/tests/.gitignore index 5d414ef3d7..37cd245de0 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,24 +1,6 @@ -!Makefile -Makefile.in -*.o -tags -TAGS -.cache -.deps -.nfs* -*~ -*.loT -*.lo -*.la -*.libs -*.bak *.log *.sum *.xml -*.pyc -.arch-inventory -.arch-ids -__pycache__ .pytest_cache /bgpd/test_aspath /bgpd/test_bgp_table diff --git a/tools/.gitignore b/tools/.gitignore index 7bf3be9dbf..d400dfe8fa 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -1,10 +1,2 @@ -!Makefile -.arch-inventory -.arch-ids - -*~ -*.loT -.libs -*.o /permutations /ssd diff --git a/vtysh/.gitignore b/vtysh/.gitignore index e63d1e2dee..c1a39b8a12 100644 --- a/vtysh/.gitignore +++ b/vtysh/.gitignore @@ -1,16 +1,3 @@ -!Makefile -Makefile.in -*.o vtysh -tags -TAGS -.deps vtysh_cmd.c -.nfs* extract.pl -.libs -.arch-inventory -.arch-ids -*~ -*.loT - diff --git a/watchfrr/.gitignore b/watchfrr/.gitignore index b8c020c04c..b3f5a6cd91 100644 --- a/watchfrr/.gitignore +++ b/watchfrr/.gitignore @@ -1,16 +1 @@ -!Makefile -Makefile.in -*.o watchfrr -tags -TAGS -.deps -.nfs* -*.lo -*.la -*.libs -.arch-inventory -.arch-ids -*~ -*.loT - diff --git a/zebra/.gitignore b/zebra/.gitignore index 4a06756a2d..41a86e7d75 100644 --- a/zebra/.gitignore +++ b/zebra/.gitignore @@ -1,15 +1,3 @@ -!Makefile -Makefile.in -*.o zebra zebra.conf client -tags -TAGS -.deps -.nfs* -.libs -.arch-inventory -.arch-ids -*~ -*.loT From ae9eebcaeb68cae4f2e4ae6cc689b505c9814591 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 28 Aug 2018 10:59:02 +0200 Subject: [PATCH 65/84] *: fix some solaris warnings Signed-off-by: David Lamparter --- bfdd/ptm_adapter.c | 2 +- lib/if.c | 2 +- python/clidef.py | 2 +- tools/start-stop-daemon.c | 30 +++++++++++++++--------------- zebra/if_ioctl_solaris.c | 6 ++---- zebra/rtread_getmsg.c | 1 + 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c index 4287891621..a5fae3383c 100644 --- a/bfdd/ptm_adapter.c +++ b/bfdd/ptm_adapter.c @@ -289,7 +289,7 @@ static int _ptm_msg_read(struct stream *msg, int command, { uint32_t pid; uint8_t ttl __attribute__((unused)); - uint8_t ifnamelen; + size_t ifnamelen; /* * Register/Deregister/Update Message format: diff --git a/lib/if.c b/lib/if.c index 2bf0c6e6b5..a03c9da6f9 100644 --- a/lib/if.c +++ b/lib/if.c @@ -619,7 +619,7 @@ DEFUN (no_interface_desc, * if not: * - no idea, just get the name in its entirety. */ -static struct interface *if_sunwzebra_get(char *name, vrf_id_t vrf_id) +static struct interface *if_sunwzebra_get(const char *name, vrf_id_t vrf_id) { struct interface *ifp; char *cp; diff --git a/python/clidef.py b/python/clidef.py index 4134f4c94e..a140ce3d54 100644 --- a/python/clidef.py +++ b/python/clidef.py @@ -96,7 +96,7 @@ class IP4Handler(IPBase): code = Template('_fail = !inet_aton(argv[_i]->arg, &$varname);') class IP6Handler(IPBase): argtype = 'struct in6_addr' - decl = Template('struct in6_addr $varname = IN6ADDR_ANY_INIT;') + decl = Template('struct in6_addr $varname = {};') code = Template('_fail = !inet_pton(AF_INET6, argv[_i]->arg, &$varname);') class IPGenHandler(IPBase): argtype = 'const union sockunion *' diff --git a/tools/start-stop-daemon.c b/tools/start-stop-daemon.c index e1cce85557..cc0315c130 100644 --- a/tools/start-stop-daemon.c +++ b/tools/start-stop-daemon.c @@ -607,7 +607,7 @@ static int pid_is_exec(pid_t pid, const struct stat *esb) struct stat sb; char buf[32]; - sprintf(buf, "/proc/%d/exe", pid); + sprintf(buf, "/proc/%ld/exe", (long)pid); if (stat(buf, &sb) != 0) return 0; return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino); @@ -619,7 +619,7 @@ static int pid_is_user(pid_t pid, uid_t uid) struct stat sb; char buf[32]; - sprintf(buf, "/proc/%d", pid); + sprintf(buf, "/proc/%ld", (long)pid); if (stat(buf, &sb) != 0) return 0; return (sb.st_uid == uid); @@ -632,7 +632,7 @@ static int pid_is_cmd(pid_t pid, const char *name) FILE *f; int c; - sprintf(buf, "/proc/%d/stat", pid); + sprintf(buf, "/proc/%ld/stat", (long)pid); f = fopen(buf, "r"); if (!f) return 0; @@ -664,12 +664,12 @@ static void check(pid_t pid) static void do_pidfile(const char *name) { FILE *f; - pid_t pid; + long pid; f = fopen(name, "r"); if (f) { - if (fscanf(f, "%d", &pid) == 1) - check(pid); + if (fscanf(f, "%ld", &pid) == 1) + check((pid_t)pid); fclose(f); } else if (errno != ENOENT) fatal("open pidfile %s: %s", name, strerror(errno)); @@ -682,7 +682,7 @@ static void do_procinit(void) DIR *procdir; struct dirent *entry; int foundany; - pid_t pid; + long pid; procdir = opendir("/proc"); if (!procdir) @@ -690,10 +690,10 @@ static void do_procinit(void) foundany = 0; while ((entry = readdir(procdir)) != NULL) { - if (sscanf(entry->d_name, "%d", &pid) != 1) + if (sscanf(entry->d_name, "%ld", &pid) != 1) continue; foundany++; - check(pid); + check((pid_t)pid); } closedir(procdir); if (!foundany) @@ -728,21 +728,21 @@ static void do_stop(int signal_nr, int quietmode, int *n_killed, for (p = found; p; p = p->next) { if (testmode) - printf("Would send signal %d to %d.\n", signal_nr, - p->pid); + printf("Would send signal %d to %ld.\n", signal_nr, + (long)p->pid); else if (kill(p->pid, signal_nr) == 0) { push(&killed, p->pid); (*n_killed)++; } else { - printf("%s: warning: failed to kill %d: %s\n", progname, - p->pid, strerror(errno)); + printf("%s: warning: failed to kill %ld: %s\n", + progname, (long)p->pid, strerror(errno)); (*n_notkilled)++; } } if (quietmode < 0 && killed) { printf("Stopped %s (pid", what_stop); for (p = killed; p; p = p->next) - printf(" %d", p->pid); + printf(" %ld", (long)p->pid); putchar(')'); if (retry_nr > 0) printf(", retry #%d", retry_nr); @@ -1055,7 +1055,7 @@ int main(int argc, char **argv) if (pidf == NULL) fatal("Unable to open pidfile `%s' for writing: %s", pidfile, strerror(errno)); - fprintf(pidf, "%d\n", pidt); + fprintf(pidf, "%ld\n", (long)pidt); fclose(pidf); } set_namespaces(); diff --git a/zebra/if_ioctl_solaris.c b/zebra/if_ioctl_solaris.c index 5a58fe1751..7ec73ea111 100644 --- a/zebra/if_ioctl_solaris.c +++ b/zebra/if_ioctl_solaris.c @@ -39,6 +39,7 @@ #include "zebra/interface.h" #include "zebra/ioctl_solaris.h" #include "zebra/rib.h" +#include "zebra/rt.h" static int if_get_addr(struct interface *, struct sockaddr *, const char *); static void interface_info_ioctl(struct interface *); @@ -55,7 +56,6 @@ static int interface_list_ioctl(int af) struct lifconf lifconf; struct interface *ifp; int n; - int save_errno; size_t needed, lastneeded = 0; char *buf = NULL; @@ -76,13 +76,11 @@ calculate_lifc_len: lifn.lifn_flags = LIFC_NOXMIT; /* we want NOXMIT interfaces too */ ret = ioctl(sock, SIOCGLIFNUM, &lifn); - save_errno = errno; - } if (ret < 0) { zlog_warn("interface_list_ioctl: SIOCGLIFNUM failed %s", - safe_strerror(save_errno)); + safe_strerror(errno)); close(sock); return -1; } diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c index b3aeaf2f76..8e5d7fbdbe 100644 --- a/zebra/rtread_getmsg.c +++ b/zebra/rtread_getmsg.c @@ -31,6 +31,7 @@ #include "zebra/rib.h" #include "zebra/rt.h" +#include "zebra/zebra_pbr.h" /* Thank you, Solaris, for polluting application symbol namespace. */ #undef hook_register From 086aec2ad407bbea696e53666c4df51c0d1533c9 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 18 Aug 2018 18:03:16 +0200 Subject: [PATCH 66/84] vtysh: make RPKI node non-conditional Whether or not RPKI is enabled during build shouldn't really influence vtysh; the user can always manually install bgpd_rpki.so later and it should work. This also means that the behaviour of "RPKI module not loaded" is consistent regardless of whether it was a compile-time or runtime decision. Signed-off-by: David Lamparter --- bgpd/subdir.am | 2 -- configure.ac | 3 +-- vtysh/vtysh.c | 11 ----------- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/bgpd/subdir.am b/bgpd/subdir.am index 7bb8b8ed8d..f54fbab1fd 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -26,9 +26,7 @@ vtysh_scan += \ # end # can be loaded as DSO - always include for vtysh -if RPKI vtysh_scan += $(top_srcdir)/bgpd/bgp_rpki.c -endif if ENABLE_BGP_VNC vtysh_scan += \ diff --git a/configure.ac b/configure.ac index 32aa178379..521d27a682 100755 --- a/configure.ac +++ b/configure.ac @@ -1975,8 +1975,7 @@ dnl Enable RPKI and add librtr to libs dnl ------------------------------------ if test "${enable_rpki}" = "yes"; then PKG_CHECK_MODULES(RTRLIB,[rtrlib >= 0.5.0], - [AC_DEFINE(HAVE_RPKI,1,Enable RPKI prefix validation for BGP) - RPKI=true], + [RPKI=true], [RPKI=false AC_MSG_ERROR([rtrlib was not found on your system or is too old.])] ) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 39b756ddaf..6a92b90813 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1257,9 +1257,7 @@ struct cmd_node link_params_node = { LINK_PARAMS_NODE, "%s(config-link-params)# ", }; -#if defined(HAVE_RPKI) static struct cmd_node rpki_node = {RPKI_NODE, "%s(config-rpki)# ", 1}; -#endif #if HAVE_BFDD > 0 static struct cmd_node bfd_node = { @@ -1429,7 +1427,6 @@ DEFUNSH(VTYSH_BGPD, address_family_ipv6_labeled_unicast, return CMD_SUCCESS; } -#if defined(HAVE_RPKI) DEFUNSH(VTYSH_BGPD, rpki, rpki_cmd, @@ -1440,8 +1437,6 @@ DEFUNSH(VTYSH_BGPD, return CMD_SUCCESS; } -#endif - DEFUNSH(VTYSH_BGPD, address_family_evpn, address_family_evpn_cmd, "address-family ", "Enter Address Family command mode\n" @@ -1884,7 +1879,6 @@ DEFUNSH(VTYSH_BGPD, exit_vnc_config, exit_vnc_config_cmd, "exit-vnc", } -#if defined(HAVE_RPKI) DEFUNSH(VTYSH_BGPD, rpki_exit, rpki_exit_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -1897,7 +1891,6 @@ DEFUNSH(VTYSH_BGPD, rpki_quit, rpki_quit_cmd, "quit", { return rpki_exit(self, vty, argc, argv); } -#endif /* HAVE_RPKI */ DEFUNSH(VTYSH_PIMD|VTYSH_ZEBRA, exit_vrf_config, exit_vrf_config_cmd, "exit-vrf", "Exit from VRF configuration mode\n") @@ -3525,9 +3518,7 @@ void vtysh_init_vty(void) install_node(&isis_node, NULL); install_node(&openfabric_node, NULL); install_node(&vty_node, NULL); -#if defined(HAVE_RPKI) install_node(&rpki_node, NULL); -#endif #if HAVE_BFDD > 0 install_node(&bfd_node, NULL); install_node(&bfd_peer_node, NULL); @@ -3766,12 +3757,10 @@ void vtysh_init_vty(void) install_element(BGP_FLOWSPECV4_NODE, &exit_address_family_cmd); install_element(BGP_FLOWSPECV6_NODE, &exit_address_family_cmd); -#if defined(HAVE_RPKI) install_element(CONFIG_NODE, &rpki_cmd); install_element(RPKI_NODE, &rpki_exit_cmd); install_element(RPKI_NODE, &rpki_quit_cmd); install_element(RPKI_NODE, &vtysh_end_all_cmd); -#endif /* EVPN commands */ install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd); From 0718b5624c20132450ba39454a8e245bc543281b Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 28 Aug 2018 11:40:57 +0200 Subject: [PATCH 67/84] build: use _POSIX_C_SOURCE Need this to get CMSG_SPACE/CMSG_LEN on Solaris. Also, AC_GNU_SOURCE is deprecated, AC_USE_SYSTEM_EXTENSIONS does that. Signed-off-by: David Lamparter --- configure.ac | 8 ++------ lib/zebra.h | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 326794f2cc..e6ca7d9589 100755 --- a/configure.ac +++ b/configure.ac @@ -298,11 +298,6 @@ AC_PROG_LN_S AC_PROG_MAKE_SET AC_CHECK_TOOL(AR, ar) -dnl ----------------- -dnl System extensions -dnl ----------------- -AC_GNU_SOURCE - dnl ------- dnl libtool dnl ------- @@ -823,7 +818,7 @@ int main(int argc, char **argv) { dnl Utility macro to avoid retyping includes all the time m4_define([FRR_INCLUDES], [#ifdef SUNOS_5 -#define _XPG4_2 +#define _POSIX_C_SOURCE 200809L #define __EXTENSIONS__ #endif #include @@ -916,6 +911,7 @@ case "$host_os" in AC_DEFINE(SUNOS_5, 1, [SunOS 5]) AC_DEFINE(SOLARIS_IPV6, 1, Solaris IPv6) + AC_DEFINE(_POSIX_C_SOURCE, 200809L, [enable POSIX.1-2008 and XPG7/SUSv4]) AC_CHECK_LIB(socket, main) AC_CHECK_LIB(nsl, main) diff --git a/lib/zebra.h b/lib/zebra.h index 7b7a42d908..d80aa06935 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -28,7 +28,6 @@ #include "compiler.h" #ifdef SUNOS_5 -#define _XPG4_2 typedef unsigned int uint32_t; typedef unsigned short uint16_t; typedef unsigned char uint8_t; From 82f9e9b35839e849a83e8a9341ca1a1ca4a1e537 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 21 Aug 2018 20:56:16 +0200 Subject: [PATCH 68/84] build: move RFPLDADD to bgpd/rfp-example/librfp This makes it slightly easier to replace the stub RFP code with an external implementation. Signed-off-by: David Lamparter --- bgpd/rfp-example/librfp/subdir.am | 1 + bgpd/rfp-example/rfptest/subdir.am | 2 +- bgpd/subdir.am | 6 ++---- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/bgpd/rfp-example/librfp/subdir.am b/bgpd/rfp-example/librfp/subdir.am index 2800f5aa36..254ab716d5 100644 --- a/bgpd/rfp-example/librfp/subdir.am +++ b/bgpd/rfp-example/librfp/subdir.am @@ -4,6 +4,7 @@ if ENABLE_BGP_VNC noinst_LIBRARIES += bgpd/rfp-example/librfp/librfp.a +RFPLDADD = bgpd/rfp-example/librfp/librfp.a endif bgpd_rfp_example_librfp_librfp_a_SOURCES = \ diff --git a/bgpd/rfp-example/rfptest/subdir.am b/bgpd/rfp-example/rfptest/subdir.am index e1975e6b05..fa7c660116 100644 --- a/bgpd/rfp-example/rfptest/subdir.am +++ b/bgpd/rfp-example/rfptest/subdir.am @@ -16,5 +16,5 @@ noinst_HEADERS += \ bgpd_rfp_example_rfptest_rfptest_LDADD = \ lib/libfrr.la \ - bgpd/rfp-example/librfp/librfp.a \ + $(RFPLDADD) \ # end diff --git a/bgpd/subdir.am b/bgpd/subdir.am index f54fbab1fd..6ab63abea5 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -192,11 +192,9 @@ bgpd_bgpd_CFLAGS = -Irfapi -I@top_srcdir@/$(RFPINC) bgpd_bgp_btoa_SOURCES += bgpd/rfapi/rfapi_descriptor_rfp_utils.c bgpd_bgp_btoa_CFLAGS = -Irfapi -I@top_srcdir@/$(RFPINC) - -RFPLDADD = bgpd/rfp-example/librfp/librfp.a -else -RFPLDADD = endif + +# RFPLDADD is set in bgpd/rfp-example/librfp/subdir.am bgpd_bgpd_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la @LIBCAP@ @LIBM@ bgpd_bgp_btoa_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la @LIBCAP@ @LIBM@ From a1884235977fd86a1c8d990a12b4e88864054175 Mon Sep 17 00:00:00 2001 From: Wilhelm Wijkander Date: Sat, 8 Sep 2018 21:56:25 +0200 Subject: [PATCH 69/84] Update RPM building instructions --- redhat/README.rpm_build.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/redhat/README.rpm_build.md b/redhat/README.rpm_build.md index c461e543d2..93a731d685 100644 --- a/redhat/README.rpm_build.md +++ b/redhat/README.rpm_build.md @@ -3,16 +3,15 @@ Building your own FRRouting RPM (Tested on CentOS 6, CentOS 7 and Fedora 24.) 1. On CentOS 6 (which doesn't provide a bison/automake/autoconf of a recent enough version): - - Check out ../doc/Building_FRR_on_CentOS6.md for details on installing + - Check out ../doc/developer/building-frr-for-centos6.rst for details on installing a bison/automake/autoconf to support frr building. Newer automake/autoconf/bison is only needed to build the rpm and is **not** needed to install the binary rpm package -2. Install the build packages as documented in doc/Building_on_xxxxx.md - and the following additional packages: +2. Install the build packages as documented in doc/developer/building-frr-for-xxxxx.rst and the following additional packages: - yum install rpm-build net-snmp-devel pam-devel + yum install rpm-build net-snmp-devel pam-devel libcap-devel Additionally, on systems with systemd (CentOS 7, Fedora) From 324be174d79290281ec42b41636a5303c569a133 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 4 Sep 2018 12:24:22 +0200 Subject: [PATCH 70/84] build: check {malloc,pthread}_np.h for *BSD FreeBSD has malloc_usable_size() in malloc_np.h OpenBSD has pthread_set_name_np() in pthread_np.h Signed-off-by: David Lamparter --- configure.ac | 9 ++++++++- lib/frr_pthread.c | 3 +++ lib/memory.c | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index e6ca7d9589..779c7ebb6a 100755 --- a/configure.ac +++ b/configure.ac @@ -815,6 +815,10 @@ int main(int argc, char **argv) { ]) ]) +AC_CHECK_HEADERS([pthread_np.h],,, [ +#include +]) + dnl Utility macro to avoid retyping includes all the time m4_define([FRR_INCLUDES], [#ifdef SUNOS_5 @@ -1784,13 +1788,16 @@ dnl order to check no alternative allocator dnl has been specified, which might not provide dnl mallinfo, e.g. such as Umem on Solaris. dnl ----------------------------------------- -AC_CHECK_HEADERS([malloc.h malloc/malloc.h],,, [FRR_INCLUDES]) +AC_CHECK_HEADERS([malloc.h malloc_np.h malloc/malloc.h],,, [FRR_INCLUDES]) AC_CACHE_CHECK([whether mallinfo is available], [frr_cv_mallinfo], [ AC_LINK_IFELSE([AC_LANG_PROGRAM([FRR_INCLUDES [ #ifdef HAVE_MALLOC_H #include #endif +#ifdef HAVE_MALLOC_NP_H +#include +#endif #ifdef HAVE_MALLOC_MALLOC_H #include #endif diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index c0aae5e52e..7cae889ca9 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -19,6 +19,9 @@ #include #include +#ifdef HAVE_PTHREAD_NP_H +#include +#endif #include #include "frr_pthread.h" diff --git a/lib/memory.c b/lib/memory.c index 695bbfe115..fee23a75ac 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -20,6 +20,9 @@ #ifdef HAVE_MALLOC_H #include #endif +#ifdef HAVE_MALLOC_NP_H +#include +#endif #ifdef HAVE_MALLOC_MALLOC_H #include #endif From e9d938b82a630d61a5cb0890c424c42f3e9ce241 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 4 Sep 2018 13:15:56 +0200 Subject: [PATCH 71/84] lib: make pthread_set[_]name_np test OS agnostic FreeBSD supports pthread_set_name_np() too. Also, pthread_set_name_np() returns void. And NetBSD has pthread_setname_np() with an extra arg... Signed-off-by: David Lamparter --- configure.ac | 1 + lib/frr_pthread.c | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 779c7ebb6a..294d3bf325 100755 --- a/configure.ac +++ b/configure.ac @@ -818,6 +818,7 @@ int main(int argc, char **argv) { AC_CHECK_HEADERS([pthread_np.h],,, [ #include ]) +AC_CHECK_FUNCS([pthread_setname_np pthread_set_name_np]) dnl Utility macro to avoid retyping includes all the time m4_define([FRR_INCLUDES], diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index 7cae889ca9..d48b23f38a 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -166,10 +166,14 @@ int frr_pthread_set_name(struct frr_pthread *fpt, const char *name, pthread_mutex_lock(&fpt->mtx); snprintf(fpt->os_name, OS_THREAD_NAMELEN, "%s", os_name); pthread_mutex_unlock(&fpt->mtx); -#ifdef GNU_LINUX +#ifdef HAVE_PTHREAD_SETNAME_NP +# ifdef GNU_LINUX ret = pthread_setname_np(fpt->thread, fpt->os_name); -#elif defined(OPEN_BSD) - ret = pthread_set_name_np(fpt->thread, fpt->os_name); +# else /* NetBSD */ + ret = pthread_setname_np(fpt->thread, fpt->os_name, NULL); +# endif +#elif defined(HAVE_PTHREAD_SET_NAME_NP) + pthread_set_name_np(fpt->thread, fpt->os_name); #endif } From 3009394b3cb659b3df74637b0d27314913f18230 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 4 Sep 2018 12:27:46 +0200 Subject: [PATCH 72/84] *: fix some random warnings Signed-off-by: David Lamparter --- isisd/isis_bpf.c | 4 ++-- lib/stream.c | 8 ++++---- pimd/pim_igmp_mtrace.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c index 7e8a4a4eda..28750278b0 100644 --- a/isisd/isis_bpf.c +++ b/isisd/isis_bpf.c @@ -213,7 +213,7 @@ int isis_sock_init(struct isis_circuit *circuit) int isis_recv_pdu_bcast(struct isis_circuit *circuit, uint8_t *ssnpa) { - int bytesread = 0, bytestoread, offset, one = 1, err = ISIS_OK; + int bytesread = 0, bytestoread, offset, one = 1; uint8_t *buff_ptr; struct bpf_hdr *bpf_hdr; @@ -249,7 +249,7 @@ int isis_recv_pdu_bcast(struct isis_circuit *circuit, uint8_t *ssnpa) memcpy(ssnpa, buff_ptr + bpf_hdr->bh_hdrlen + ETHER_ADDR_LEN, ETHER_ADDR_LEN); - err = isis_handle_pdu(circuit, ssnpa); + isis_handle_pdu(circuit, ssnpa); stream_reset(circuit->rcv_stream); buff_ptr += BPF_WORDALIGN(bpf_hdr->bh_hdrlen + bpf_hdr->bh_datalen); diff --git a/lib/stream.c b/lib/stream.c index 55e7f64358..589a229256 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -270,7 +270,7 @@ void stream_forward_endp(struct stream *s, size_t size) } /* Copy from stream to destination. */ -inline bool stream_get2(void *dst, struct stream *s, size_t size) +bool stream_get2(void *dst, struct stream *s, size_t size) { STREAM_VERIFY_SANE(s); @@ -299,7 +299,7 @@ void stream_get(void *dst, struct stream *s, size_t size) } /* Get next character from the stream. */ -inline bool stream_getc2(struct stream *s, uint8_t *byte) +bool stream_getc2(struct stream *s, uint8_t *byte) { STREAM_VERIFY_SANE(s); @@ -344,7 +344,7 @@ uint8_t stream_getc_from(struct stream *s, size_t from) return c; } -inline bool stream_getw2(struct stream *s, uint16_t *word) +bool stream_getw2(struct stream *s, uint16_t *word) { STREAM_VERIFY_SANE(s); @@ -465,7 +465,7 @@ void stream_get_from(void *dst, struct stream *s, size_t from, size_t size) memcpy(dst, s->data + from, size); } -inline bool stream_getl2(struct stream *s, uint32_t *l) +bool stream_getl2(struct stream *s, uint32_t *l) { STREAM_VERIFY_SANE(s); diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c index 95d0278a34..1fb624a6a0 100644 --- a/pimd/pim_igmp_mtrace.c +++ b/pimd/pim_igmp_mtrace.c @@ -615,7 +615,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, static uint32_t qry_id, qry_src; char mtrace_buf[MTRACE_HDR_SIZE + MTRACE_MAX_HOPS * MTRACE_RSP_SIZE]; struct interface *ifp; - struct interface *out_ifp; + struct interface *out_ifp = NULL; struct pim_interface *pim_ifp; struct pim_instance *pim; struct igmp_mtrace *mtracep; From 01db90cd83edbcd2f806ece2c2d1620478f51c8f Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 4 Sep 2018 12:55:19 +0200 Subject: [PATCH 73/84] ospf6d: remove extra struct in ospf6_lsa_handler This serves no other purpose than to generate stupid warnings for overwritten initializers on old gcc versions. Signed-off-by: David Lamparter --- ospf6d/ospf6_lsa.c | 27 ++++++++++++++------------- ospf6d/ospf6_lsa.h | 23 ++++++++--------------- ospf6d/ospf6_neighbor.c | 2 +- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 8b720b6d84..40b3522c3d 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -130,7 +130,7 @@ uint8_t ospf6_lstype_debug(uint16_t type) { const struct ospf6_lsa_handler *handler; handler = ospf6_get_lsa_handler(type); - return handler->debug; + return handler->lh_debug; } /* RFC2328: Section 13.2 */ @@ -844,13 +844,13 @@ DEFUN (debug_ospf6_lsa_type, if (argc == 5) { if (strmatch(argv[idx_type]->text, "originate")) - SET_FLAG(handler->debug, OSPF6_LSA_DEBUG_ORIGINATE); + SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ORIGINATE); else if (strmatch(argv[idx_type]->text, "examine")) - SET_FLAG(handler->debug, OSPF6_LSA_DEBUG_EXAMIN); + SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_EXAMIN); else if (strmatch(argv[idx_type]->text, "flooding")) - SET_FLAG(handler->debug, OSPF6_LSA_DEBUG_FLOOD); + SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_FLOOD); } else - SET_FLAG(handler->debug, OSPF6_LSA_DEBUG); + SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG); return CMD_SUCCESS; } @@ -896,13 +896,14 @@ DEFUN (no_debug_ospf6_lsa_type, if (argc == 6) { if (strmatch(argv[idx_type]->text, "originate")) - UNSET_FLAG(handler->debug, OSPF6_LSA_DEBUG_ORIGINATE); + UNSET_FLAG(handler->lh_debug, + OSPF6_LSA_DEBUG_ORIGINATE); if (strmatch(argv[idx_type]->text, "examine")) - UNSET_FLAG(handler->debug, OSPF6_LSA_DEBUG_EXAMIN); + UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_EXAMIN); if (strmatch(argv[idx_type]->text, "flooding")) - UNSET_FLAG(handler->debug, OSPF6_LSA_DEBUG_FLOOD); + UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_FLOOD); } else - UNSET_FLAG(handler->debug, OSPF6_LSA_DEBUG); + UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG); return CMD_SUCCESS; } @@ -924,16 +925,16 @@ int config_write_ospf6_debug_lsa(struct vty *vty) handler = vector_slot(ospf6_lsa_handler_vector, i); if (handler == NULL) continue; - if (CHECK_FLAG(handler->debug, OSPF6_LSA_DEBUG)) + if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG)) vty_out(vty, "debug ospf6 lsa %s\n", ospf6_lsa_handler_name(handler)); - if (CHECK_FLAG(handler->debug, OSPF6_LSA_DEBUG_ORIGINATE)) + if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ORIGINATE)) vty_out(vty, "debug ospf6 lsa %s originate\n", ospf6_lsa_handler_name(handler)); - if (CHECK_FLAG(handler->debug, OSPF6_LSA_DEBUG_EXAMIN)) + if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_EXAMIN)) vty_out(vty, "debug ospf6 lsa %s examine\n", ospf6_lsa_handler_name(handler)); - if (CHECK_FLAG(handler->debug, OSPF6_LSA_DEBUG_FLOOD)) + if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_FLOOD)) vty_out(vty, "debug ospf6 lsa %s flooding\n", ospf6_lsa_handler_name(handler)); } diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index e88d10ad7e..d871a8842e 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -137,21 +137,14 @@ struct ospf6_lsa { #define OSPF6_LSA_SEQWRAPPED 0x20 struct ospf6_lsa_handler { - const struct { - uint16_t type; /* host byte order */ - const char *name; - const char *short_name; - int (*show)(struct vty *, struct ospf6_lsa *); - char *(*get_prefix_str)(struct ospf6_lsa *, char *buf, - int buflen, int pos); - } s; -#define lh_type s.type -#define lh_name s.name -#define lh_short_name s.short_name -#define lh_show s.show -#define lh_get_prefix_str s.get_prefix_str - uint8_t debug; -#define lh_debug debug + uint16_t lh_type; /* host byte order */ + const char *lh_name; + const char *lh_short_name; + int (*lh_show)(struct vty *, struct ospf6_lsa *); + char *(*lh_get_prefix_str)(struct ospf6_lsa *, char *buf, + int buflen, int pos); + + uint8_t lh_debug; }; #define OSPF6_LSA_IS_KNOWN(t) \ diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 0cc7294d67..4c24f47131 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -921,7 +921,7 @@ DEFUN (no_debug_ospf6, handler = vector_slot(ospf6_lsa_handler_vector, i); if (handler != NULL) { - UNSET_FLAG(handler->debug, OSPF6_LSA_DEBUG); + UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG); } } From 7b34167d7dac6e898c49c675cfc80ae68c64bc98 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sun, 9 Sep 2018 01:00:42 +0200 Subject: [PATCH 74/84] lib: early-include "config.h" in flex lexers This is neccessary to get _FILE_OFFSET_BITS right. Signed-off-by: David Lamparter --- lib/command_lex.l | 5 +++++ lib/defun_lex.l | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/command_lex.l b/lib/command_lex.l index 0d6e6ee7e5..3b18b58a2e 100644 --- a/lib/command_lex.l +++ b/lib/command_lex.l @@ -22,6 +22,11 @@ * 02111-1307, USA. */ +%top{ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +} %{ /* ignore flex generated code in static analyzer */ #ifndef __clang_analyzer__ diff --git a/lib/defun_lex.l b/lib/defun_lex.l index d901c26a2e..6c0805a4fa 100644 --- a/lib/defun_lex.l +++ b/lib/defun_lex.l @@ -1,4 +1,3 @@ -%{ /* * clippy (CLI preparator in python) C pseudo-lexer * Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc. @@ -34,6 +33,12 @@ * code documentation in it. */ +%top{ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +} +%{ /* ignore harmless bugs in old versions of flex */ #pragma GCC diagnostic ignored "-Wsign-compare" #pragma GCC diagnostic ignored "-Wunused-value" From f4f2f2ccac75f5498924288adf814b54ee1e1c81 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 5 Sep 2018 11:25:24 +0200 Subject: [PATCH 75/84] build: fix not building docs w/o sphinx Can't build manpages without sphinx-build, oops... Signed-off-by: David Lamparter --- bfdd/subdir.am | 2 +- bgpd/subdir.am | 2 +- configure.ac | 11 ++++++++--- doc/manpages/subdir.am | 10 ++++++++-- eigrpd/subdir.am | 2 +- isisd/subdir.am | 2 +- ldpd/subdir.am | 2 +- nhrpd/subdir.am | 2 +- ospf6d/subdir.am | 2 +- ospfclient/subdir.am | 2 +- ospfd/subdir.am | 2 +- pbrd/subdir.am | 2 +- pimd/subdir.am | 4 ++-- ripd/subdir.am | 2 +- ripngd/subdir.am | 2 +- sharpd/subdir.am | 2 +- staticd/subdir.am | 2 +- vtysh/subdir.am | 2 +- watchfrr/subdir.am | 2 +- zebra/subdir.am | 2 +- 20 files changed, 35 insertions(+), 24 deletions(-) diff --git a/bfdd/subdir.am b/bfdd/subdir.am index 7447519b21..334e974b04 100644 --- a/bfdd/subdir.am +++ b/bfdd/subdir.am @@ -7,7 +7,7 @@ noinst_LIBRARIES += bfdd/libbfd.a sbin_PROGRAMS += bfdd/bfdd dist_examples_DATA += bfdd/bfdd.conf.sample vtysh_scan += $(top_srcdir)/bfdd/bfdd_vty.c -rstman8_DATA += $(MANBUILD)/bfdd.8 +man8 += $(MANBUILD)/bfdd.8 endif bfdd_libbfd_a_SOURCES = \ diff --git a/bgpd/subdir.am b/bgpd/subdir.am index 6ab63abea5..4291388567 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -42,7 +42,7 @@ endif if RPKI module_LTLIBRARIES += bgpd/bgpd_rpki.la endif -rstman8_DATA += $(MANBUILD)/bgpd.8 +man8 += $(MANBUILD)/bgpd.8 endif bgpd_libbgp_a_SOURCES = \ diff --git a/configure.ac b/configure.ac index 521d27a682..27fca411d1 100755 --- a/configure.ac +++ b/configure.ac @@ -1351,8 +1351,13 @@ FRR_INCLUDES ])dnl dnl disable doc check -AC_CHECK_PROGS([SPHINXBUILD], [sphinx-build sphinx-build3 sphinx-build2], [no]) -AM_CONDITIONAL(DOC, test "${enable_doc}" != "no") +AC_CHECK_PROGS([SPHINXBUILD], [sphinx-build sphinx-build3 sphinx-build2], [/bin/false]) +if test "$SPHINXBUILD" = "/bin/false"; then + if test "${enable_doc}" = "yes"; then + AC_MSG_ERROR([Documentation was explicitly requested with --enable-doc but sphinx-build is not available. Please disable docs or install sphinx.]) + fi +fi +AM_CONDITIONAL(DOC, test "${enable_doc}" != "no" -a "$SPHINXBUILD" != "/bin/false") AM_CONDITIONAL(DOC_HTML, test "${enable_doc_html}" = "yes") dnl -------------------- @@ -2077,6 +2082,6 @@ The above user and group must have read/write access to the state file directory and to the config files in the config file directory." if test "${enable_doc}" != "no";then - AS_IF([test "x$SPHINXBUILD" = xno], + AS_IF([test "$SPHINXBUILD" = /bin/false], AC_MSG_WARN(sphinx-build is missing but required to build documentation)) fi diff --git a/doc/manpages/subdir.am b/doc/manpages/subdir.am index 0ce9161db6..4a9aa4de4d 100644 --- a/doc/manpages/subdir.am +++ b/doc/manpages/subdir.am @@ -47,10 +47,16 @@ rstman8dir = $(mandir)/man8 rstman1_DATA = rstman8_DATA = -rstman1_DATA += $(MANBUILD)/frr.1 +if DOC +rstman1_DATA += $(man1) +rstman8_DATA += $(man8) +endif # DOC + +man1 = $(MANBUILD)/frr.1 +man8 = # dependency -$(rstman8_DATA) $(rstman1_DATA): $(MANBUILD)/man.stamp +$(man8) $(man1): $(MANBUILD)/man.stamp # # hook-ins for clean / doc diff --git a/eigrpd/subdir.am b/eigrpd/subdir.am index 75b77feee6..bc48173bba 100644 --- a/eigrpd/subdir.am +++ b/eigrpd/subdir.am @@ -11,7 +11,7 @@ vtysh_scan += \ $(top_srcdir)/eigrpd/eigrp_vty.c \ # end # $(top_srcdir)/eigrpd/eigrp_routemap.c -rstman8_DATA += $(MANBUILD)/eigrpd.8 +man8 += $(MANBUILD)/eigrpd.8 endif eigrpd_libeigrp_a_SOURCES = \ diff --git a/isisd/subdir.am b/isisd/subdir.am index 855cd9dc54..7571255e59 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -15,7 +15,7 @@ vtysh_scan += \ $(top_srcdir)/isisd/isis_vty_isisd.c \ $(top_srcdir)/isisd/isisd.c \ # end -rstman8_DATA += $(MANBUILD)/isisd.8 +man8 += $(MANBUILD)/isisd.8 endif if FABRICD diff --git a/ldpd/subdir.am b/ldpd/subdir.am index b42f401f25..24e738d622 100644 --- a/ldpd/subdir.am +++ b/ldpd/subdir.am @@ -7,7 +7,7 @@ noinst_LIBRARIES += ldpd/libldp.a sbin_PROGRAMS += ldpd/ldpd dist_examples_DATA += ldpd/ldpd.conf.sample vtysh_scan += $(top_srcdir)/ldpd/ldp_vty_cmds.c -rstman8_DATA += $(MANBUILD)/ldpd.8 +man8 += $(MANBUILD)/ldpd.8 endif ldpd_libldp_a_SOURCES = \ diff --git a/nhrpd/subdir.am b/nhrpd/subdir.am index f7575971e9..758c22e2be 100644 --- a/nhrpd/subdir.am +++ b/nhrpd/subdir.am @@ -5,7 +5,7 @@ if NHRPD sbin_PROGRAMS += nhrpd/nhrpd vtysh_scan += $(top_srcdir)/nhrpd/nhrp_vty.c -rstman8_DATA += $(MANBUILD)/nhrpd.8 +man8 += $(MANBUILD)/nhrpd.8 endif nhrpd_nhrpd_LDADD = lib/libfrr.la @LIBCAP@ @CARES_LIBS@ diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index 5338e1ea37..d9c29f2651 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -26,7 +26,7 @@ vtysh_scan += \ if SNMP module_LTLIBRARIES += ospf6d/ospf6d_snmp.la endif -rstman8_DATA += $(MANBUILD)/ospf6d.8 +man8 += $(MANBUILD)/ospf6d.8 endif ospf6d_libospf6_a_SOURCES = \ diff --git a/ospfclient/subdir.am b/ospfclient/subdir.am index d880f9fc70..df7d85a1f5 100644 --- a/ospfclient/subdir.am +++ b/ospfclient/subdir.am @@ -5,7 +5,7 @@ if OSPFCLIENT lib_LTLIBRARIES += ospfclient/libfrrospfapiclient.la sbin_PROGRAMS += ospfclient/ospfclient -rstman8_DATA += $(MANBUILD)/ospfclient.8 +man8 += $(MANBUILD)/ospfclient.8 endif ospfclient_libfrrospfapiclient_la_LDFLAGS = -version-info 0:0:0 diff --git a/ospfd/subdir.am b/ospfd/subdir.am index 2b42b5230b..83074b5ac0 100644 --- a/ospfd/subdir.am +++ b/ospfd/subdir.am @@ -19,7 +19,7 @@ vtysh_scan += \ if SNMP module_LTLIBRARIES += ospfd/ospfd_snmp.la endif -rstman8_DATA += $(MANBUILD)/ospfd.8 +man8 += $(MANBUILD)/ospfd.8 endif ospfd_libfrrospf_a_SOURCES = \ diff --git a/pbrd/subdir.am b/pbrd/subdir.am index 49a07e1f60..7947559034 100644 --- a/pbrd/subdir.am +++ b/pbrd/subdir.am @@ -10,7 +10,7 @@ vtysh_scan += \ $(top_srcdir)/pbrd/pbr_vty.c \ $(top_srcdir)/pbrd/pbr_debug.c \ # end -rstman8_DATA += $(MANBUILD)/pbrd.8 +man8 += $(MANBUILD)/pbrd.8 endif pbrd_libpbr_a_SOURCES = \ diff --git a/pimd/subdir.am b/pimd/subdir.am index 00cabb99b0..fef8e36577 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -9,8 +9,8 @@ bin_PROGRAMS += pimd/mtracebis noinst_PROGRAMS += pimd/test_igmpv3_join dist_examples_DATA += pimd/pimd.conf.sample vtysh_scan += $(top_srcdir)/pimd/pim_cmd.c -rstman8_DATA += $(MANBUILD)/pimd.8 -rstman8_DATA += $(MANBUILD)/mtracebis.8 +man8 += $(MANBUILD)/pimd.8 +man8 += $(MANBUILD)/mtracebis.8 endif pimd_libpim_a_SOURCES = \ diff --git a/ripd/subdir.am b/ripd/subdir.am index f2c54c835e..0d06e7e653 100644 --- a/ripd/subdir.am +++ b/ripd/subdir.am @@ -17,7 +17,7 @@ vtysh_scan += \ if SNMP module_LTLIBRARIES += ripd/ripd_snmp.la endif -rstman8_DATA += $(MANBUILD)/ripd.8 +man8 += $(MANBUILD)/ripd.8 endif ripd_librip_a_SOURCES = \ diff --git a/ripngd/subdir.am b/ripngd/subdir.am index 0948b23342..8f834a1d29 100644 --- a/ripngd/subdir.am +++ b/ripngd/subdir.am @@ -12,7 +12,7 @@ vtysh_scan += \ $(top_srcdir)/ripngd/ripng_zebra.c \ $(top_srcdir)/ripngd/ripngd.c \ # end -rstman8_DATA += $(MANBUILD)/ripngd.8 +man8 += $(MANBUILD)/ripngd.8 endif ripngd_libripng_a_SOURCES = \ diff --git a/sharpd/subdir.am b/sharpd/subdir.am index ecc62af142..2a34aecfb3 100644 --- a/sharpd/subdir.am +++ b/sharpd/subdir.am @@ -7,7 +7,7 @@ noinst_LIBRARIES += sharpd/libsharp.a sbin_PROGRAMS += sharpd/sharpd dist_examples_DATA += sharpd/sharpd.conf.sample vtysh_scan += $(top_srcdir)/sharpd/sharp_vty.c -rstman8_DATA += $(MANBUILD)/sharpd.8 +man8 += $(MANBUILD)/sharpd.8 endif sharpd_libsharp_a_SOURCES = \ diff --git a/staticd/subdir.am b/staticd/subdir.am index f1071545ab..33cc0e2050 100644 --- a/staticd/subdir.am +++ b/staticd/subdir.am @@ -7,7 +7,7 @@ noinst_LIBRARIES += staticd/libstatic.a sbin_PROGRAMS += staticd/staticd dist_examples_DATA += staticd/staticd.conf.sample vtysh_scan += $(top_srcdir)/staticd/static_vty.c -rstman8_DATA += $(MANBUILD)/staticd.8 +man8 += $(MANBUILD)/staticd.8 endif staticd_libstatic_a_SOURCES = \ diff --git a/vtysh/subdir.am b/vtysh/subdir.am index 3d40f37d22..932429a87c 100644 --- a/vtysh/subdir.am +++ b/vtysh/subdir.am @@ -5,7 +5,7 @@ if VTYSH bin_PROGRAMS += vtysh/vtysh dist_examples_DATA += vtysh/vtysh.conf.sample -rstman1_DATA += $(MANBUILD)/vtysh.1 +man1 += $(MANBUILD)/vtysh.1 endif vtysh_vtysh_SOURCES = \ diff --git a/watchfrr/subdir.am b/watchfrr/subdir.am index 96df81d4a6..f0b49c9a84 100644 --- a/watchfrr/subdir.am +++ b/watchfrr/subdir.am @@ -5,7 +5,7 @@ if WATCHFRR sbin_PROGRAMS += watchfrr/watchfrr vtysh_scan += $(top_srcdir)/watchfrr/watchfrr_vty.c -rstman8_DATA += $(MANBUILD)/watchfrr.8 +man8 += $(MANBUILD)/watchfrr.8 endif noinst_HEADERS += \ diff --git a/zebra/subdir.am b/zebra/subdir.am index 91bd792b00..a8c5afb1d1 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -32,7 +32,7 @@ if FPM module_LTLIBRARIES += zebra/zebra_fpm.la endif -rstman8_DATA += $(MANBUILD)/zebra.8 +man8 += $(MANBUILD)/zebra.8 ## endif ZEBRA endif From a214288c507a79ff52760c56173234f216acdb00 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 8 Sep 2018 14:59:24 +0200 Subject: [PATCH 76/84] build: move SPHINXBUILD= to configure in rpm build Need to pass this on configure now to work properly. Signed-off-by: David Lamparter --- doc/developer/building-frr-for-centos6.rst | 9 +++++---- redhat/README.rpm_build.md | 5 +++-- redhat/frr.spec.in | 11 ++++++----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/doc/developer/building-frr-for-centos6.rst b/doc/developer/building-frr-for-centos6.rst index d4c2c3bfd0..fd75854452 100644 --- a/doc/developer/building-frr-for-centos6.rst +++ b/doc/developer/building-frr-for-centos6.rst @@ -168,10 +168,11 @@ an example.) --enable-eigrpd \ --enable-babeld \ --with-pkg-git-version \ - --with-pkg-extra-version=-MyOwnFRRVersion - make SPHINXBUILD=sphinx-build2.7 - make check PYTHON=/usr/bin/python2.7 SPHINXBUILD=sphinx-build2.7 - sudo make SPHINXBUILD=sphinx-build2.7 install + --with-pkg-extra-version=-MyOwnFRRVersion \ + SPHINXBUILD=sphinx-build2.7 + make + make check PYTHON=/usr/bin/python2.7 + sudo make install Create empty FRR configuration files ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/redhat/README.rpm_build.md b/redhat/README.rpm_build.md index 93a731d685..a3f095786d 100644 --- a/redhat/README.rpm_build.md +++ b/redhat/README.rpm_build.md @@ -27,8 +27,9 @@ Building your own FRRouting RPM cd frr ./bootstrap.sh - ./configure --with-pkg-extra-version=-MyRPMVersion - make SPHINXBUILD=sphinx-build2.7 dist + ./configure --with-pkg-extra-version=-MyRPMVersion \ + SPHINXBUILD=sphinx-build2.7 + make dist Note: configure parameters are not important for the RPM building - except the `with-pkg-extra-version` if you want to give the RPM a specific name to mark your own unoffical build diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index d001f3c395..9c232c2bd5 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -343,22 +343,23 @@ developing OSPF-API and frr applications. --disable-rpki \ %endif %if %{with_bfdd} - --enable-bfdd + --enable-bfdd \ %else - --disable-bfdd + --disable-bfdd \ %endif + SPHINXBUILD=%{sphinx} -make %{?_smp_mflags} MAKEINFO="makeinfo --no-split" SPHINXBUILD=%{sphinx} +make %{?_smp_mflags} MAKEINFO="makeinfo --no-split" pushd doc -make SPHINXBUILD=%{sphinx} info +make info popd %install mkdir -p %{buildroot}%{_sysconfdir}/{frr,sysconfig,logrotate.d,pam.d,default} \ %{buildroot}%{_localstatedir}/log/frr %{buildroot}%{_infodir} -make DESTDIR=%{buildroot} INSTALL="install -p" CP="cp -p" SPHINXBUILD=%{sphinx} install +make DESTDIR=%{buildroot} INSTALL="install -p" CP="cp -p" install # Remove this file, as it is uninstalled and causes errors when building on RH9 rm -rf %{buildroot}/usr/share/info/dir From 2f62815fed47973f1091fb8a772f7db340cdd8ef Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 10 Sep 2018 08:46:50 -0400 Subject: [PATCH 77/84] doc: Fixup to use consistent frrvty group name In some places we were using `frrvt` instead of `frrvty`. Make it consistent with every other place and use frrvty. Signed-off-by: Donald Sharp --- doc/developer/building-frr-for-centos6.rst | 6 +++--- doc/developer/building-frr-for-centos7.rst | 8 ++++---- doc/developer/building-frr-for-fedora24.rst | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/developer/building-frr-for-centos6.rst b/doc/developer/building-frr-for-centos6.rst index fd75854452..5f10f3715d 100644 --- a/doc/developer/building-frr-for-centos6.rst +++ b/doc/developer/building-frr-for-centos6.rst @@ -128,8 +128,8 @@ Add frr groups and user .. code-block:: shell sudo groupadd -g 92 frr - sudo groupadd -r -g 85 frrvt - sudo useradd -u 92 -g 92 -M -r -G frrvt -s /sbin/nologin \ + sudo groupadd -r -g 85 frrvty + sudo useradd -u 92 -g 92 -M -r -G frrvty -s /sbin/nologin \ -c "FRR FRRouting suite" -d /var/run/frr frr Download Source, configure and compile it @@ -158,7 +158,7 @@ an example.) --enable-ospfapi=yes \ --enable-user=frr \ --enable-group=frr \ - --enable-vty-group=frrvt \ + --enable-vty-group=frrvty \ --enable-rtadv \ --disable-exampledir \ --enable-watchfrr \ diff --git a/doc/developer/building-frr-for-centos7.rst b/doc/developer/building-frr-for-centos7.rst index 31cd4dcc49..b157f540ab 100644 --- a/doc/developer/building-frr-for-centos7.rst +++ b/doc/developer/building-frr-for-centos7.rst @@ -36,8 +36,8 @@ Add frr groups and user :: sudo groupadd -g 92 frr - sudo groupadd -r -g 85 frrvt - sudo useradd -u 92 -g 92 -M -r -G frrvt -s /sbin/nologin \ + sudo groupadd -r -g 85 frrvty + sudo useradd -u 92 -g 92 -M -r -G frrvty -s /sbin/nologin \ -c "FRR FRRouting suite" -d /var/run/frr frr Download Source, configure and compile it @@ -66,7 +66,7 @@ an example.) --enable-ospfapi=yes \ --enable-user=frr \ --enable-group=frr \ - --enable-vty-group=frrvt \ + --enable-vty-group=frrvty \ --enable-rtadv \ --enable-systemd=yes \ --disable-exampledir \ @@ -102,7 +102,7 @@ Create empty FRR configuration files sudo touch /etc/frr/babeld.conf sudo chown -R frr:frr /etc/frr/ sudo touch /etc/frr/vtysh.conf - sudo chown frr:frrvt /etc/frr/vtysh.conf + sudo chown frr:frrvty /etc/frr/vtysh.conf sudo chmod 640 /etc/frr/*.conf Install daemon config file diff --git a/doc/developer/building-frr-for-fedora24.rst b/doc/developer/building-frr-for-fedora24.rst index 208c580b63..669cc4ae2f 100644 --- a/doc/developer/building-frr-for-fedora24.rst +++ b/doc/developer/building-frr-for-fedora24.rst @@ -29,8 +29,8 @@ Add frr groups and user :: sudo groupadd -g 92 frr - sudo groupadd -r -g 85 frrvt - sudo useradd -u 92 -g 92 -M -r -G frrvt -s /sbin/nologin \ + sudo groupadd -r -g 85 frrvty + sudo useradd -u 92 -g 92 -M -r -G frrvty -s /sbin/nologin \ -c "FRR FRRouting suite" -d /var/run/frr frr Download Source, configure and compile it @@ -59,7 +59,7 @@ an example.) --enable-ospfapi=yes \ --enable-user=frr \ --enable-group=frr \ - --enable-vty-group=frrvt \ + --enable-vty-group=frrvty \ --enable-rtadv \ --disable-exampledir \ --enable-watchfrr \ @@ -95,7 +95,7 @@ Create empty FRR configuration files sudo touch /etc/frr/babeld.conf sudo chown -R frr:frr /etc/frr/ sudo touch /etc/frr/vtysh.conf - sudo chown frr:frrvt /etc/frr/vtysh.conf + sudo chown frr:frrvty /etc/frr/vtysh.conf sudo chmod 640 /etc/frr/*.conf Install daemon config file From 29f7d0232e90d913955e77378be15cefdbc19487 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 10 Sep 2018 10:19:03 -0400 Subject: [PATCH 78/84] bgpd: Honor origin change in bgp aggregates When the origin changed we must honor and update the aggregate to the peer. This code adds a bit of code to the bgp_aggregate_info_same code to see if the origin has changed and to indicate that it has. Fixes: #2993 Signed-off-by: Donald Sharp --- bgpd/bgp_route.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 32506623c7..2107d1f9f9 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -5456,7 +5456,8 @@ static void bgp_aggregate_free(struct bgp_aggregate *aggregate) XFREE(MTYPE_BGP_AGGREGATE, aggregate); } -static int bgp_aggregate_info_same(struct bgp_info *ri, struct aspath *aspath, +static int bgp_aggregate_info_same(struct bgp_info *ri, uint8_t origin, + struct aspath *aspath, struct community *comm) { static struct aspath *ae = NULL; @@ -5467,6 +5468,9 @@ static int bgp_aggregate_info_same(struct bgp_info *ri, struct aspath *aspath, if (!ri) return 0; + if (origin != ri->attr->origin) + return 0; + if (!aspath_cmp(ri->attr->aspath, (aspath) ? aspath : ae)) return 0; @@ -5501,7 +5505,8 @@ static void bgp_aggregate_install(struct bgp *bgp, afi_t afi, safi_t safi, * If the aggregate information has not changed * no need to re-install it again. */ - if (bgp_aggregate_info_same(rn->info, aspath, community)) { + if (bgp_aggregate_info_same(rn->info, origin, aspath, + community)) { bgp_unlock_node(rn); if (aspath) From d88f11441afec7be3a34a623fe9343444bcb7dc4 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 5 Sep 2018 17:47:53 +0000 Subject: [PATCH 79/84] frr: remove stale files, allow non-GNU Automake * Add 'foreign' option to allow Automake to ignore missing GNU standard files * Remove AUTHORS * Remove NEWS * Remove ChangeLog Signed-off-by: Quentin Young --- AUTHORS | 6 - ChangeLog | 4 - NEWS | 2574 ---------------------------------------- configure.ac | 2 +- debianpkg/frr-doc.docs | 2 - redhat/frr.spec.in | 4 +- 6 files changed, 3 insertions(+), 2589 deletions(-) delete mode 100644 AUTHORS delete mode 100644 ChangeLog delete mode 100644 NEWS diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 61867a860c..0000000000 --- a/AUTHORS +++ /dev/null @@ -1,6 +0,0 @@ -Kunihiro Ishiguro -Toshiaki Takada -Yasuhiro Ohara -Alex D. Zinin -Gleb Natapov -Akihiro Mizutani diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index ec7e6cdde8..0000000000 --- a/ChangeLog +++ /dev/null @@ -1,4 +0,0 @@ -ChangeLog information for FRRouting is for now recorded in source-code -management system. Please see: - - http://www.frrouting.org/ diff --git a/NEWS b/NEWS deleted file mode 100644 index 8f9dd7a11f..0000000000 --- a/NEWS +++ /dev/null @@ -1,2574 +0,0 @@ -Note: this file lists major user-visible changes only. - -* Changes in Quagga 0.99.24 - -User-visible changes: -- [pimd] New daemon: pimd provides IPv4 PIM-SSM multicast routing. -- [bgpd] New feature: "next-hop-self all" to override nexthop on iBGP route - reflector setups. -- [bgpd] route-maps have a new action "set ipv6 next-hop peer-address" -- [bgpd] route-maps have a new action "set as-path prepend last-as" -- [bgpd] Update validity checking (particularly MP-BGP / IPv6 routes) was - touched up significantly. Please report possible bugs. -- [ripd] New feature: RIP for IPv4 now supports equal-cost multipath (ECMP) -- [zebra] Multicast RIB support has been extended. It still is IPv4 only. -- [zebra] "no link-detect" is now printed in configurations since it won't - be the default anymore soon. To retain current behaviour, re-save your - configuration after updating to 0.99.24. - -Distributor-visible changes: -- --enable-pimd is added to enable pimd. It is considered experimental, though - unless the distribution target is embedded systems with little flash, there - is no reason to not include it in packages. -- --disable-ipv6 no longer exists as an option. It's 2015, your C library - really needs to have IPv6 support by now. -- --disable-netlink no longer exists as an option. It didn't work anyway. -- --disable-solaris no longer exists as an option. It only controlled some - init scripts. -- --enable-isisd is now the default. -- mrlg.cgi is no longer included (it was severely outdated). It can be found - independently at http://mrlg.op-sec.us/ -- build on Linux with the musl C library should now work - -* Changes in Quagga 0.99.23 - -Known issues: -- [bgpd] setting an extcommunity in a route map on a route that already has - an extcommunity attribute will cause bgpd to crash. This issue will be - fixed in a followup minor release. - -User-visible changes: -- [lib] Performance enhancements on hashes and timers. -- [bgpd] New feature: iBGP TTL security. -- [bgpd] New feature: relaxed bestpath criteria for multipath and improved - display of multipath routes in "show ip bgp". Scripts parsing this output - may need to be updated. -- [bgpd] Multiprotocol peerings over IPv6 now try to find a more appropriate - IPv4 nexthop by looking at the interface. -- [ospf6d] A large amount of changes has been merged for ospf6d. Careful - evaluation prior to deployment is recommended. -- [zebra] Recursive route support has been overhauled. Scripts parsing - "show ip route" output may need adaptation. -- [zebra] IPv6 address management has been improved regarding tentative - addresses. This is visible in that a freshly configured address will not - immediately be marked as usable. -- [*] a lot of bugs have been fixed, please refer to the git log - -* Changes in Quagga 0.99.22 - -- [bgpd] The semantics of default-originate route-map have changed. - The route-map is now used to advertise the default route conditionally. - The old behaviour which allowed to set attributes on the originated - default route is no longer supported. -- [bgpd] There is now a replace-as option to neighbor ... local-as ... - no-prepend. For details, refer to the user documentation. -- [zebra] An FPM interface has been added. This provides an alternate - interface to routing information and is geared at OpenFlow & co. -- [snmp] AgentX is now supported; the old smux backend is considered - deprecated. ospf6d has also had OSPFV3-MIB added. -- [*] several issues with configuration save/load/apply have been fixed, - in particular on ospf "max-metric router-lsa administrative" and - "distribute-list", bgpd "no neighbor activate", isisd "metric-style", -- [*] a lot of bugs have been fixed, please refer to the git log - -* Changes in Quagga 0.99.21 - -- [bgpd] BGP multipath support has been merged -- [bgpd] SAFI (Multicast topology) support has been extended to propagate - the topology to zebra. -- [bgpd] AS path limit functionality has been removed -- [babeld] a new routing daemon implementing the BABEL ad-hoc mesh routing - protocol has been merged. -- [isisd] a major overhaul has been picked up. Please note that isisd is - STILL NOT SUITABLE FOR PRODUCTION USE. -- [*] a lot of bugs have been fixed, please refer to the git log - -* Changes in Quagga 0.99.10 - -- [bgpd] 4-byte AS support added -- [bgpd] MRT format changes to version 2. Those relying on - bgpd MRT table dumps may need to update their tools. -- [bgpd] Added new route-map set statement: "as-path exclude" -- Zebra RIB updates queue has evolved into a multi-level - structure to address RIB consistency issues. - -* Changes in Quagga 0.99.2 - -- [bgpd] Work queues added to bgpd to split up update processing, - particularly beneficial when a peer session goes down. AS_PATH - parsing rewritten to be clearer, more robust and ready for 4-byte. - -- [ripd] Simple authentication is no longer the default authentication - mode for ripd. The default is now no-authentication. Any setups which - used simple authentication will probably need to update their - configuration manually. - -- [ospfd] 1s dead-interval with sub-second Hellos feature added. - SPF timers now specified in milliseconds, and with adaptive - hold-time support. RFC3137 Stub-router support added. Default ABR - type is now 'cisco'. - -- Solaris least privileges support added. - -* Changes in Quagga 0.99.1 - -- Zserv is now buffered via threads and non-blocking in most cases for both - clients and zebra, which should improve responsiveness of daemons when - they must send many messages to zebra. - -- 'show thread cpu' now displays both cpu+system and wall-clock time, - where getrusage() is available. - -- Background threads added and workqueue API added, with a - 'show work-queues' command. Thread scheduling improved slightly. - -- Zebra now has a work-queue for RIB processing. See 'show work-queues' in - the zebra daemon vty. - -- Support for interface renaming on Linux netlink systems. - -- GNU Zebra bgpd merges, including BGP Graceful-restart and "match ip - route-source" command. - -- Automatic logging of backtraces should daemons crash to assist in - diagnosis. See the documentation for more information on configuring - logging correctly, and set --enable-gcc-rdynamic if compiling with gcc. - -* Changes in Quagga 0.98.0 - -- Logging facilities upgraded. One can now specify a severity level - for each logging destination. And a new "show logging" command gives - thorough information on the current logging system configuration. - -- Watchquagga daemon added. This is not well tested yet. Please try - monitor mode first before enabling restart features. It is important - to make sure that the various timers are configured with appropriate - values for your site. - -- BGP route-server support added. See the texinfo documentation. - -- OSPF API initialisation is disabled by default even if compiled in. You - can enable it with -a/--apiserver command line switch. - -- "write-config integrated" vtysh command replaced with "service - integrated-vtysh-config" command. - -- Router id is now handled by zebra daemon and all daemons receive changes - from it. Router id can be overriden in daemons' configurations of course. - To fix common router id in zebra daemon you can either install non-127 - address on loopback or use "router-id x.x.x.x" command. - -- "secondary" keyword is removed from ip address configuration. All - supported OS'es have their own vision what's secondary address and - how to handle it. - -- Zebra no longer enables forwarding by default. If you rely on zebra to - enable forwarding make sure to add ' forwarding' statements - to your zebra configuration file. - -- All libraries are built and used shared, on platforms where libtool - supports shared libraries. - -- Router advertisement syntax is changed. In usual cases (if you didn't do - any fancy stuff) it's enough to change lines in configuration from: - "ipv6 nd prefix-advertisement X:X:X:X::/X 2592000 604800 autoconfig on-link" - to: - "ipv6 nd prefix X:X:X:X::/X" - - All router advertisement options are documented in texi documentation. - -- --enable-nssa configure switch is removed. NSSA support is stable enough. - -- Daemons don't look at current directory for config file any more. - -* Changes in Quagga 0.96.5 - -- include files are installed in $(prefix)/include/quagga. Programs - building against these includes should -I$(prefix)/include and e.g. - #include - -- New option --enable-exampledir puts example files in a separate - directory from $(sysconfdir), easing NetBSD pkgsrc hierarchy rules - compliance. - -- New configure options --enable-configfile-mask and - --enable-logfile-mask to set umask values for config and log - values. Masks default to 0600, matching previous behavior. - -- Import current CVS isisd from SourceForge, then merge it with - the Quagga's Framework. - -* Changes in Quagga 0.96.4 - -- Further fixes to ospfd, some relating to the PtP revert. Interface -lookups should be a lot more robust now. - -- Fix for a remote triggerable crash in vty layer. - -- Improvements to ripd, and addition of split horizon support. - -- Improved bgpd table support, now dumps at time of day intervals rather -than time from startup intervals. Much improved support for IPv6 table -dumps. show commands for views improved. - -* Changes in Quagga 0.96.3 - -- revert the 'generic PtP' patch. Means Quagga will no longer work with -FreeSWAN, however, on the plus side this gets rid of a lot of niggly bugs -which the PtP patch introduced. - -* Changes in Quagga 0.96.2 - -- Fix crash in ospfd - -* Changes in Quagga 0.96.1 - -- Iron out problem with the privileges definitions - -* Changes in Quagga 0.96 - -- Privilege support, daemons now run with the minimal privileges needed, see - the documentation for details. - -- NSSA ABR support in ospfd. - -- OSPF-API support merged in. - -- 6WIND patch merged in. - -* Changes in zebra-0.93 - -* Changes in bgpd - -** Configuration is changed to new format. - -* Changes in ospfd - -** Crush bugs which reported on Zebra ML is fixed. - -** Opaque LSA and TE LSA support is added by KDD R&D Laboratories, - Inc. - -* Chages in ospf6d - -** Many bugs are fixed. - -* Changes in zebra-0.92a - -* Changes in bgpd - -** Fix "^$" community list bug. - -** Below command's Address Family specific configurations are added - - nexthop-self - route-reflector-client - route-server-client - soft-reconfiguration inbound - -* Changes in zebra - -** Treat kernel type routes as EGP routes. - -* Changes in zebra-0.92 - -** Overall security is improved. Default umask is 0077. - -* Changes in ripd - -** If output interface is in simple password authentication mode, -substruct one from rtemax. - -* Changes in bgpd - -** IPv4 multicast and IPv6 unicast configuration is changed to so -called new config. All of AFI and SAFI specific configuration is -moved to "address-family" node. When you have many IPv6 only -configuration, you will see many "no neighbor X:X::X:X activate" line -in your configuration to disable IPv4 unicast NLRI exchange. In that -case please use "no bgp default ipv4-unicast" command to suppress the -output. Until zebra-0.93, old config is still left for compatibility. - -Old config -========== -router bgp 7675 - bgp router-id 10.0.0.1 - redistribute connected - network 192.168.0.0/24 - neighbor 10.0.0.2 remote-as 7675 - ipv6 bgp network 3ffe:506::/33 - ipv6 bgp network 3ffe:1800:e800::/40 - ipv6 bgp aggregate-address 3ffe:506::/32 - ipv6 bgp redistribute connected - ipv6 bgp neighbor 3ffe:506:1000::2 remote-as 1 - -New config -========== -router bgp 7675 - bgp router-id 10.0.0.1 - network 192.168.0.0/24 - redistribute connected - neighbor 10.0.0.2 remote-as 7675 - neighbor 3ffe:506:1000::2 remote-as 1 - no neighbor 3ffe:506:1000::2 activate -! - address-family ipv6 - network 3ffe:506::/33 - network 3ffe:1800:e800::/40 - aggregate-address 3ffe:506::/32 - redistribute connected - neighbor 3ffe:506:1000::2 activate - exit-address-family - -* Changes in ospfd - -** Internal interface treatment is changed. Now ospfd can handle -multiple IP address for an interface. - -** Redistribution of loopback interface's address works fine. - -* Changes in zebra-0.91 - -** --enable-oldrib configure option is removed. - -** HAVE_IF_PSEUDO part is removed. Same feature is now supported by -default. - -* Changes in ripd - -** When redistributed route is withdrawn, perform poisoned reverse. - -* Changes in zebra - -** When interface's address is removed, kernel route pointing out to -the address is removed. - -** IPv6 RIB is now based upon new RIB code. - -** zebra can handle same connected route to one interface. - -** New command for interface address. Currently this commands are -only supported on GNU/Linux with netlink interface. - -"ip address A.B.C.D secondary" -"ip address A.B.C.D label LABEL" - -* Changes in bgpd - -** BGP flap dampening bugs are fixed. - -** BGP non-blocking TCP connection bug is fixed. - -** "show ip bgp summary" shows AS path and community entry number. - -** New commands have been added. - "show ip bgp cidr-only" - "show ip bgp ipv4 (unicast|multicast) cidr-only" - "show ip bgp A.B.C.D/M longer-prefixes" - "show ip bgp ipv4 (unicast|multicast) A.B.C.D/M longer-prefixes" - "show ipv6 bgp X:X::X:X/M longer-prefixes" - "show ipv6 mbgp X:X::X:X/M longer-prefixes" - -** IPv6 IBGP nexthop change is monitored. - -** Unknown transitive attribute is passed with partial flag bit on. - -* Changes in ospfd - -** Fix bug of LSA MaxAge flood. - -** Fix bug of NSSA codes. - -* Changes in zebra-0.90 - -** From this beta release, --enable-unixdomain and --enable-newrib -becomes default. So both options are removed from configure.in. To -revert old behavior please specify below option. - ---enable-tcp-zebra # TCP/IP socket is used for protocol daemon and zebra. ---enable-oldrib # Turn on old RIB implementation. - -Old RIB implementation will be removed in zebra-0.91. - -** From this beta release --enable-multipath is supported. This -option is only effective on GNU/Linux kernel with -CONFIG_IP_ADVANCED_ROUTER and CONFIG_IP_ROUTE_MULTIPATH is set. - ---enable-multipath=ARG # ARG must be digit. When ARG is 0 unlimit multipath number. - -** From this release we do not include guile files. - -* Changes in lib - -** newlist.[ch] is merged with linklist.[ch]. - -** Now Zebra works on MacOS X public beta. - -** Access-list can have remark. "access-list WORD remark LINE" define -remark for specified access-list. - -** Key of key-chain is sorted by it's idetifier value. - -** prefix-list rule is slightly changed. The rule of "len <= ge-value -<= le-value" is changed to "len < ge-value <= le-value". - -** According to above prefix-list rule change, add automatic -conversion function of an old rule. ex.) 10.0.0.0/8 ge 8 -> 10.0.0.0/8 -le 32 - -** SMUX can handle SNMP trap. - -** In our event library, event thread is executed before any other -thread like timer, read and write event. - -** Robust method for writing configuration file and recover from -backing up config file. - -** Display "end" at the end of configuration. - -** Fix memory leak in vtysh_read(). - -** Fix memroy leak about access-list and prefix-list name. - -* Changes in zebra - -** UNIX domain socket server of zebra protocol is added. - -** Fix PointoPoint interface network bug. The destination network -should be installed into routing table instead of local network. - -** Metric value is reflected to kernel routing table. - -** "show ip route" display uptime of RIP,OSPF,BGP routes. - -** New RIB implementation is added. - -Now we have enhanced RIB (routing information base) implementation in -zebra. New RIB has many new features and fixed some bugs which exist -in old RIB code. - -*** Static route with distance value - - Static route can be specified with administrative distance. The - distance value 255 means it is not installed into the kernel. - Default value of distance for static route is 1. - - ip route A.B.C.D/M A.B.C.D <1-255> - ip route A.B.C.D/M IFNAME <1-255> - - If the least distance value's route's nexthop are unreachable, - select the least distance value route which has reachable nexthop is - selected. - - ip route 0.0.0.0/0 10.0.0.1 - ip route 0.0.0.0/0 11.0.0.1 2 - - In this case, when 10.0.0.1 is unreachable and 11.0.0.1 is - reachable. The route with nexthop 11.0.0.1 will be installed into - forwarding table. - - zebra> show ip route - S>* 0.0.0.0/0 [2/0] via 11.0.0.1 - S 0.0.0.0/0 [1/0] via 10.0.0.1 inactive - - If the nexthop is unreachable "inactive" is displayed. You can - specify any string to IFNAME. There is no need of the interface is - there when you configure the route. - - ip route 1.1.1.1/32 ppp0 - - When ppp0 comes up, the route is installed properly. - -*** Multiple nexthop routes for one prefix - - Multiple nexthop routes can be specified for one prefix. Even the - kernel support only one nexthop for one prefix user can configure - multiple nexthop. - - When you configure routes like below, prefix 10.0.0.1 has three - nexthop. - - ip route 10.0.0.1/32 10.0.0.2 - ip route 10.0.0.1/32 10.0.0.3 - ip route 10.0.0.1/32 eth0 - - If there is no route to 10.0.0.2 and 10.0.0.3. And interface eth0 - is reachable, then the last route is installed into the kernel. - - zebra> show ip route - S> 10.0.0.1/32 [1/0] via 10.0.0.2 inactive - via 10.0.0.3 inactive - * is directly connected, eth0 - - '*' means this nexthop is installed into the kernel. - -*** Multipath (more than one nexthop for one prefix) can be installed into the kernel. - - When the kernel support multipath, zebra can install multipath - routes into the kernel. Before doing that please make it sure that - setting --enable-multipath=ARG to configure script. ARG must be digit - value. When specify 0 to ARG, there is no limitation of the number - of the multipath. Currently only GNU/Linux with netlink interface is - supported. - - ip route 10.0.0.1/32 10.0.0.2 - ip route 10.0.0.1/32 10.0.0.3 - ip route 10.0.0.1/32 eth0 - - zebra> show ip route - S>* 10.0.0.1/32 [1/0] via 10.0.0.2 - * via 10.0.0.3 - is directly connected, eth0 - -*** Kernel message delete installed route. - - After zebra install static or dynamic route into the kernel. - - R>* 0.0.0.0/0 [120/3] via 10.0.0.1 - - If you delete this route outside zebra, old zebra does not reinstall - route again. Now the route is re-processed and properly reinstall the - static or dynamic route into the kernel. - -** GNU/Linux netlink socket handling is improved to fix race condition -between kernel message and user command responce. - -* Changes in bgpd - -** Add show neighbor's routes command. - - "show ip bgp neighbors (A.B.C.D|X:X::X:X) routes" - "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) routes" - "show ipv6 bgp neighbors (A.B.C.D|X:X::X:X) routes" - "show ipv6 mbgp neighbors (A.B.C.D|X:X::X:X) routes" - -** BGP passive peer support problem is fixed. - -** Redistributed IGP nexthop is passed to BGP nexthop. - -** On multiaccess media, if the nexthop is reachable nexthop is passed -as it is. - -** Remove zebra-0.88 compatibility commands. - - "match ip prefix-list WORD" - "match ipv6 prefix-list WORD" - - Instead of above please use below commands. - - "match ip address prefix-list WORD" - "match ipv6 address prefix-list WORD" - -** Fix bug of holdtimer is not reset when bgp cleared. - -** "show ip bgp summary" display peer establish/drop count. - -** Change "match ip next-hop" argument from IP address to access-list -name. - -** When "bgp enforce-first-as" is enabled, check EBGP peer's update -has it's AS number in the first AS number in AS sequence. - -** New route-map command "set community-delete COMMUNITY-LIST" is -added. Community matched the CoMMUNITY-LIST is removed from the -community. - -** BGP-MIB implementation is finished. - -** When BGP connection comes from unconfigured IP address, close -socket immediately. - -** Do not compare router ID when the routes comes from EBGP peer. -When originator ID is same, take shorter cluster-list route. If -cluster-list is same take smaller IP address neighbor's route. - -** Add "bgp bestpath as-path ignore" command. When this option is -set, do not concider AS path length when route selection. - -** Add "bgp bestpath compare-routerid". When this option is set, -compare router ID when the routes comes from EBGP peer. - -** Add "bgp deterministic-med" process. - -** BGP flap dampening feature is added. - -** When IBGP nexthop is changed, it is reflected to RIB. - -** Change "neighbor route-refresh" command to "neighbor capability -route-refresh". - -* Changes in ripd - -** Change "match ip next-hop" argument from IP address to access-list -name. - -** "no ip rip (send|receive)" command accept version number argument. - -** Memory leak related classfull network generation is fixed. - -** When a route is in garbage collection process (invalid with metric -16) and a router receives the same route with valid metric then route -was not installed into zebra rib, but only into ripd rib. Moreover , -it will never get into zebra rib, because ripd wrongly assumes it's -already there. - -* Change in ospfd - -** Fix bug of refreshing default route. - -** --enable-nssa turn on undergoing NSSA feature. - -** Fix bug of Hello packet's option is not properly set when interface -comes up. - -** Reduce unconditional logging. - -** Add nexthop to OSPF path only when it is not there. - -** When there is no DR on network (suppose you have only one router -with interface priority 0). It's router LSA does not contain the link -information about this network. - -** When you change a priority of interface from/to 0 -ISM_NeighborChange event should be scheduled in order to elect new -DR/BDR on the network. - -** When we add some LSA into retransmit list we need to check whether -the present old LSA in retransmit list is not more recent than the new -one. - -** In states Loading and Full the slave must resend its last Database -Description packet in response to duplicate Database Description -packets received from the master. For this reason the slave must wait -RouterDeadInterval seconds before freeing the last Database -Description packet. Reception of a Database Description packet from -the master after this interval will generate a SeqNumberMismatch -neighbor event. RFC2328 Section 10.8 - -** Virtual link can not configured in stub area. - -** Clear a ls_upd_queue queue of the interface when interface goes -down. - -** "no router ospf" unregister redistribution requests from zebra. - -** New command for virtual-link configuration is added. - - "area A.B.C.D virtual-link A.B.C.D" - "area A.B.C.D virtual-link A.B.C.D hello-interval <1-65535> retransmit-interval <3-65535> transmit-delay <1-65535> dead-interval <1-65535>" - "area A.B.C.D virtual-link A.B.C.D hello-interval <1-65535> retransmit-interval <3-65535> transmit-delay <1-65535> dead-interval <1-65535> authentication-key AUTH_KEY" - "area A.B.C.D virtual-link A.B.C.D authentication-key AUTH_KEY" - "area A.B.C.D virtual-link A.B.C.D hello-interval <1-65535> retransmit-interval <3-65535> transmit-delay <1-65535> dead-interval <1-65535> message-digest-key <1-255> md5 KEY" - "area A.B.C.D virtual-link A.B.C.D message-digest-key <1-255> md5 KEY" - -** Clear cryptographic sequence number when neighbor status is changed -to NSM down. - -** Make Summary LSA's origination and refreshment as same as other -type of LSA. - -** New OSPF pakcet read method. Now maximum packet length may be 65535 -bytes (maximum IP packet length). - -** Checking the age of the found LSA and if the LSA is MAXAGE we -should call refresh instead of originate. - -** Install multipath information to zebra. - -** Fix socket descriptor leak when system call failed. - -* Changes in ospf6d - -** Whole functionality has been rewritten as new code. new command -"show ipv6 ospf6 spf node", "show ipv6 ospf6 spf tree", "show ipv6 -ospf6 spf table" has been added. - -** Change to do not send garbage route whose nexthop is not linklocal -address. - -** "redistribute ospf6" was generated in "router ospf6" in config -file. It is fixed. - -** LSDB sync bug is fixed. - -** Fix bug of using unavailable route. - -* Changes in vtysh - -** route-map and access-list configuration is merged into one -configuration. - -** /usr/local/etc/Zebra.conf is integrated configuration file. "write -memory" in vtysh will write whole configuration to this file. - -** When -b option is specified to vtysh, vtysh read -/usr/local/etc/Zebra.conf file then pass the confuguration to proper -protocol daemon. So make all protocol daemon's configuration file -empty then invoke all daemon. After that vtysh -b will setup saved -configuration. - -zebrastart.sh -============= -/usr/local/sbin/zebra -d -/usr/local/sbin/ripd -d -/usr/local/sbin/ospfd -d -/usr/local/sbin/bgpd -d -/usr/local/bin/vtysh -b - -* Changes in zebra-0.89 - -* Changes in lib - -** distribute-list can set all interface's access-list and prefix-list -configuration. - -* Changes in ripd - -** "show ip protocols" display proper distribute-list settings and -distance settings. - -** When metric infinity route received withdraw the route from kernel -immediately it used to be wait garbage collection. - -** key-chain can be used for simple password authentication. - -** RIPv2 MIB getnext interface bug is fixed. - -* Changes in vtysh - -** --with-libpam enable PAM authentication for vtysh. - -** Now vtysh read vtysh.conf. This file should be -${SYSCONFDIR}/etc/vtysh.conf for security reason. Usually it is -/usr/local/etc/vtysh.conf. - -** "username WORD nopassword" command is added to vtysh. - -* Chagees in ospfd - -** NBMA interface support is added. - -** OSPF area is sorted by area ID. - -** New implementation of OSPF refreesh. - -** OSPF-MIB read function is partly added. - -* Changes in bgpd - -** When the peering is done by ebgp-multihop, nexthop is looked up -like IBGP routes. - -** "show ip mbgp" commands are changed to "show ip bgp ipv4 -multicast". - -** New terminal commands are added. - "show ip bgp ipv4 (unicast|multicast) filter-list WORD" - "show ip bgp ipv4 (unicast|multicast) community" - "show ip bgp ipv4 (unicast|multicast) community-list WORD" - "show ip bgp ipv4 (unicast|multicast) community-list WORD exact-match" - -** MBGP soft-reconfiguration command is added. - "clear ip bgp x.x.x.x ipv4 (unicast|multicast) in" - "clear ip bgp x.x.x.x ipv4 (unicast|multicast) out" - "clear ip bgp x.x.x.x ipv4 (unicast|multicast) soft" - "clear ip bgp <1-65535> ipv4 (unicast|multicast) in" - "clear ip bgp <1-65535> ipv4 (unicast|multicast) out" - "clear ip bgp <1-65535> ipv4 (unicast|multicast) soft" - "clear ip bgp * ipv4 (unicast|multicast) in" - "clear ip bgp * ipv4 (unicast|multicast) out" - "clear ip bgp * ipv4 (unicast|multicast) soft" - -** MED related commands are added. - "bgp deterministic-med" - "bgp bestpath med confed" - "bgp bestpath med missing-as-worst" - -** "bgp default local-preference" command is added. - -** BGP confederation peer's routes are passed to zebra like IBGP route. - -** Community match command is added. - "show ip bgp community " - "show ip bgp community exact-match" - -** EBGP multihop route treatment bug is fixed. Now nexthop is -resolved by IGP routes. - -** Some commands are added to show routes by filter-list and community -value. - "show ip bgp ipv4 (unicast|multicast) filter-list WORD" - "show ip bgp ipv4 (unicast|multicast) community" - "show ip bgp ipv4 (unicast|multicast) community-list WORD" - "show ip bgp ipv4 (unicast|multicast) community-list WORD exact-match" - -* Changes in zebra - -** zebra read interface's address information using getifaddrs() when -it is available. - -** Reflect IPv6 interface's address change to protocol daemons. - -* Changes in zebra-0.88 - -* Changes in lib - -** "exact-match" option is added to "access-list" and "ipv6 -access-list" command. If this option is specified, the prefix and -prefix length is compared as exact match mode. - -* Changes in zebra - -** New Zebra message ZEBRA_REDISTRIBUTE_DEFAULT_ADD and -ZEBRA_REDISTRIBUTE_DEFAULT_DELTE are added. - -** Default administrative distance value is changed. - - Old New ------------------------------------------- -system 10 0 -kernel 20 0 -connected 30 0 -static 40 1 -rip 50 120 -ripng 50 120 -ospf 60 110 -ospf6 49 110 -bgp 70 200(iBGP) 20(eBGP) ------------------------------------------- - -** Distance value can be passed from protocol daemon to zebra. - -** "show ip route" shows [metric/distance] value pair. - -** Zebra Protocol is changed to support multi-path route and distance -value. - -* Changes in ospfd - -** "default-information originate [always]" command is added. - -** "default-metric <0-16777214>" command is added. - -** "show ip ospf database" command is integrated. LS-ID and AdvRouter can - be specifed. The commands are - - show ip ospf database TYPE LS-ID - show ip ospf database TYPE LS-ID ADV-ROUTER - show ip ospf database TYPE LS-ID self-originate - show ip ospf database TYPE self-originate - -** route-map support for `redistribute' command are added. - Supported `match' statements are - - match interface - match ip address - match next-hop - - Supported `set' statements are - - set metric - set metric-type - -** Pass OSPF metric value to zebra daemon. - -* Changes in ripd - -** When specified route-map does not exist, it means all deny. - -** "default-metric <1-16>" command is added. - -** "offset-list ACCESS-LIST-NAME <0-16>" and "offset-list -ACCESS-LIST-NAME <0-16> IFNAME" commands are added. - -** "redistribute ROUTE-TYPE metric <0-16>" command is added. - -** "default-information originate" command is added. - -** "ip split-horizon" and "no ip split-horizon" is added to interface -configuration. - -** "no router rip" command is added. - -** "ip rip authentication mode (md5|text)" is added to interface -configuration. - -** "ip rip authentication key-chain KEY-CHAIN" is added to interface -configuration. - -** Pass RIP metric value to zebra daemon. - -** Distance manipulation functions are added. - -* Changes in bgpd - -** Fix bug of next hop treatment for MPLS-VPN route exchange. - -** BGP peer MIB is updated. - -** Aggregated route has origin IGP, atomic-aggregate and proper -aggregator attribute. - -** Suppressed route now installed into BGP table. It is only -suppressed from announcement. - -** BGP router-id is properly set after "no router bgp ASN" and "router -bgp ASN". - -** Add check for nexthop is accessible or not for IBGP routes. - -** Add cehck for nexthop is on connected or not for EBGP routes. - -** "dump bgp route" command is changed to "dump bgp route-mrt" for -generating MRT compatible dump output. - -** Soft reconfiguration inbound and outbound is supported. - -** Route refresh feature is supported. - -* Changes in vtysh - -** VTY shell is now included into the distribution. - -* Changes in zebra-0.87 - -* Changes in lib - -** "show startup-config" command is added. - -** "show history" command is added. - -** Memory statistics command is changed. New command - - show memory all - show memory lib - show memory rip - show memory ospf - show memory bgp - -are added. - -** Filters can be removed only specify it's name. New command - - no access-list NAME - no ip community-list NAME - no ip as-path access-list NAME - no route-map NAME - -are added. - -** At any node, user can view/save user configuration. - - write terminal - write file - wirte memory - -are added to every node in default. - -** LCD completion is added. For example both "ip" and "ipv6" command -are exist, "i" then press TAB will be expanded to "ip". - -* Changes in bgpd - -** "show ip bgp" family shows total number of prefixes. - -** "no bgp default ipv4-unicast" command is added. - -** Extended Communities support is added. - -** "no neighbor PEER send-community extended" command is added. - -** MPLS-VPN PE-RR support is added. - - New address family vpnv4 unicast is introduced. - - ! - address-family vpnv4 unicast - neighobr PEER activate - network A.B.C.D rd RD tag TAG - exit-address-family - ! - - To make it route-reflector, please configure it under normal router -bgp ASN. - - ! - router bgp 7675 - no bgp default ipv4-unicast - bgp router-id 10.0.0.100 - bgp cluster-id 10.0.0.100 - neighbor 10.0.0.1 remote-as 65535 - neighbor 10.0.0.1 route-reflector-client - neighbor 10.0.0.2 remote-as 65535 - neighbor 10.0.0.2 route-reflector-client - neighbor 10.0.0.3 remote-as 65535 - neighbor 10.0.0.3 route-reflector-client - ! - address-family vpnv4 unicast - neighbor 10.0.0.1 activate - neighbor 10.0.0.2 activate - neighbor 10.0.0.3 activate - exit-address-family - ! - -* Changes in ospfd - -** Many many bugs are fixed. - -* Changes in ripd - -** Better interface up/down event handle. - -* Changes in zebra - -** Better interface up/down event handle. - -* Changes in zebra-0.86 - -* Changes in lib - -** Fix bug of exec-timeout command which may cause crush. - -** Multiple same policy for "access-list", "ip prefix-list, "as-path -access-list", "ip community-list" is not duplicated. - -** It used to be "ip prefix-list A.B.C.D/M" match routes which mask >= -M. Now default behavior is exact match so it only match routes which -mask == M. - -* Changes in bgpd - -** "match ip address prefix-list" is added to route-map. - -** A route without local preference is evaluated as 100 local preference. - -** Select smaller router-id route when other values are same. - -** Compare MED only both routes comes from same neighboring AS. - -** "bgp always-compare-med" command is added. - -** Now MED value is passed to IBGP peer. - -** When neighbor's filter is configured with non-existent access-list, -as-path access-list, ip prefix-list, route-map. The behavior is -changed from all permit to all deny. - -* Changes in ospfd - -** Fix bug of external route tag byte order. - -** OSPF Neighbor deletion bug which cause crush is fixed. - -** Some route calculation bug are fixed. - -** Add sanity check with router routing table. - -** Fix bug of memory leak about linklist. - -** Fix bug of 1-WayReceived in NSM. - -** Take care of BIGENDIAN architecture. - -** Fix bug of NSM state flapping between ExStart and Exchange. - -** Fix bug of Network-LSA originated in stub network. - -** Fix bug of MS flag unset. - -** Add to schedule router_lsa origination when the interface cost -changes. - -** Increment LS age by configured interface transmit_delay. - -** distribute-list is reimplemented. - -** Fix bug of refresh never occurs. - -** Fix bug of summary-LSAs reorigination. Correctly copy -OSPF_LSA_APPROVED flag to new LSA. when summary-LSA is reoriginatd. - -** Fix bug of re-origination when a neighbor disappears. - -** Fix bug of segmentation fault with DD retransmission. - -** Fix network-LSA re-origination problem. - -** Fix problem of remaining withdrawn routes on zebra. - -* Changes in ripd - -** Do not leave from multicast group when interface goes down bug is -fixed. - -* Changes in zebra - -** Remove client structure when client dies. - -** Take care static route when interface goes up/down. - -* Changes in zebra-0.85 - -* Changes in bgpd - -** "transparent-nexthop" and "transparenet-as" commands are added. - -** Route reflector's originator-id bug is fixed. - -* Changes in ospfd - -** Fix bug of OSPF LSA memory leak. - -** Fix bug of OSPF external route memory leak. - -** AS-external-LSA origination bug was fixed. - -** LS request treatment is completely rewritten. Now performance is -drastically improved. - -* Changes in ripd - -** RIPv1 update is done by class-full manner. - -* Changes in zebra-0.84b - -* Changes in lib - -** Fix bug of inet_pton return value handling - -* Changes in bgpd - -** Fix bug of BGP-4+ link-local address nexthop check for IBGP peer. - -** Don't allocate whole buffer for displaying "show ip bgp". Now it -consume only one screen size memory. - -* Changes in ripd - -** Fix debug output string. - -** Add RIP peer handling. RIP peer are shown by "show ip protocols". - -* Changes in zebra-0.84a - -* Changes in bgpd - -** Fix serious bug of BGP-4+ peering under IPv6 link-local address. - Due to the bug BGP-4+ peering may not be established. - -* Changes in zebra-0.84 - -* Changes in lib - -** IPv6 address and prefix parser is added to VTY by Toshiaki Takada - . DEFUN string is "X:X::X:X" for IPv6 address, - "X:X::X:X/M" for IPv6 prefix. You can use it like this. - - DEFUN (func, cmd, "neighbor (A.B.C.D|X:X::X:X) remote-as <1-65535>") - -** VTY configuration is locked during configuration. This is for - avoiding unconditional crush from two terminals modify the - configuration at the same time. "who" command shows which termnal - lock the configuration. VTY which has '*' character at the head of - line is locking the configuration. - -** Old logging functions are removed. Functions like - log_open,log_close,openlog are deleted. Instead of that please use - zlog_* functions. zvlog_* used in ospf6d are deleted also. - -** "terminal monitor" command is added. "no terminal monitor" is for - disabling. This command simply display logging information to the - VTY. - -** dropline.[ch] files are deleted. - -* Changes in bgpd - -** BGP neighbor configuration are sorted by it's IP address. - -** BGP peer configuration and actual peer is separated. This is - preparation for Route Server support. - -** "no neighbor PEER" command is added. You can delete neighbor - without specifying AS number. - -** "no neighbor ebgp-multihop" command is added. - -** "no neighbor port PORT" command is added. - -** To conform RFC1771, "neighbor PEER send-community" is default - behavior. If you want to disable sending community attribute, - please specify "no neighbor PEER send-community" to the peer. - -** "neighbor maximum-prefix NUMBER" command is added. - -** Multi-protocol extention NLRI is proceeded only when the peer is - configured proper Address Family and Subsequent Address Family. If - not, those NLRI are simply ignored. - -** Aggregate-address support is improved. Currently below commands - works. - - "aggregate-address" - "aggregate-address summary-only" - "no aggregate-address" - "no aggregate-address summary-only" - - "ipv6 bgp aggregate-address" - "ipv6 bgp aggregate-address summary-only" - "no ipv6 bgp aggregate-address" - "no ipv6 bgp aggregate-address summary-only" - -** redistribute route-map bug is fixed. - -** MBGP support becomes default. "configure" option --enable-mbgp is - removed. - -** New command "neighbor PEER timers connect <1-65535>" is added. - -** New command "neighbor PEER override-capability" is added. - -** New command "show ip bgp neighbor A.B.C.D advertised-route" is added. - -** New command "show ip bgp neighbor A.B.C.D routes" is added. To use - this command, you have to configure neighbor with - "neighbor A.B.C.D soft-reconfiguration inbound" beforehand. - - -* Changes in zebra-0.83 - -* bgpd - -** Serious bug fix about fetching global and link-local address at the -same time. Due to this bug, corrupted IPv6 prefix is generated. If -you uses bgpd for BGP-4+ please update to this version. The bug is -introduced in zebra-0.82. - -** When bgpd send Notify message, don't use thread manager. It is now -send to neighbor immediately. - -* Changes in zebra-0.82 - -** Solaris 2.6 support is added by Michael Handler -. - -** MBGP support is added by Robert Olsson . -Please specify --enable-mbgp to configure script. This option will be -removed in the future and MBGP support will be default. - -* Changes in zebra - -** When interface goes down, withdraw connected routes from routing -table. When interface goes up, restore the routes to the routing -table. - -** `show interface' show interface's statistics on Linux and BSD with -routing socket. - -** Now zebra can get MTU value on BSDI/OS. - -* Changes in bgpd - -** Add capability option support based upon -draft-ietf-idr-bgp4-cap-neg-04.txt. - -** Add `show ipv6 bgp prefix-list' command. - -** Check self AS appeared in received routes. - -** redistribute route-map support is added. - -** BGP packet dump feature compatible with MRT. - -* Changes in ripd - -** Fix bug of `timers basic' command's argument format. - -* Changes in ripngd - -** Calculate max RTE using interface's MTU value. - -* Changes in ospfd - -** Some correction to LSU processing. - -** Add check for lsa->refresh_list. - -* Changes in ospf6d - -** Many debug feature is added. - -* Changes in zebra-0.81 - -** SNMP support is disabled in default.--enable-snmp option is added -to configure script. - -* Changes in bgpd - -** Fix FSM bug which introduced in zebra-0.80. - -* Changes in zebra-0.80 - -* access-list - - New access-list name space `ipv6 access-list' is added. At the same - time, `access-list' statemant only accepts IPv4 prefix. Please be - careful if you use IPv6 filtering. You will need to change your - configuration. For IPv6 filtering please use `ipv6 access-list'. - - As of zebra-0.7x, user can use `access-list' for both IPv4 and IPv6 - filtering. - - ! zebra-0.7x - access-list DML-net permit 203.181.89.0/24 - access-list DML-net permit 3ffe:506::0/32 - access-list DML-net deny any - ! - - Above configuration is not valid for zebra-08x. Please add `ipv6' - before 'access-list' when you configure IPv6 filtering. - - ! zebra-0.8x - access-list DML-net permit 203.181.89.0/24 - access-list DML-net deny any - ! - ipv6 access-list DML-net permit 3ffe:506::0/32 - ipv6 access-list DML-net deny any - ! - -* prefix-list - - And also new prefix-list name space `ipv6 prefix-list' is added. It - is the same as the change of `access-list'. `ip prefix-list' now only - accept IPv4 prefix. It was source of confusion that `ip prefix-list' - can be used both IPv4 and IPv6 filtering. Now name space is separated - to clear the meaning of the filter. - - If you use `ip prefix-list' for IPv6 filtering, please change the - stetement. - - ! zebra-0.7x - ip prefix-list 6bone-filter seq 5 permit 3ffe::/17 le 24 ge 24 - ip prefix-list 6bone-filter seq 10 permit 3ffe:8000::/17 le 28 ge 28 - ip prefix-list 6bone-filter seq 12 deny 3ffe::/16 - ip prefix-list 6bone-filter seq 15 permit 2000::/3 le 16 ge 16 - ip prefix-list 6bone-filter seq 20 permit 2001::/16 le 35 ge 35 - ip prefix-list 6bone-filter seq 30 deny any - ! - - Now user can explicitly configure it as IPv6 prefix-list. - - ! zebra-0.8x - ipv6 prefix-list 6bone-filter seq 5 permit 3ffe::/17 le 24 ge 24 - ipv6 prefix-list 6bone-filter seq 10 permit 3ffe:8000::/17 le 28 ge 28 - ipv6 prefix-list 6bone-filter seq 12 deny 3ffe::/16 - ipv6 prefix-list 6bone-filter seq 15 permit 2000::/3 le 16 ge 16 - ipv6 prefix-list 6bone-filter seq 20 permit 2001::/16 le 35 ge 35 - ipv6 prefix-list 6bone-filter seq 30 deny any - ! - -* RIP configuration - - If you want to filter only default route (0.0.0.0/0) and permit other - routes, it was hard to do that. Now `ip prefix-list' can be used for - RIP route filtering. - - New statement: - - `distribute-list prefix PLIST_NAME (in|out) IFNAME' - - is added to ripd. So you can configure on eth0 interface accept all - routes other than default routes. - - ! - router rip - distribute-list prefix filter-default in eth0 - ! - ip prefix-list filter-default deny 0.0.0.0/0 le 0 - ip prefix-list filter-default permit any - ! - -* RIPng configuration - - Same change is done for ripngd. You can use `ipv6 prefix-list' for - filtering. - - ! - router ripng - distribute-list prefix filter-default in eth0 - ! - ipv6 prefix-list filter-default deny ::/0 le 0 - ipv6 prefix-list filter-default permit any - ! - -* BGP configuration - - So far, Multiprotocol Extensions for BGP-4 (RFC2283) configuration is - done with traditional IPv4 peering statement like blow. - - ! - router bgp 7675 - neighbor 3ffe:506::1 remote-as 2500 - neighbor 3ffe:506::1 prefix-list 6bone-filter out - ! - - For separating configuration IPv4 and IPv6, and for retaining Cisco - configuration compatibility, now IPv6 configuration is done by IPv6 - specific statement. IPv6 BGP configuration is done by statement which - start from `ipv6 bgp'. - - ! - router bgp 7675 - ! - ipv6 bgp neighbor 3ffe:506::1 remote-as 2500 - ipv6 bgp neighbor 3ffe:506::1 prefix-list 6bone-filter out - ! - - At the same time some IPv6 specific commands are deleted from IPv4 - configuration. - - o redistribute ripng - o redistribute ospf6 - o neighbor PEER version BGP_VERSION - o neighbor PEER interface IFNAME - - Those commands are only accepted as like below. - - o ipv6 bgp redistribute ripng - o ipv6 bgp redistribute ospf6 - o ipv6 bgp neighbor PEER version BGP_VERSION - o ipv6 bgp neighbor PEER interface IFNAME - - And below new commands are added. - - o ipv6 bgp network IPV6_PREFIX - o ipv6 bgp redistribute static - o ipv6 bgp redistribute connected - o ipv6 bgp neighbor PEER remote-as <1-65535> [passive] - o ipv6 bgp neighbor PEER ebgp-multihop [TTL] - o ipv6 bgp neighbor PEER description DESCRIPTION - o ipv6 bgp neighbor PEER shutdown - o ipv6 bgp neighbor PEER route-reflector-client - o ipv6 bgp neighbor PEER update-source IFNAME - o ipv6 bgp neighbor PEER next-hop-self - o ipv6 bgp neighbor PEER timers holdtime <0-65535> - o ipv6 bgp neighbor PEER timers keepalive <0-65535> - o ipv6 bgp neighbor PEER send-community - o ipv6 bgp neighbor PEER weight <0-65535> - o ipv6 bgp neighbor PEER default-originate - o ipv6 bgp neighbor PEER filter-list FILTER_LIST_NAME (in|out) - o ipv6 bgp neighbor PEER prefix-list PREFIX_LIST_NAME (in|out) - o ipv6 bgp neighbor PEER distribute-list AS_LIST_NAME (in|out) - o ipv6 bgp neighbor PEER route-map ROUTE_MAP_NAME (in|out) - - And some utility commands are introduced. - - o clear ipv6 bgp [PEER] - o show ipv6 bgp neighbors [PEER] - o show ipv6 bgp summary - - I hope these changes are easy to understand for current Zebra users... - -* To restrict connection to VTY interface. - - It used to be both IPv4 and IPv6 filter can be specified with one - access-list. Then the access-list can be appried to VTY interface - with `access-class' stetement in `line vty' node. Below is example in - zebra-0.7x. - - ! - access-list local-only permit 127.0.0.1/32 - access-list local-only permit ::1/128 - access-list local-only deny any - ! - line vty - access-class local-only - ! - - Now IPv4 and IPv6 filter have each name space. It is not possible to - specify IPv4 and IPv6 filter with one access-list. For setting IPv6 - access-list in `line vty', `ipv6 access-class' statement is - introduced. Let me show the configuration in zebra-0.8x. - - ! - access-list local-only permit 127.0.0.1/32 - access-list local-only deny any - ! - ipv6 access-list local-only permit ::1/128 - ipv6 access-list local-only dny any - ! - line vty - access-class local-only - ipv6 access-class local-only - ! - -* route-map - - New IPv6 related route-map match commands are added. - - o match ipv6 address - o match ipv6 next-hop - - Please change your configuration if you use IP match statement for - IPv6 route. - - zebra-0.7x config - ================= - ! - access-list all permit any - ! - route-map set-nexthop permit 10 - match ip address all - set ipv6 next-hop global 3ffe:506::1 - set ipv6 next-hop local fe80::cbb5:591a - ! - - zebra-0.8x config - ================= - ! - ipv6 access-list all permit any - ! - route-map set-nexthop permit 10 - match ipv6 address all - set ipv6 next-hop global 3ffe:506::1 - set ipv6 next-hop local fe80::cbb5:591a - ! - -* zebra connection - - Protocol daemon such as ripd, bgpd, ospfd will reconnect zebra daemon - when the connection fail. Those daemons try to connect zebra every 10 - seconds first three trial, then the interval changed to 60 seconds. - After all, if ten connections are fail, protocol daemon give up the - connection to the zebra daemon. - -* SNMP support (is not yet finished) - - Zebra uses SMUX protocol (RFC1227) for making communication with SNMP - agent. Currently lib/smux.c can be compiled only with ucd-snmp-4.0.1 - and http://ucd-snmp.ucdavis.edu/patches/012.patch. It can not be - compiled with ucd-snmp-3.6.2. - - After applying the patch to ucd-snmp-4.0.1, please configure it with - SMUX module. - - % configure --with-mib-modules=smux - - After compile & install ucd-snmp-4.0.1, you will need to configure - smuxpeer. I'm now using below configuration. - - /usr/local/share/snmp/snmpd.conf - ================================ - smuxpeer 1.3.6.1.6.3.1 test - - Above 1.3.6.1.6.3.1 and test is temporary configuration which is hard - coded in lib/smux.c. Yes, I know it is bad, I'll change it ASAP. - -* HUP signal treatment - - From zebra-0.80, ripd will reload it's configuration file when ripd - receives HUP signal. Other daemon such as bgpd, ospfd will support - HUP signal treatment soon. - -* Changes in zebra-0.79 - -* Changes in zebra - -** Broadcast address setting on Linux box bug is fixed. - -** Protocol daemon can install connected IPv6 route into the kernel. - -** Now zebra can handle blackhole route. - -* Changes in ripd - -** Add route-map feature for RIP protocol. - -** In case of RIP version 2 routing table entry has IPv4 address and -netmask pair which host part bit is on, ignore the entry. - -* Changes in ripngd - -** Change CMSG_DATA cast from (u_char *) to (int *). (u_char *) does -not work for NetBSD-currnet on SparcStation 10. - -* Changes in ospfd - -** MaxAge LSA treatment is added. - -** ABR/ASBR functionality is added. - -** Virtual Link funtionality is added. - -** ABR behaviors IBM/Cisco/Shortcut is added. - -* Changes in ospf6d - -** Enclosed KAME specific part with #ifdef #endif - -* Changes in zebra-0.78 - -* Changes in lib - -** SNMP support is started. - -** Now Zebra can work on BSD/OS 4.X. - -** Now Zebra can compiled on vanilla OpenBSD 2.5 but not yet working correcltly. - -* Changes in zebra - -** Interface index detection using ioctl() bug is fixed. - -** Interface information protocol is changed. Now interface -addition/deletion and interface's address addition/deletion is -separated. - -* Changes in bgpd - -** BGP hold timer bug is fixed. - -** BGP keepavlie timer becomes configurable. - -* Changes in ripd - -** When making reply to rip's REQUEST message, fill in -RIP_METRIC_INFINITY with network byte order using htonl (). - -** Pass host byte order address to IN_CLASSC and IN_CLASSB macro. - -* Changes in ospfd - -** LSA flooding works. - -** Fix bug of DD processing. - -** Fix bug of originating router-LSA bug is fixed. - -** LSA structure is changed to support LSA aging. - -* Changes in ospf6d - -** `ip6' statement in configuration is changed to `ipv6'. - -* Changes in zebra-0.77 - -* Changes in lib - -** SIGUSR1 reopen logging file. - -** route-map is extended to support multi-protocol routing -information. - -** When compiling under GNU libc 2.1 environment don't use inet6-apps. - -* Changes in zebra - -** Basic IPv6 router advertisement codes added. It is not yet usable. - -** Fix IPv6 route addition/deletion bug is fixed. - -** `show ip route A.B.C.D' works - -* Changes in bgpd - -** When invalid unfeasible routes length comes, bgpd send notify then -continue to process the packet. Now bgpd stop parsing invalid packet -then return to main loop. - -** BGP-4+ withdrawn routes parse bug is fixed. - -** When BGP-4+ information passed to non shared network's peer, trim -link-local next-hop information. - -** `no redistribute ROUTE_TYPE' withdraw installed routes from BGP -routing information. - -** `show ipv6 route IPV6ADDR' command added. - -** BGP start timer has jitter. - -** Holdtimer configuration bug is fixed. Now configuration does not -show unconfigured hold time value. - -* Changes in ripngd - -** Now update timer (default 30 seconds) has +/- 50% jitter value. - -** Add timers basic command. - -** `network' configuration is dynamically reflected. - -** `timers basic ' added. - -* Changes in ripd - -** Reconstruct almost codes. - -** `network' configuration is dynamically reflected. - -** RIP timers now conforms to RFC2453. So user can configure update, -timeout, garbage timer. - -** `timers basic ' works. - -* Changes in ospfd - -** Bug of originating network LSA is fixed. - -** `no router ospf' core dump bug is fixed. - -* Changes in ospf6d - -** Redistribute route works. - -* Changes in zebra-0.76 - -* Changes in lib - -** configure.in Linux IPv6 detection problem is fixed. - -** Include SERVICES file to the distribution - -** Update zebra.texi to zebra-0.76. - -* Changes in zebra-0.75 - -* Changes in lib - -** `termnal length 0' bug is fixed. - -* Changes in zebra - -** When zebra starts up, sweep all zebra installed routes. If -k or ---keep_kernel option is specified to zebra dameon. This function is -not performed. - -* Changes in ripngd - -** Aggreagte address command supported. In router ripngd, -`aggregate-address IPV6PREFIX' works. - -* Changes in bgpd - -** Input route-map's bug which cause segmentation violation is fixed. - -** route-map method improved. - -** BGP-4+ nexthop detection improved. - -** BGP-4+ route re-selection bug is fixed. - -** BGP-4+ iBGP route's nexthop calculation works. - -** After connection Established `show ip bgp neighbor' display BGP TCP -connection's source and destination address. - -** In case of BGP-4+ `show ip bgp neighbor' display BGP-4+ global and -local nexthop which used for originated route. This address will be -used when `next-hop-self'. - -* Changes in ospfd - -** Fix bug of DR election. - -** Set IP precedence field with IPTOS_PREC_INTERNET_CONTROL. - -** Schedule NeighborChange event if NSM status change. - -** Never include a neighbor in Hello packet, when the neighbor goes -down. - -* Changes in zebra-0.74 - -* Changes in lib - -** Now `terminal length 0' means no line output control. - -** `line LINES' command deleted. Instead of this please use `terminal -length <0-512>'. - -** `terminal length <0-512>' is each vty specific configuration so it -can not be configured in the configuration file. If you want to -configure system wide line control, please use `service -terminal-length <0-512>'. This configuration affects to the all vty -interface. - -* Changes in zebra - -** Installation of IPv6 route bug is fixed. - -* Changes in bgpd - -** Very serious bug of bgp_stop () is fixed. When multiple route to -the same destination exist, bgpd try to announce the information to -stopped peer. Then add orphan write thread is added. This cause -many strange behavior of bgpd. - -** Router-id parsing bug is fixed. - -** With BGP-4+ nexthop installation was done with global address but -it should be link-local address. This bug is fixed now. - -** When incoming route-map prepend AS, old AS path remained. Now bgpd -free old AS path. - -** `neighbor PEER weight <0-65535>' command added. - -* Changes in ripngd - -** Almost codes are rewritten to conform to RFC2080. - -* Changes in ospfd - -** SPF calculation timer is added. Currently it is set to 30 seconds. - -** SPF calculation works now. - -** OSPF routing table codes are added. - -** OSPF's internal routes installed into the kernel routing table. - -** Now `ospfd' works as non-area, non-external route support OSPF -router. - -** Call of log_rotate() is removed. - -* Changes in ospf6d - -** LSA data structure is changed. - -** Call of log_rotate() is removed. - -* Changes in zebra-0.73 - -* Changes in lib - -** `config terminal' is changed to `configure terminal'. - -** `terminal length <0-512>' command is added. - -** Variable length argument was specified by `...'. Now all strings -started with character `.' is variable length argument. - -* Changes in zebra - -** Internal route (such as iBGP, internal OSPF route) handling works -correctly. - -** In interface node, `ipv6 address' and `no ipv6 address' works. - -** Interface's address remain after `no ip address' bug is fixed. - -** Host route such as IPv4 with /32 mask and IPv6 with /128 mask -didn't set RTF_GATEWAY even it has gateway. This bug if fixed now. - -* Changes in bgpd - -** `match as-path' argument is used to be specify AS PATH value itself -directly (e.g. ^$). But it is changed to specify `ip as-apth -access-list' name. - -** iBGP route handle works without getting error from the kernel. - -** `set aggregator as AS A.B.C.D' command is added to route-map. - -** `set atomic-aggregate' command is added to bgpd's routemap. - -** Announcement of atomic aggregate attribute and aggregator attribute -works. - -** `update-source' bug is fixed. - -** When a route learned from eBGP is announced to iBGP, local -preference was set to zero. But now it set to -DEFAULT_LOCAL_PREF(100). - -* Changes in ripd - -** RIPv1 route filter bug is fixed. - -** Some memory leak is fixed. - -* Changes in ospfd - -** Fix bug of DR Election. - -** Fix bug of adjacency forming. - -* Changes in ospf6d - -** Clean up logging message. - -** Reflect routing information to zebra daemon. - -* Changes in zebra-0.72 - -* Changes in lib - -** When getsockname return IPv4 mapped IPv6 address. Convert it to -IPv4 address. - -* Changes in bgpd - -** Change route-map's next-hop related settings. - -set ip nexthop -> set ip next-hop -set ipv6 nexthop global -> set ipv6 next-hop global -set ipv6 nexthop local -> set ipv6 next-hop local - -** Add `next-hop-self' command. - -* Changes in ospfd - -** Fix bug of multiple `network area' directive crashes. - -* Changes in zebra-0.71 - -* Changes in lib - -** `log syslog' command is added. - -** Use getaddrinfo function to bind IPv4/IPv6 server socket. - -** `no banner motd' will suppress motd output when user connect to VTY. - -** Bind `quit' command to major nodes. - -* Changes in zebra - -** Point-to-point link address handling bug is fixed. - -* Changes in bgpd - -** AS path validity check is added. If malformed AS path is received -NOTIFY Malformed AS path is send to the peer. - -** Use getaddrinfo function to bind IPv4/IPv6 server socket. - -* Changes in ripd - -** Connected network announcement bug is fixed. - -** `broadcast' command is deleted. - -** `network' command is added. - -** `neighbor' command is added. - -** `redistribute' command is added. - -** `timers basic' command is added. - -** `route' command is added. - -* Changes in ripngd - -** Fix metric calculation bug. - -* Changes in ospfd - -** Check sum bug is fixed. - -* Chanegs in ospf6d - -** Routing table code is rewritten. - -* Changes in zebra-0.70 - -* Changes in zebra - -** Critical routing information base calculation bug check is fixed. - -** zebra ipv4 message is extended to support external/internal route -flavor. - -** Now if internal route doesn't has direct connected nexthop, then -nexthop is calculated by looking up IGP routing table. - -* Changes in bgpd - -** `neighbor PEER update-source IFNAME' command added as ALIAS to -`neighbor PEER interface IFNAME'. - -* Changes in ospfd - -** DD null pointer bug is fixed. - -* Changes in zebra-0.69 - -* Changes in zebra - -** zebra redistirbution supports dynamic notification of the route -change. If you add static route while running zebra, it will be -reflected to other protocol daemon which set `redistribute static'. - -** If static route installation is failed due to the error. The -static route is not added to the configuration and zebra routing -table. - -** zebra sets forwarding flag to on when it starts up. - -** `no ip forwarding' turn off IPv4 forwarding. - -** `no ipv6 forwarding' turn off IPv6 forwarding. - -** Change `show ipforward' command to `show ip forwarding'. - -** Change `show ipv6forward' command to `show ipv6 forwarding'. - -** `ip route A.B.C.D/M INTERFACE' works. So you can set `ip route -10.0.0.0/8 eth0'. - -* Changes in bgpd - -** `neighbor PEER send-community' command is added. If the option is -set, bgpd will send community attribute to the peer. - -** When a BGP route has no-export community attribute and -send-community is set to the peer, the route is not announced to the -peer. - -* Changes in ripngd - -** When ripngd terminates, delete all installed route. - -** `redistribute static', `redistribute connected' works. - -** Change `debug ripng event' to `debug ripng events'. - -** Change `show debug ripng' to `show debugging ripng'. - -** Bug of static route deletion is fixed. - -* Changes in ospfd - -** LS request and LS update can be send and received. - -* Changes in zebra-0.68 - -* Changes in lib - -** DEFUN() is extended to support (a|b|c) statement. - -** Input buffer overflow bug is fixed. - -* Changes in bgpd - -** `ip community-list' is added. - -** set community and match community is added to route-map statement. - -** aggregate-address A.B.C.D/M partly works. Now it works only -summary-only mode. - -* Changes in zebra - -** IPv6 network address delete bug is fixed. - -* Changes in ospfd - -** DR election bug fixed. - -** Now Database Description can be send or received. - -** Neighbor State Machine goes to Full state. - -* Changes in ospf6d - -** router zebra related bug is fixed. - -* Changes in zebra-0.67 - -* Changes in lib - -** `service password-encryption' is added for encrypted password. - -* Changes in bgpd - -** `set as-path prepend ASPATH' is added to route-map command. - -** `set weight WEIGHT' is added to route-map command. - -** `no set ipv6 nexthop global' and `no set ipv6 nexthop local' -command is added to route-map. - -** `neighbor IP_ADDR version BGP_VERSION' command's BGP_VERSION -argument changed. - -Old New -===================== -bgp4 4 -bgp4+ 4+ -bgp4+-draft-00 4- -===================== - -If you want to peer with old draft version of BGP-4+, please configure -like below: - -router bgp ASN - neighbor PEER version 4- - -** Some AS path isn't correctly compared during route selection. Now -it is fixed. - -* Changes in ospfd - -** `router zebra' is default behavior. - -* Changes in ospf6d - -** `router zebra' is default behavior. - -* Changes in zebra-0.66 - -* Changes in zebra - -** When other daemon such as gated install routes into the kernel then -zebra blocks. This is only occur with netlink socket. Now socket is -set as NONBLOCKING and problem is fixed. Reported and fixed by -Patrick Koppen - -* Changes in bgpd - -** Now `router zebra' is not needed to insert BGP routes into the -kernel. It is default behavior. If you don't want to install the BGP -routes to the kernel, please configure like below: - -! -router zebra - no redistribute bgp -! - -** redistribute connected works. - -** redistribute static now filter local loopback routes and link local -network. - -* Changes in ripd - -** Some network check is added. Patch is done by Carlos Alberto -Barcenilla - -* Changes in ripngd - -** Sometimes ripngd install wrong nexthop into the kernel. This bug -is fixed now. - -** Now `router zebra' is not needed to insert RIPng routes into the -kernel. It is default behavior. If you don't want to install the BGP -routes to the kernel, please configure like below: - -! -router zebra - no redistribute ripng -! - -* Changes in zebra-0.65 - -* Changes in lib - -** `C-c' changes current node to ENABLE_NODE. Previously it doesn't. - -** In ENABLE_NODE, `exit' command close vty connection. - -** `service advanced-vty' enable advanced vty function. If this -service is specified one can directly connect to ENABLE_NODE when -enable password is not set. - -** `lines LINES' command is added by Stephen R. van den Berg -. - -* Changes in zebra - -** Basic Linux policy based routing table support is added by Stephen -R. van den Berg . - -* Changes in bgpd - -** route-map command is improved: - `match ip next-hop': New command. - `match metric': New command. - `set metric': Doc fixed. - `set local-preference': DEFUN added. - -* Changes in ripd - -** Check of announced network is added. Now multicast address is -filtered. Reported by Carlos Alberto Barcenilla - - -** Check of network 127 is added. Reported by Carlos Alberto -Barcenilla - -* Changes in ripngd - -** Aging route bug is fixed. - -** `router zebra' semantics changed. ripngd automatically connect to -zebra. - -* Changes in ospfd - -** `no router ospf' works. - -* Changes in ospf6d - -** Bug fix about network vertex. - -* Changes in zebra-0.64.1. - -This is bug fix release. - -* Changes in lib - -** Add check of sin6_scope_id in struct sockaddr_in6. For compilation -on implementation which doesn't have sin6_scope_id. Reported by Wim -Biemolt . - -* Changes in zebra - -** Fix bug of display BGP routes as "O" instead of "B". Reported by -"William F. Maton" and Dave Hartzell -. - -* Changes in bgpd - -** `no network IPV6_NETWORK' statement and `no neighbor IP_ADDR timers -holdtime [TIMER]' statement doesn't work. Reported by Georg Hitsch -. Now both statement work. - -* Changes in ospfd - -** Last interface is not updated by ospf_if_update(). Reported by -Dave Hartzell . - -* Changes in ospf6d - -** Byte order of ifid is changed. Due to this change, this code will -not work with previous version, sorry. - -** Fix `show ip route' route type mismatch. - -** Fix bug of no network IPV6_NETWORK. - -** Important bug fix about intra-area-prefix-lsa. - -* Changes in zebra-0.64. - -* Changes in lib - -** prefix-list based filtering routine is added. Currently used in -bgpd but it will be in other daemons. - -* Changes in bgpd - -** `no router bgp' works. But network statement is not cleared. This -should be fixed in next beta. - -** Route reflector related statement is added. - - router bgp ASN - bgp cluster-id a.b.c.d - neighbor a.b.c.d route-reflector-client - - is added. - -** Prefix list based filtering is added. - - router bgp ASN - neighbor a.b.c.d prefix-list PREFIX_LIST_NAME - -** Prefix list based routing display works. - - show ip bgp prefix-list PREFIX_LIST_NAME - -* Changes in ripd - -** Fix route metric check bug. Reported from Mr. Carlos Alberto -Barcenilla. - -* Changes in ospf6d - -** There are many changes. If you have interested in ospf6d please -visit ospf6d/README file. - -* Changes in zebra-0.63 first beta package. - -* Changes in lib - -** `copy running-config stgartup-config' command is added. - -** prefix length check bug is fixed. Thanks Marlos Barcenilla -. - -* Changes in ospfd - -** DR and BDR election works. - -** OSPF Hello simple authentication works. - -* Changes in ospf6d - -** Now ospf6d can be compiled on both Linux and *BSD system. - -* Changes in zebra-19990420 snapshot - -** `make dist' at top directory works now. - -* Changes in lib - -** VTY has now access-class to restrict remote connection. -Implemented by Alex Bligh . - -! -line vty - access-class ACCESS-LIST-NAME -! - -** `show version' command added. Implemented by Carlos Alberto -Barcenilla - -* Changes in zebra - -** `ip address' command on *BSD bug is fixed. - -** `no ip address' works now for IPv4 address. - -** Now `write terminal' display `ip address' configuration. - -* Changes in bgpd - -** Redistribute static works now. Please run both zebra and bgpd. -bgpd.conf should be like this: - -! -router zebra -! -router bgp ASN - redisitribute static -! - -* Changes in guile - -** configure --enable-guile turns on zebra-guile build. - -** (router-bgp ASN) allocates real bgp structre. - -* Changes in zebra-19990416 snapshot - -** Set version to 0.60 for preparation of beta release. - -** New directory guile is added for linking with guile interpreter. - -* Changes in zebra - -** On GNU/Linux Kernel 2.2.x (with netlink support), zebra detects -asynchronous routing updates. *BSD support is not yet finished. - -* Changes in bgpd - -** `show ip bgp regexp ASPATH_REGEX' uses CISCO like regular expression -instead of RPSL like regular expression. I'm planing to provide RPSL -like regular expression with `show ip bgp rpsl' or something. - -* Changes in lib - -** Press '?' at variable mandatory argument, vty prints nothing. Now -vty outputs description about the argument. Fixed by Alex Bligh - - -** buffer.c has some ugly bugs. Due to the bug, vty interface hangs -when large output date exists. This bug is fixed. Reported by Alex -Bligh . - -* Changes in ospfd - -** DR and BDR information is shown by `show ip ospf interface' command. - -* Changes in zebra-19990408 snapshot - -* Changes in bgpd - -** Old BGP-4+ specification (described in old draft) treatment bug is -fixed. It seems that mrtd uses this format as default. So if you -have problem peering with mrtd and want to use old draft format please -use version statement like this. - -neighbor PEER_ADDRESS remote-as ASN -neighbor PEER_ADDRESS version bgp4+-draft-00 - -** When AS path is epmty (routes generated by bgpd), SEGV is occur -when announce the routes to eBGP peer. Reported by -kad@gibson.skif.net. - -** ip as-path access-list command is added. - -** neighbor PEER_ADDRESS filter-list AS_LIST [in|out] command is added. - -** neighbor PEER_ADDRESS timers holdtimer TIMER command is added. - -* Changes in all daemons - -** With KAME stack, terminal interface is now bind AF_INET socket -instead of AF_INET6 one. - -* Changes in zebra-19990403 snapshot - -* Changes in bgpd - -** When bgpd has 'router zebra', bgpd automatically select it's router -ID as most highest interface's IP Address. - -** When AS path is empty (in case of iBGP), it doesn't include any AS -segment. This change is for announcement to gated under iBGP. - -* Changes in ospfd - -** OSPF hello packet send/receive works. - -* Changes in ospf6d - -** Yasuhiro Ohara's ospf6d codes is imported. It is under development -and can't be compiled on any platform. - -* Changes in zebra-19990327 snapshot - -* Changes in bgpd - -** When BGP-4+ connection is done by IPv6 link-local address. One -have to specify interface index for the connection. So I've added -interface statement to the neighbor commmand. Please specify -interface name for getting interface index like below. This statement -only works on GNU/Linux. I'll support BSD ASAP. - -router bgp 7675 - neighbor fe80::200:f8ff:fe01:5fd3 remote-as 2500 - neighbor fe80::200:f8ff:fe01:5fd3 interface sit3 - -** For disable BGP peering `shutdown' command is added. - -router bgp 7675 - neighbor 10.0.0.1 shutdown - -** `description' command is added to neighbor statement. - -router bgp 7675 - neighbor 10.0.0.1 description peering with Norway. - -** `show ip bgp regexp AS-REGEXP' works again. - -show ip bgp regexp AS7675 - -will show routes which include AS7675. - -** When a route which is made from `network' statement is send to -neighbor. Set it's nexthop to self. So 10.0.0.0/8 is announced to -the peer A with source address 192.168.1.1. The routes nexthop is set -to 192.168.1.1. - -* Changes in zebra - -** In zebra/rtread_sysctl.c, function rtm_read() may overrun allocated -buffer when the address family is not supported and the length is big -(i.e link address). Reported Achim Patzner . - -* Changes in ospfd - -** Now ospfd receive OSPF packet. - -* Changes in zebra-19990319 snapshot - -* Changes in configuration and libraries - -** User can disable IPv6 feature and/or pthread feature by configure - option. - - To disable IPv6: configure --disable-ipv6 - To disable pthread: configure --disable-pthread - -** User can disable specified daemon by configure option. - - Don't make zebra: configure --disable-zebra - Don't make bgpd: configure --disable-bgpd - Don't make ripd: configure --disable-ripd - Don't make ripngd: configure --disable-ripngd - Don't make ospfd: configure --disable-ospfd - Don't make ospf6d: configure --disable-ospf6d - -** Sample configuration files are installed as 600 file flag. - Suggested by Jeroen Ruigrok/Asmodai . - -** syslog logging feature is added by Peter Galbavy - - -** Inclusion of standard header files is reworked by Peter Galbavy - - -** Change description from GNU/Linux 2.1.X to GNU/Linux 2.2.X - -** If daemon function exists in standard C library use it. - -** To generate configure script we upgrade autoconf to 2.13. To -generate Makefile.in we upgrade automake to 1.4. - -** doc/texinfo.tex is added to distribution. - -** Update ports/pkg/DESCR description. - -** Update doc/zebra.texi. - -** logfile FILENAME statement deleted. Instead of that please use log -file FILENAME. - -* Changes in zebra - -* Changes in bgpd - -** Communication between zebra and bgpd works now. So if there is - `router zebra' line in bgpd.conf, selected route is installed - into kernel routing table. - -** Delete all routes which inserted by bgpd when bgpd dies. If you -want to retain routes even bgpd dies please specify [-r|--retain] -option to bgpd. - -** BGP announcement code is reworked. Now bgpd announce selected - routes to other peer. - -** All output bgp packet is buffered. It's written to the socket when - it gets ready. - -** Output route-map works now. You can specify output route-map by: - - neighbor IP_ADDR route-map ROUTE_MAP_NAME out - -** New route-map command added. - - set ip nexthop IP_ADDR - set ipv6 nexthop global IP_ADDR - -** Fix bug about unlock of the route_node structure. - -** BGP-4+ support is added. bgpd can listen and speak BGP-4+ packet -specified in RFC2283. You can view IPv6 bgp table by: `show ipv6 bgp'. - -** Meny packet overflow check is added. - -* Changes in ripd - -* Changes in ripngd - -* Changes in ospfd - -** ospfd work is started by Toshiaki Takada . Now -several files are included in ospfd directory. - -** ospf6d codes are merged from Yasuhiro Ohara 's -ospfd work. Now codes are located in ospf6d directory. - - -Local variables: -mode: outline -paragraph-separate: "[ ]*$" -end: diff --git a/configure.ac b/configure.ac index 0f5ca0da20..9378f1f23b 100755 --- a/configure.ac +++ b/configure.ac @@ -57,7 +57,7 @@ AM_CONDITIONAL([BUILD_CLIPPY], [$build_clippy]) # Disable portability warnings -- our automake code (in particular # common.am) uses some constructs specific to gmake. -AM_INIT_AUTOMAKE([1.12 -Wno-portability]) +AM_INIT_AUTOMAKE([1.12 -Wno-portability], [foreign]) m4_ifndef([AM_SILENT_RULES], [m4_define([AM_SILENT_RULES],[])]) AM_SILENT_RULES([yes]) AC_CONFIG_HEADERS(config.h) diff --git a/debianpkg/frr-doc.docs b/debianpkg/frr-doc.docs index d2218d00f9..d4043d9ea8 100644 --- a/debianpkg/frr-doc.docs +++ b/debianpkg/frr-doc.docs @@ -1,5 +1,3 @@ -AUTHORS -NEWS README doc/user/*.rst doc/figures/*.png diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 9c232c2bd5..8e9d376b67 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -561,9 +561,9 @@ fi %files -%doc */*.sample* AUTHORS COPYING +%doc */*.sample* COPYING %doc doc/mpls -%doc ChangeLog NEWS README +%doc README %if 0%{?frr_user:1} %dir %attr(751,%{frr_user},%{frr_user}) %{configdir} %dir %attr(750,%{frr_user},%{frr_user}) %{_localstatedir}/log/frr From f20aafeec01f27c573dcf76c87b3e9228c5a8dd0 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 4 Sep 2018 20:27:54 +0000 Subject: [PATCH 80/84] frr: improve README Friendly READMEs are all the rage! Signed-off-by: Quentin Young --- README | 16 --------- README.md | 74 ++++++++++++++++++++++++++++++++++++++++++ configure.ac | 2 +- debianpkg/frr-doc.docs | 2 +- redhat/frr.spec.in | 2 +- 5 files changed, 77 insertions(+), 19 deletions(-) delete mode 100644 README create mode 100644 README.md diff --git a/README b/README deleted file mode 100644 index 7600ec54cc..0000000000 --- a/README +++ /dev/null @@ -1,16 +0,0 @@ -FRRouting is free software that implements and manages various IPv4 and IPv6 -routing protocols. - -Currently FRRouting supports BGP4, BGP4+, OSPFv2, OSPFv3, RIPv1, RIPv2, RIPng, -IS-IS, PIM-SM/MSDP, LDP and Babel as well as very early support for EIGRP and -NHRP. - -See doc/user/bugs.rst for information on how to report bugs. - -See doc/developer/workflow.rst for information on contributing. - -See the file COPYING for copying conditions. - -Public email discussion can be found at https://lists.frrouting.org/listinfo - -Our public slack channel is at https://frrouting.slack.com diff --git a/README.md b/README.md new file mode 100644 index 0000000000..48142f21b7 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +FRRouting +========= + +FRR is free software that implements and manages various IPv4 and IPv6 routing +protocols. It runs on nearly all distributions of Linux and BSD as well as +Solaris and supports all modern CPU architectures. + +FRR currently supports the following protocols: + +* BGP +* OSPFv2 +* OSPFv3 +* RIPv1 +* RIPv2 +* RIPng +* IS-IS +* PIM-SM/MSDP +* LDP +* BFD +* Babel +* EIGRP (alpha) +* NHRP (alpha) + +Installation & Use +------------------ + +Packages are available for various distributions on our +[releases page](https://github.com/FRRouting/frr/releases). + +Snaps are also available [here](https://snapcraft.io/frr). + +Instructions on building and installing from source for supported platforms may +be found +[here](http://docs.frrouting.org/projects/dev-guide/en/latest/building.html). + +Once installed, please refer to the [user guide](http://docs.frrouting.org/) +for instructions on use. + +Community +--------- + +The FRRouting email list server is located +[here](https://lists.frrouting.org/listinfo) and offers the following public +lists: + +| Topic | List | +|-------------------|------------------------------| +| Development | dev@lists.frrouting.org | +| Users & Operators | frog@lists.frrouting.org | +| Announcements | announce@lists.frrouting.org | + +For chat, we currently use [Slack](https://frrouting.slack.com). Please email +the mailing list to request an invite as we do not issue automatic invites. + + +Contributing +------------ + +FRR maintains [developer's documentation](http://docs.frrouting.org/projects/dev-guide/en/latest/index.html) +which contains the [project workflow](http://docs.frrouting.org/projects/dev-guide/en/latest/workflow.html) +and expectations for contributors. Some technical documentation on project +internals is also available. + +We welcome and appreciate all contributions, no matter how small! + + +Security +-------- + +To report security issues, please use our security mailing list: + +``` +security [at] lists.frrouting.org +``` diff --git a/configure.ac b/configure.ac index 9378f1f23b..08f1c117ec 100755 --- a/configure.ac +++ b/configure.ac @@ -57,7 +57,7 @@ AM_CONDITIONAL([BUILD_CLIPPY], [$build_clippy]) # Disable portability warnings -- our automake code (in particular # common.am) uses some constructs specific to gmake. -AM_INIT_AUTOMAKE([1.12 -Wno-portability], [foreign]) +AM_INIT_AUTOMAKE([1.12 -Wno-portability foreign]) m4_ifndef([AM_SILENT_RULES], [m4_define([AM_SILENT_RULES],[])]) AM_SILENT_RULES([yes]) AC_CONFIG_HEADERS(config.h) diff --git a/debianpkg/frr-doc.docs b/debianpkg/frr-doc.docs index d4043d9ea8..605353289c 100644 --- a/debianpkg/frr-doc.docs +++ b/debianpkg/frr-doc.docs @@ -1,3 +1,3 @@ -README +README.md doc/user/*.rst doc/figures/*.png diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 8e9d376b67..bff0b40515 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -563,7 +563,7 @@ fi %files %doc */*.sample* COPYING %doc doc/mpls -%doc README +%doc README.md %if 0%{?frr_user:1} %dir %attr(751,%{frr_user},%{frr_user}) %{configdir} %dir %attr(750,%{frr_user},%{frr_user}) %{_localstatedir}/log/frr From d85cb6b6a6ed75320fad641827ae11b55c3ccbdc Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 6 Sep 2018 17:14:03 +0000 Subject: [PATCH 81/84] frr: add README.md to EXTRA_DIST Signed-off-by: Quentin Young --- Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.am b/Makefile.am index 303881b077..fb052a8dea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -152,6 +152,7 @@ endif EXTRA_DIST += \ aclocal.m4 \ + README.md \ m4/README.txt \ \ python/clidef.py \ From 2f5e937c49dec6b422e8f2ebede4bded44f70efd Mon Sep 17 00:00:00 2001 From: root Date: Fri, 7 Sep 2018 04:46:57 -0700 Subject: [PATCH 82/84] pimd: create a new command "ip pim" configuring pim sm A new command "ip pim" is created to configure pim sm on an interface, which replaces the existing commands "ip pim sm" and "ip pim ssm" and make "ip pim sm" and "ip pim ssm" as hidden commands. The command "ip multicast-routing" is removed since it is already enabled on FRR by default. Signed-off-by: Sarita Patra saritap@vmware.com --- pimd/pim_cmd.c | 93 ++++++++++++++++++++++++++------------------------ pimd/pim_vty.c | 2 +- 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 5dc86417cf..8ef8f87d15 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -5640,27 +5640,6 @@ DEFUN (show_ip_pim_group_type, return CMD_SUCCESS; } -DEFUN_HIDDEN (ip_multicast_routing, - ip_multicast_routing_cmd, - "ip multicast-routing", - IP_STR - "Enable IP multicast forwarding\n") -{ - return CMD_SUCCESS; -} - -DEFUN_HIDDEN (no_ip_multicast_routing, - no_ip_multicast_routing_cmd, - "no ip multicast-routing", - NO_STR - IP_STR - "Enable IP multicast forwarding\n") -{ - vty_out(vty, - "Command is Disabled and will be removed in a future version\n"); - return CMD_SUCCESS; -} - DEFUN (ip_ssmpingd, ip_ssmpingd_cmd, "ip ssmpingd [A.B.C.D]", @@ -6431,16 +6410,12 @@ DEFUN_HIDDEN (interface_ip_pim_ssm, return CMD_SUCCESS; } -DEFUN (interface_ip_pim_sm, - interface_ip_pim_sm_cmd, - "ip pim sm", - IP_STR - PIM_STR - IFACE_PIM_SM_STR) +static int interface_ip_pim_helper(struct vty *vty) { struct pim_interface *pim_ifp; VTY_DECLVAR_CONTEXT(interface, ifp); + if (!pim_cmd_interface_add(ifp)) { vty_out(vty, "Could not enable PIM SM on interface\n"); return CMD_WARNING_CONFIG_FAILED; @@ -6453,6 +6428,25 @@ DEFUN (interface_ip_pim_sm, return CMD_SUCCESS; } +DEFUN_HIDDEN (interface_ip_pim_sm, + interface_ip_pim_sm_cmd, + "ip pim sm", + IP_STR + PIM_STR + IFACE_PIM_SM_STR) +{ + return interface_ip_pim_helper(vty); +} + +DEFUN (interface_ip_pim, + interface_ip_pim_cmd, + "ip pim", + IP_STR + PIM_STR) +{ + return interface_ip_pim_helper(vty); +} + static int pim_cmd_interface_delete(struct interface *ifp) { struct pim_interface *pim_ifp = ifp->info; @@ -6478,6 +6472,17 @@ static int pim_cmd_interface_delete(struct interface *ifp) return 1; } +static int interface_no_ip_pim_helper(struct vty *vty) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + if (!pim_cmd_interface_delete(ifp)) { + vty_out(vty, "Unable to delete interface information\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + DEFUN_HIDDEN (interface_no_ip_pim_ssm, interface_no_ip_pim_ssm_cmd, "no ip pim ssm", @@ -6486,16 +6491,10 @@ DEFUN_HIDDEN (interface_no_ip_pim_ssm, PIM_STR IFACE_PIM_STR) { - VTY_DECLVAR_CONTEXT(interface, ifp); - if (!pim_cmd_interface_delete(ifp)) { - vty_out(vty, "Unable to delete interface information\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; + return interface_no_ip_pim_helper(vty); } -DEFUN (interface_no_ip_pim_sm, +DEFUN_HIDDEN (interface_no_ip_pim_sm, interface_no_ip_pim_sm_cmd, "no ip pim sm", NO_STR @@ -6503,13 +6502,17 @@ DEFUN (interface_no_ip_pim_sm, PIM_STR IFACE_PIM_SM_STR) { - VTY_DECLVAR_CONTEXT(interface, ifp); - if (!pim_cmd_interface_delete(ifp)) { - vty_out(vty, "Unable to delete interface information\n"); - return CMD_WARNING_CONFIG_FAILED; - } + return interface_no_ip_pim_helper(vty); +} - return CMD_SUCCESS; +DEFUN (interface_no_ip_pim, + interface_no_ip_pim_cmd, + "no ip pim", + NO_STR + IP_STR + PIM_STR) +{ + return interface_no_ip_pim_helper(vty); } /* boundaries */ @@ -7466,7 +7469,7 @@ DEFUN (interface_pim_use_source, interface_pim_use_source_cmd, "ip pim use-source A.B.C.D", IP_STR - "pim multicast routing\n" + PIM_STR "Configure primary IP address\n" "source ip address\n") { @@ -7478,7 +7481,7 @@ DEFUN (interface_no_pim_use_source, "no ip pim use-source [A.B.C.D]", NO_STR IP_STR - "pim multicast routing\n" + PIM_STR "Delete source IP address\n" "source ip address\n") { @@ -8634,8 +8637,6 @@ void pim_cmd_init(void) install_node(&debug_node, pim_debug_config_write); - install_element(CONFIG_NODE, &ip_multicast_routing_cmd); - install_element(CONFIG_NODE, &no_ip_multicast_routing_cmd); install_element(CONFIG_NODE, &ip_pim_rp_cmd); install_element(VRF_NODE, &ip_pim_rp_cmd); install_element(CONFIG_NODE, &no_ip_pim_rp_cmd); @@ -8721,6 +8722,8 @@ void pim_cmd_init(void) install_element(INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd); install_element(INTERFACE_NODE, &interface_ip_pim_sm_cmd); install_element(INTERFACE_NODE, &interface_no_ip_pim_sm_cmd); + install_element(INTERFACE_NODE, &interface_ip_pim_cmd); + install_element(INTERFACE_NODE, &interface_no_ip_pim_cmd); install_element(INTERFACE_NODE, &interface_ip_pim_drprio_cmd); install_element(INTERFACE_NODE, &interface_no_ip_pim_drprio_cmd); install_element(INTERFACE_NODE, &interface_ip_pim_hello_cmd); diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 88be195bee..a4aec710e9 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -267,7 +267,7 @@ int pim_interface_config_write(struct vty *vty) struct pim_interface *pim_ifp = ifp->info; if (PIM_IF_TEST_PIM(pim_ifp->options)) { - vty_out(vty, " ip pim sm\n"); + vty_out(vty, " ip pim\n"); ++writes; } From 371bfb5c3ef9aa852c7f143c4357f3a18a589fb5 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 11 Sep 2018 16:55:56 +0200 Subject: [PATCH 83/84] lib: whitespace/spelling fix Signed-off-by: David Lamparter --- lib/libfrr.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/libfrr.c b/lib/libfrr.c index 115c70f7c9..61f5fa7081 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -266,8 +266,8 @@ static void frr_guard_daemon(void) { int fd; struct flock lock; - const char *path = di->pid_file; + fd = open(path, O_RDWR); if (fd != -1) { memset(&lock, 0, sizeof(lock)); @@ -280,7 +280,7 @@ static void frr_guard_daemon(void) exit(1); } else if (lock.l_type == F_WRLCK) { flog_err_sys(LIB_ERR_SYSTEM_CALL, - "Process %d has a write lock on file %s already! Error : ( %s)", + "Process %d has a write lock on file %s already! Error: (%s)", lock.l_pid, path, safe_strerror(errno)); exit(1); } @@ -294,7 +294,6 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) /* basename(), opencoded. */ char *p = strrchr(argv[0], '/'); - di->progname = p ? p + 1 : argv[0]; umask(0027); From eccdcfe2b67df199d795642dba8ab368a81cf313 Mon Sep 17 00:00:00 2001 From: Sarita Patra Date: Tue, 11 Sep 2018 22:00:15 -0700 Subject: [PATCH 84/84] pimd: fix indentation warnings Signed-off-by: Sarita Patra --- pimd/pim_cmd.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 8ef8f87d15..968fc378f0 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -6474,13 +6474,13 @@ static int pim_cmd_interface_delete(struct interface *ifp) static int interface_no_ip_pim_helper(struct vty *vty) { - VTY_DECLVAR_CONTEXT(interface, ifp); - if (!pim_cmd_interface_delete(ifp)) { - vty_out(vty, "Unable to delete interface information\n"); - return CMD_WARNING_CONFIG_FAILED; - } + VTY_DECLVAR_CONTEXT(interface, ifp); + if (!pim_cmd_interface_delete(ifp)) { + vty_out(vty, "Unable to delete interface information\n"); + return CMD_WARNING_CONFIG_FAILED; + } - return CMD_SUCCESS; + return CMD_SUCCESS; } DEFUN_HIDDEN (interface_no_ip_pim_ssm,