/* * Copyright (c) 1997 by the University of Southern California. * All rights reserved. * * Permission to use, copy, modify, and distribute this software and * its documentation in source and binary forms for lawful * non-commercial purposes and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both * the copyright notice and this permission notice appear in supporting * documentation, and that any documentation, advertising materials, * and other materials related to such distribution and use acknowledge * that the software was developed by the University of Southern * California and/or Information Sciences Institute. * The name of the University of Southern California may not * be used to endorse or promote products derived from this software * without specific prior written permission. * * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND * NON-INFRINGEMENT. * * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, * THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Questions concerning this software should be directed to * eddy@isi.edu. * * $Id: pim.c,v 1.28 1998/11/21 03:53:26 eddy Exp $ */ /* * BUGS * * 1. a previously disabled interface does not properly get * enabled upon a SIGHUP */ #define INCLUDE_IF #define INCLUDE_MROUTE_KERNEL #include "include.h" #ifdef PROTO_PIM #define INCLUDE_ROUTE #include "inet.h" #include "mrt.h" #include "mbr.h" #include "targets.h" #include "igmp.h" #include "mroute.h" #include "pim.h" #ifdef PROTO_PIMSM #include "pimsm.h" #endif #ifdef PROTO_PIMDM #include "pimdm.h" #endif /************************************************************************* * Global Miscelleany ************************************************************************/ PROTOTYPE (pim_comp_name, static char *, (void_t, int)); PROTOTYPE (pim_recv, static void, (task *)); PROTOTYPE (pim_cleanup, static void, (task *)); PROTOTYPE (pim_terminate, static void, (task *)); PROTOTYPE (pim_terminate_ifap, static void, (if_addr *)); PROTOTYPE (pim_start_ifap, static void, (task *, if_addr *)); PROTOTYPE (pim_tsi_dump, static void, (FILE *, rt_head *, void_t, const char *)); PROTOTYPE (pim_dump, static void, (task *, FILE *)); PROTOTYPE (pim_dump_policy, static void, (task *, FILE *)); PROTOTYPE (pim_dump_int_policy, static void, (FILE *, config_entry *)); PROTOTYPE (pim_dump_comp, static char *, (void_t, int)); PROTOTYPE (pim_send, int, (if_addr *, struct ip *, int, sockaddr_un *, int)); PROTOTYPE (pim_control_reset, static void, (task *, if_addr *)); PROTOTYPE (pim_control_set, static void, (task *, if_addr *)); PROTOTYPE (pim_ifachange, static void, (task *, if_addr *)); PROTOTYPE (pim_hello_job, static void, (task_timer *, time_t)); PROTOTYPE (pim_hello_if_job, static void, (task_timer *, time_t)); PROTOTYPE (pim_hello_commence, static void, (if_addr *)); PROTOTYPE (pim_hello_send, static void, (if_addr *)); PROTOTYPE (pim_hello_recv, static void, (if_addr *, pim_t *, u_int)); PROTOTYPE (pim_nbr_refresh, static void, (if_addr *, sockaddr_un *, time_t)); PROTOTYPE (pim_nbr_purge, static void, (if_addr *)); PROTOTYPE (pim_nbr_timeout_schedule, static void, (task_timer *, time_t)); PROTOTYPE (pim_nbr_timeout, void, (task_job *)); PROTOTYPE (pim_nbr_refresh_timer, static void, (if_addr *, time_t)); /* * The pim_task is the global task and does nothing which is * component- or interface-specific. It does, however, own the * socket over which all PIM messages are sent and received. */ /* Made extern to give components access to the socket -- Hack until * the general problem with pim general/protocol-specific sockets fixed. * --kurtw */ task *pim_task = (task *) 0; static FILE *out = stdout; /* * pim component policy. an adv_list of components config'd for pim * each adv_entry has a config_list contiaining the components interfaces. */ int pim_config_status = 0; /* whether PIM is enabled in gated.conf */ adv_entry *pim_comp_policy = NULL; /* PIM control info, all components */ /************************************************************************* * TASK-INDEPENDENT VARIABLES *************************************************************************/ sockaddr_un *pim_all_routers = 0; /* All PIM Group - 224.0.0.13 */ sockaddr_un *pim_wildcard = 0; /* 224.0.0.0 */ sockaddr_un *pim_wildcard_mask = 0; /* /4 */ byte pim_wildcard_masklen = 4; /************************************************************************* * The memory blocks *************************************************************************/ static block_t pim_ifparam_block_index; static block_t pim_jpentry_block_index; static block_t pim_jplist_block_index; static block_t pim_task_data_block_index; static block_t pim_nbr_block_index; static block_t pim_src_list_block_index; /* Default time values used for all components unless specified per comp */ time_t pim_default_hello_period; time_t pim_default_mrt_timeout; time_t pim_default_mrt_period; time_t pim_default_hello_period; time_t pim_default_hello_holdtime; time_t pim_default_assert_timeout; time_t pim_default_jp_period; time_t pim_default_jp_holdtime; time_t pim_default_jp_sup_timeout; time_t pim_default_jp_delay_timeout; trace *pim_trace_options = { 0 }; static int pim_print_ifap (ifap) if_addr *ifap; { struct ifa_ps *ips = &ifap->ifa_ps[RTPROTO_PIM]; ifaddr(ifap); fprintf (stdout, "socktype == AFINET: %d\n", socktype (IFA_UNIQUE_ADDR(ifap)) == AF_INET); fprintf (stdout, "proto == RTPROTO_PIM: %d\n", mbr_get_ifproto(ifap)); fprintf (stdout, "IFF_MULTICAST: %d\n", BIT_TEST(ifap->ifa_state, IFS_MULTICAST)); fprintf (stdout, "IFF_REGISTER: %d\n", BIT_TEST(ifap->ifa_state, IFS_REGISTER)); fprintf (stdout, "IFF_TUNNEL: %d\n", BIT_TEST(ifap->ifa_state, IFS_TUNNEL)); fprintf (stdout, "IFF_LOOPBACK: %d\n", BIT_TEST(ifap->ifa_state, IFS_LOOPBACK)); fprintf (stdout, "IFF_BROADCAST: %d\n", BIT_TEST(ifap->ifa_state, IFS_BROADCAST)); fprintf (stdout, "IFF_UP: %d\n", BIT_TEST(ifap->ifa_state, IFS_UP)); fprintf (stdout, "IFPS_NOOUT: %d\n", BIT_TEST(ips->ips_state, IFPS_NOOUT)); } /************************************************************************* * Trace flags and options *************************************************************************/ const bits pim_trace_types[] = { { TR_DETAIL, "detail packets" }, { TR_DETAIL_SEND, "detail send packets" }, { TR_DETAIL_RECV, "detail recv packets" }, { TR_PACKET, "packets" }, { TR_PACKET_SEND, "send packets" }, { TR_PACKET_RECV, "recv packets" }, { TR_DETAIL_1, "detail hello" }, { TR_DETAIL_SEND_1, "detail send hello" }, { TR_DETAIL_RECV_1, "detail recv hello" }, { TR_PACKET_1, "hello" }, { TR_PACKET_SEND_1, "send hello" }, { TR_PACKET_RECV_1, "recv hello" }, { TR_DETAIL_2, "detail register" }, { TR_DETAIL_SEND_2, "detail send register" }, { TR_DETAIL_RECV_2, "detail recv register" }, { TR_PACKET_2, "register" }, { TR_PACKET_SEND_2, "send register" }, { TR_PACKET_RECV_2, "recv register" }, { TR_DETAIL_3, "detail join" }, { TR_DETAIL_SEND_3, "detail send join" }, { TR_DETAIL_RECV_3, "detail recv join" }, { TR_PACKET_3, "join" }, { TR_PACKET_SEND_3, "send join" }, { TR_PACKET_RECV_3, "recv join" }, { TR_DETAIL_4, "detail bootstrap" }, { TR_DETAIL_SEND_4, "detail send bootstrap" }, { TR_DETAIL_RECV_4, "detail recv bootstrap" }, { TR_PACKET_4, "bootstrap" }, { TR_PACKET_SEND_4, "send bootstrap" }, { TR_PACKET_RECV_4, "recv bootstrap" }, { TR_DETAIL_5, "detail assert" }, { TR_DETAIL_SEND_5, "detail send assert" }, { TR_DETAIL_RECV_5, "detail recv assert" }, { TR_PACKET_5, "assert" }, { TR_PACKET_SEND_5, "send assert" }, { TR_PACKET_RECV_5, "recv assert" }, { 0, NULL } }; static const flag_t pim_trace_masks[] = { TR_PIM_DETAIL_HELLO, /* 0 - Router Query */ TR_PIM_DETAIL_REGISTER, /* 1 - REGISTER */ TR_PIM_DETAIL_REGISTER_STOP, /* 2 - REGISTER_STOP */ TR_PIM_DETAIL_JOIN_PRUNE, /* 3 - JOIN_PRUNE */ TR_PIM_DETAIL_BOOTSTRAP, /* 4 - BOOTSTRAP */ TR_PIM_DETAIL_ASSERT, /* 5 - ASSERT */ TR_PIM_DETAIL_GRAFT, /* 6 - GRAFT */ TR_PIM_DETAIL_GRAFT_ACK, /* 7 - GRAFT_ACK */ TR_PIM_DETAIL_CRP_ADV, /* 8 - CRP - ADV */ 0 }; /************************************************************************ * INIT ************************************************************************/ adv_psfunc pim_adv_psfunc = { 0, /* ps_rtmatch */ 0, /* ps_dstmatch */ 0, /* ps_compare */ pim_comp_name, /* ps_print */ 0 /* ps_free */ }; jplist_t * pim_jplist_alloc () { return (jplist_t *) task_block_alloc (pim_jplist_block_index); } void pim_jplist_free (old) jplist_t *old; { task_block_free (pim_jplist_block_index, (void_t) old); } jpentry_t * pim_jpentry_alloc() { return (jpentry_t *) task_block_alloc (pim_jpentry_block_index); } void pim_jpentry_free(old) jpentry_t *old; { task_block_free(pim_jpentry_block_index, (void_t) old); } /* * this function takes sockaddr addr/mask and creates an eaddr_t *. * ea must be provided by the caller, such as a pointer into a header. * the flags field is zero'd to allow for reserved. the caller must * set the addr */ void sock2ea (ea, addr, mask) eaddr_t *ea; /* OUT: caller supplies the memory */ sockaddr_un *addr; /* IN: address */ sockaddr_un *mask; /* in: mask */ { ea->ea_family = ADDRF_IPv4; ea->ea_type = 0; ea->ea_addr = (sock2ip(addr) & sock2ip(mask)); ea->ea_masklen = inet_prefix_mask(mask); ea->ea_flags = 0; /* the caller must set flags if needed. */ } sockaddr_un * eu2sock(eu) euaddr_t *eu; { if (eu->eu_family != ADDRF_IPv4 || eu->eu_type != 0) { trace_log_tf(pim_trace_options, 0, LOG_ERR, ("pim eu2sock: unsupported packet family %d type %d", eu->eu_family, eu->eu_type)); return ((sockaddr_un *) 0); } return (sockbuild_in (0, (eu)->eu_addr)); } /* * pim_var_init gets called before /etc/gated.conf is parsed and * should initialize anything that needs to be done before that point */ void pim_var_init() { pim_default_mrt_period = 15; pim_default_mrt_timeout = 210; pim_default_hello_period = 30; pim_default_hello_holdtime = 105; pim_default_assert_timeout = 210; /* spec says 180 */ pim_default_jp_period = 60; pim_default_jp_holdtime = 210; pim_default_jp_sup_timeout = 75; pim_default_jp_delay_timeout = 5; /* spec sez 4.5 */ #ifdef PROTO_PIMSM pimsm_var_init(); #endif /*PROTO_PIMSM*/ #ifdef PROTO_PIMDM pimdm_var_init(); #endif /*PROTO_PIMDM*/ /* some global addrs */ if (!pim_all_routers) pim_all_routers = sockdup(sockbuild_in(0, htonl(PIM_ALL_ROUTERS))); if (!pim_wildcard) pim_wildcard = sockdup(sockbuild_in(0, htonl(PIM_WILDCARD))); if (!pim_wildcard_mask) pim_wildcard_mask = inet_masks[pim_wildcard_masklen]; adv_psfunc_add(RTPROTO_PIM, &pim_adv_psfunc); } /* macroize this thing */ config_entry * pim_adv_config (td) pim_task_data_t *td; { adv_entry *adv = td->pim_config_data; config_entry *cp; assert ((adv->adv_flag & (ADVF_TYPE|ADVFO_TYPE)) == (ADVFT_PS|ADVFOT_CONFIG) && adv->adv_config); cp = adv->adv_config->conflist_list; return cp; } /* if a component with name handle exists, it will be returned */ static task * pim_create_component(handle, adv) char *handle; /* IN: name of component for use with boundaries */ adv_entry *adv; /* IN: the config options */ { task *tp = task_alloc (handle, TASKPRI_MPROTO,pim_trace_options); pim_task_data_t *td = task_block_alloc (pim_task_data_block_index); int mode = adv->adv_proto; config_entry *clist, *cp; struct mbr_callback *cb; MBR_CALLBACK(cb) { if (!strcmp (cb->tp->task_name, handle)) return cb->tp; } MBR_CALLBACK_END(cb); bzero ((pim_task_data_t *) td, sizeof (pim_task_data_t)); td->pim_mode = mode; td->pim_mrt = mrt_table_create(tp, socktype(inet_mask_host), "PIM Group Table"); td->pim_mrttest = mrt_table_create(tp, socktype(inet_mask_host), "PIM Test Table"); tp->task_proto = IPPROTO_PIM; tp->task_rtproto = RTPROTO_PIM; tp->task_rtbit = rtbit_alloc(tp, FALSE, sizeof (mrt_src_list_t *), (void_t) 0, pim_tsi_dump); tp->task_data = td; td->pim_config_data = (adv_entry *)GA2S(adv); /* set component specific period/timeouts */ td->pim_mrt_timeout = pim_default_mrt_timeout; td->pim_mrt_period = pim_default_mrt_period; td->pim_hello_period = pim_default_hello_period; td->pim_hello_holdtime = pim_default_hello_holdtime; td->pim_jp_period = pim_default_jp_period; td->pim_jp_holdtime = pim_default_jp_holdtime; td->pim_jp_sup_timeout = pim_default_jp_sup_timeout; td->pim_jp_delay_timeout = pim_default_jp_delay_timeout; td->pim_jp_delay_list = (mrt_src_list_t *) mrt_src_list_alloc(); if (!task_create(tp)) task_quit (EINVAL); if (td->pim_mode == PIM_SPARSE) { #ifdef PROTO_PIMSM td->pimsm_crp_period = pimsm_default_crp_period; td->pimsm_crp_holdtime = pimsm_default_crp_holdtime; td->pimsm_cbsr = pimsm_default_cbsr; td->pimsm_bsr_period = pimsm_default_bsr_period; td->pimsm_reg_sup_timeout = pimsm_default_reg_sup_timeout; td->pimsm_probe_period = pimsm_default_probe_period; td->pimsm_probe_list = (mrt_src_list_t *) mrt_src_list_alloc(); td->pimsm_assert_timeout= pim_default_assert_timeout; td->pimsm_assert_list = (mrt_src_list_t *) mrt_src_list_alloc(); td->pimsm_null_iif = mrt_src_list_alloc(); td->pimsm_threshold_dr = pimsm_default_threshold_dr; td->pimsm_threshold_rp = pimsm_default_threshold_rp; td->pimsm_spt_period = pimsm_default_threshold_period; pimsm_register_callbacks(tp); task_set_reinit (tp, pimsm_reinit); task_set_flash (tp, pimsm_flash); #endif /*PROTO_PIMSM*/ } else if (td->pim_mode == PIM_DENSE) { #ifdef PROTO_PIMDM pimdm_register_callbacks(tp); task_set_reinit (tp, pimdm_reinit); task_set_flash (tp, pimdm_flash); #endif /*PROTO_PIMDM*/ } /* now set the component specific options */ clist = pim_adv_config(td); assert (clist); CONFIG_LIST(cp, clist) { /* * ignore the associated ifaps until later, xxx - we probably * should do it here... */ if (cp->config_type == PIM_CONFIG_COMP_IFACES) { continue; } else if (cp->config_type == PIMDM_CONFIG_SENDER_IS_MEMBER) { if (mode != PIM_DENSE) { trace_log_tp (tp, 0, LOG_WARNING, ("pim-mode-create[%s]: sender-is-member only allowed in dense mode", handle)); continue; } /* * XXX: this where a task data field would be set * to allow mode specific sender-is-member hueristic */ } else { /* all the other options are pim-sm */ if (mode != PIM_SPARSE) { trace_log_tp (tp, 0, LOG_WARNING, ("pim-mode-create[%s]: sender-is-member only allowed in dense mode", handle)); continue; } switch (cp->config_type) { case PIMSM_CONFIG_REG_SUP_TIMEOUT: td->pimsm_reg_sup_timeout = (time_t) cp->config_data; break; case PIMSM_CONFIG_PROBE_PERIOD: td->pimsm_probe_period = (time_t) cp->config_data; break; case PIMSM_CONFIG_CRP_PERIOD: td->pimsm_crp_period = (time_t) cp->config_data; break; case PIMSM_CONFIG_CRP_HOLDTIME: td->pimsm_crp_holdtime = (time_t) cp->config_data; break; case PIMSM_CONFIG_BSR_PERIOD: td->pimsm_bsr_period = (time_t) cp->config_data; break; case PIMSM_CONFIG_THRESHOLD: td->pimsm_threshold_dr = (u_long) cp->config_data; td->pimsm_threshold_rp = (u_long) cp->config_data; break; case PIMSM_CONFIG_THRESHOLD_DR: td->pimsm_threshold_rp = (u_long) cp->config_data; break; case PIMSM_CONFIG_THRESHOLD_RP: td->pimsm_threshold_dr = (u_long) cp->config_data; break; } } } CONFIG_LIST_END(cp, clist); return tp; } static void pim_create_components() { pim_task_data_t *td; adv_entry *adv; task *comp; /* Create components as specified in gated.conf */ pim_dump_policy(pim_task, stdout); ADV_LIST(pim_comp_policy, adv) { char *handle = adv->adv_ps; config_list *clist = adv->adv_config; config_entry *cp; assert ((adv->adv_flag & (ADVF_TYPE|ADVFO_TYPE)) == (ADVFT_PS|ADVFOT_CONFIG)); assert ((cp = clist->conflist_list) && (cp->config_type == PIM_CONFIG_COMP_IFACES)); /* find/create the task for handle */ comp = pim_create_component(handle, adv); td = (pim_task_data_t *) comp->task_data; assert (!td->pim_config_data); td->pim_config_data = (adv_entry *)GA2S(adv); } ADV_LIST_END(pim_comp_policy, adv); } static char * pim_comp_name (p, first) void_t p; int first; { return (char *) p; } void pim_init() { pim_task_data_t *td; adv_entry *adv; task *tp; if_addr *ifap; struct mbr_callback *cb; if (!BIT_TEST (pim_config_status, PIM_ENABLED)) { if (pim_task) { pim_terminate(pim_task); pim_task = (task *) 0; } return; } if (!pim_task) { pim_nbr_block_index = task_block_init (sizeof (pimnbr_t), "pim_neighbor_list"); pim_jpentry_block_index = task_block_init (sizeof (jpentry_t), "pim_jpentry"); pim_jplist_block_index = task_block_init (sizeof (jplist_t), "pim_jplist"); pim_src_list_block_index = task_block_init (sizeof (mrt_src_list_t), "pim_mrt_src_list"); pim_task_data_block_index = task_block_init (sizeof (pim_task_data_t), "pim_task_data_t"); pim_ifparam_block_index = task_block_init(sizeof(pim_ifparm_t), "pim_ifparm"); pim_task = task_alloc ("PIM", TASKPRI_MPROTO, pim_trace_options); pim_task->task_proto = IPPROTO_PIM; pim_task->task_rtproto = RTPROTO_PIM; /* set reinit in pim_create_component() */ task_set_recv (pim_task, pim_recv); task_set_cleanup (pim_task, pim_cleanup); task_set_dump (pim_task, pim_dump); task_set_terminate (pim_task, pim_terminate); task_set_ifachange (pim_task, pim_ifachange); pim_task->task_rtbit = rtbit_alloc(pim_task, FALSE, sizeof (mrt_src_list_t *), (void_t) 0, pim_tsi_dump); pim_task->task_socket = task_get_socket (pim_task, AF_INET, SOCK_RAW, IPPROTO_PIM); if (pim_task->task_socket < 0) { task_quit (errno); } if (!task_create (pim_task)) { task_quit (EINVAL); } (void) task_set_option(pim_task, TASKOPTION_RCVDSTADDR, TRUE); if (task_set_option(pim_task, TASKOPTION_RECVBUF, task_maxpacket) < 0) task_quit(errno); if (task_set_option(pim_task, TASKOPTION_SENDBUF, task_maxpacket) < 0) task_quit(errno); if (task_set_option(pim_task, TASKOPTION_NONBLOCKING, (caddr_t) TRUE) < 0) task_quit(errno); /* Disable reception of our own packets */ if (task_set_option(pim_task, TASKOPTION_MULTI_LOOP, FALSE) < 0) task_quit(errno); #ifdef IP_HDRINCL /* let the kernel know we are including the ip header */ if (task_set_option(pim_task, TASKOPTION_IPHEADER_INC, (caddr_t) TRUE) < 0) { task_quit(errno); } #endif /*IP_HDRINCL*/ /* Allocate the buffers */ task_alloc_send(pim_task, 8192 + 32); task_alloc_recv(pim_task, 8192 + 32); /* * Set kernel options for turning on Multicast Routing * and duplicate packet detection. */ mroute_enable_asserts(); /* Call protocol-specific inits. * For pimdm, this just creates additional task blocks */ #ifdef PROTO_PIMDM pimdm_init(); #endif /*PROTO_PIMDM*/ #ifdef PROTO_PIMSM pimsm_init(); #endif /*PROTO_PIMSM*/ } trace_inherit_global(pim_trace_options, pim_trace_types, (flag_t) 0); /* * this list will ensure all new components have been created, and * all configured (new and old) components have there new config * data set. It then walks the ifap list, pointing at the newly configured * task. this allows ifaps to be properly moved amoung components later * in the config process. */ ADV_LIST(pim_comp_policy, adv) { char *handle = adv->adv_ps; config_entry *clist, **iflist = (config_entry **) 0; adv_entry *ifadv = (adv_entry *) 0; if_addr *ifap; assert ((adv->adv_flag & (ADVF_TYPE|ADVFO_TYPE)) == (ADVFT_PS|ADVFOT_CONFIG)); assert (clist = adv->adv_config->conflist_list); /* create new component */ tp = pim_create_component(handle, adv); td = (pim_task_data_t *) tp->task_data; /* * Set the termination method to null so the "PIM" task can * manage sub-task termination ???? */ task_set_terminate(tp, NULL); /* assign all interfaces to the current component */ IF_ADDR(ifap) { struct ifa_ps *ips = &ifap->ifa_ps[RTPROTO_PIM]; config_entry *cp; task *ntp; if (!PIM_ABLE_IFAP(ifap)) continue; /* XXX: this should be done early in the igmp code. */ if ((ntp = mbr_get_iftask(ifap)) && ntp->task_rtproto != RTPROTO_PIM) continue; /* XXX: this should only be done if the ifap is used */ if (!ifap->pim_if_params) ifap->pim_if_params = (pim_ifparm_t *) task_block_alloc(pim_ifparam_block_index); iflist = pim_config_enabled (tp, ifap); if (iflist) { task *otp = PIM_IF_NEW_TASK(ifap); if (otp) continue; /* * if we are here, the current ifap has been config'd for * the current task. we'll also check to see if cbsr and * c-rp have been config'd for the component */ PIM_IF_NEW_TASK(ifap) = tp; #ifdef PROTO_PIMSM if (iflist[PIMSM_CONFIG_CRP]) { /* we have been configured to be the c-rp */ config_entry *cp = iflist[PIMSM_CONFIG_CRP]; IFA_ALLOC(td->pimsm_crp_ifap = ifap); td->pimsm_crp_groups = (addr_list *) cp->config_data; td->pimsm_crp = 1; } /* now set possible bsr state */ if (iflist[PIMSM_CONFIG_CBSR_PRIORITY]) { IFA_ALLOC(td->pimsm_cbsr_ifap = ifap); } #endif /*PROTO_PIMSM*/ assert (!ifap->pim_ifap_config); ifap->pim_ifap_config = iflist; } } IF_ADDR_END (ifap); } ADV_LIST_END(pim_comp_policy, adv); /* * now walk the ifap list one more time terminating and starting * ifaps according to new/different component assignments */ IF_ADDR(ifap) { struct ifa_ps *ips = &ifap->ifa_ps[RTPROTO_PIM]; task *old, *new; if (!PIM_ABLE_IFAP(ifap)) continue; old = mbr_get_iftask (ifap); new = (ifap->pim_if_params) ? ((PIM_IF_NEW_TASK(ifap)) ? PIM_IF_NEW_TASK(ifap) : 0) : 0; if (!new) { if (old) { pim_terminate_ifap(ifap); } } else { if (!old) { pim_start_ifap(new, ifap); } else { mbr_reassign_iftask (ifap, new); } } } IF_ADDR_END(ifap); } /* Terminate a single PIM-SM component */ void pim_terminate_component(tp) task *tp; { register pim_task_data_t *td = (pim_task_data_t *) tp->task_data; register mrt_src_list_t *mrtl; register addr_list *grpl; register if_addr *ifap; #ifdef PROTO_PIMSM if (td->pim_mode == PIM_SPARSE) { pimsm_shutdown(tp); } #endif /*PROTO_PIMSM*/ #ifdef PROTO_PIMDM if (td->pim_mode == PIM_DENSE) { pimdm_shutdown(tp); } #endif /*PROTO_PIMDM*/ mrt_table_delete (td->pim_mrt); td->pim_mrt = (mrt_table_t *) 0; if (td->pim_mrttest) { mrt_table_delete (td->pim_mrttest); td->pim_mrttest = (mrt_table_t *) 0; } if (td->pim_mrttest) { mrt_table_delete (td->pim_mrttest); td->pim_mrttest = (mrt_table_t *) 0; } if (td->pim_config_data) { /* don't want to free the whole list */ /*adv_freelist(td->pim_config_data);*/ } /* now loop through the interfaces freeing up all the pim stuff */ IF_ADDR(ifap) { if (tp == mbr_get_iftask(ifap) && !BIT_TEST(ifap->ifa_state, IFS_REGISTER)) pim_terminate_ifap (ifap); } IF_ADDR_END(ifap); mbr_unregister (tp); task_block_free (pim_task_data_block_index, (void_t) tp->task_data); rtbit_free (tp, tp->task_rtbit); task_delete(tp); tp = (task *) 0; } /* Terminate all PIM components */ static void pim_terminate(tp) task *tp; { struct mbr_callback *comp, *gonner; trace_tp (tp, TR_NORMAL, TRC_NL_AFTER, ("pim_terminate[%s]", tp->task_name)); /* Free up global info */ /* Free up each component, this will in turn free up interface info */ MBR_CALLBACK(comp) { if (comp->tp->task_rtproto != RTPROTO_PIM) continue; gonner = comp; comp->back->forw = comp->forw; comp->forw->back = comp->back; comp = comp->forw; pim_terminate_component(gonner->tp); } MBR_CALLBACK_END(comp); pim_cleanup (pim_task); /* delete the task here */ task_delete(pim_task); pim_task = (task *) 0; } /* * pim_cleanup gets called by pim_task. which clears out * gloabal things, then calls recursively call itself with all * other pim tasks, passing the specific task pointer. */ static void pim_cleanup __PF1 (tp, task *) { if (tp == pim_task) { struct mbr_callback *cb; if_addr *ifap; if (pim_comp_policy) { adv_free_list (pim_comp_policy); pim_comp_policy = (adv_entry *) 0; } MBR_CALLBACK(cb) { if (cb->tp->task_rtproto != RTPROTO_PIM) continue; pim_cleanup(cb->tp); } MBR_CALLBACK_END(cb); trace_freeup(pim_trace_options); /* clear the config task for all ifaps */ IF_ADDR(ifap) { if (ifap->pim_if_params) PIM_IF_NEW_TASK(ifap) = (task *) 0; ifap->pim_ifap_config = (void_t) 0; } IF_ADDR_END(ifap); } else { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; assert (td); if (td->pim_config_data) { td->pim_config_data = (adv_entry *) 0; } #ifdef PROTO_PIMSM if (td->pimsm_crp_ifap) IFA_FREE(td->pimsm_crp_ifap); td->pimsm_crp_ifap = 0; if(td->pimsm_cbsr_ifap) IFA_FREE(td->pimsm_cbsr_ifap); td->pimsm_cbsr_ifap = 0; /* used to determine if new config c-bsr */ #endif /*PROTO_PIMSM*/ trace_freeup(tp->task_trace); } } /************************************************************************* * Interface functions *************************************************************************/ /* * See whether a given interface is administratively enabled or disabled * in this component's configuration list. */ static config_entry ** pim_config_enabled(tp, ifap) task *tp; if_addr *ifap; { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; adv_entry *adv; config_entry **list, *clist, *cp; trace_tp (tp, TR_NORMAL, 0, ("pim_config_enabled[%s]: %s", tp->task_name, ifap->ifa_name)); if (!td || !td->pim_config_data) { /* nope, it doesn't belong to this component */ return (config_entry **) 0; } clist = pim_adv_config(td); CONFIG_LIST(cp, clist) { if (cp->config_type != PIM_CONFIG_COMP_IFACES) continue; adv = (adv_entry *) cp->config_data; list = config_resolv_ifa(adv, ifap, PIM_CONFIG_MAX); if (list) break; } CONFIG_LIST_END(cp, clist); if (!list || list[PIM_CONFIG_DISABLE]) return (config_entry **) 0; return list; /* XXX: list must be freed by caller */ } static void pim_terminate_ifap (ifap) if_addr *ifap; { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; task_timer *tip; task_job *tjp; if (!tp || tp->task_rtproto != RTPROTO_PIM) return; #ifdef PROTO_PIMDM if (td->pim_mode == PIM_DENSE) pimdm_sg_rl_delete_ifap(ifap); #endif if (BIT_TEST(ifap->ifa_state, IFS_BROADCAST)) { krt_multicast_delete(pim_all_routers); task_set_option(pim_task, TASKOPTION_GROUP_DROP, ifap, pim_all_routers); } mroute_del_vif(ifap); pim_nbr_purge(ifap); /* XXX: should this be freed in pim_terminate_ifap()? config_list_free (*((config_list **) ifap->pim_ifap_config)); */ ifap->pim_ifap_config = (config_entry **) 0; /* remove the hello timer and job */ if (tip = PIM_IF_HELLO_TIMER(ifap)) task_timer_delete(tip); /* remove the nbr timeout timer and job */ if (tip = PIM_IF_NBR_TIMER(ifap)) task_timer_delete(tip); if (tjp = PIM_IF_NBR_JOB(ifap)) task_job_delete(tjp); #ifdef PROTO_PIMSM if (td->pim_mode == PIM_SPARSE) { /* remove the j/p timer and job */ if (tip = PIMSM_IF_JP_TIMER(ifap)) task_timer_delete(tip); if (tjp = PIMSM_IF_JP_JOB(ifap)) task_job_delete(tjp); } #endif PROTO_PIMSM mbr_reset_iftask (ifap, mbr_get_iftask(ifap)); task_block_free (pim_ifparam_block_index, (void_t) ifap->pim_if_params); ifap->pim_if_params = (pim_ifparm_t *) 0; } static void pim_start_ifap(tp, ifap) task *tp; if_addr *ifap; { struct ifa_ps *ips = &ifap->ifa_ps[tp->task_rtproto]; pim_task_data_t *td = (pim_task_data_t *) tp->task_data; task *owner = mbr_get_iftask(ifap); trace_tp (tp, TR_NORMAL, 0, ("pim_start_ifap[%s]: called for %s(%A)", tp->task_name, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); /* PIM never uses loopback interface? */ if (!PIM_ABLE_IFAP(ifap)) { BIT_SET(ips->ips_state, IFPS_NOIN); BIT_SET(ips->ips_state, IFPS_NOOUT); trace_log_tf (pim_trace_options, 0, 0, ("pim_start_ifap[%s]: called for non-pimable ifap %s(%A)", tp->task_name, ifap->ifa_name, IFA_UNIQUE_ADDR(ifap))); return; } /* * If we can lock this interface for PIM, * do so, otherwise, another multicast protocol has it */ if (!owner) { assert (mbr_set_iftask (ifap, tp)); } else { assert (owner == tp); } pim_control_set (tp, ifap); /* * create vif and enable sending/recving to/from the * All-PIM-Routers group */ if (ifap->ifa_vif < 1) { mroute_assert_vif(ifap); krt_multicast_add(pim_all_routers); task_set_option(pim_task, TASKOPTION_GROUP_ADD, ifap, pim_all_routers); } /* start ifap specific timers here */ pim_hello_commence(ifap); #ifdef PROTO_PIMSM #ifdef notnow if (td->pim_mode == PIM_SPARSE) pimsm_jp_commence(ifap); #endif notnow #endif /*PROTO_PIMSM*/ #ifdef PROTO_PIMDM if (td->pim_mode == PIM_DENSE) pimdm_sg_rl_add_ifap(ifap); #endif } static void pim_ifachange(tp, ifap) task *tp; /* IN: PIM (global) component */ if_addr *ifap; /* IN: changed interface */ { struct ifa_ps *ips = &ifap->ifa_ps[tp->task_rtproto]; pim_task_data_t *td = (pim_task_data_t *) tp->task_data; task *owner = mbr_get_iftask(ifap); int enabled; if (owner && owner != tp) { trace_tp (tp, TR_NORMAL, TRC_NL_AFTER, ("pim_ifachange[%s]: recv'd ifachange for %A owned by %s", tp->task_name, IFA_UNIQUE_ADDR(ifap), owner ? owner->task_name : "nobody")); return; } if (PIM_IGNORE_IFAP(ifap) || BIT_TEST (ifap->ifa_state, IFS_REGISTER)) { trace_tp (tp, TR_NORMAL, TRC_NL_AFTER, ("pim_ifachange[%s]: ifap %A is not applicable", tp->task_name, IFA_UNIQUE_ADDR(ifap))); return; } pim_control_set (owner, ifap); /* catch the case were pim_control_set disabled us */ if (owner != mbr_get_iftask(ifap)) return; assert(ifap->pim_ifap_config); switch (ifap->ifa_change) { case IFC_NOCHANGE: case IFC_ADD: if (BIT_TEST (ifap->ifa_state, IFS_UP)) pim_start_ifap (owner, ifap); break; case IFC_DELETE: case IFC_DELETE|IFC_UPDOWN: pim_terminate_ifap(ifap); break; default: if (BIT_TEST(ifap->ifa_change, IFC_UPDOWN)) { if (BIT_TEST(ifap->ifa_state, IFS_UP)) { pim_start_ifap (owner, ifap); } else { /* UP to DOWN transition */ pim_terminate_ifap(ifap); } } } } /* * Deal with interface policy */ static void pim_control_reset (tp, ifap) task *tp; if_addr *ifap; { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; struct ifa_ps *ips = &ifap->ifa_ps[tp->task_rtproto]; if (!ifap->pim_if_params) ifap->pim_if_params = (pim_ifparm_t *) task_block_alloc (pim_ifparam_block_index); PIM_IF_HELLO_PERIOD(ifap) = pim_default_hello_period; #ifdef PROTO_PIMSM PIMSM_IF_JP_PERIOD(ifap) = pim_default_jp_period; PIMSM_IF_CRP(ifap) = pimsm_default_crp; PIMSM_IF_CBSR_PRIORITY(ifap) = pimsm_default_cbsr_pri; #endif /*PROTO_PIMSM*/ BIT_RESET(ips->ips_state, IFPS_RESET); } /* Note that this function is called even if the given task doesn't own * the interface. */ static void pim_control_set (tp, ifap) task *tp; if_addr *ifap; { struct ifa_ps *ips = &ifap->ifa_ps[tp->task_rtproto]; pim_task_data_t *td = (pim_task_data_t *) tp->task_data; config_entry **list = ifap->pim_ifap_config; /* Init */ pim_control_reset(tp, ifap); if (list) { int type = PIM_CONFIG_MAX; int enabled = 0; config_entry *cp; /* Fill in the parameters */ while (--type) { if ((cp = list[type])) { switch (type) { case PIM_CONFIG_ENABLE: #ifndef RUSTY_DEBUG if (BIT_TEST(ifap->ifa_state, IFS_LOOPBACK)) { trace_only_tf(pim_trace_options, 0, ("pim_control_set: can't enable pim on loopback")); return; } #endif RUSTY_DEBUG if (BIT_TEST(ifap->ifa_state, IFS_MULTICAST)) { BIT_RESET(ips->ips_state, IFPS_NOIN); BIT_RESET(ips->ips_state, IFPS_NOOUT); } else { trace_only_tf(pim_trace_options, 0, ("control_set: interface %A(%s) not multicast capable", IFA_UNIQUE_ADDR(ifap), ifap->ifa_link->ifl_name)); } break; case PIM_CONFIG_DISABLE: BIT_SET(ips->ips_state, IFPS_NOIN); BIT_SET(ips->ips_state, IFPS_NOOUT); if (mbr_get_iftask(ifap) == tp) mbr_reset_iftask(ifap, tp); return; case PIM_CONFIG_HELLO_PERIOD: PIM_IF_HELLO_PERIOD(ifap) = GA2S(cp->config_data); break; #ifdef PROTO_PIMSM case PIMSM_CONFIG_JP_PERIOD: PIMSM_IF_JP_PERIOD(ifap) = GA2S(cp->config_data); break; case PIMSM_CONFIG_CRP: PIMSM_IF_CRP(ifap) = TRUE; break; case PIMSM_CONFIG_CBSR_PRIORITY: PIMSM_IF_CBSR_PRIORITY(ifap) = GA2S(cp->config_data); break; #endif /*PROTO_PIMSM*/ } } } /*config_resolv_free(list, PIM_CONFIG_MAX);*/ } } void pim_set_pref (proto, pref) int proto; int pref; { assert(0); /* gotta do this!!! */ } /************************************************************************* * Trace PIM packets *************************************************************************/ static void pim_trace __PF7(trp, trace *, dir, int, ifap, if_addr *, who, sockaddr_un *, pim, pim_t *, size, register size_t, detail, int) { task *tp; pim_task_data_t *td; register const char *cmd; byte vers, type, masklen; byte *cp = (byte *) (pim + 1); sockaddr_un *gaddr, *saddr; sockaddr_un *gmask, *smask; eaddr_t egrp, esrc; euaddr_t eu; char *mode; tp = mbr_get_iftask(ifap); td = (pim_task_data_t *) tp->task_data; if (!(tp && td)) { tracef ("pim_trace: %s(%A) is not owned by a pim task %s", ifap->ifa_name, IFA_UNIQUE_ADDR(ifap), (tp) ? tp->task_name : ""); return; } mode = (td->pim_mode == PIM_SPARSE) ? "SM" : "DM"; vers = pim->pim_vers >> 4; type = pim->pim_vers & 0x0f; cmd = trace_state(pim_type_bits, type < PIM_MAX_TYPE ? type : -1); if (dir) { /* Trace packet transmission */ tracef("PIM-%s %sSENT %A -> %#A ", mode, dir > 0 ? "" : "*NOT* ", ifap ? IFA_UNIQUE_ADDR(ifap) : sockbuild_str(""), who); } else { /* Trace packet reception */ tracef("PIM-%s RECV %#A -> %#A ", mode, task_recv_srcaddr, who); if (task_recv_dstaddr) { /* Some systems report the destination address */ tracef("-> %A ", task_recv_dstaddr); } } tracef ("vers: %d type %s", vers, cmd); trace_only_tf(trp, 0, (NULL)); if (detail) { switch (type) { case PIM_HELLO: { hello_t *hel = (hello_t *) (pim + 1); u_int16 type, optlen, val = 0; char *msg = ""; type = ntohs (hel->hel_type); optlen = ntohs (hel->hel_optlen); cp = (byte *) (hel + 1); if (type == PIMHELLO_HOLDTIME) { if (optlen == 2) { GET_SHORT(val, cp); } else { /* bad option length */ msg = "bad option"; val = optlen; } } else { msg = "unknown option"; val = type; } tracef ("\toption(%d) %d: %s %d", optlen, type, msg, val); } break; case PIM_REGISTER: { u_long *reserved = (u_long *) (pim+1); struct ip *ip = (struct ip *) (reserved + 1); tracef ("\tBorder bit %d Null bit %d, src %A, dst %A", BIT_TEST(ntohl(*reserved), REG_PMBR_BIT), BIT_TEST(ntohl(*reserved), REG_NULL_BIT), sockbuild_in (0, ip->ip_src.s_addr), sockbuild_in (0, ip->ip_dst.s_addr)); } break; case PIM_REGISTER_STOP: { if (detail) { GET_EADDR_SOCK((eaddr_t *) &egrp, gaddr, gmask, cp); GET_EADDR_SOCK((eaddr_t *) &esrc, saddr, smask, cp); if (egrp.ea_family != 1 || esrc.ea_family) { tracef ("\tRegister Stop: bad address family, group: %d, source: %d", egrp.ea_family, esrc); } else if (gaddr && saddr) { tracef ("\tRegister Stop: (%A, %A)", gaddr, saddr); } else { tracef ("\tRegister Stop: bad (S, G)"); } } } break; case PIM_GRAFT: case PIM_GRAFT_ACK: case PIM_JOIN_PRUNE: { byte groups, joins, prunes; u_int16 holdtime; /* cp is point at the jp header */ GET_EUADDR_SOCK((euaddr_t *) &eu, saddr, cp);/* dst of this JP */ if (eu.eu_family != 1 || !saddr) { tracef ("\tbad group family %d ", eu.eu_family); return; } *cp++; /* reserved */ groups = *cp++; /* # of groups */ GET_SHORT(holdtime, cp); tracef (" To %A, Groups %d, Holdtime %d ", saddr, groups, holdtime); while (groups--) { u_int16 joins, prunes; GET_EADDR_SOCK((eaddr_t *) &egrp, gaddr, gmask, cp); if (egrp.ea_family != 1 || !gaddr) { tracef ("bad address family, group: %d, source: %d", egrp.ea_family); break; } GET_SHORT(joins, cp); GET_SHORT(prunes, cp); tracef ("\tGroup: %A/%d %d joins %d prunes", gaddr, egrp.ea_masklen, joins, prunes); tracef ("\tJoins"); while (joins--) { GET_EADDR_SOCK((eaddr_t *) &esrc, saddr, smask, cp); if (esrc.ea_family != 1 || !saddr) { tracef ("bad address family, group: %d, source: %d", esrc.ea_family); break; } tracef ("\t\tSource %A/%d 0x%x", saddr, esrc.ea_masklen, esrc.ea_flags); } tracef ("\tPrunes"); while (prunes--) { GET_EADDR_SOCK((eaddr_t *) &esrc, saddr, smask, cp); if (esrc.ea_family != 1 || !saddr) { tracef ("bad address family, group: %d, source: %d", esrc.ea_family); break; } tracef ("\t\tSource %A/%d 0x%x", saddr, esrc.ea_masklen, esrc.ea_flags); } } break; } case PIM_BOOTSTRAP: { u_int16 fragtag; u_int8 hmlen, bsrpri; sockaddr_un *bsraddr; /* read the bsr header */ GET_SHORT(fragtag, cp); hmlen = *cp++; bsrpri = *cp++; /* read the encoded bsr addr */ GET_EUADDR_SOCK((euaddr_t *) &eu, bsraddr, cp); if (eu.eu_family != 1 || !bsraddr) { tracef ("\tbad group family %d", eu.eu_family); goto done; } tracef ("\tBSR %A, frag %d, pri %d, host mask len %d", bsraddr, fragtag, bsrpri, hmlen); trace_only_tf(trp, 0, (NULL)); /* * process group addresses until we consume 'size' * bytes worth of the packet */ while ((caddr_t) (pim + size) > (caddr_t) cp) { byte nrp, nfrag; GET_EADDR_SOCK((eaddr_t *) &egrp, gaddr, gmask, cp); if (!gaddr) return; /* print info about current group */ nrp = *cp++; nfrag = *cp++; tracef ("\t\tGroup %A/%d: frags %d, C-RP's[%d]: ", gaddr, egrp.ea_masklen, nfrag, nrp); *cp++; *cp++; /* reserved */ /* print each RP for group G */ while (nrp--) { u_int16 holdtime; GET_EUADDR_SOCK((euaddr_t *) &eu, saddr, cp); if (eu.eu_family != 1 || !saddr) { tracef ("bad group family %d ", eu.eu_family); return; } GET_SHORT(holdtime, cp); tracef (" %A [%ld]", saddr, holdtime); } } } break; case PIM_ASSERT: { u_int32 m, mpref; GET_EADDR_SOCK((eaddr_t *) &egrp, gaddr, gmask, cp); GET_EUADDR_SOCK((euaddr_t *) &eu, saddr, cp); if (eu.eu_family != 1 || !saddr) { tracef ("\tbad group family %d", eu.eu_family); return; } GET_LONG(mpref, cp); /* metric pref */ GET_LONG(m, cp); tracef ("\tfor %A/%d from %A, metric pref %#x, metric %#x", gaddr, egrp.ea_masklen, saddr, mpref, m); } break; case PIM_CRP_ADV: { byte npfx, auth; u_short holdtime; npfx= *cp++; /* # prefixes */ auth = *cp++; /* priority */ GET_SHORT(holdtime, cp); GET_EUADDR_SOCK((euaddr_t *) &eu, saddr, cp); /* get the rpaddr */ if (eu.eu_family != 1 || !saddr) { tracef ("\tbad group family %d", eu.eu_family); return; } tracef ("\tC-RP (%A), pref count [%d], Auth Bit: %d, holdtime %ld", saddr, npfx, BIT_TEST(CRP_AUTH_BIT, auth), holdtime); trace_only_tf(trp, 0, (NULL)); tracef ("\t\tGroups: "); if (!npfx) { tracef ("224/4"); } else { while (npfx--) { GET_EADDR_SOCK((eaddr_t *) &egrp, gaddr, gmask, cp); if (egrp.ea_family != 1 || !gaddr) { tracef ("bad address family, group: %d, source: %d", egrp.ea_family); break; } tracef (" %A/%d", gaddr, egrp.ea_masklen); } } } break; default: trace_log_tf(pim_trace_options, 0, LOG_WARNING, ("\tpim_trace: Unsupported Code %d from %A", type, task_recv_srcaddr)); } } done: trace_only_tf(trp, 0, (NULL)); } /************************************************************************* * Dumping ************************************************************************/ static void pim_dump_int_policy (fp, list) FILE *fp; config_entry *list; { register config_entry *cp; CONFIG_LIST (cp, list) { switch (cp->config_type) { case PIM_CONFIG_ENABLE: (void) fprintf (fp, " %s enabled ", cp->config_data ? "" : "not"); break; case PIM_CONFIG_DISABLE: (void) fprintf(fp, " %s disabled ", cp->config_data ? "" : "not"); break; case PIM_CONFIG_HELLO_PERIOD: (void) fprintf(fp, " HelloPeriod=%T ", GA2S(cp->config_data)); break; case PIM_CONFIG_HELLO_HOLDTIME: (void) fprintf(fp, " HelloTime=%T ", GA2S(cp->config_data)); break; #ifdef PROTO_PIMSM case PIMSM_CONFIG_JP_PERIOD: (void) fprintf(fp, " JP-Period=%T ", GA2S(cp->config_data)); break; case PIMSM_CONFIG_JP_HOLDTIME: (void) fprintf(fp, " JP-Holdtime=%T ", GA2S(cp->config_data)); break; case PIMSM_CONFIG_JP_SUP_TIMEOUT: (void) fprintf(fp, " JP-Suppresion-Timeout=%T ", GA2S(cp->config_data)); break; case PIMSM_CONFIG_JP_DELAY_TIMEOUT: (void) fprintf(fp, " JP-Delay-Timeout=%T ", GA2S(cp->config_data)); break; case PIMSM_CONFIG_CRP: (void) fprintf(fp, " %s C-RP ", cp->config_data ? "" : "not"); break; case PIMSM_CONFIG_CRP_PERIOD: (void) fprintf(fp, " C-RP-Period=%T", GA2S(cp->config_data)); break; case PIMSM_CONFIG_CRP_HOLDTIME: (void) fprintf(fp, " C-RP-Holdtime=%T", GA2S(cp->config_data)); break; case PIMSM_CONFIG_CBSR_PRIORITY: (void) fprintf(fp, " C-BSR-Priority=%d", GA2S(cp->config_data)); break; case PIMSM_CONFIG_CBSR_TIMEOUT: (void) fprintf(fp, " C-BSR-TImeout=%T", GA2S(cp->config_data)); break; case PIMSM_CONFIG_BSR_PERIOD: (void) fprintf(fp, " BSR-Period=%T", GA2S(cp->config_data)); break; #endif /*PROTO_PIMSM*/ case PIM_CONFIG_ASSERT_TIMEOUT: (void) fprintf(fp, " Assert-Timeout=%T", GA2S(cp->config_data)); break; case PIM_CONFIG_ASSERT_HOLDTIME: (void) fprintf(fp, " Assert-Holdtime=%T", GA2S(cp->config_data)); break; default: fprintf (stderr, "pim_dump_int_policy: illegal type %d", cp->config_type); assert (FALSE); } } CONFIG_LIST_END(cp, list); } static void pim_dump_interfaces (tp, fp) task *tp; FILE *fp; { if_addr *ifap; char tmp[80], *ptr = (char *) &tmp; IF_ADDR(ifap) { task *iftp = mbr_get_iftask(ifap); pim_task_data_t *td; register pimnbr_t *nbr; pimnbr_t *list; int jp = 0, hello = 0, crp = 0; int nbrcount=0; if ((tp && tp != iftp) || iftp->task_rtproto != RTPROTO_PIM || !ifap->pim_if_params) continue; td = (pim_task_data_t *) tp->task_data; list = (pimnbr_t *) ifap->pim_nbrs; hello = PIM_IF_HELLO_PERIOD(ifap); fprintf (fp, "\tInterface: %15A(%s) owner: %s\t hello(%5) ", IFA_UNIQUE_ADDR(ifap), BIT_TEST (ifap->ifa_state, IFS_REGISTER) ? "register" : ifap->ifa_name, tp->task_name, ifap->ifa_vif, hello, jp); #ifdef PROTO_PIMSM jp = PIMSM_IF_JP_PERIOD(ifap); fprintf (fp, "jp(%5d)", jp); #else /*PROTO_PIMSM*/ fprintf (fp, "\n"); #endif /*PROTO_PIMSM*/ /* now list the neighbors */ if (list) { int count = 0; fprintf (fp, "\tNeighbors:\t"); PIMNBR_LIST(nbr, list) { fprintf (fp, "%A(%T) ", nbr->nbr_addr, time_sec - nbr->nbr_ctime); if (++count > 4) { fprintf (fp, "\n\t\t"); count = 0; } } PIMNBR_LIST_END(nbr, list); } fprintf (fp, "\n"); } IF_ADDR_END(ifap); } /* * This routine is called from the mrinfo handler. It returns a NULL ptr * if no neighbors exist, and a list of neighbors with a dummy head entry * otherwise. */ struct sockaddr_list * /* OUT: list of neighbors on the interface */ pim_get_neighbors(ifap) if_addr *ifap; /* IN: interface */ { register pimnbr_t *nbr; pimnbr_t *list = (pimnbr_t *) ifap->pim_nbrs; static struct sockaddr_list salist; struct sockaddr_list *new; if (!list) return NULL; salist.forw = salist.back = &salist; PIMNBR_LIST(nbr, list) { new = mroute_neighbor_alloc(nbr->nbr_addr); INSQUE(new, salist.back); } PIMNBR_LIST_END(nbr, list); return &salist; } static void pim_dump_jplist(fp, jpl) FILE *fp; jplist_t *jpl; { jplist_t *jp; jpentry_t *jpe; if (!jpl) { fprintf (fp, "Empty jp list\n"); return; } for (jp = jpl; jp; jp = jp->forw) { fprintf (fp, "Join Prune list for Group %A\n", jp->group->addr); fprintf (fp, "\tJoin:\n"); for (jpe = jp->join; jpe; jpe = jpe->forw) { fprintf (fp, "\t\tSource (%A): flags: 0x%x\n", jpe->src->addr, jpe->flags); } fprintf (fp, "\tPrunes:\n"); for (jpe = jp->prune; jpe; jpe = jpe->forw) { fprintf (fp, "\t\tSource (%A): flags: 0x%x\n", jpe->src->addr, jpe->flags); } } } #ifdef NOMORE static void pim_dump_nbr_list(fd) FILE *fd; { register if_addr *ifap; IF_ADDR (ifap) { pimnbr_t *list = (pimnbr_t *) ifap->pim_nbrs; pimnbr_t *n; if (!(BIT_TEST(ifap->ifa_state, IFS_MULTICAST) && BIT_TEST(ifap->ifa_state, IFS_UP))) continue; (void) fprintf (fd, "\tNeighbors of %A(%s)\n\t", IFA_UNIQUE_ADDR(ifap), ifap->ifa_link->ifl_name); if (list) { PIMNBR_LIST (n, list) { (void) fprintf (fd, "%A(%T) ", n->nbr_addr, time_sec - n->nbr_rtime); } PIMNBR_LIST_END(n, list); (void) fprintf (fd, "\n"); } } IF_ADDR_END (ifap); } #endif NOMORE static void pim_dump_policy (tp, fp) task *tp; FILE *fp; { adv_entry *adv; if (pim_comp_policy) { (void) fprintf (fp, "\tComponents:\n"); control_entry_dump (fp, 2, pim_comp_policy); } ADV_LIST (pim_comp_policy, adv) { char *name = (char *) adv->adv_ps; config_entry *cp; assert ((adv->adv_flag & ADVF_TYPE) == ADVFT_PS); assert ((adv->adv_flag & ADVFO_TYPE) == ADVFOT_CONFIG); fprintf (fp, "\nComponent: %s\n", name); CONFIG_LIST(cp, adv->adv_config->conflist_list) { if (cp->config_type == PIM_CONFIG_COMP_IFACES) { control_interface_dump (fp, 2, (void_t) cp->config_data, pim_dump_int_policy); } else { fprintf (fp, "\t\toption: %d, val: %d", cp->config_type, (int) cp->config_data); } } CONFIG_LIST_END(cp, adv->adv_config->conflist_list); } ADV_LIST_END (pim_comp_policy, adv); } static void pim_dump_mrt (tp, fp) task *tp; FILE *fp; { group_t *grp; source_t *src; assert(0); } static void pim_dump (tp, fp) task *tp; FILE *fp; { pim_task_data_t *td = (pim_task_data_t *) tp->task_data; struct mbr_callback *comp; assert (tp == pim_task); fprintf (stdout, "PIM dump task %s\n", tp->task_name); pim_dump_policy(tp, fp); MBR_CALLBACK(comp) { pim_task_data_t *td; if (comp->tp->task_rtproto != RTPROTO_PIM) continue; td = comp->tp->task_data; pim_dump_interfaces (comp->tp, fp); mrt_print_table (fp, td->pim_mrt); #ifdef PROTO_PIMSM if (td->pim_mode == PIM_SPARSE) pimsm_dump (tp, fp); #endif /*PROTO_PIMSM*/ #ifdef PROTO_PIMDM if (td->pim_mode == PIM_DENSE) pimdm_dump (tp, fp); #endif /*PROTO_PIMDM*/ } MBR_CALLBACK_END(comp); } static void pim_tsi_dump __PF4(fp, FILE *, rth, rt_head *, data, void_t, pfx, const char *) { target *tlp = (target *) data; td_entry *tdp; #ifdef notyet TD_TSI_GET(tlp, rth, tdp); /* XXX: unfinished */ if (tdp) { if (BIT_TEST(tdp->td_flags, TDF_HOLDDOWN|TDF_POISON)) { (void) fprintf(fp, "%sRIP %A%x <%s> remaining %#T", pfx, *tlp->target_dst, tlp->target_flags, trace_bits(target_entry_bits, tdp->td_flags), tdp->td_metric * 1); } else { (void) fprintf(fp, "%sRIP %A%s <%s> metric %u", pfx, *tlp->target_dst, tlp->target_flags, trace_bits(target_entry_bits, tdp->td_flags), tdp->td_metric); } } #endif /*notyet*/ return; } void pim_src_refresh(src) source_t *src; { time_t nexttime = time_sec + src->src_holdtime; downstream_t *dp; /* if a downstream interface has a greater timeout use that. */ DOWNSTREAM_LIST(dp, src->src_downstream) { if (dp->ds_timeout > nexttime) nexttime = dp->ds_timeout; } DOWNSTREAM_LIST_END(dp, src->src_downstream); src->src_rtime = time_sec; if (src->src_timeout < nexttime) src->src_timeout = nexttime; } static void print_downstream (str, src, dp) char *str; source_t *src; downstream_t *dp; { fprintf (stdout, "%s (%A, %A), ifap: %s (%A): protos %#x\n", str, src->addr, src->src_gaddr, dp->ds_ifap->ifa_link->ifl_name, IFA_UNIQUE_ADDR(dp->ds_ifap), dp->ds_ifap, dp->ds_protos); fflush (stdout); } /************************************************************************* * PIM Recieve function *************************************************************************/ static void pim_recv (tp) task *tp; { task *iftp; pim_task_data_t *td; /* Task data for the SM/DM task (not tp) */ size_t iphl, pktlen, explen, pimlen; int n_packets = TASK_PACKET_LIMIT; int type, vers; while (n_packets-- && !task_receive_packet(tp, &pktlen)) { register pim_t *pim; register struct ip *ip; if_addr *ifap; ip = task_get_recv_buffer(struct ip *); iphl = task_parse_ip_hl(ip); pim = (pim_t *) ((caddr_t) ip + iphl); vers = pim->pim_vers >> 4; if (vers != 2) { trace_log_tf(pim_trace_options, 0, LOG_WARNING, ("pim_recv: Unsupported Version %d from %A", pim->pim_vers >> 4, task_recv_srcaddr)); continue; } /* * if this is a register message (i.e. sent to us (RP) from S's * DR. the kernel has fwd'd the msg and we have recieved the * a copy of the register head. in this case the length we're * expecting is 48 (ip header + pim header + register header + * inner ip header), otherwise pktlen must equal the ip->ip_len. */ type = pim->pim_vers & 0x0f; explen = (type == PIM_REGISTER) ? iphl + sizeof (pim_t) + sizeof (u_long) + iphl : iphl + ip->ip_len; if (pktlen < explen) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pim_recv: register header too short: only %d of %d, from %A", pktlen, explen, task_recv_srcaddr)); continue; } pimlen = ip->ip_len; /* * let's be sure we're not the sender to avoid loops, and * interfaces with multiple addrs */ ifap = if_withaddr(task_recv_srcaddr, FALSE); if (ifap) { trace_log_tf(pim_trace_options, 0, LOG_ERR, ("pim_recv: ignoring our own packet (src=%A), type: %d", task_recv_srcaddr, type)); continue; } /* * Find the interface that matches the source of the PIM packet. * In the presence of tunnels, doing an RPF check for the source * of the packet may return the wrong interface when the packet * delivery is valid but not via the tunnel. */ ifap = if_withdst(task_recv_srcaddr); if (!ifap) { trace_log_tp (tp, 0, LOG_ERR, ("pim_recv: could not determine the RPF ifap for %A, ignoring", task_recv_srcaddr)); continue; } iftp = mbr_get_iftask(ifap); if (!iftp || iftp->task_rtproto != RTPROTO_PIM) { trace_log_tp (pim_task, 0, LOG_ERR, ("pim_recv: pim packet (%A, %A) type: %d recieved on a non-pim interface %s", task_recv_srcaddr, task_recv_dstaddr, type, (ifap) ? ifap->ifa_name : "(NULL)")); continue; } td = (pim_task_data_t *) iftp->task_data; /* * finally, call the appropriate function to handle the current * packet. NOTE: most routines take the pim header while reg_recv * and bsr_recv take the ip header. Gated passes the ip packet * yet deducts the size of the ip header from the ip->ip_len. so * the resultant ip->ip_len is really pimlen, however pktlen is the * sizeof of the packet we've been given, which always includes the * ip header. in short pimlen is the sizeof the contained pim message * and pktlen is the size of the entire ip packet. */ switch (type) { case PIM_HELLO: pim_hello_recv (ifap, pim, pimlen); break; case PIM_JOIN_PRUNE: #ifdef PROTO_PIMSM if (td->pim_mode == PIM_SPARSE) pimsm_jp_recv (ifap, pim, pimlen); #endif /*PROTO_PIMSM*/ #ifdef PROTO_PIMDM if (td->pim_mode == PIM_DENSE) pimdm_jp_recv (ifap, pim, pimlen); #endif /*PROTO_PIMDM*/ break; case PIM_ASSERT: #ifdef PROTO_PIMSM if (td->pim_mode == PIM_SPARSE) pimsm_assert_recv (ifap, pim, pimlen); #endif /*PROTO_PIMSM*/ #ifdef PROTO_PIMDM if (td->pim_mode == PIM_DENSE) pimdm_assert_recv (ifap, pim, pimlen); #endif /*PROTO_PIMDM*/ break; #ifdef PROTO_PIMDM case PIM_GRAFT: pimdm_graft_recv (ifap, pim, pimlen); break; case PIM_GRAFT_ACK: pimdm_graft_ack_recv (ifap, pim, pimlen); break; #endif /*PROTO_PIMDM*/ #ifdef PROTO_PIMSM case PIM_REGISTER: if (td->pim_mode == PIM_SPARSE) pimsm_reg_recv (ifap, ip, pktlen); else trace_log_tf(pim_trace_options, 0, LOG_WARNING, ("pim_recv: recv'd a register message from (%A) on a DM iface", task_recv_srcaddr)); break; case PIM_REGISTER_STOP: if (td->pim_mode == PIM_SPARSE) pimsm_regstop_recv (ifap, pim, pimlen); else trace_log_tf(pim_trace_options, 0, LOG_WARNING, ("pim_recv: recv'd a register stop from (%A) on a DM iface", task_recv_srcaddr)); break; case PIM_BOOTSTRAP: /* we pass ip and not pim, because this packet must be fwd'd */ if (td->pim_mode == PIM_SPARSE) pimsm_bsr_recv (ifap, ip, pktlen); else trace_log_tf(pim_trace_options, 0, LOG_WARNING, ("pim_recv: recv'd a bootstrap from (%A) on a DM iface", task_recv_srcaddr)); break; case PIM_CRP_ADV: if (td->pim_mode == PIM_SPARSE) pimsm_crp_recv (ifap, pim, pimlen); else trace_log_tf(pim_trace_options, 0, LOG_WARNING, ("pim_recv: recv'd a crp from (%A) on a DM iface", task_recv_srcaddr)); break; #endif /*PROTO_PIMSM*/ default: trace_log_tf(pim_trace_options, 0, LOG_WARNING, ("pim_recv: Unsupported Code %d from %A", type, task_recv_srcaddr)); continue; } if (TRACE_PACKET_RECV_TP (tp, (pim->pim_vers >> 2) - PIM_BASE_TYPE, PIM_MAX_TYPE - PIM_BASE_TYPE, pim_trace_masks)) { pim_trace (tp->task_trace, 0, ifap, pim_all_routers, pim, sizeof (pim_t), TRACE_DETAIL_RECV_TP (tp, (pim->pim_vers >> 2) - PIM_BASE_TYPE, PIM_MAX_TYPE - PIM_BASE_TYPE, pim_trace_masks)); } } } /************************************************************************* * pim send, generic pimsm send routine *************************************************************************/ int pim_send __PF5 (ifap, if_addr *, ip, struct ip *, iplen, int, dst, sockaddr_un *, loop, int) { static if_addr *last_ifap; pim_t *pim; size_t rc, pimlen, iphl; task *tp = mbr_get_iftask(ifap); ip->ip_v = IPVERSION; ip->ip_hl = sizeof(struct ip) >> 2; ip->ip_tos = 0; ip->ip_off = 0; ip->ip_p = IPPROTO_PIM; ip->ip_len = iplen; ip->ip_src.s_addr = sock2ip(ifap->ifa_addr_local); ip->ip_dst.s_addr = sock2ip(dst); ip->ip_ttl = MAXTTL; /* xxx: do we need to do this everytime? */ if (IN_MULTICAST (ntohl(sock2ip (dst)))) { (void) task_set_option (pim_task, TASKOPTION_MULTI_TTL, 1); if (last_ifap != ifap) (void) task_set_option (pim_task, TASKOPTION_MULTI_IF, last_ifap = ifap); } /* * now finish the pim headers */ pim = (pim_t *) (ip + 1); pim->pim_resvd = 0; pim->pim_cksum = 0; pimlen = iplen - sizeof (struct ip); pim->pim_cksum = inet_cksum((void_t) pim, pimlen); rc = task_send_packet (pim_task, (void_t) ip, iplen, 0, dst); if (rc != iplen) { if (rc < 0) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pim_send: %s(%A) send error %d", ifap->ifa_name, IFA_UNIQUE_ADDR(ifap), errno)); } else { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pim_send: %s(%A) only sent %d of %d bytes", ifap->ifa_name, IFA_UNIQUE_ADDR(ifap), rc, iplen)); } } if (loop && IN_MULTICAST(ntohl(sock2ip(dst)))) { /* XXX send copy to our loop back */ if (task_set_option(pim_task, TASKOPTION_MULTI_LOOP, TRUE) < 0) { task_quit(errno); } } if (TRACE_PACKET_SEND_TP (tp, (pim->pim_vers >> 2) - PIM_BASE_TYPE, PIM_MAX_TYPE - PIM_BASE_TYPE, pim_trace_masks)) { pim_trace (tp->task_trace, rc, ifap, pim_all_routers, pim, sizeof (pim_t), TRACE_DETAIL_SEND_TP (tp, (pim->pim_vers >> 2) - PIM_BASE_TYPE, PIM_MAX_TYPE - PIM_BASE_TYPE, pim_trace_masks)); } return rc; } /************************************************************************* * Hello's, neighbors and dr election *************************************************************************/ static void pim_dr_enable(ifap) if_addr *ifap; { struct ifa_ps *ips = &ifap->ifa_ps[RTPROTO_PIM]; task *tp = mbr_get_iftask(ifap); if (!BIT_TEST(ips->ips_state, IFPS_DR_STATUS)) { trace_tp (tp, TR_STATE, 0, ("pim_dr_enable: Elected DR on interface %A (%s)", IFA_UNIQUE_ADDR(ifap), ifap->ifa_name)); BIT_SET(ips->ips_state, IFPS_DR_STATUS); #ifdef PROTO_IGMP /* only has an effect if IGMPv1 is running, IGMPv2 is a noop */ igmp_enable_querier (ifap); #endif } } static void pim_dr_disable(ifap) if_addr *ifap; { struct ifa_ps *ips = &ifap->ifa_ps[RTPROTO_PIM]; if (BIT_TEST(ips->ips_state, IFPS_DR_STATUS)) { trace_tp (mbr_get_iftask(ifap), TR_STATE, 0, ("pim_dr_disable: Unelected DR on interface %A (%s)", IFA_UNIQUE_ADDR(ifap), ifap->ifa_link->ifl_name)); BIT_RESET(ips->ips_state, IFPS_DR_STATUS); #ifdef PROTO_IGMP igmp_disable_querier(ifap, ((pimnbr_t *) ifap->pim_nbrs)->forw->nbr_addr); #endif } } static void pim_dr_reset (ifap) if_addr *ifap; { if (sockaddrcmp_in (ifap->ifa_addr_local, ((pimnbr_t *) ifap->pim_nbrs)->forw->nbr_addr)) pim_dr_enable(ifap); else pim_dr_disable(ifap); } static void pim_hello_commence (ifap) if_addr *ifap; { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; pimnbr_t *list, **listp = (pimnbr_t **) &ifap->pim_nbrs; task_timer **ttpp; time_t period; if (!*listp) { list = (pimnbr_t *) task_block_alloc (pim_nbr_block_index); list->forw = list; list->back = list; *listp = list; } else { list = *listp; } /* * place ourselves on the neighbor list. this allows us * to use this list to quickly determine the dr. */ if (list == list->forw) pim_nbr_refresh (ifap, IFA_UNIQUE_ADDR(ifap), PIM_HOLD_FOREVER); period = (PIM_IF_HELLO_PERIOD(ifap)) ? PIM_IF_HELLO_PERIOD(ifap) : td->pim_hello_period; /* give each interface a timer for now */ ttpp = &PIM_IF_HELLO_TIMER(ifap); task_restart_timer(ttpp, tp, "Hello", period, PIM_OFFSET, pim_hello_if_job, (void_t) ifap); } static void pim_hello_recv __PF3 (ifap, if_addr *, pim, pim_t *, pimlen, u_int) { task *tp = mbr_get_iftask(ifap); hello_t *hello = (hello_t *) (pim + 1); u_int16 *opt = (u_int16 *) (hello + 1); u_int16 holdtime = 0; u_int32 priority = -1; /* * do a sanity check on the src addr */ if (IN_BADCLASS (task_recv_srcaddr) || IN_CLASSD (task_recv_srcaddr) || sockaddrcmp (task_recv_srcaddr, inet_addr_allhosts) || sockaddrcmp (task_recv_srcaddr, inet_addr_default)) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pim_hello: invalid src address: %A", task_recv_srcaddr)); return; } pimlen -= sizeof (pim_t); if (pimlen <= sizeof (hello_t) && pimlen <= sizeof (hello_t) + ntohs(hello->hel_optlen)) { trace_log_tf(pim_trace_options, 0, LOG_WARNING, ("pim_hello: ignoring hello: short packet %d, from %A", pimlen, task_recv_srcaddr)); return; } /* * process hello options, currently we only know of HoldTime */ while (pimlen > sizeof (hello_t)) { u_int16 type = ntohs (hello->hel_type); u_int16 optlen = ntohs (hello->hel_optlen); int size; switch (type) { case PIMHELLO_HOLDTIME: if (optlen != 2) { trace_log_tf(pim_trace_options, 0, LOG_ERR, ("pim_hello: invalid holdtime len: %d, from %A", optlen, task_recv_srcaddr)); return; } holdtime = ntohs(*opt); break; default: if (type > 2 && type <= 16) { trace_log_tf(pim_trace_options, 0, LOG_WARNING, ("pim_hello: unsupported option : %d, from %A", type, task_recv_srcaddr)); } else { trace_log_tf(pim_trace_options, 0, LOG_ERR, ("pim_hello: invalid option : %d, from %A", type, task_recv_srcaddr)); } continue; } size = sizeof (hello_t) + optlen; pimlen -= size; hello += size; } if (pimlen > 0) { trace_log_tf(pim_trace_options, 0, LOG_ERR, ("pim_hello: extra data[%d] from %A", pimlen, task_recv_srcaddr)); } if (holdtime == 0) { trace_log_tf (pim_trace_options, 0, LOG_ERR, ("pim_hello: got no holdtime from %A", task_recv_srcaddr)); return; } /* * if we've made it here we have a hold time from a valid nbr. * we refresh (or add) the nbr to our nbr list. the * nbr list is sorted in DR order, so the head of the list is * always the DR. */ trace_tp (mbr_get_iftask(ifap), TR_STATE, 0, ("pim_hello_recv[%s]: nbr %A being refreshed at %T\n", tp->task_name, task_recv_srcaddr, time_sec)); pim_nbr_refresh (ifap, task_recv_srcaddr, holdtime); return; } static void pim_hello_send __PF1 (ifap, if_addr *) { struct ip *ip = (struct ip *) task_get_send_buffer(struct ip *); pim_t *pim = (pim_t *) (ip + 1); hello_t *hello = (hello_t *) (pim + 1); byte *cp, len; pim->pim_vers = 0x20; /* version and type */ hello->hel_type = htons(PIMHELLO_HOLDTIME); hello->hel_optlen = htons(PIMHELLO_HOLDTIME_LEN); cp = (byte *) (hello + 1); PUT_SHORT (pim_default_hello_holdtime, cp); len = cp - (byte *) ip; pim_send (ifap, ip, len, pim_all_routers, FALSE); } static void pim_hello_if_job __PF2(tip, task_timer *, interval, time_t) { register if_addr *ifap = (if_addr *) (tip->task_timer_data); struct ifa_ps *ips = &ifap->ifa_ps[RTPROTO_PIM]; trace_tp (tip->task_timer_task, TR_TIMER, 0, ("pim_hello_if_job[%s] activated for %A", tip->task_timer_task->task_name, IFA_UNIQUE_ADDR(ifap))); if (PIM_BCAST_IFAP(ifap)) { pim_hello_send(ifap); } else { task_timer_reset(PIM_IF_HELLO_TIMER(ifap)); } } /* * this function will insert or refresh the nbr according to * the given src addr. This is a sorted list where the DR appears * as the first entry */ static void pim_nbr_refresh __PF3 (ifap, if_addr *, src, sockaddr_un *, holdtime, time_t) { task *tp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) tp->task_data; pimnbr_t *list = (pimnbr_t *) ifap->pim_nbrs; time_t expire = list->forw->nbr_timeout - time_sec; pimnbr_t *nbr = list, *n; pimnbr_t *nn; int cmp = -1; /* locate the position in the nbr list for the src param */ assert (list); for (n = list->forw; n != list; n = n->forw) { /* determine position in descending ip addr order */ if (cmp < 0) { cmp = sockaddrcmp2(src, n->nbr_addr); if (cmp >= 0) nbr = n; } /* determine the next timeout */ if (expire > n->nbr_timeout - time_sec) expire = n->nbr_timeout - time_sec; } if (nbr == list || cmp != 0) { #ifdef PROTO_PIMDM if (td->pim_mode == PIM_DENSE) { pimdm_new_nbr(ifap); } #endif /*PROTO_PIMDM*/ #ifdef PROTO_PIMSM if (td->pim_mode == PIM_SPARSE) { pimsm_new_nbr(ifap, src); } #endif /*PROTO_PIMSM*/ nn = (pimnbr_t *) task_block_alloc (pim_nbr_block_index); nn->nbr_addr = sockdup(src); nn->nbr_ctime = time_sec; NBR_ENQ (nn, nbr->back); nbr = nn; /* send our own hello immediatly to let them know about us */ pim_hello_send(ifap); } nbr->nbr_rtime = time_sec; nbr->nbr_holdtime = holdtime; nbr->nbr_timeout = (holdtime == PIM_HOLD_FOREVER) ? PIM_HOLD_FOREVER : time_sec + holdtime; if (expire > holdtime) expire = holdtime; /* we have a new DR */ pim_dr_reset (ifap); pim_nbr_refresh_timer(ifap, expire); } pimnbr_t * pim_nbr_locate (ifap, saddr) if_addr *ifap; sockaddr_un *saddr; { pimnbr_t *n = (pimnbr_t *) 0, *np; PIMNBR_LIST (np, (pimnbr_t *) ifap->pim_nbrs) { if (sockaddrcmp_in (np->nbr_addr, saddr)) { n = np; break; } } PIMNBR_LIST_END (np, ifap->pim_nbrs); return n; } static void pim_nbr_refresh_timer __PF2 (ifap, if_addr *, timeout, time_t) { pim_ifparm_t *ifparms = ifap->pim_if_params; if (!ifparms->pimif_nbr_timer) { ifparms->pimif_nbr_timer = (void_t) task_timer_create (mbr_get_iftask(ifap), "Neighbor Timeout", (flag_t) 0, (time_t) 0, timeout, pim_nbr_timeout_schedule, (void_t) ifap); } else { if (!BIT_TEST (((task_timer *) ifparms->pimif_nbr_timer)->task_timer_flags, TIMERF_INACTIVE)) { task_timer_reset ((task_timer *)ifparms->pimif_nbr_timer); } if (timeout != PIM_HOLD_FOREVER) { ((task_timer *) ifparms->pimif_nbr_timer)->task_timer_data = (void_t) ifap; task_timer_set((task_timer *)ifparms->pimif_nbr_timer, (time_t) 0, timeout); } } } static void pim_nbr_purge __PF1(ifap, if_addr *) { pim_ifparm_t *ifparms = ifap->pim_if_params; pimnbr_t *list = (pimnbr_t *) ifap->pim_nbrs; pimnbr_t *n, *delete; if (list) { if (ifparms->pimif_nbr_timer) { if (ifparms->pimif_nbr_timer->task_timer_job) task_timer_delete((task_timer *) ifparms->pimif_nbr_timer); ifparms->pimif_nbr_timer = (task_timer *) 0; } #ifdef PROTO_IGMP if (sockaddrcmp_in (ifap->ifa_addr_local, list->forw->nbr_addr)) { igmp_disable_querier(ifap, list->forw->nbr_addr); } #endif n = list->forw; while (n != list) { delete = n; n = n->forw; NBR_DEQ(delete); task_block_free (pim_nbr_block_index, (void_t) delete); } task_block_free (pim_nbr_block_index, (void_t) list); ifap->pim_nbrs = (void_t) 0; } } static void pim_nbr_timeout_schedule __PF2(tip, task_timer *, interval UNUSED, time_t) { if_addr *ifap = (if_addr *) (tip->task_timer_data); if (PIM_IF_NBR_JOB(ifap)) { trace_log_tf(pim_trace_options, 0, LOG_ERR, ("PIM: a refresh job already exists on %A(%s)", IFA_UNIQUE_ADDR(ifap), ifap->ifa_name)); return; } PIM_IF_NBR_JOB(ifap) = task_job_create (pim_task, TASK_JOB_PRIO_WORST, "PIM NBR timeout", pim_nbr_timeout, ifap); } void pim_nbr_timeout __PF1 (tjp, task_job *) { task *tp = tjp->task_job_task; if_addr *ifap = (if_addr *) (tjp->task_job_data); task *iftp = mbr_get_iftask(ifap); pim_task_data_t *td = (pim_task_data_t *) iftp->task_data; pimnbr_t *list = (pimnbr_t *) ifap->pim_nbrs; time_t nextime = PIM_HOLD_FOREVER; pimnbr_t *n, *aged; /* * search for aged out nbrs */ n = list->forw; while (n != list) { /* leave PIM_HOLD_FOREVER nbrs alone */ if (n->nbr_holdtime == PIM_HOLD_FOREVER) { n = n->forw; /* it'd be nice to have one n = n->forw */ } else if (n->nbr_timeout <= time_sec) { aged = n; n = n->forw; /* next to check */ NBR_DEQ (aged); trace_tp (tjp->task_job_task, TR_TIMER, 0, ("pim_nbr_timeout: PIM Nieghbor %A on interface %A(%s) timed out.", aged->nbr_addr, IFA_UNIQUE_ADDR(ifap), ifap->ifa_name)); #ifdef PROTO_PIMDM if (td->pim_mode == PIM_DENSE) { pimdm_nbr_timeout(ifap, aged); } #endif task_block_free (pim_nbr_block_index, (void_t) aged); } else { if (nextime > n->nbr_timeout) nextime = n->nbr_timeout; n = n->forw; } } pim_nbr_refresh_timer (ifap, nextime - time_sec); pim_dr_reset (ifap); task_job_delete(tjp); PIM_IF_NBR_JOB(ifap) = (task_job *) 0; } /* Same as config_append except duplicates are allowed */ config_entry * config_append_dup __PF2(list, config_entry *, new, config_entry *) { if (list) { config_entry *last = (config_entry *) 0; register config_entry *cp; CONFIG_LIST(cp, list) { if (new->config_type <= cp->config_type) { /* Insert before this one */ break; } last = cp; } CONFIG_LIST_END(cp, list) ; if (last) { new->config_next = last->config_next; last->config_next = new; } else { new->config_next = list; list = new; } } else { list = new; } return list; } /************************************************************************* * Some print functions ************************************************************************/ static void grp(grp) group_t *grp; { fprintf (stdout, "Group %A/%d, # srcs: %d, %s\n", grp->addr, inet_prefix_mask(grp->mask), grp->grp_srcs->mrt_routes, grp->grp_srcs->mrt_name); } static void src (source_t *); static void src(src) source_t *src; { mrt_print_src (stdout, src); } static void nbrs (ifap) if_addr *ifap; { pimnbr_t *n; fprintf (stdout, "\tNbrs for %A\n\t", IFA_UNIQUE_ADDR(ifap)); PIMNBR_LIST(n, (pimnbr_t *) ifap->pim_nbrs) { fprintf (stdout, "%A ", n->nbr_addr); } PIMNBR_LIST_END(n, ifap->pim_nbrs); fprintf (stdout, "\n"); } static void addr (saddr) sockaddr_un *saddr; { fprintf (stdout, "%A\n", saddr); } static void time_seconds () { fprintf (stdout, "%T(%#x)\n", time_sec, time_sec); } void ifaddr(ifap) if_addr *ifap; { task *owner = mbr_get_iftask(ifap); fprintf (stdout, "%s: %A/%d, local(%A) mtu: %d state: %#x, vif: %d, owner %s\n", ifap->ifa_name, IFA_UNIQUE_ADDR(ifap), inet_prefix_mask(ifap->ifa_netmask), ifap->ifa_addr_local, ifap->ifa_mtu, ifap->ifa_state, ifap->ifa_vif, (owner) ? owner->task_name : "none"); } char *config_types[] = { "None", "Enable", "Disable", "Hello Period", "Hello Holdtime", "JP Period", "Interfaces", "C-RP", "BSR Priority", "Max", }; int foo() { puts ("foo"); return; } static void clist (clist) config_list *clist; { static void advlist(adv_entry *); config_entry *cp; static int level = 0; int i; ++level; #ifdef notnow for (i = level; i; i--) fprintf (stdout, " "); fprintf (stdout, "config list[%d]: ref count: %d, ", level, clist->conflist_refcount); #endif notnow CONFIG_LIST(cp, clist->conflist_list) { fprintf (stdout, "%s ", config_types[cp->config_type]); switch (cp->config_type) { case PIM_CONFIG_NONE: fprintf (stdout, "Nothing "); break; case PIM_CONFIG_ENABLE: case PIM_CONFIG_DISABLE: break; case PIMSM_CONFIG_CRP: fprintf (stdout, (u_long) cp->config_data == TRUE ? "enabled " : "disabled "); break; case PIM_CONFIG_HELLO_PERIOD: #ifdef PROTO_PIMSM case PIMSM_CONFIG_JP_PERIOD: case PIMSM_CONFIG_CBSR_PRIORITY: fprintf (stdout, "%ld ", (u_long) cp->config_data); break; #endif /*PROTO_PIMSM*/ case PIM_CONFIG_COMP_IFACES: fprintf (stdout, "adv\n"); advlist(cp->config_data); break; case PIM_CONFIG_MAX: fprintf (stdout, "max "); break; default: fprintf (stdout, "unknown "); break; } } CONFIG_LIST_END (cp, clist->conflist_list); done: fprintf (stdout, "\n"); level--; } static void advlist (advlist) adv_entry *advlist; { adv_entry *adv; static int level = 0; int i; level++; ADV_LIST (advlist, adv) { char *handle = adv->adv_ps; config_list *list = adv->adv_config; for (i = level-1; i; i--) fprintf (stdout, " "); fprintf (stdout, "ADV LIST[%d]: ", level); if (adv->adv_flag & ADVFT_IFN) { fprintf (stdout, "%A, ref: %d, flags: 0x%x ", adv->adv_ifn->ifae_addr, adv->adv_refcount, adv->adv_flag); } else if (adv->adv_flag & (ADVFT_IFAE_UNIQUE|ADVFT_IFAE_LOCAL|ADVFT_IFAE_REMOTE)) { fprintf (stdout, "%A, ref: %d, flags: 0x%x ", adv->adv_ifn->ifae_addr, adv->adv_refcount, adv->adv_flag); } else if (adv->adv_flag & ADVFT_DM) { fprintf (stdout, "dest/mask, ref: %d, flags: 0x%x ", adv->adv_refcount, adv->adv_flag); } else if (adv->adv_flag & ADVFT_PS) { fprintf (stdout, "%s, ref: %d, flags: 0x%x ", handle, adv->adv_refcount, adv->adv_flag); } else { fprintf (stdout, "unknown, ref: %d, flags: 0x%x ", handle, adv->adv_refcount, adv->adv_flag); } if (adv->adv_flag & (ADVFOT_CONFIG | ADVF_TYPE)) clist(adv->adv_config); else fprintf (stdout, "\n"); } ADV_LIST_END (adv_list, adv); level--; } #endif /*PROTO_PIM*/