/* * OSPF Interface functions. * Copyright (C) 1999, 2000 Toshiaki Takada * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "thread.h" #include "linklist.h" #include "prefix.h" #include "if.h" #include "table.h" #include "memory.h" #include "command.h" #include "stream.h" #include "log.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_spf.h" #include "ospfd/ospf_interface.h" #include "ospfd/ospf_ism.h" #include "ospfd/ospf_asbr.h" #include "ospfd/ospf_lsa.h" #include "ospfd/ospf_lsdb.h" #include "ospfd/ospf_neighbor.h" #include "ospfd/ospf_nsm.h" #include "ospfd/ospf_packet.h" #include "ospfd/ospf_abr.h" #include "ospfd/ospf_network.h" #include "ospfd/ospf_dump.h" #ifdef HAVE_SNMP #include "ospfd/ospf_snmp.h" #endif /* HAVE_SNMP */ int ospf_if_get_output_cost (struct ospf_interface *oi) { /* If all else fails, use default OSPF cost */ u_int32_t cost; u_int32_t bw, refbw; bw = oi->ifp->bandwidth ? oi->ifp->bandwidth : OSPF_DEFAULT_BANDWIDTH; refbw = oi->ospf->ref_bandwidth; /* A specifed ip ospf cost overrides a calculated one. */ if (OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (oi->ifp), output_cost_cmd) || OSPF_IF_PARAM_CONFIGURED (oi->params, output_cost_cmd)) cost = OSPF_IF_PARAM (oi, output_cost_cmd); /* See if a cost can be calculated from the zebra processes interface bandwidth field. */ else { cost = (u_int32_t) ((double)refbw / (double)bw + (double)0.5); if (cost < 1) cost = 1; else if (cost > 65535) cost = 65535; } return cost; } void ospf_if_recalculate_output_cost (struct interface *ifp) { u_int32_t newcost; struct route_node *rn; for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) { struct ospf_interface *oi; if ( (oi = rn->info) == NULL) continue; newcost = ospf_if_get_output_cost (oi); /* Is actual output cost changed? */ if (oi->output_cost != newcost) { oi->output_cost = newcost; ospf_router_lsa_update_area (oi->area); } } } /* Simulate down/up on the interface. This is needed, for example, when the MTU changes. */ void ospf_if_reset(struct interface *ifp) { struct route_node *rn; for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) { struct ospf_interface *oi; if ( (oi = rn->info) == NULL) continue; ospf_if_down(oi); ospf_if_up(oi); } } void ospf_if_reset_variables (struct ospf_interface *oi) { /* Set default values. */ /* don't clear this flag. oi->flag = OSPF_IF_DISABLE; */ if (oi->vl_data) oi->type = OSPF_IFTYPE_VIRTUALLINK; else /* preserve network-type */ if (oi->type != OSPF_IFTYPE_NBMA) oi->type = OSPF_IFTYPE_BROADCAST; oi->state = ISM_Down; oi->crypt_seqnum = 0; /* This must be short, (less than RxmtInterval) - RFC 2328 Section 13.5 para 3. Set to 1 second to avoid Acks being held back for too long - MAG */ oi->v_ls_ack = 1; } /* lookup oi for specified prefix/ifp */ struct ospf_interface * ospf_if_table_lookup (struct interface *ifp, struct prefix *prefix) { struct prefix p; struct route_node *rn; struct ospf_interface *rninfo = NULL; p = *prefix; p.prefixlen = IPV4_MAX_PREFIXLEN; /* route_node_get implicitely locks */ if ((rn = route_node_lookup (IF_OIFS (ifp), &p))) { rninfo = (struct ospf_interface *) rn->info; route_unlock_node (rn); } return rninfo; } static void ospf_add_to_if (struct interface *ifp, struct ospf_interface *oi) { struct route_node *rn; struct prefix p; p = *oi->address; p.prefixlen = IPV4_MAX_PREFIXLEN; rn = route_node_get (IF_OIFS (ifp), &p); /* rn->info should either be NULL or equal to this oi * as route_node_get may return an existing node */ assert (!rn->info || rn->info == oi); rn->info = oi; } static void ospf_delete_from_if (struct interface *ifp, struct ospf_interface *oi) { struct route_node *rn; struct prefix p; p = *oi->address; p.prefixlen = IPV4_MAX_PREFIXLEN; rn = route_node_lookup (IF_OIFS (oi->ifp), &p); assert (rn); assert (rn->info); rn->info = NULL; route_unlock_node (rn); route_unlock_node (rn); } struct ospf_interface * ospf_if_new (struct ospf *ospf, struct interface *ifp, struct prefix *p) { struct ospf_interface *oi; if ((oi = ospf_if_table_lookup (ifp, p)) == NULL) { oi = XCALLOC (MTYPE_OSPF_IF, sizeof (struct ospf_interface)); memset (oi, 0, sizeof (struct ospf_interface)); } else return oi; /* Set zebra interface pointer. */ oi->ifp = ifp; oi->address = p; ospf_add_to_if (ifp, oi); listnode_add (ospf->oiflist, oi); /* Initialize neighbor list. */ oi->nbrs = route_table_init (); /* Initialize static neighbor list. */ oi->nbr_nbma = list_new (); /* Initialize Link State Acknowledgment list. */ oi->ls_ack = list_new (); oi->ls_ack_direct.ls_ack = list_new (); /* Set default values. */ ospf_if_reset_variables (oi); /* Add pseudo neighbor. */ oi->nbr_self = ospf_nbr_new (oi); oi->ls_upd_queue = route_table_init (); oi->t_ls_upd_event = NULL; oi->t_ls_ack_direct = NULL; oi->crypt_seqnum = time (NULL); #ifdef HAVE_OPAQUE_LSA ospf_opaque_type9_lsa_init (oi); #endif /* HAVE_OPAQUE_LSA */ oi->ospf = ospf; return oi; } /* Restore an interface to its pre UP state Used from ism_interface_down only */ void ospf_if_cleanup (struct ospf_interface *oi) { struct route_node *rn; struct listnode *node, *nnode; struct ospf_neighbor *nbr; struct ospf_nbr_nbma *nbr_nbma; struct ospf_lsa *lsa; /* oi->nbrs and oi->nbr_nbma should be deleted on InterfaceDown event */ /* delete all static neighbors attached to this interface */ for (ALL_LIST_ELEMENTS (oi->nbr_nbma, node, nnode, nbr_nbma)) { OSPF_POLL_TIMER_OFF (nbr_nbma->t_poll); if (nbr_nbma->nbr) { nbr_nbma->nbr->nbr_nbma = NULL; nbr_nbma->nbr = NULL; } nbr_nbma->oi = NULL; listnode_delete (oi->nbr_nbma, nbr_nbma); } /* send Neighbor event KillNbr to all associated neighbors. */ for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) if ((nbr = rn->info) != NULL) if (nbr != oi->nbr_self) OSPF_NSM_EVENT_EXECUTE (nbr, NSM_KillNbr); /* Cleanup Link State Acknowlegdment list. */ for (ALL_LIST_ELEMENTS (oi->ls_ack, node, nnode, lsa)) ospf_lsa_unlock (&lsa); /* oi->ls_ack */ list_delete_all_node (oi->ls_ack); oi->crypt_seqnum = 0; /* Empty link state update queue */ ospf_ls_upd_queue_empty (oi); /* Reset pseudo neighbor. */ ospf_nbr_delete (oi->nbr_self); oi->nbr_self = ospf_nbr_new (oi); ospf_nbr_add_self (oi); } void ospf_if_free (struct ospf_interface *oi) { ospf_if_down (oi); assert (oi->state == ISM_Down); #ifdef HAVE_OPAQUE_LSA ospf_opaque_type9_lsa_term (oi); #endif /* HAVE_OPAQUE_LSA */ /* Free Pseudo Neighbour */ ospf_nbr_delete (oi->nbr_self); route_table_finish (oi->nbrs); route_table_finish (oi->ls_upd_queue); /* Free any lists that should be freed */ list_free (oi->nbr_nbma); list_free (oi->ls_ack); list_free (oi->ls_ack_direct.ls_ack); ospf_delete_from_if (oi->ifp, oi); listnode_delete (oi->ospf->oiflist, oi); listnode_delete (oi->area->oiflist, oi); thread_cancel_event (master, oi); memset (oi, 0, sizeof (*oi)); XFREE (MTYPE_OSPF_IF, oi); } /* * check if interface with given address is configured and * return it if yes. special treatment for PtP networks. */ struct ospf_interface * ospf_if_is_configured (struct ospf *ospf, struct in_addr *address) { struct listnode *node, *nnode; struct ospf_interface *oi; struct prefix_ipv4 addr; addr.family = AF_INET; addr.prefix = *address; addr.prefixlen = IPV4_MAX_PREFIXLEN; for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi)) if (oi->type != OSPF_IFTYPE_VIRTUALLINK) { if (oi->type == OSPF_IFTYPE_POINTOPOINT) { /* special leniency: match if addr is anywhere on peer subnet */ if (prefix_match(CONNECTED_PREFIX(oi->connected), (struct prefix *)&addr)) return oi; } else { if (IPV4_ADDR_SAME (address, &oi->address->u.prefix4)) return oi; } } return NULL; } int ospf_if_is_up (struct ospf_interface *oi) { return if_is_up (oi->ifp); } struct ospf_interface * ospf_if_exists (struct ospf_interface *oic) { struct listnode *node; struct ospf *ospf; struct ospf_interface *oi; if ((ospf = ospf_lookup ()) == NULL) return NULL; for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) if (oi == oic) return oi; return NULL; } /* Lookup OSPF interface by router LSA posistion */ struct ospf_interface * ospf_if_lookup_by_lsa_pos (struct ospf_area *area, int lsa_pos) { struct listnode *node; struct ospf_interface *oi; for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi)) { if (lsa_pos >= oi->lsa_pos_beg && lsa_pos < oi->lsa_pos_end) return oi; } return NULL; } struct ospf_interface * ospf_if_lookup_by_local_addr (struct ospf *ospf, struct interface *ifp, struct in_addr address) { struct listnode *node; struct ospf_interface *oi; for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) if (oi->type != OSPF_IFTYPE_VIRTUALLINK) { if (ifp && oi->ifp != ifp) continue; if (IPV4_ADDR_SAME (&address, &oi->address->u.prefix4)) return oi; } return NULL; } struct ospf_interface * ospf_if_lookup_by_prefix (struct ospf *ospf, struct prefix_ipv4 *p) { struct listnode *node; struct ospf_interface *oi; /* Check each Interface. */ for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) { if (oi->type != OSPF_IFTYPE_VIRTUALLINK) { struct prefix ptmp; prefix_copy (&ptmp, CONNECTED_PREFIX(oi->connected)); apply_mask (&ptmp); if (prefix_same (&ptmp, (struct prefix *) p)) return oi; } } return NULL; } /* determine receiving interface by ifp and source address */ struct ospf_interface * ospf_if_lookup_recv_if (struct ospf *ospf, struct in_addr src, struct interface *ifp) { struct route_node *rn; struct prefix_ipv4 addr; struct ospf_interface *oi, *match; addr.family = AF_INET; addr.prefix = src; addr.prefixlen = IPV4_MAX_BITLEN; match = NULL; for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) { oi = rn->info; if (!oi) /* oi can be NULL for PtP aliases */ continue; if (oi->type == OSPF_IFTYPE_VIRTUALLINK) continue; if (if_is_loopback (oi->ifp)) continue; if (prefix_match (CONNECTED_PREFIX(oi->connected), (struct prefix *) &addr)) { if ( (match == NULL) || (match->address->prefixlen < oi->address->prefixlen) ) match = oi; } } return match; } void ospf_if_stream_set (struct ospf_interface *oi) { /* set output fifo queue. */ if (oi->obuf == NULL) oi->obuf = ospf_fifo_new (); } void ospf_if_stream_unset (struct ospf_interface *oi) { struct ospf *ospf = oi->ospf; if (oi->obuf) { ospf_fifo_free (oi->obuf); oi->obuf = NULL; if (oi->on_write_q) { listnode_delete (ospf->oi_write_q, oi); if (list_isempty(ospf->oi_write_q)) OSPF_TIMER_OFF (ospf->t_write); oi->on_write_q = 0; } } } static struct ospf_if_params * ospf_new_if_params (void) { struct ospf_if_params *oip; oip = XCALLOC (MTYPE_OSPF_IF_PARAMS, sizeof (struct ospf_if_params)); if (!oip) return NULL; UNSET_IF_PARAM (oip, output_cost_cmd); UNSET_IF_PARAM (oip, transmit_delay); UNSET_IF_PARAM (oip, retransmit_interval); UNSET_IF_PARAM (oip, passive_interface); UNSET_IF_PARAM (oip, v_hello); UNSET_IF_PARAM (oip, fast_hello); UNSET_IF_PARAM (oip, v_wait); UNSET_IF_PARAM (oip, priority); UNSET_IF_PARAM (oip, type); UNSET_IF_PARAM (oip, auth_simple); UNSET_IF_PARAM (oip, auth_crypt); UNSET_IF_PARAM (oip, auth_type); oip->auth_crypt = list_new (); oip->network_lsa_seqnum = htonl(OSPF_INITIAL_SEQUENCE_NUMBER); return oip; } void ospf_del_if_params (struct ospf_if_params *oip) { list_delete (oip->auth_crypt); XFREE (MTYPE_OSPF_IF_PARAMS, oip); } void ospf_free_if_params (struct interface *ifp, struct in_addr addr) { struct ospf_if_params *oip; struct prefix_ipv4 p; struct route_node *rn; p.family = AF_INET; p.prefixlen = IPV4_MAX_PREFIXLEN; p.prefix = addr; rn = route_node_lookup (IF_OIFS_PARAMS (ifp), (struct prefix*)&p); if (!rn || !rn->info) return; oip = rn->info; route_unlock_node (rn); if (!OSPF_IF_PARAM_CONFIGURED (oip, output_cost_cmd) && !OSPF_IF_PARAM_CONFIGURED (oip, transmit_delay) && !OSPF_IF_PARAM_CONFIGURED (oip, retransmit_interval) && !OSPF_IF_PARAM_CONFIGURED (oip, passive_interface) && !OSPF_IF_PARAM_CONFIGURED (oip, v_hello) && !OSPF_IF_PARAM_CONFIGURED (oip, fast_hello) && !OSPF_IF_PARAM_CONFIGURED (oip, v_wait) && !OSPF_IF_PARAM_CONFIGURED (oip, priority) && !OSPF_IF_PARAM_CONFIGURED (oip, type) && !OSPF_IF_PARAM_CONFIGURED (oip, auth_simple) && !OSPF_IF_PARAM_CONFIGURED (oip, auth_type) && listcount (oip->auth_crypt) == 0 && ntohl (oip->network_lsa_seqnum) != OSPF_INITIAL_SEQUENCE_NUMBER) { ospf_del_if_params (oip); rn->info = NULL; route_unlock_node (rn); } } struct ospf_if_params * ospf_lookup_if_params (struct interface *ifp, struct in_addr addr) { struct prefix_ipv4 p; struct route_node *rn; p.family = AF_INET; p.prefixlen = IPV4_MAX_PREFIXLEN; p.prefix = addr; rn = route_node_lookup (IF_OIFS_PARAMS (ifp), (struct prefix*)&p); if (rn) { route_unlock_node (rn); return rn->info; } return NULL; } struct ospf_if_params * ospf_get_if_params (struct interface *ifp, struct in_addr addr) { struct prefix_ipv4 p; struct route_node *rn; p.family = AF_INET; p.prefixlen = IPV4_MAX_PREFIXLEN; p.prefix = addr; rn = route_node_get (IF_OIFS_PARAMS (ifp), (struct prefix*)&p); if (rn->info == NULL) rn->info = ospf_new_if_params (); else route_unlock_node (rn); return rn->info; } void ospf_if_update_params (struct interface *ifp, struct in_addr addr) { struct route_node *rn; struct ospf_interface *oi; for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) { if ((oi = rn->info) == NULL) continue; if (IPV4_ADDR_SAME (&oi->address->u.prefix4, &addr)) oi->params = ospf_lookup_if_params (ifp, oi->address->u.prefix4); } } int ospf_if_new_hook (struct interface *ifp) { int rc = 0; ifp->info = XCALLOC (MTYPE_OSPF_IF_INFO, sizeof (struct ospf_if_info)); IF_OIFS (ifp) = route_table_init (); IF_OIFS_PARAMS (ifp) = route_table_init (); IF_DEF_PARAMS (ifp) = ospf_new_if_params (); SET_IF_PARAM (IF_DEF_PARAMS (ifp), transmit_delay); IF_DEF_PARAMS (ifp)->transmit_delay = OSPF_TRANSMIT_DELAY_DEFAULT; SET_IF_PARAM (IF_DEF_PARAMS (ifp), retransmit_interval); IF_DEF_PARAMS (ifp)->retransmit_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT; SET_IF_PARAM (IF_DEF_PARAMS (ifp), priority); IF_DEF_PARAMS (ifp)->priority = OSPF_ROUTER_PRIORITY_DEFAULT; IF_DEF_PARAMS (ifp)->mtu_ignore = OSPF_MTU_IGNORE_DEFAULT; SET_IF_PARAM (IF_DEF_PARAMS (ifp), v_hello); IF_DEF_PARAMS (ifp)->v_hello = OSPF_HELLO_INTERVAL_DEFAULT; SET_IF_PARAM (IF_DEF_PARAMS (ifp), fast_hello); IF_DEF_PARAMS (ifp)->fast_hello = OSPF_FAST_HELLO_DEFAULT; SET_IF_PARAM (IF_DEF_PARAMS (ifp), v_wait); IF_DEF_PARAMS (ifp)->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; SET_IF_PARAM (IF_DEF_PARAMS (ifp), auth_simple); memset (IF_DEF_PARAMS (ifp)->auth_simple, 0, OSPF_AUTH_SIMPLE_SIZE); SET_IF_PARAM (IF_DEF_PARAMS (ifp), auth_type); IF_DEF_PARAMS (ifp)->auth_type = OSPF_AUTH_NOTSET; #ifdef HAVE_OPAQUE_LSA rc = ospf_opaque_new_if (ifp); #endif /* HAVE_OPAQUE_LSA */ return rc; } static int ospf_if_delete_hook (struct interface *ifp) { int rc = 0; struct route_node *rn; #ifdef HAVE_OPAQUE_LSA rc = ospf_opaque_del_if (ifp); #endif /* HAVE_OPAQUE_LSA */ route_table_finish (IF_OIFS (ifp)); for (rn = route_top (IF_OIFS_PARAMS (ifp)); rn; rn = route_next (rn)) if (rn->info) ospf_del_if_params (rn->info); route_table_finish (IF_OIFS_PARAMS (ifp)); ospf_del_if_params ((struct ospf_if_params *) IF_DEF_PARAMS (ifp)); XFREE (MTYPE_OSPF_IF_INFO, ifp->info); ifp->info = NULL; return rc; } int ospf_if_is_enable (struct ospf_interface *oi) { if (!if_is_loopback (oi->ifp)) if (if_is_up (oi->ifp)) return 1; return 0; } void ospf_if_set_multicast(struct ospf_interface *oi) { if ((oi->state > ISM_Loopback) && (oi->type != OSPF_IFTYPE_LOOPBACK) && (oi->type != OSPF_IFTYPE_VIRTUALLINK) && (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_ACTIVE)) { /* The interface should belong to the OSPF-all-routers group. */ if (!OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS) && (ospf_if_add_allspfrouters(oi->ospf, oi->address, oi->ifp->ifindex) >= 0)) /* Set the flag only if the system call to join succeeded. */ OI_MEMBER_JOINED(oi, MEMBER_ALLROUTERS); } else { /* The interface should NOT belong to the OSPF-all-routers group. */ if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS)) { /* Only actually drop if this is the last reference */ if (OI_MEMBER_COUNT(oi, MEMBER_ALLROUTERS) == 1) ospf_if_drop_allspfrouters (oi->ospf, oi->address, oi->ifp->ifindex); /* Unset the flag regardless of whether the system call to leave the group succeeded, since it's much safer to assume that we are not a member. */ OI_MEMBER_LEFT(oi,MEMBER_ALLROUTERS); } } if (((oi->type == OSPF_IFTYPE_BROADCAST) || (oi->type == OSPF_IFTYPE_POINTOPOINT)) && ((oi->state == ISM_DR) || (oi->state == ISM_Backup)) && (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_ACTIVE)) { /* The interface should belong to the OSPF-designated-routers group. */ if (!OI_MEMBER_CHECK(oi, MEMBER_DROUTERS) && (ospf_if_add_alldrouters(oi->ospf, oi->address, oi->ifp->ifindex) >= 0)) /* Set the flag only if the system call to join succeeded. */ OI_MEMBER_JOINED(oi, MEMBER_DROUTERS); } else { /* The interface should NOT belong to the OSPF-designated-routers group */ if (OI_MEMBER_CHECK(oi, MEMBER_DROUTERS)) { /* drop only if last reference */ if (OI_MEMBER_COUNT(oi, MEMBER_DROUTERS) == 1) ospf_if_drop_alldrouters(oi->ospf, oi->address, oi->ifp->ifindex); /* Unset the flag regardless of whether the system call to leave the group succeeded, since it's much safer to assume that we are not a member. */ OI_MEMBER_LEFT(oi, MEMBER_DROUTERS); } } } int ospf_if_up (struct ospf_interface *oi) { if (oi == NULL) return 0; if (oi->type == OSPF_IFTYPE_LOOPBACK) OSPF_ISM_EVENT_SCHEDULE (oi, ISM_LoopInd); else { struct ospf *ospf = ospf_lookup (); if (ospf != NULL) ospf_adjust_sndbuflen (ospf, oi->ifp->mtu); else zlog_warn ("%s: ospf_lookup() returned NULL", __func__); ospf_if_stream_set (oi); OSPF_ISM_EVENT_SCHEDULE (oi, ISM_InterfaceUp); } return 1; } int ospf_if_down (struct ospf_interface *oi) { if (oi == NULL) return 0; OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceDown); /* delete position in router LSA */ oi->lsa_pos_beg = 0; oi->lsa_pos_end = 0; /* Shutdown packet reception and sending */ ospf_if_stream_unset (oi); return 1; } /* Virtual Link related functions. */ struct ospf_vl_data * ospf_vl_data_new (struct ospf_area *area, struct in_addr vl_peer) { struct ospf_vl_data *vl_data; vl_data = XCALLOC (MTYPE_OSPF_VL_DATA, sizeof (struct ospf_vl_data)); vl_data->vl_peer.s_addr = vl_peer.s_addr; vl_data->vl_area_id = area->area_id; vl_data->format = area->format; return vl_data; } void ospf_vl_data_free (struct ospf_vl_data *vl_data) { XFREE (MTYPE_OSPF_VL_DATA, vl_data); } u_int vlink_count = 0; struct ospf_interface * ospf_vl_new (struct ospf *ospf, struct ospf_vl_data *vl_data) { struct ospf_interface * voi; struct interface * vi; char ifname[INTERFACE_NAMSIZ + 1]; struct ospf_area *area; struct in_addr area_id; struct connected *co; struct prefix_ipv4 *p; if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_vl_new(): Start"); if (vlink_count == OSPF_VL_MAX_COUNT) { if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_vl_new(): Alarm: " "cannot create more than OSPF_MAX_VL_COUNT virtual links"); return NULL; } if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_vl_new(): creating pseudo zebra interface"); snprintf (ifname, sizeof(ifname), "VLINK%d", vlink_count); vi = if_create (ifname, strnlen(ifname, sizeof(ifname))); co = connected_new (); co->ifp = vi; listnode_add (vi->connected, co); p = prefix_ipv4_new (); p->family = AF_INET; p->prefix.s_addr = 0; p->prefixlen = 0; co->address = (struct prefix *)p; voi = ospf_if_new (ospf, vi, co->address); if (voi == NULL) { if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_vl_new(): Alarm: OSPF int structure is not created"); return NULL; } voi->connected = co; voi->vl_data = vl_data; voi->ifp->mtu = OSPF_VL_MTU; voi->type = OSPF_IFTYPE_VIRTUALLINK; vlink_count++; if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_vl_new(): Created name: %s", ifname); if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_vl_new(): set if->name to %s", vi->name); area_id.s_addr = 0; area = ospf_area_get (ospf, area_id, OSPF_AREA_ID_FORMAT_ADDRESS); voi->area = area; if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_vl_new(): set associated area to the backbone"); ospf_nbr_add_self (voi); ospf_area_add_if (voi->area, voi); ospf_if_stream_set (voi); if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_vl_new(): Stop"); return voi; } static void ospf_vl_if_delete (struct ospf_vl_data *vl_data) { struct interface *ifp = vl_data->vl_oi->ifp; vl_data->vl_oi->address->u.prefix4.s_addr = 0; vl_data->vl_oi->address->prefixlen = 0; ospf_if_free (vl_data->vl_oi); if_delete (ifp); vlink_count--; } /* Look up vl_data for given peer, optionally qualified to be in the * specified area. NULL area returns first found.. */ struct ospf_vl_data * ospf_vl_lookup (struct ospf *ospf, struct ospf_area *area, struct in_addr vl_peer) { struct ospf_vl_data *vl_data; struct listnode *node; if (IS_DEBUG_OSPF_EVENT) { zlog_debug ("%s: Looking for %s", __func__, inet_ntoa (vl_peer)); if (area) zlog_debug ("%s: in area %s", __func__, inet_ntoa (area->area_id)); } for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl_data)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug ("%s: VL %s, peer %s", __func__, vl_data->vl_oi->ifp->name, inet_ntoa (vl_data->vl_peer)); if (area && !IPV4_ADDR_SAME (&vl_data->vl_area_id, &area->area_id)) continue; if (IPV4_ADDR_SAME (&vl_data->vl_peer, &vl_peer)) return vl_data; } return NULL; } static void ospf_vl_shutdown (struct ospf_vl_data *vl_data) { struct ospf_interface *oi; if ((oi = vl_data->vl_oi) == NULL) return; oi->address->u.prefix4.s_addr = 0; oi->address->prefixlen = 0; UNSET_FLAG (oi->ifp->flags, IFF_UP); /* OSPF_ISM_EVENT_SCHEDULE (oi, ISM_InterfaceDown); */ OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceDown); } void ospf_vl_add (struct ospf *ospf, struct ospf_vl_data *vl_data) { listnode_add (ospf->vlinks, vl_data); #ifdef HAVE_SNMP ospf_snmp_vl_add (vl_data); #endif /* HAVE_SNMP */ } void ospf_vl_delete (struct ospf *ospf, struct ospf_vl_data *vl_data) { ospf_vl_shutdown (vl_data); ospf_vl_if_delete (vl_data); #ifdef HAVE_SNMP ospf_snmp_vl_delete (vl_data); #endif /* HAVE_SNMP */ listnode_delete (ospf->vlinks, vl_data); ospf_vl_data_free (vl_data); } static int ospf_vl_set_params (struct ospf_vl_data *vl_data, struct vertex *v) { int changed = 0; struct ospf_interface *voi; struct listnode *node; struct vertex_parent *vp = NULL; int i; struct router_lsa *rl; voi = vl_data->vl_oi; if (voi->output_cost != v->distance) { voi->output_cost = v->distance; changed = 1; } for (ALL_LIST_ELEMENTS_RO (v->parents, node, vp)) { vl_data->nexthop.oi = vp->nexthop->oi; vl_data->nexthop.router = vp->nexthop->router; if (!IPV4_ADDR_SAME(&voi->address->u.prefix4, &vl_data->nexthop.oi->address->u.prefix4)) changed = 1; voi->address->u.prefix4 = vl_data->nexthop.oi->address->u.prefix4; voi->address->prefixlen = vl_data->nexthop.oi->address->prefixlen; break; /* We take the first interface. */ } rl = (struct router_lsa *)v->lsa; /* use SPF determined backlink index in struct vertex * for virtual link destination address */ if (vp && vp->backlink >= 0) { if (!IPV4_ADDR_SAME (&vl_data->peer_addr, &rl->link[vp->backlink].link_data)) changed = 1; vl_data->peer_addr = rl->link[vp->backlink].link_data; } else { /* This is highly odd, there is no backlink index * there should be due to the ospf_spf_has_link() check * in SPF. Lets warn and try pick a link anyway. */ zlog_warn ("ospf_vl_set_params: No backlink for %s!", vl_data->vl_oi->ifp->name); for (i = 0; i < ntohs (rl->links); i++) { switch (rl->link[i].type) { case LSA_LINK_TYPE_VIRTUALLINK: if (IS_DEBUG_OSPF_EVENT) zlog_debug ("found back link through VL"); case LSA_LINK_TYPE_TRANSIT: case LSA_LINK_TYPE_POINTOPOINT: if (!IPV4_ADDR_SAME (&vl_data->peer_addr, &rl->link[i].link_data)) changed = 1; vl_data->peer_addr = rl->link[i].link_data; } } } if (IS_DEBUG_OSPF_EVENT) zlog_debug ("%s: %s peer address: %s, cost: %d,%schanged", __func__, vl_data->vl_oi->ifp->name, inet_ntoa(vl_data->peer_addr), voi->output_cost, (changed ? " " : " un")); return changed; } void ospf_vl_up_check (struct ospf_area *area, struct in_addr rid, struct vertex *v) { struct ospf *ospf = area->ospf; struct listnode *node; struct ospf_vl_data *vl_data; struct ospf_interface *oi; if (IS_DEBUG_OSPF_EVENT) { zlog_debug ("ospf_vl_up_check(): Start"); zlog_debug ("ospf_vl_up_check(): Router ID is %s", inet_ntoa (rid)); zlog_debug ("ospf_vl_up_check(): Area is %s", inet_ntoa (area->area_id)); } for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl_data)) { if (IS_DEBUG_OSPF_EVENT) { zlog_debug ("%s: considering VL, %s in area %s", __func__, vl_data->vl_oi->ifp->name, inet_ntoa (vl_data->vl_area_id)); zlog_debug ("%s: peer ID: %s", __func__, inet_ntoa (vl_data->vl_peer)); } if (IPV4_ADDR_SAME (&vl_data->vl_peer, &rid) && IPV4_ADDR_SAME (&vl_data->vl_area_id, &area->area_id)) { oi = vl_data->vl_oi; SET_FLAG (vl_data->flags, OSPF_VL_FLAG_APPROVED); if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_vl_up_check(): this VL matched"); if (oi->state == ISM_Down) { if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_vl_up_check(): VL is down, waking it up"); SET_FLAG (oi->ifp->flags, IFF_UP); OSPF_ISM_EVENT_EXECUTE(oi,ISM_InterfaceUp); } if (ospf_vl_set_params (vl_data, v)) { if (IS_DEBUG_OSPF (ism, ISM_EVENTS)) zlog_debug ("ospf_vl_up_check: VL cost change," " scheduling router lsa refresh"); if (ospf->backbone) ospf_router_lsa_update_area (ospf->backbone); else if (IS_DEBUG_OSPF (ism, ISM_EVENTS)) zlog_debug ("ospf_vl_up_check: VL cost change, no backbone!"); } } } } void ospf_vl_unapprove (struct ospf *ospf) { struct listnode *node; struct ospf_vl_data *vl_data; for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl_data)) UNSET_FLAG (vl_data->flags, OSPF_VL_FLAG_APPROVED); } void ospf_vl_shut_unapproved (struct ospf *ospf) { struct listnode *node, *nnode; struct ospf_vl_data *vl_data; for (ALL_LIST_ELEMENTS (ospf->vlinks, node, nnode, vl_data)) if (!CHECK_FLAG (vl_data->flags, OSPF_VL_FLAG_APPROVED)) ospf_vl_shutdown (vl_data); } int ospf_full_virtual_nbrs (struct ospf_area *area) { if (IS_DEBUG_OSPF_EVENT) { zlog_debug ("counting fully adjacent virtual neighbors in area %s", inet_ntoa (area->area_id)); zlog_debug ("there are %d of them", area->full_vls); } return area->full_vls; } int ospf_vls_in_area (struct ospf_area *area) { struct listnode *node; struct ospf_vl_data *vl_data; int c = 0; for (ALL_LIST_ELEMENTS_RO (area->ospf->vlinks, node, vl_data)) if (IPV4_ADDR_SAME (&vl_data->vl_area_id, &area->area_id)) c++; return c; } struct crypt_key * ospf_crypt_key_new () { return XCALLOC (MTYPE_OSPF_CRYPT_KEY, sizeof (struct crypt_key)); } void ospf_crypt_key_add (struct list *crypt, struct crypt_key *ck) { listnode_add (crypt, ck); } struct crypt_key * ospf_crypt_key_lookup (struct list *auth_crypt, u_char key_id) { struct listnode *node; struct crypt_key *ck; for (ALL_LIST_ELEMENTS_RO (auth_crypt, node, ck)) if (ck->key_id == key_id) return ck; return NULL; } int ospf_crypt_key_delete (struct list *auth_crypt, u_char key_id) { struct listnode *node, *nnode; struct crypt_key *ck; for (ALL_LIST_ELEMENTS (auth_crypt, node, nnode, ck)) { if (ck->key_id == key_id) { listnode_delete (auth_crypt, ck); XFREE (MTYPE_OSPF_CRYPT_KEY, ck); return 1; } } return 0; } u_char ospf_default_iftype(struct interface *ifp) { if (if_is_pointopoint (ifp)) return OSPF_IFTYPE_POINTOPOINT; else if (if_is_loopback (ifp)) return OSPF_IFTYPE_LOOPBACK; else return OSPF_IFTYPE_BROADCAST; } void ospf_if_init () { /* Initialize Zebra interface data structure. */ if_init (); om->iflist = iflist; if_add_hook (IF_NEW_HOOK, ospf_if_new_hook); if_add_hook (IF_DELETE_HOOK, ospf_if_delete_hook); }