summaryrefslogtreecommitdiffstats
path: root/ldpd/interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldpd/interface.c')
-rw-r--r--ldpd/interface.c417
1 files changed, 417 insertions, 0 deletions
diff --git a/ldpd/interface.c b/ldpd/interface.c
index 371c7d0bb..bc8f26bc5 100644
--- a/ldpd/interface.c
+++ b/ldpd/interface.c
@@ -23,6 +23,7 @@
#include "ldpd.h"
#include "ldpe.h"
#include "log.h"
+#include "ldp_debug.h"
#include "sockopt.h"
@@ -40,6 +41,17 @@ static int if_leave_ipv4_group(struct iface *, struct in_addr *);
static int if_join_ipv6_group(struct iface *, struct in6_addr *);
static int if_leave_ipv6_group(struct iface *, struct in6_addr *);
+static int ldp_sync_fsm_init(struct iface *iface, int state);
+static int ldp_sync_act_iface_start_sync(struct iface *iface);
+static int iface_wait_for_ldp_sync_timer(struct thread *thread);
+static void start_wait_for_ldp_sync_timer(struct iface *iface);
+static void stop_wait_for_ldp_sync_timer(struct iface *iface);
+static int ldp_sync_act_ldp_start_sync(struct iface *iface);
+static int ldp_sync_act_ldp_complete_sync(struct iface *iface);
+static int iface_to_oper_nbr_count(struct iface *iface, unsigned int type);
+static void ldp_sync_get_peer_ldp_id(struct iface *iface,
+ struct in_addr *peer_ldp_id);
+
RB_GENERATE(iface_head, iface, entry, iface_compare)
static __inline int
@@ -87,6 +99,9 @@ ldpe_if_init(struct iface *iface)
iface->ipv6.iface = iface;
iface->ipv6.state = IF_STA_DOWN;
RB_INIT(ia_adj_head, &iface->ipv6.adj_tree);
+
+ /* LGP IGP Sync */
+ ldp_sync_fsm_init(iface, LDP_SYNC_STA_NOT_ACH);
}
void
@@ -96,6 +111,8 @@ ldpe_if_exit(struct iface *iface)
log_debug("%s: interface %s", __func__, iface->name);
+ ldp_sync_fsm(iface, LDP_SYNC_EVT_CONFIG_LDP_OFF);
+
if (iface->ipv4.state == IF_STA_ACTIVE)
if_reset(iface, AF_INET);
if (iface->ipv6.state == IF_STA_ACTIVE)
@@ -138,6 +155,10 @@ if_update_info(struct iface *iface, struct kif *kif)
kif->flags & IFF_MULTICAST)
iface->type = IF_TYPE_BROADCAST;
+ if (ldpd_process == PROC_LDP_ENGINE && iface->operative &&
+ !kif->operative)
+ ldp_sync_fsm(iface, LDP_SYNC_EVT_IFACE_SHUTDOWN);
+
/* get index and flags */
iface->ifindex = kif->ifindex;
iface->operative = kif->operative;
@@ -426,6 +447,12 @@ if_get_hello_interval(struct iface_af *ia)
return (leconf->lhello_interval);
}
+uint16_t
+if_get_wait_for_sync_interval(void)
+{
+ return (leconf->wait_for_sync_interval);
+}
+
/* timers */
/* ARGSUSED */
static int
@@ -484,6 +511,55 @@ if_to_ctl(struct iface_af *ia)
return (&ictl);
}
+static void
+ldp_sync_get_peer_ldp_id(struct iface *iface, struct in_addr *peer_ldp_id)
+{
+ struct iface_af *ia;
+ struct adj *adj;
+
+ if (iface->ipv4.state == IF_STA_ACTIVE) {
+ ia = iface_af_get(iface, AF_INET);
+ RB_FOREACH(adj, ia_adj_head, &ia->adj_tree)
+ if (adj->nbr && adj->nbr->state == NBR_STA_OPER) {
+ *peer_ldp_id = adj->nbr->id;
+ return;
+ }
+ }
+
+ if (iface->ipv6.state == IF_STA_ACTIVE) {
+ ia = iface_af_get(iface, AF_INET6);
+ RB_FOREACH(adj, ia_adj_head, &ia->adj_tree)
+ if (adj->nbr && adj->nbr->state == NBR_STA_OPER) {
+ *peer_ldp_id = adj->nbr->id;
+ return;
+ }
+ }
+}
+
+struct ctl_ldp_sync *
+ldp_sync_to_ctl(struct iface *iface)
+{
+ static struct ctl_ldp_sync ictl;
+
+ memcpy(ictl.name, iface->name, sizeof(ictl.name));
+ ictl.ifindex = iface->ifindex;
+ ictl.in_sync = (iface->ldp_sync.state == LDP_SYNC_STA_ACH);
+ ictl.wait_time = if_get_wait_for_sync_interval();
+ ictl.timer_running = iface->ldp_sync.wait_for_sync_timer ? true : false;
+
+ if (iface->ldp_sync.wait_for_sync_timer)
+ ictl.wait_time_remaining =
+ thread_timer_remain_second(iface->ldp_sync.wait_for_sync_timer);
+ else
+ ictl.wait_time_remaining = 0;
+
+ memset(&ictl.peer_ldp_id, 0, sizeof(ictl.peer_ldp_id));
+
+ ldp_sync_get_peer_ldp_id(iface, &ictl.peer_ldp_id);
+
+ return (&ictl);
+}
+
/* multicast membership sockopts */
in_addr_t
if_get_ipv4_addr(struct iface *iface)
@@ -576,3 +652,344 @@ if_leave_ipv6_group(struct iface *iface, struct in6_addr *addr)
return (0);
}
+
+const struct {
+ int state;
+ enum ldp_sync_event event;
+ enum ldp_sync_action action;
+ int new_state;
+} ldp_sync_fsm_tbl[] = {
+ /* current state event that happened action to take resulting state */
+/* LDP IGP Sync not achieved */
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_LDP_SYNC_START, LDP_SYNC_ACT_LDP_START_SYNC, 0},
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_LDP_SYNC_COMPLETE, LDP_SYNC_ACT_LDP_COMPLETE_SYNC, LDP_SYNC_STA_ACH},
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_CONFIG_LDP_OFF, LDP_SYNC_ACT_CONFIG_LDP_OFF, 0},
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_IFACE_SHUTDOWN, LDP_SYNC_ACT_IFACE_SHUTDOWN, 0},
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_SESSION_CLOSE, LDP_SYNC_ACT_NOTHING, 0},
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_ADJ_DEL, LDP_SYNC_ACT_NOTHING, 0},
+ {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_ADJ_NEW, LDP_SYNC_ACT_NOTHING, 0},
+/* LDP IGP Sync achieved */
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_CONFIG_LDP_OFF, LDP_SYNC_ACT_CONFIG_LDP_OFF, LDP_SYNC_STA_NOT_ACH},
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_LDP_SYNC_COMPLETE, LDP_SYNC_ACT_NOTHING, 0},
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_LDP_SYNC_START, LDP_SYNC_ACT_NOTHING, 0},
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_IFACE_SHUTDOWN, LDP_SYNC_ACT_IFACE_SHUTDOWN, LDP_SYNC_STA_NOT_ACH},
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_SESSION_CLOSE, LDP_SYNC_ACT_IFACE_START_SYNC, LDP_SYNC_STA_NOT_ACH},
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_ADJ_DEL, LDP_SYNC_ACT_IFACE_START_SYNC, LDP_SYNC_STA_NOT_ACH},
+ {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_ADJ_NEW, LDP_SYNC_ACT_NOTHING, 0},
+ {-1, LDP_SYNC_EVT_NOTHING, LDP_SYNC_ACT_NOTHING, 0},
+};
+
+const char * const ldp_sync_event_names[] = {
+ "NOTHING",
+ "LDP SYNC START",
+ "LDP SYNC COMPLETE",
+ "CONFIG LDP OFF",
+ "IFACE SYNC START (ADJ DEL)",
+ "IFACE SYNC START (ADJ NEW)",
+ "IFACE SYNC START (SESSION CLOSE)",
+ "IFACE SYNC START (CONFIG LDP ON)",
+ "IFACE SHUTDOWN",
+ "N/A"
+};
+
+const char * const ldp_sync_action_names[] = {
+ "NOTHING",
+ "IFACE SYNC START",
+ "LDP START SYNC",
+ "LDP COMPLETE SYNC",
+ "CONFIG LDP OFF",
+ "IFACE SHUTDOWN",
+ "N/A"
+};
+
+const char *
+ldp_sync_state_name(int state)
+{
+ switch (state) {
+ case LDP_SYNC_STA_NOT_ACH:
+ return ("NOT ACHIEVED");
+ case LDP_SYNC_STA_ACH:
+ return ("ACHIEVED");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+static int
+send_ldp_sync_state_update(char *name, int ifindex, int sync_start)
+{
+ debug_evt_ldp_sync("%s: interface %s (%d), sync_start=%d",
+ __func__, name, ifindex, sync_start);
+
+ struct ldp_igp_sync_if_state state;
+
+ state.ifindex = ifindex;
+ state.sync_start = sync_start;
+
+ return ldpe_imsg_compose_parent(IMSG_LDP_SYNC_IF_STATE_UPDATE,
+ getpid(), &state, sizeof(state));
+}
+
+static int
+ldp_sync_act_iface_start_sync(struct iface *iface)
+{
+ send_ldp_sync_state_update(iface->name, iface->ifindex, true);
+
+ return (0);
+}
+
+static int
+iface_wait_for_ldp_sync_timer(struct thread *thread)
+{
+ struct iface *iface = THREAD_ARG(thread);
+
+ ldp_sync_fsm(iface, LDP_SYNC_EVT_LDP_SYNC_COMPLETE);
+
+ return (0);
+}
+
+static void start_wait_for_ldp_sync_timer(struct iface *iface)
+{
+ if (iface->ldp_sync.wait_for_sync_timer)
+ return;
+
+ THREAD_TIMER_OFF(iface->ldp_sync.wait_for_sync_timer);
+ iface->ldp_sync.wait_for_sync_timer = NULL;
+ thread_add_timer(master, iface_wait_for_ldp_sync_timer, iface,
+ if_get_wait_for_sync_interval(),
+ &iface->ldp_sync.wait_for_sync_timer);
+}
+
+static void stop_wait_for_ldp_sync_timer(struct iface *iface)
+{
+ THREAD_TIMER_OFF(iface->ldp_sync.wait_for_sync_timer);
+ iface->ldp_sync.wait_for_sync_timer = NULL;
+}
+
+static int
+ldp_sync_act_ldp_start_sync(struct iface *iface)
+{
+ start_wait_for_ldp_sync_timer(iface);
+
+ return 0;
+}
+
+static int
+ldp_sync_act_ldp_complete_sync(struct iface *iface)
+{
+ send_ldp_sync_state_update(iface->name, iface->ifindex, false);
+
+ return 0;
+}
+
+static int
+iface_to_oper_nbr_count(struct iface *iface, unsigned int type)
+{
+ int oper_nbr_count = 0;
+ struct adj *adj;
+
+ RB_FOREACH(adj, ia_adj_head, &iface->ipv4.adj_tree) {
+ if (type == adj->source.type && adj->nbr &&
+ adj->nbr->state == NBR_STA_OPER)
+ oper_nbr_count++;
+ }
+
+ RB_FOREACH(adj, ia_adj_head, &iface->ipv6.adj_tree) {
+ if (type == adj->source.type && adj->nbr &&
+ adj->nbr->state == NBR_STA_OPER)
+ oper_nbr_count++;
+ }
+
+ return oper_nbr_count;
+}
+
+int
+ldp_sync_fsm_adj_event(struct adj *adj, enum ldp_sync_event event)
+{
+ if (adj->source.type != HELLO_LINK)
+ return -1;
+
+ struct iface *iface = adj->source.link.ia->iface;
+
+ if (!iface->operative)
+ return 0;
+
+ if (event == LDP_SYNC_EVT_ADJ_NEW) {
+ struct nbr *nbr = adj->nbr;
+ if (nbr && nbr->state == NBR_STA_OPER) {
+ event = LDP_SYNC_EVT_LDP_SYNC_START;
+ }
+ } else if (event == LDP_SYNC_EVT_ADJ_DEL) {
+ /* Ignore if an operational neighbor exists.
+ */
+ int oper_nbr_count = iface_to_oper_nbr_count(iface, HELLO_LINK);
+ if (oper_nbr_count > 0)
+ return 0;
+ }
+
+ debug_evt_ldp_sync("%s: event %s, "
+ "adj iface %s (%d) lsr-id %s "
+ "source address %s transport address %s",
+ __func__, ldp_sync_event_names[event],
+ adj->source.link.ia->iface->name,
+ adj->source.link.ia->iface->ifindex,
+ inet_ntoa(adj->lsr_id),
+ log_addr(adj_get_af(adj), &adj->source.link.src_addr),
+ log_addr(adj_get_af(adj), &adj->trans_addr));
+
+ return ldp_sync_fsm(iface, event);
+}
+
+int
+ldp_sync_fsm_nbr_event(struct nbr *nbr, enum ldp_sync_event event)
+{
+ struct adj *adj;
+ struct iface *iface = NULL;
+ RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree) {
+ if (HELLO_LINK != adj->source.type)
+ continue;
+
+ iface = adj->source.link.ia->iface;
+
+ if (!iface || !iface->operative)
+ continue;
+
+ int oper_nbr_count = iface_to_oper_nbr_count(iface, HELLO_LINK);
+
+ if (event == LDP_SYNC_EVT_SESSION_CLOSE && oper_nbr_count > 0)
+ /* Ignore if an operational neighbor exists.
+ */
+ continue;
+
+ debug_evt_ldp_sync("%s: event %s, iface %s, lsr-id %s",
+ __func__, ldp_sync_event_names[event],
+ iface->name, inet_ntoa(nbr->id));
+
+ ldp_sync_fsm(iface, event);
+ }
+
+ return 0;
+}
+
+int
+ldp_sync_fsm_state_req(struct ldp_igp_sync_if_state_req *state_req)
+{
+ debug_evt_ldp_sync("%s: interface %s (%d) proto %s",
+ __func__, state_req->name, state_req->ifindex,
+ zebra_route_string(state_req->proto));
+
+ struct iface *iface = if_lookup_name(leconf, state_req->name);
+
+ if (!iface) {
+ debug_evt_ldp_sync("%s: Warning: Ignoring LDP IGP SYNC "
+ "interface state request for interface %s (%d). "
+ "Interface does not exist in LDP.",
+ __func__, state_req->name, state_req->ifindex);
+
+ return 0;
+ }
+
+ return send_ldp_sync_state_update(state_req->name,
+ state_req->ifindex,
+ (iface->ldp_sync.state != LDP_SYNC_STA_ACH));
+}
+
+static int
+ldp_sync_fsm_init(struct iface *iface, int state)
+{
+ int old_state = iface->ldp_sync.state;
+
+ iface->ldp_sync.state = state;
+ stop_wait_for_ldp_sync_timer(iface);
+
+ send_ldp_sync_state_update(iface->name, iface->ifindex,
+ (iface->ldp_sync.state != LDP_SYNC_STA_ACH));
+
+ if (old_state != iface->ldp_sync.state) {
+ debug_evt_ldp_sync("%s: resulted in "
+ "changing state for interface %s (%d) from %s to %s",
+ __func__,
+ iface->name, iface->ifindex,
+ ldp_sync_state_name(old_state),
+ ldp_sync_state_name(iface->ldp_sync.state));
+ }
+
+ return 0;
+}
+
+int
+ldp_sync_fsm(struct iface *iface, enum ldp_sync_event event)
+{
+ int old_state = iface->ldp_sync.state;
+ int new_state = 0;
+ int i;
+
+ for (i = 0; ldp_sync_fsm_tbl[i].state != -1; i++)
+ if ((ldp_sync_fsm_tbl[i].state & old_state) &&
+ (ldp_sync_fsm_tbl[i].event == event)) {
+ new_state = ldp_sync_fsm_tbl[i].new_state;
+ break;
+ }
+
+ if (ldp_sync_fsm_tbl[i].state == -1) {
+ /* event outside of the defined fsm, ignore it. */
+ log_warnx("%s: interface %s, event %s not expected in "
+ "state %s ", __func__, iface->name,
+ ldp_sync_event_names[event],
+ ldp_sync_state_name(old_state));
+ return (0);
+ }
+
+ if (new_state != 0)
+ iface->ldp_sync.state = new_state;
+
+ switch (ldp_sync_fsm_tbl[i].action) {
+ case LDP_SYNC_ACT_IFACE_START_SYNC:
+ ldp_sync_act_iface_start_sync(iface);
+ break;
+ case LDP_SYNC_ACT_LDP_START_SYNC:
+ ldp_sync_act_ldp_start_sync(iface);
+ break;
+ case LDP_SYNC_ACT_LDP_COMPLETE_SYNC:
+ ldp_sync_act_ldp_complete_sync(iface);
+ break;
+ case LDP_SYNC_ACT_CONFIG_LDP_OFF:
+ ldp_sync_fsm_init(iface, LDP_SYNC_STA_NOT_ACH);
+ break;
+ case LDP_SYNC_ACT_IFACE_SHUTDOWN:
+ ldp_sync_fsm_init(iface, iface->ldp_sync.state);
+ break;
+ case LDP_SYNC_ACT_NOTHING:
+ /* do nothing */
+ break;
+ }
+
+ if (old_state != iface->ldp_sync.state) {
+
+ debug_evt_ldp_sync("%s: event %s resulted in action %s "
+ "for interface %s, changing state from %s to %s",
+ __func__, ldp_sync_event_names[event],
+ ldp_sync_action_names[ldp_sync_fsm_tbl[i].action],
+ iface->name, ldp_sync_state_name(old_state),
+ ldp_sync_state_name(iface->ldp_sync.state));
+
+ } else {
+ debug_evt_ldp_sync("%s: event %s resulted in action %s "
+ "for interface %s, remaining in state %s",
+ __func__, ldp_sync_event_names[event],
+ ldp_sync_action_names[ldp_sync_fsm_tbl[i].action],
+ iface->name,
+ ldp_sync_state_name(iface->ldp_sync.state));
+ }
+
+ return (0);
+}
+
+void
+ldp_sync_fsm_reset_all(void)
+{
+ struct iface *iface;
+
+ RB_FOREACH(iface, iface_head, &leconf->iface_tree)
+ ldp_sync_fsm(iface, LDP_SYNC_EVT_CONFIG_LDP_OFF);
+}