From dbce358222e5483140389795f790cfabd93808e1 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 22 Mar 2017 12:53:36 -0300 Subject: [PATCH] ldpd: do a full configuration reload upon receiving a SIGHUP The rationale here is to do something very similar to what the original OpenBSD's ldpd(8) does to reload configuration files: * create a new empty base configuration (vty_conf); * parse the configuration file and store the result in vty_conf; * merge the new configuration into the current configuration. To do this, introduce a new global variable called "sighup" so that ldpd doesn't attempt to merge vty_conf into ldpd_conf for each command in the configuration file, which would be very disruptive. vty_conf should be merged only after the whole configuration file is parsed in order to avoid taking actions like shutting down neighbors when not necessary. Signed-off-by: Renato Westphal --- ldpd/ldp_vty_conf.c | 172 ++++++++++++++++++++++++++++++++++---------- ldpd/ldpd.c | 21 ++++++ ldpd/ldpd.h | 1 + 3 files changed, 156 insertions(+), 38 deletions(-) diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c index 68e3a36013..f2b7d6f366 100644 --- a/ldpd/ldp_vty_conf.c +++ b/ldpd/ldp_vty_conf.c @@ -37,11 +37,17 @@ static void ldp_af_iface_config_write(struct vty *, int); static void ldp_af_config_write(struct vty *, int, struct ldpd_conf *, struct ldpd_af_conf *); static void ldp_l2vpn_pw_config_write(struct vty *, struct l2vpn_pw *); +static void ldp_vty_push_node(struct vty *, int, void *); +static void *ldp_vty_get_node(struct vty *, void *, int); static int ldp_vty_get_af(struct vty *); static int ldp_iface_is_configured(struct ldpd_conf *, const char *); static int ldp_vty_nbr_session_holdtime(struct vty *, struct vty_arg *[]); static int ldp_vty_af_session_holdtime(struct vty *, struct vty_arg *[]); +static struct iface *vty_iface; +static struct l2vpn *vty_l2vpn; +static struct l2vpn_pw *vty_pw; + static struct cmd_node interface_node = { INTERFACE_NODE, @@ -413,6 +419,94 @@ ldp_l2vpn_config_write(struct vty *vty) return (0); } +void +ldp_vty_push_node(struct vty *vty, int node, void *ptr) +{ + if (global.sighup) { + switch (node) { + case LDP_IPV4_IFACE_NODE: + case LDP_IPV6_IFACE_NODE: + vty_iface = ptr; + break; + case LDP_L2VPN_NODE: + vty_l2vpn = ptr; + break; + case LDP_PSEUDOWIRE_NODE: + vty_pw = ptr; + break; + default: + fatalx("ldp_vty_push_node: unexpected node"); + } + vty->node = node; + return; + } + + switch (node) { + case LDP_IPV4_IFACE_NODE: + case LDP_IPV6_IFACE_NODE: + VTY_PUSH_CONTEXT(node, (struct iface *)ptr); + break; + case LDP_L2VPN_NODE: + VTY_PUSH_CONTEXT(node, (struct l2vpn *)ptr); + break; + case LDP_PSEUDOWIRE_NODE: + VTY_PUSH_CONTEXT_SUB(node, (struct l2vpn_pw *)ptr); + break; + default: + fatalx("ldp_vty_push_node: unexpected node"); + } +} + +void * +ldp_vty_get_node(struct vty *vty, void *parent, int node) +{ + struct iface *iface; + struct l2vpn *l2vpn; + struct l2vpn_pw *pw; + + if (global.sighup) { + switch (node) { + case LDP_IPV4_IFACE_NODE: + case LDP_IPV6_IFACE_NODE: + return (vty_iface); + case LDP_L2VPN_NODE: + return (vty_l2vpn); + case LDP_PSEUDOWIRE_NODE: + return (vty_pw); + default: + fatalx("ldp_vty_get_node: unexpected node"); + } + } + + /* + * Since VTY_GET_CONTEXT() returns a pointer to an element of ldpd_conf, + * we have to find the equivalent element inside vty_conf (which should + * always exist as vty_conf is a duplicate of ldpd_conf). + */ + switch (node) { + case LDP_IPV4_IFACE_NODE: + case LDP_IPV6_IFACE_NODE: + iface = VTY_GET_CONTEXT(iface); + if (iface) + return (if_lookup_name(vty_conf, iface->name)); + break; + case LDP_L2VPN_NODE: + l2vpn = VTY_GET_CONTEXT(l2vpn); + if (l2vpn) + return (l2vpn_find(vty_conf, l2vpn->name)); + break; + case LDP_PSEUDOWIRE_NODE: + pw = VTY_GET_CONTEXT_SUB(l2vpn_pw); + if (pw) + return (l2vpn_pw_find_name(parent, pw->ifname)); + break; + default: + fatalx("ldp_vty_get_node: unexpected node"); + } + + return (NULL); +} + static int ldp_vty_get_af(struct vty *vty) { @@ -590,7 +684,8 @@ ldp_vty_disc_holdtime(struct vty *vty, struct vty_arg *args[]) case LDP_IPV4_IFACE_NODE: case LDP_IPV6_IFACE_NODE: af = ldp_vty_get_af(vty); - iface = VTY_GET_CONTEXT(iface); + iface = ldp_vty_get_node(vty, NULL, vty->node); + VTY_CHECK_CONTEXT(iface); ia = iface_af_get(iface, af); if (disable) @@ -689,7 +784,8 @@ ldp_vty_disc_interval(struct vty *vty, struct vty_arg *args[]) case LDP_IPV4_IFACE_NODE: case LDP_IPV6_IFACE_NODE: af = ldp_vty_get_af(vty); - iface = VTY_GET_CONTEXT(iface); + iface = ldp_vty_get_node(vty, NULL, vty->node); + VTY_CHECK_CONTEXT(iface); ia = iface_af_get(iface, af); if (disable) @@ -898,10 +994,10 @@ ldp_vty_interface(struct vty *vty, struct vty_arg *args[]) switch (af) { case AF_INET: - VTY_PUSH_CONTEXT(LDP_IPV4_IFACE_NODE, iface); + ldp_vty_push_node(vty, LDP_IPV4_IFACE_NODE, iface); break; case AF_INET6: - VTY_PUSH_CONTEXT(LDP_IPV6_IFACE_NODE, iface); + ldp_vty_push_node(vty, LDP_IPV6_IFACE_NODE, iface); break; default: break; @@ -1335,7 +1431,7 @@ ldp_vty_l2vpn(struct vty *vty, struct vty_arg *args[]) } if (l2vpn) { - VTY_PUSH_CONTEXT(LDP_L2VPN_NODE, l2vpn); + ldp_vty_push_node(vty, LDP_L2VPN_NODE, l2vpn); return (CMD_SUCCESS); } @@ -1344,7 +1440,7 @@ ldp_vty_l2vpn(struct vty *vty, struct vty_arg *args[]) RB_INSERT(l2vpn_head, &vty_conf->l2vpn_tree, l2vpn); ldp_reload(vty_conf); - VTY_PUSH_CONTEXT(LDP_L2VPN_NODE, l2vpn); + ldp_vty_push_node(vty, LDP_L2VPN_NODE, l2vpn); return (CMD_SUCCESS); } @@ -1359,8 +1455,8 @@ ldp_vty_l2vpn_bridge(struct vty *vty, struct vty_arg *args[]) disable = (vty_get_arg_value(args, "no")) ? 1 : 0; ifname = vty_get_arg_value(args, "ifname"); - l2vpn = VTY_GET_CONTEXT(l2vpn); - l2vpn = l2vpn_find(vty_conf, l2vpn->name); + l2vpn = ldp_vty_get_node(vty, NULL, LDP_L2VPN_NODE); + VTY_CHECK_CONTEXT(l2vpn); if (disable) memset(l2vpn->br_ifname, 0, sizeof(l2vpn->br_ifname)); @@ -1390,8 +1486,8 @@ ldp_vty_l2vpn_mtu(struct vty *vty, struct vty_arg *args[]) return (CMD_WARNING); } - l2vpn = VTY_GET_CONTEXT(l2vpn); - l2vpn = l2vpn_find(vty_conf, l2vpn->name); + l2vpn = ldp_vty_get_node(vty, NULL, LDP_L2VPN_NODE); + VTY_CHECK_CONTEXT(l2vpn); if (disable) l2vpn->mtu = DEFAULT_L2VPN_MTU; @@ -1419,8 +1515,8 @@ ldp_vty_l2vpn_pwtype(struct vty *vty, struct vty_arg *args[]) else pw_type = PW_TYPE_ETHERNET_TAGGED; - l2vpn = VTY_GET_CONTEXT(l2vpn); - l2vpn = l2vpn_find(vty_conf, l2vpn->name); + l2vpn = ldp_vty_get_node(vty, NULL, LDP_L2VPN_NODE); + VTY_CHECK_CONTEXT(l2vpn); if (disable) l2vpn->pw_type = DEFAULT_PW_TYPE; @@ -1445,8 +1541,8 @@ ldp_vty_l2vpn_interface(struct vty *vty, struct vty_arg *args[]) disable = (vty_get_arg_value(args, "no")) ? 1 : 0; ifname = vty_get_arg_value(args, "ifname"); - l2vpn = VTY_GET_CONTEXT(l2vpn); - l2vpn = l2vpn_find(vty_conf, l2vpn->name); + l2vpn = ldp_vty_get_node(vty, NULL, LDP_L2VPN_NODE); + VTY_CHECK_CONTEXT(l2vpn); lif = l2vpn_if_find_name(l2vpn, ifname); if (disable) { @@ -1496,8 +1592,8 @@ ldp_vty_l2vpn_pseudowire(struct vty *vty, struct vty_arg *args[]) disable = (vty_get_arg_value(args, "no")) ? 1 : 0; ifname = vty_get_arg_value(args, "ifname"); - l2vpn = VTY_GET_CONTEXT(l2vpn); - l2vpn = l2vpn_find(vty_conf, l2vpn->name); + l2vpn = ldp_vty_get_node(vty, NULL, LDP_L2VPN_NODE); + VTY_CHECK_CONTEXT(l2vpn); pw = l2vpn_pw_find_name(l2vpn, ifname); if (disable) { @@ -1511,7 +1607,7 @@ ldp_vty_l2vpn_pseudowire(struct vty *vty, struct vty_arg *args[]) } if (pw) { - VTY_PUSH_CONTEXT_SUB(LDP_PSEUDOWIRE_NODE, pw); + ldp_vty_push_node(vty, LDP_PSEUDOWIRE_NODE, pw); return (CMD_SUCCESS); } @@ -1533,7 +1629,7 @@ ldp_vty_l2vpn_pseudowire(struct vty *vty, struct vty_arg *args[]) RB_INSERT(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw); ldp_reload(vty_conf); - VTY_PUSH_CONTEXT_SUB(LDP_PSEUDOWIRE_NODE, pw); + ldp_vty_push_node(vty, LDP_PSEUDOWIRE_NODE, pw); return (CMD_SUCCESS); } @@ -1549,10 +1645,10 @@ ldp_vty_l2vpn_pw_cword(struct vty *vty, struct vty_arg *args[]) disable = (vty_get_arg_value(args, "no")) ? 1 : 0; preference_str = vty_get_arg_value(args, "preference"); - l2vpn = VTY_GET_CONTEXT(l2vpn); - l2vpn = l2vpn_find(vty_conf, l2vpn->name); - pw = VTY_GET_CONTEXT_SUB(l2vpn_pw); - pw = l2vpn_pw_find_name(l2vpn, pw->ifname); + l2vpn = ldp_vty_get_node(vty, NULL, LDP_L2VPN_NODE); + VTY_CHECK_CONTEXT(l2vpn); + pw = ldp_vty_get_node(vty, l2vpn, LDP_PSEUDOWIRE_NODE); + VTY_CHECK_CONTEXT(pw); if (disable) pw->flags |= F_PW_CWORD_CONF; @@ -1587,10 +1683,10 @@ ldp_vty_l2vpn_pw_nbr_addr(struct vty *vty, struct vty_arg *args[]) return (CMD_WARNING); } - l2vpn = VTY_GET_CONTEXT(l2vpn); - l2vpn = l2vpn_find(vty_conf, l2vpn->name); - pw = VTY_GET_CONTEXT_SUB(l2vpn_pw); - pw = l2vpn_pw_find_name(l2vpn, pw->ifname); + l2vpn = ldp_vty_get_node(vty, NULL, LDP_L2VPN_NODE); + VTY_CHECK_CONTEXT(l2vpn); + pw = ldp_vty_get_node(vty, l2vpn, LDP_PSEUDOWIRE_NODE); + VTY_CHECK_CONTEXT(pw); if (disable) { pw->af = AF_UNSPEC; @@ -1625,10 +1721,10 @@ ldp_vty_l2vpn_pw_nbr_id(struct vty *vty, struct vty_arg *args[]) return (CMD_WARNING); } - l2vpn = VTY_GET_CONTEXT(l2vpn); - l2vpn = l2vpn_find(vty_conf, l2vpn->name); - pw = VTY_GET_CONTEXT_SUB(l2vpn_pw); - pw = l2vpn_pw_find_name(l2vpn, pw->ifname); + l2vpn = ldp_vty_get_node(vty, NULL, LDP_L2VPN_NODE); + VTY_CHECK_CONTEXT(l2vpn); + pw = ldp_vty_get_node(vty, l2vpn, LDP_PSEUDOWIRE_NODE); + VTY_CHECK_CONTEXT(pw); if (disable) pw->lsr_id.s_addr = INADDR_ANY; @@ -1659,10 +1755,10 @@ ldp_vty_l2vpn_pw_pwid(struct vty *vty, struct vty_arg *args[]) return (CMD_WARNING); } - l2vpn = VTY_GET_CONTEXT(l2vpn); - l2vpn = l2vpn_find(vty_conf, l2vpn->name); - pw = VTY_GET_CONTEXT_SUB(l2vpn_pw); - pw = l2vpn_pw_find_name(l2vpn, pw->ifname); + l2vpn = ldp_vty_get_node(vty, NULL, LDP_L2VPN_NODE); + VTY_CHECK_CONTEXT(l2vpn); + pw = ldp_vty_get_node(vty, l2vpn, LDP_PSEUDOWIRE_NODE); + VTY_CHECK_CONTEXT(pw); if (disable) pw->pwid = 0; @@ -1683,10 +1779,10 @@ ldp_vty_l2vpn_pw_pwstatus(struct vty *vty, struct vty_arg *args[]) disable = (vty_get_arg_value(args, "no")) ? 1 : 0; - l2vpn = VTY_GET_CONTEXT(l2vpn); - l2vpn = l2vpn_find(vty_conf, l2vpn->name); - pw = VTY_GET_CONTEXT_SUB(l2vpn_pw); - pw = l2vpn_pw_find_name(l2vpn, pw->ifname); + l2vpn = ldp_vty_get_node(vty, NULL, LDP_L2VPN_NODE); + VTY_CHECK_CONTEXT(l2vpn); + pw = ldp_vty_get_node(vty, l2vpn, LDP_PSEUDOWIRE_NODE); + VTY_CHECK_CONTEXT(pw); if (disable) pw->flags |= F_PW_STATUSTLV_CONF; diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index c3d72d022a..96ae5ad879 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -89,6 +89,8 @@ static pid_t lde_pid; /* Master of threads. */ struct thread_master *master; +static struct frr_daemon_info ldpd_di; + /* ldpd privileges */ static zebra_capabilities_t _caps_p [] = { @@ -127,6 +129,22 @@ static void sighup(void) { log_info("SIGHUP received"); + + /* reset vty_conf */ + ldp_clear_config(vty_conf); + vty_conf = config_new_empty(); + ldp_config_reset_main(vty_conf); + + /* read configuration file without applying any changes */ + global.sighup = 1; + vty_read_config(ldpd_di.config_file, config_default); + global.sighup = 0; + + /* + * Apply the new configuration all at once, this way merge_config() + * will be the least disruptive as possible. + */ + ldp_reload(vty_conf); } /* SIGINT / SIGTERM handler. */ @@ -965,6 +983,9 @@ main_imsg_send_config(struct ldpd_conf *xconf) int ldp_reload(struct ldpd_conf *xconf) { + if (global.sighup) + return (0); + ldp_config_normalize(xconf); if (main_imsg_send_config(xconf) == -1) diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 69235556f8..a0474a5a2d 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -503,6 +503,7 @@ struct ldpd_af_global { struct ldpd_global { int cmd_opts; + int sighup; time_t uptime; struct in_addr rtr_id; struct ldpd_af_global ipv4; -- 2.39.5