/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #include "defs.h" #define WARN(fmt, args...) logit(LOG_WARNING, 0, "%s:%u - " fmt, config_file, lineno, ##args) #define BAILOUT(msg, arg...) { WARN(msg ", bailing out!", ##arg); return FALSE; } #define IGNORING(msg, arg...) { WARN(msg ", ignoring ...", ##arg); continue; } /* Helper macros */ #define QUERIER_TIMEOUT(qintv) (IGMP_ROBUSTNESS_VARIABLE * (qintv) + IGMP_QUERY_RESPONSE_INTERVAL / 2) #define LINE_BUFSIZ 1024 /* Max. line length of the config file */ #define CONF_UNKNOWN -1 #define CONF_EMPTY 1 #define CONF_PHYINT 2 #define CONF_CANDIDATE_RP 3 #define CONF_RP_ADDRESS 4 #define CONF_GROUP_PREFIX 5 #define CONF_BOOTSTRAP_RP 6 #define CONF_COMPAT_THRESHOLD 7 #define CONF_SPT_THRESHOLD 8 #define CONF_DEFAULT_ROUTE_METRIC 9 #define CONF_DEFAULT_ROUTE_DISTANCE 10 #define CONF_ALTNET 11 #define CONF_MASKLEN 12 #define CONF_SCOPED 13 #define CONF_IGMP_QUERY_INTERVAL 14 #define CONF_IGMP_QUERIER_TIMEOUT 15 #define CONF_HELLO_INTERVAL 16 /* * Global settings */ uint16_t pim_timer_hello_interval = PIM_TIMER_HELLO_INTERVAL; uint16_t pim_timer_hello_holdtime = PIM_TIMER_HELLO_HOLDTIME; /* * Forward declarations. */ static char *next_word (char **); static int parse_phyint (char *s); static uint32_t ifname2addr (char *s); static uint32_t lineno; extern struct rp_hold *g_rp_hold; /* * Query the kernel to find network interfaces that are multicast-capable * and install them in the uvifs array. */ void config_vifs_from_kernel(void) { struct ifreq *ifrp, *ifend; struct uvif *v; vifi_t vifi; uint32_t n; uint32_t addr, mask, subnet; short flags; int num_ifreq = 64; struct ifconf ifc; char *newbuf; total_interfaces = 0; /* The total number of physical interfaces */ ifc.ifc_len = num_ifreq * sizeof(struct ifreq); ifc.ifc_buf = calloc(ifc.ifc_len, sizeof(char)); while (ifc.ifc_buf) { if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0) logit(LOG_ERR, errno, "Failed querying kernel network interfaces"); /* * If the buffer was large enough to hold all the addresses * then break out, otherwise increase the buffer size and * try again. * * The only way to know that we definitely had enough space * is to know that there was enough space for at least one * more struct ifreq. ??? */ if ((num_ifreq * sizeof(struct ifreq)) >= ifc.ifc_len + sizeof(struct ifreq)) break; num_ifreq *= 2; ifc.ifc_len = num_ifreq * sizeof(struct ifreq); newbuf = realloc(ifc.ifc_buf, ifc.ifc_len); if (newbuf == NULL) free(ifc.ifc_buf); ifc.ifc_buf = newbuf; } if (ifc.ifc_buf == NULL) logit(LOG_ERR, 0, "config_vifs_from_kernel() ran out of memory"); ifrp = (struct ifreq *)ifc.ifc_buf; ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); /* * Loop through all of the interfaces. */ for (; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) { struct ifreq ifr; memset (&ifr, 0, sizeof (ifr)); #ifdef HAVE_SA_LEN n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); if (n < sizeof(*ifrp)) n = sizeof(*ifrp); #else n = sizeof(*ifrp); #endif /* HAVE_SA_LEN */ /* * Ignore any interface for an address family other than IP. */ if (ifrp->ifr_addr.sa_family != AF_INET) { total_interfaces++; /* Eventually may have IP address later */ continue; } addr = ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr; /* * Need a template to preserve address info that is * used below to locate the next entry. (Otherwise, * SIOCGIFFLAGS stomps over it because the requests * are returned in a union.) */ memcpy(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.ifr_name)); /* * Ignore loopback interfaces and interfaces that do not * support multicast. */ if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0) logit(LOG_ERR, errno, "Failed reading interface flags for phyint %s", ifr.ifr_name); flags = ifr.ifr_flags; if ((flags & (IFF_LOOPBACK | IFF_MULTICAST)) != IFF_MULTICAST) continue; /* * Everyone below is a potential vif interface. * We don't care if it has wrong configuration or not configured * at all. */ total_interfaces++; /* * Ignore any interface whose address and mask do not define a * valid subnet number, or whose address is of the form * {subnet,0} or {subnet,-1}. */ if (ioctl(udp_socket, SIOCGIFNETMASK, (char *)&ifr) < 0) { if (!(flags & IFF_POINTOPOINT)) logit(LOG_ERR, errno, "Failed reading interface netmask for phyint %s", ifr.ifr_name); mask = 0xffffffff; } else { mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; } subnet = addr & mask; #ifdef DISABLE_MASKLEN_CHECK if (mask != 0xffffffff) { #endif if ((!inet_valid_subnet(subnet, mask)) || (addr == subnet) || addr == (subnet | ~mask)) { if (!(inet_valid_host(addr) && ((mask == htonl(0xfffffffe)) || (flags & IFF_POINTOPOINT)))) { logit(LOG_WARNING, 0, "Ignoring %s, has invalid address %s and/or netmask %s", ifr.ifr_name, inet_fmt(addr, s1, sizeof(s1)), inet_fmt(mask, s2, sizeof(s2))); continue; } } #ifdef DISABLE_MASKLEN_CHECK } #endif /* * Ignore any interface that is connected to the same subnet as * one already installed in the uvifs array. */ /* * TODO: XXX: bug or "feature" is to allow only one interface per * subnet? */ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (strcmp(v->uv_name, ifr.ifr_name) == 0) { logit(LOG_DEBUG, 0, "Ignoring %s (%s on subnet %s) (alias for vif#%u?)", v->uv_name, inet_fmt(addr, s1, sizeof(s1)), netname(subnet, mask), vifi); break; } /* we don't care about point-to-point links in same subnet */ if (flags & IFF_POINTOPOINT) continue; if (v->uv_flags & VIFF_POINT_TO_POINT) continue; #if 0 /* * TODO: to allow different interfaces belong to * overlapping subnet addresses, use this version instead */ if (((addr & mask ) == v->uv_subnet) && (v->uv_subnetmask == mask)) { logit(LOG_WARNING, 0, "Ignoring %s, same subnet as %s", ifr.ifr_name, v->uv_name); break; } #else if ((addr & v->uv_subnetmask) == v->uv_subnet || (v->uv_subnet & mask) == subnet) { logit(LOG_WARNING, 0, "Ignoring %s, same subnet as %s", ifr.ifr_name, v->uv_name); break; } #endif /* 0 */ } if (vifi != numvifs) continue; /* * If there is room in the uvifs array, install this interface. */ if (numvifs == MAXVIFS) { logit(LOG_WARNING, 0, "Too many vifs, ignoring %s", ifr.ifr_name); continue; } v = &uvifs[numvifs]; zero_vif(v, FALSE); v->uv_lcl_addr = addr; v->uv_subnet = subnet; v->uv_subnetmask = mask; if (mask != htonl(0xfffffffe)) v->uv_subnetbcast = subnet | ~mask; else v->uv_subnetbcast = 0xffffffff; strlcpy(v->uv_name, ifr.ifr_name, IFNAMSIZ); /* * Figure out MTU of interface, needed as a seed value when * fragmenting PIM register messages. We should really do * a PMTU check on initial PIM register send to a new RP... */ if (ioctl(udp_socket, SIOCGIFMTU, &ifr) < 0) v->uv_mtu = 1500; else v->uv_mtu = ifr.ifr_mtu; if (flags & IFF_POINTOPOINT) { v->uv_flags |= (VIFF_REXMIT_PRUNES | VIFF_POINT_TO_POINT); if (ioctl(udp_socket, SIOCGIFDSTADDR, (char *)&ifr) < 0) logit(LOG_ERR, errno, "Failed reading point-to-point address for %s", v->uv_name); else v->uv_rmt_addr = ((struct sockaddr_in *)(&ifr.ifr_dstaddr))->sin_addr.s_addr; } else if (mask == htonl(0xfffffffe)) { /* * Handle RFC 3021 /31 netmasks as point-to-point links */ v->uv_flags |= (VIFF_REXMIT_PRUNES | VIFF_POINT_TO_POINT); if (addr == subnet) v->uv_rmt_addr = addr + htonl(1); else v->uv_rmt_addr = subnet; } #ifdef __linux__ { struct ifreq ifridx; memset(&ifridx, 0, sizeof(ifridx)); strlcpy(ifridx.ifr_name,v->uv_name, IFNAMSIZ); if (ioctl(udp_socket, SIOGIFINDEX, (char *) &ifridx) < 0) logit(LOG_ERR, errno, "Failed reading interface index for %s", ifridx.ifr_name); v->uv_ifindex = ifridx.ifr_ifindex; } if (v->uv_flags & VIFF_POINT_TO_POINT) { logit(LOG_INFO, 0, "Installing %s (%s -> %s) as vif #%u-%d - rate %d", v->uv_name, inet_fmt(addr, s1, sizeof(s1)), inet_fmt(v->uv_rmt_addr, s2, sizeof(s2)), numvifs, v->uv_ifindex, v->uv_rate_limit); } else { logit(LOG_INFO, 0, "Installing %s (%s on subnet %s) as vif #%u-%d - rate %d", v->uv_name, inet_fmt(addr, s1, sizeof(s1)), netname(subnet, mask), numvifs, v->uv_ifindex, v->uv_rate_limit); } #else /* !__linux__ */ if (v->uv_flags & VIFF_POINT_TO_POINT) { logit(LOG_INFO, 0, "Installing %s (%s -> %s) as vif #%u - rate=%d", v->uv_name, inet_fmt(addr, s1, sizeof(s1)), inet_fmt(v->uv_rmt_addr, s2, sizeof(s2)), numvifs, v->uv_rate_limit); } else { logit(LOG_INFO, 0, "Installing %s (%s on subnet %s) as vif #%u - rate %d", v->uv_name, inet_fmt(addr, s1, sizeof(s1)), netname(subnet, mask), numvifs, v->uv_rate_limit); } #endif /* __linux__ */ ++numvifs; /* * If the interface is not yet up, set the vifs_down flag to * remind us to check again later. */ if (!(flags & IFF_UP)) { v->uv_flags |= VIFF_DOWN; vifs_down = TRUE; } } } static int deprecated(char *word, char *new_word, int code) { WARN("The %s option is deprecated, replaced with %s", word, new_word); WARN("Please update your configuration file!"); return code; } /** * parse_option - Convert result of string comparisons into numerics. * @input: Pointer to the word * * This function is called by config_vifs_from_file(). * * Returns: * A number corresponding to the code of the word, or %CONF_UNKNOWN. */ static int parse_option(char *word) { if (EQUAL(word, "")) return CONF_EMPTY; if (EQUAL(word, "phyint")) return CONF_PHYINT; if (EQUAL(word, "bsr-candidate")) return CONF_BOOTSTRAP_RP; if (EQUAL(word, "rp-candidate")) return CONF_CANDIDATE_RP; if (EQUAL(word, "rp-address")) return CONF_RP_ADDRESS; if (EQUAL(word, "group-prefix")) return CONF_GROUP_PREFIX; if (EQUAL(word, "spt-threshold")) return CONF_SPT_THRESHOLD; if (EQUAL(word, "default-route-metric")) return CONF_DEFAULT_ROUTE_METRIC; if (EQUAL(word, "default-route-distance")) return CONF_DEFAULT_ROUTE_DISTANCE; if (EQUAL(word, "igmp-query-interval")) return CONF_IGMP_QUERY_INTERVAL; if (EQUAL(word, "igmp-querier-timeout")) return CONF_IGMP_QUERIER_TIMEOUT; if (EQUAL(word, "altnet")) return CONF_ALTNET; if (EQUAL(word, "masklen")) return CONF_MASKLEN; if (EQUAL(word, "scoped")) return CONF_SCOPED; if (EQUAL(word, "hello-interval")) return CONF_HELLO_INTERVAL; /* Compatibility with old config files that use _ instead of - */ if (EQUAL(word, "cand_bootstrap_router")) return CONF_BOOTSTRAP_RP; if (EQUAL(word, "cand_rp")) return CONF_CANDIDATE_RP; if (EQUAL(word, "group_prefix")) return CONF_GROUP_PREFIX; if (EQUAL(word, "rp_address")) return CONF_RP_ADDRESS; if (EQUAL(word, "switch_register_threshold")) return deprecated(word, "spt-threshold", CONF_COMPAT_THRESHOLD); if (EQUAL(word, "switch_data_threshold")) return deprecated(word, "spt-threshold", CONF_COMPAT_THRESHOLD); if (EQUAL(word, "spt_threshold")) return CONF_SPT_THRESHOLD; if (EQUAL(word, "default_source_metric")) return CONF_DEFAULT_ROUTE_METRIC; if (EQUAL(word, "default_source_preference")) return CONF_DEFAULT_ROUTE_DISTANCE; if (EQUAL(word, "default_igmp_query_interval")) /* compat */ return CONF_IGMP_QUERY_INTERVAL; if (EQUAL(word, "default_igmp_querier_timeout")) /* compat */ return CONF_IGMP_QUERIER_TIMEOUT; if (EQUAL(word, "hello_period")) return CONF_HELLO_INTERVAL; return CONF_UNKNOWN; } /* Check for optional /PREFIXLEN suffix to the address/group */ static void parse_prefix_len(char *token, uint32_t *len) { char *masklen = strchr(token, '/'); if (masklen) { *masklen = 0; masklen++; if (!sscanf(masklen, "%u", len)) { WARN("Invalid masklen '%s'", masklen); *len = PIM_GROUP_PREFIX_DEFAULT_MASKLEN; } } } static void validate_prefix_len(uint32_t *len) { if (*len > (sizeof(uint32_t) * 8)) { *len = (sizeof(uint32_t) * 8); } else if (*len < PIM_GROUP_PREFIX_MIN_MASKLEN) { WARN("Too small masklen %u. Defaulting to %d", *len, PIM_GROUP_PREFIX_MIN_MASKLEN); *len = PIM_GROUP_PREFIX_MIN_MASKLEN; } } /** * parse_phyint - Parse physical interface configuration, if any. * @s: String token * * Syntax: * phyint [disable | enable] * [igmpv2 | igmpv3] * [dr-priority <1-4294967294>] * [ttl-threshold <1-255>] * [distance <1-255>] [metric <1-1024>] * [altnet /] * [altnet masklen ] * [scoped /] * [scoped masklen ] * * Returns: * %TRUE if the parsing was successful, o.w. %FALSE */ static int parse_phyint(char *s) { char *w, c; uint32_t local, altnet_addr, scoped_addr; vifi_t vifi; struct uvif *v; uint32_t n, altnet_masklen = 0, scoped_masklen = 0; struct phaddr *ph; struct vif_acl *v_acl; if (EQUAL((w = next_word(&s)), "")) { WARN("Missing phyint address"); return FALSE; } local = ifname2addr(w); if (!local) { local = inet_parse(w, 4); if (!inet_valid_host(local)) { WARN("Invalid phyint address '%s'", w); return FALSE; } } for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (vifi == numvifs) { WARN("phyint %s is not a valid interface", inet_fmt(local, s1, sizeof(s1))); return FALSE; } if (local != v->uv_lcl_addr) continue; while (!EQUAL((w = next_word(&s)), "")) { if (EQUAL(w, "disable")) { v->uv_flags |= VIFF_DISABLED; continue; } if (EQUAL(w, "enable")) { v->uv_flags &= ~VIFF_DISABLED; continue; } if (EQUAL(w, "igmpv2")) { v->uv_flags &= ~VIFF_IGMPV1; v->uv_flags |= VIFF_IGMPV2; continue; } if (EQUAL(w, "igmpv3")) { v->uv_flags &= ~VIFF_IGMPV1; v->uv_flags &= ~VIFF_IGMPV2; continue; } if (EQUAL(w, "altnet")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing ALTNET for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } parse_prefix_len (w, &altnet_masklen); altnet_addr = ifname2addr(w); if (!altnet_addr) { altnet_addr = inet_parse(w, 4); if (!inet_valid_host(altnet_addr)) { WARN("Invalid altnet address '%s'", w); return FALSE; } } if (EQUAL((w = next_word(&s)), "masklen")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing ALTNET masklen for phyint %s", inet_fmt(local, s1, sizeof (s1))); continue; } if (!sscanf(w, "%u", &altnet_masklen)) { WARN("Invalid altnet masklen '%s' for phyint %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } } ph = (struct phaddr *)calloc(1, sizeof(struct phaddr)); if (!ph) return FALSE; if (altnet_masklen) { VAL_TO_MASK(ph->pa_subnetmask, altnet_masklen); } else { ph->pa_subnetmask = v->uv_subnetmask; } ph->pa_subnet = altnet_addr & ph->pa_subnetmask; ph->pa_subnetbcast = ph->pa_subnet | ~ph->pa_subnetmask; if (altnet_addr & ~ph->pa_subnetmask) WARN("Extra subnet %s/%d has host bits set", inet_fmt(altnet_addr, s1, sizeof(s1)), altnet_masklen); ph->pa_next = v->uv_addrs; v->uv_addrs = ph; logit(LOG_DEBUG, 0, "ALTNET: %s/%d", inet_fmt(altnet_addr, s1, sizeof(s1)), altnet_masklen); } /* altnet */ /* scoped mcast groups/masklen */ if (EQUAL(w, "scoped")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing SCOPED for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } parse_prefix_len (w, &scoped_masklen); scoped_addr = ifname2addr(w); if (!scoped_addr) { scoped_addr = inet_parse(w, 4); if (!IN_MULTICAST(ntohl(scoped_addr))) { WARN("Invalid scoped address '%s'", w); return FALSE; } } if (EQUAL((w = next_word(&s)), "masklen")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing SCOPED masklen for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } if (sscanf(w, "%u", &scoped_masklen) != 1) { WARN("Invalid scoped masklen '%s' for phyint %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } } /* Invalid config. VAL_TO_MASK() also requires len > 0 or shift op will fail. */ if (!scoped_masklen) { WARN("Too small (0) scoped masklen for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } v_acl = (struct vif_acl *)calloc(1, sizeof(struct vif_acl)); if (!v_acl) return FALSE; VAL_TO_MASK(v_acl->acl_mask, scoped_masklen); v_acl->acl_addr = scoped_addr & v_acl->acl_mask; if (scoped_addr & ~v_acl->acl_mask) WARN("Boundary spec %s/%d has host bits set", inet_fmt(scoped_addr, s1, sizeof(s1)), scoped_masklen); v_acl->acl_next = v->uv_acl; v->uv_acl = v_acl; logit(LOG_DEBUG, 0, "SCOPED %s/%x", inet_fmt(v_acl->acl_addr, s1, sizeof(s1)), v_acl->acl_mask); } /* scoped */ if (EQUAL(w, "ttl-threshold") || EQUAL(w, "threshold")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing threshold for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } if (sscanf(w, "%u%c", &n, &c) != 1 || n < 1 || n > 255 ) { WARN("Invalid threshold '%s' for phyint %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } v->uv_threshold = n; continue; } /* threshold */ if (EQUAL(w, "distance") || EQUAL(w, "preference")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing distance value for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } if (sscanf(w, "%u%c", &n, &c) != 1 || n < 1 || n > 255 ) { WARN("Invalid distance value '%s' for phyint %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } IF_DEBUG(DEBUG_ASSERT) logit(LOG_DEBUG, 0, "Config setting default local preference on %s to %d", inet_fmt(local, s1, sizeof(s1)), n); v->uv_local_pref = n; continue; } if (EQUAL(w, "metric")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing metric value for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } if (sscanf(w, "%u%c", &n, &c) != 1 || n < 1 || n > 1024 ) { WARN("Invalid metric value '%s' for phyint %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } IF_DEBUG(DEBUG_ASSERT) logit(LOG_DEBUG, 0, "Setting default local metric on %s to %d", inet_fmt(local, s1, sizeof(s1)), n); v->uv_local_metric = n; continue; } if (EQUAL(w, "dr-priority")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing dr-priority value for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } if (sscanf(w, "%u%c", &n, &c) != 1 || n < 1 || n > 4294967294u) { WARN("Invalid dr-priority value '%s' for phyint %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } IF_DEBUG(DEBUG_PIM_HELLO) logit(LOG_DEBUG, 0, "Setting dr-priority on %s to %d", inet_fmt(local, s1, sizeof(s1)), n); v->uv_dr_prio = n; continue; } } /* while(... != "") */ break; } return TRUE; } /** * parse_rp_candidate - Parse candidate Rendez-Vous Point information. * @s: String token * * Syntax: * rp-candidate [address | ifname] [priority <0-255>] [time <10-16383>] * * Returns: * %TRUE if the parsing was successful, o.w. %FALSE */ int parse_rp_candidate(char *s) { u_int time = PIM_DEFAULT_CAND_RP_ADV_PERIOD; u_int priority = PIM_DEFAULT_CAND_RP_PRIORITY; char *w; uint32_t local = INADDR_ANY_N; cand_rp_flag = FALSE; my_cand_rp_adv_period = PIM_DEFAULT_CAND_RP_ADV_PERIOD; while (!EQUAL((w = next_word(&s)), "")) { if (EQUAL(w, "priority")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing priority, defaulting to %u", w, PIM_DEFAULT_CAND_RP_PRIORITY); priority = PIM_DEFAULT_CAND_RP_PRIORITY; continue; } if (sscanf(w, "%u", &priority) != 1) { WARN("Invalid priority %s, defaulting to %u", w, PIM_DEFAULT_CAND_RP_PRIORITY); priority = PIM_DEFAULT_CAND_RP_PRIORITY; } if (priority > PIM_MAX_CAND_RP_PRIORITY) { WARN("Too high Cand-RP priority %u, defaulting to %d", priority, PIM_MAX_CAND_RP_PRIORITY); priority = PIM_MAX_CAND_RP_PRIORITY; } continue; } if (EQUAL(w, "time")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing Cand-RP announce interval, defaulting to %u", PIM_DEFAULT_CAND_RP_ADV_PERIOD); time = PIM_DEFAULT_CAND_RP_ADV_PERIOD; continue; } if (sscanf(w, "%u", &time) != 1) { WARN("Invalid Cand-RP announce interval, defaulting to %u", PIM_DEFAULT_CAND_RP_ADV_PERIOD); time = PIM_DEFAULT_CAND_RP_ADV_PERIOD; continue; } if (time < PIM_MIN_CAND_RP_ADV_PERIOD) time = PIM_MIN_CAND_RP_ADV_PERIOD; if (time > PIM_MAX_CAND_RP_ADV_PERIOD) time = PIM_MAX_CAND_RP_ADV_PERIOD; my_cand_rp_adv_period = time; continue; } /* Cand-RP interface or address */ local = ifname2addr(w); if (!local) local = inet_parse(w, 4); if (!inet_valid_host(local)) { local = max_local_address(); WARN("Invalid Cand-RP address '%s', defaulting to %s", w, inet_fmt(local, s1, sizeof(s1))); } else if (local_address(local) == NO_VIF) { local = max_local_address(); WARN("Cand-RP address '%s' is not local, defaulting to %s", w, inet_fmt(local, s1, sizeof(s1))); } } if (local == INADDR_ANY_N) { /* If address not provided, use the max. local */ local = max_local_address(); } my_cand_rp_address = local; my_cand_rp_priority = priority; my_cand_rp_adv_period = time; cand_rp_flag = TRUE; logit(LOG_INFO, 0, "Local Cand-RP address %s, priority %u, interval %u sec", inet_fmt(local, s1, sizeof(s1)), priority, time); return TRUE; } /** * parse_group_prefix - Parse group-prefix configured information. * @s: String token * Syntax: * group-prefix [/] * [masklen ] * * Returns: * %TRUE if the parsing was successful, o.w. %FALSE */ int parse_group_prefix(char *s) { char *w; uint32_t group_addr; uint32_t masklen = PIM_GROUP_PREFIX_DEFAULT_MASKLEN; w = next_word(&s); if (EQUAL(w, "")) { WARN("Missing group-prefix address"); return FALSE; } parse_prefix_len (w, &masklen); group_addr = inet_parse(w, 4); if (!IN_MULTICAST(ntohl(group_addr))) { WARN("Group address '%s' is not a valid multicast address", inet_fmt(group_addr, s1, sizeof(s1))); return FALSE; } /* Was if (!(~(*cand_rp_adv_message.prefix_cnt_ptr))) which Arm GCC 4.4.2 dislikes: * --> "config.c:693: warning: promoted ~unsigned is always non-zero" * The prefix_cnt_ptr is a uint8_t so it seems this check was to prevent overruns. * I've changed the check to see if we've already read 255 entries, if so the cnt * is maximized and we need to tell the user. --Joachim Nilsson 2010-01-16 */ if (*cand_rp_adv_message.prefix_cnt_ptr == 255) { WARN("Too many multicast groups configured!"); return FALSE; } if (EQUAL((w = next_word(&s)), "masklen")) { w = next_word(&s); if (!sscanf(w, "%u", &masklen)) masklen = PIM_GROUP_PREFIX_DEFAULT_MASKLEN; } validate_prefix_len(&masklen); PUT_EGADDR(group_addr, (uint8_t)masklen, 0, cand_rp_adv_message.insert_data_ptr); (*cand_rp_adv_message.prefix_cnt_ptr)++; logit(LOG_INFO, 0, "Adding Cand-RP group prefix %s/%d", inet_fmt(group_addr, s1, sizeof(s1)), masklen); return TRUE; } /** * parse_bsr_candidate - Parse the candidate BSR configured information. * @s: String token * * Syntax: * bsr-candidate [address | ifname] [priority <0-255>] */ int parse_bsr_candidate(char *s) { char *w; uint32_t local = INADDR_ANY_N; uint32_t priority = PIM_DEFAULT_BSR_PRIORITY; cand_bsr_flag = FALSE; while (!EQUAL((w = next_word(&s)), "")) { if (EQUAL(w, "priority")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing Cand-BSR priority, defaulting to %u", PIM_DEFAULT_BSR_PRIORITY); priority = PIM_DEFAULT_BSR_PRIORITY; continue; } if (sscanf(w, "%u", &priority) != 1) { WARN("Invalid Cand-BSR priority %s, defaulting to %u", PIM_DEFAULT_BSR_PRIORITY); priority = PIM_DEFAULT_BSR_PRIORITY; continue; } if (priority > PIM_MAX_CAND_BSR_PRIORITY) { WARN("Too high Cand-BSR priority %u, defaulting to %d", priority, PIM_MAX_CAND_BSR_PRIORITY); priority = PIM_MAX_CAND_BSR_PRIORITY; } my_bsr_priority = (uint8_t)priority; continue; } /* Cand-BSR interface or address */ local = ifname2addr(w); if (!local) local = inet_parse(w, 4); if (!inet_valid_host(local)) { local = max_local_address(); WARN("Invalid Cand-BSR address '%s', defaulting to %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } if (local_address(local) == NO_VIF) { local = max_local_address(); WARN("Cand-BSR address '%s' is not local, defaulting to %s", w, inet_fmt(local, s1, sizeof(s1))); } } if (local == INADDR_ANY_N) { /* If address not provided, use the max. local */ local = max_local_address(); } my_bsr_address = local; my_bsr_priority = priority; MASKLEN_TO_MASK(RP_DEFAULT_IPV4_HASHMASKLEN, my_bsr_hash_mask); cand_bsr_flag = TRUE; logit(LOG_INFO, 0, "Local Cand-BSR address %s, priority %u", inet_fmt(local, s1, sizeof(s1)), priority); return TRUE; } /** * parse_rp_address - Parse rp-address config option. * @s: String token. * * This is an extension to the original pimd to add pimd.conf support for static * Rendez-Vous Point addresses. * * The function has been extended by pjf@asn.pl, of Lintrack, to allow specifying * multicast group addresses as well. * * Syntax: * rp-address
[[ masklen ] * * Returns: * When parsing @s is successful this function returns %TRUE, otherwise %FALSE. */ int parse_rp_address(char *s) { char *w; uint32_t local = 0xffffff; uint32_t group_addr = htonl(INADDR_UNSPEC_GROUP); uint32_t masklen = PIM_GROUP_PREFIX_DEFAULT_MASKLEN; struct rp_hold *rph; /* next is RP addr */ w = next_word(&s); if (EQUAL(w, "")) { logit(LOG_WARNING, 0, "Missing rp-address argument"); return FALSE; } local = inet_parse(w, 4); if (local == 0xffffff) { WARN("Invalid rp-address %s", w); return FALSE; } /* next is group addr if exist */ w = next_word(&s); if (!EQUAL(w, "")) { parse_prefix_len (w, &masklen); group_addr = inet_parse(w, 4); if (!IN_MULTICAST(ntohl(group_addr))) { WARN("%s is not a valid multicast address", inet_fmt(group_addr, s1, sizeof(s1))); return FALSE; } /* next is prefix or priority if exist */ while (!EQUAL((w = next_word(&s)), "")) { if (EQUAL(w, "masklen")) { w = next_word(&s); if (!sscanf(w, "%u", &masklen)) { WARN("Invalid masklen %s. Defaulting to %d)", w, PIM_GROUP_PREFIX_DEFAULT_MASKLEN); masklen = PIM_GROUP_PREFIX_DEFAULT_MASKLEN; } } /* Unused, but keeping for backwards compatibility for people who * may still have this option in their pimd.conf * The priority of a static RP is hardcoded to always be 1, see Juniper's * configuration or similar sources for reference. */ if (EQUAL(w, "priority")) { w = next_word(&s); WARN("The priority of static RP's is, as of pimd 2.2.0, always 1."); } } } else { group_addr = htonl(INADDR_UNSPEC_GROUP); masklen = PIM_GROUP_PREFIX_MIN_MASKLEN; } validate_prefix_len(&masklen); rph = calloc(1, sizeof(*rph)); if (!rph) { logit(LOG_WARNING, 0, "Out of memory when parsing rp-address %s", inet_fmt(local, s1, sizeof(s1))); return FALSE; } rph->address = local; rph->group = group_addr; VAL_TO_MASK(rph->mask, masklen); /* attach at the beginning */ rph->next = g_rp_hold; g_rp_hold = rph; logit(LOG_INFO, 0, "Local static RP: %s, group %s/%d", inet_fmt(local, s1, sizeof(s1)), inet_fmt(group_addr, s2, sizeof(s2)), masklen); return TRUE; } /** * parse_compat_threshold - Parse old deprecated pimd.conf thresholds * @line: * * This is a backwards compatible parser for the two older threshold * settings used in pimd prior to v2.2.0. The switchover mechanism has * been completely changed, however, so we simply read the settings as * if they where the same as the new spt-threshold, only converting the * rate argument differently (bps vs kbps). Last line to be read is * what is activated in pimd as spt-threshold. * * Note, previously the parser was very lenient to errors, but since the * default has changed it is much more strict. Any syntax error and pimd * bails out ignoring the line. * * Syntax: * switch_register_threshold [rate interval ] * switch_data_threshold [rate interval ] * * Returns: * When parsing @line is successful, returns %TRUE, otherwise %FALSE. */ static int parse_compat_threshold(char *line) { char *w; int rate = -1; int interval = -1; while (!EQUAL((w = next_word(&line)), "")) { if (EQUAL(w, "rate")) { if (EQUAL((w = next_word(&line)), "")) BAILOUT("Missing rate value in compat threshold parser"); /* 10 --> 1,000,000,000 == 100 Gbps */ if (sscanf(w, "%10d", &rate) != 1) BAILOUT("Invalid rate value %s in compat threshold parser", w); continue; } if (EQUAL(w, "interval")) { if (EQUAL((w = next_word(&line)), "")) IGNORING("Missing interval value in compat threshold parser"); /* 5 --> 99,999 ~= 27h */ if (sscanf(w, "%5d", &interval) != 1) IGNORING("Invalid interval %s in compat threshold parser", w); continue; } } /* Set polling mode */ spt_threshold.mode = SPT_RATE; /* Only accept values if they don't messup for new spt-threshold */ if (interval >= TIMER_INTERVAL) spt_threshold.interval = interval; /* Accounting for headers we can approximate 1 byte/s == 10 bits/s (bps) */ spt_threshold.bytes = rate * spt_threshold.interval / 10; logit(LOG_INFO, 0, "Compatibility set spt-treshold rate %u kbps with interval %u sec", spt_threshold.bytes, spt_threshold.interval); return TRUE; } /** * parse_hello_interval - Parse and assign the hello interval * @s: Input data * * Syntax: * hello-interval * * Returns: * %TRUE if successful, otherwise %FALSE. */ int parse_hello_interval(char *s) { char *w; u_int period; u_int holdtime; if (!EQUAL((w = next_word(&s)), "")) { if (sscanf(w, "%u", &period) != 1) { logit(LOG_WARNING, 0, "Invalid hello-interval %s; defaulting to %u", w, PIM_TIMER_HELLO_INTERVAL); period = PIM_TIMER_HELLO_INTERVAL; holdtime = PIM_TIMER_HELLO_HOLDTIME; } else { if (period <= (u_int)(UINT16_MAX / 3.5)) { holdtime = period * 3.5; } else { logit(LOG_WARNING, 0, "Too large hello-interval %s; defaulting to %u", w, PIM_TIMER_HELLO_INTERVAL); period = PIM_TIMER_HELLO_INTERVAL; holdtime = PIM_TIMER_HELLO_HOLDTIME; } } } else { logit(LOG_WARNING, 0, "Missing hello-interval value; defaulting to %u", PIM_TIMER_HELLO_INTERVAL); period = PIM_TIMER_HELLO_INTERVAL; holdtime = PIM_TIMER_HELLO_HOLDTIME; } logit(LOG_INFO, 0, "hello-interval is %u", period); pim_timer_hello_interval = period; pim_timer_hello_holdtime = holdtime; return TRUE; } /** * parse_spt_threshold - Parse spt-threshold option * @s: String token * * This configuration setting replaces the switch_register_threshold and * switch_data_threshold. It is more intuitive and more in line with * what major vendors are also using. * * Note that the rate is in kbps instead of bps, compared to the old * syntax. Both the above parse_compat_threshold() and this function * target the same backend. * * Syntax: * spt-threshold [rate | packets | infinity] [interval ] * * Returns: * When parsing @s is successful this function returns %TRUE, otherwise %FALSE. */ static int parse_spt_threshold(char *s) { char *w; uint32_t rate = SPT_THRESHOLD_DEFAULT_RATE; uint32_t packets = SPT_THRESHOLD_DEFAULT_PACKETS; uint32_t interval = SPT_THRESHOLD_DEFAULT_INTERVAL; spt_mode_t mode = SPT_THRESHOLD_DEFAULT_MODE; while (!EQUAL((w = next_word(&s)), "")) { if (EQUAL(w, "rate")) { mode = SPT_RATE; if (EQUAL((w = next_word(&s)), "")) { WARN("Missing spt-threshold rate argument, defaulting to %u", SPT_THRESHOLD_DEFAULT_RATE); rate = SPT_THRESHOLD_DEFAULT_RATE; continue; } /* 10 --> 1,000,000,000 == 100 Gbps */ if (sscanf(w, "%10u", &rate) != 1) { WARN("Invalid spt-threshold rate %s, defaulting to %u", w, SPT_THRESHOLD_DEFAULT_RATE); rate = SPT_THRESHOLD_DEFAULT_RATE; } continue; } if (EQUAL(w, "interval")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing spt-threshold interval; defaulting to %u sec", SPT_THRESHOLD_DEFAULT_INTERVAL); interval = SPT_THRESHOLD_DEFAULT_INTERVAL; continue; } /* 5 --> 99,999 ~= 27h */ if (sscanf(w, "%5u", &interval) != 1) { WARN("Invalid spt-threshold interval %s; defaulting to %u sec", w, SPT_THRESHOLD_DEFAULT_INTERVAL); interval = SPT_THRESHOLD_DEFAULT_INTERVAL; } if (interval < TIMER_INTERVAL) { WARN("Too low spt-threshold interval %s; defaulting to %u sec", w, TIMER_INTERVAL); interval = TIMER_INTERVAL; } continue; } if (EQUAL(w, "packets")) { mode = SPT_PACKETS; if (EQUAL((w = next_word(&s)), "")) { WARN("Missing spt-threshold number of packets; defaulting to %u", SPT_THRESHOLD_DEFAULT_PACKETS); packets = SPT_THRESHOLD_DEFAULT_PACKETS; continue; } /* 10 --> 4294967295, which is max of uint32_t */ if (sscanf(w, "%10u", &packets) != 1) { WARN("Invalid spt-threshold packets %s; defaulting to %u", w, SPT_THRESHOLD_DEFAULT_PACKETS); packets = SPT_THRESHOLD_DEFAULT_INTERVAL; } continue; } if (EQUAL(w, "infinity")) { mode = SPT_INF; continue; } WARN("Invalid spt-threshold parameter %s; reverting to defaults.", w); mode = SPT_THRESHOLD_DEFAULT_MODE; rate = SPT_THRESHOLD_DEFAULT_RATE; packets = SPT_THRESHOLD_DEFAULT_PACKETS; interval = SPT_THRESHOLD_DEFAULT_INTERVAL; break; } spt_threshold.mode = mode; switch (mode) { case SPT_INF: logit(LOG_INFO, 0, "spt-threshold infinity => RP and lasthop router will never switch to SPT."); break; case SPT_RATE: /* Accounting for headers we can approximate 1 byte/s == 10 bits/s (bps) * Note, in the new spt_threshold setting the rate is in kbps as well! */ spt_threshold.bytes = rate * interval / 10 * 1000; spt_threshold.interval = interval; logit(LOG_INFO, 0, "spt-threshold rate %u interval %u", rate, interval); break; case SPT_PACKETS: spt_threshold.packets = packets; spt_threshold.interval = interval; logit(LOG_INFO, 0, "spt-threshold packets %u interval %u", packets, interval); break; } return TRUE; } /** * parse_default_route_metric - Parse default-route-metric option * @s: String token * * Reads and assigns the route metric used for PIM Asserts by default. * This is used if pimd cannot read unicast route metrics from the * OS/kernel. * * Syntax: * default-route-metric <1-1024> * * Default routing protocol distance and route metric statements should * precede all phyint statements in the config file. * * Returns: * When parsing @s is successful this function returns %TRUE, otherwise %FALSE. */ int parse_default_route_metric(char *s) { char *w; u_int value; vifi_t vifi; struct uvif *v; value = UCAST_DEFAULT_ROUTE_METRIC; if (EQUAL((w = next_word(&s)), "")) { WARN("Missing route metric default; defaulting to %u", UCAST_DEFAULT_ROUTE_METRIC); } else if (sscanf(w, "%u", &value) != 1) { WARN("Invalid route metric default; defaulting to %u", UCAST_DEFAULT_ROUTE_METRIC); value = UCAST_DEFAULT_ROUTE_METRIC; } default_route_metric = value; logit(LOG_INFO, 0, "default-route-metric is %u", value); for (vifi = 0, v = uvifs; vifi < MAXVIFS; ++vifi, ++v) v->uv_local_metric = default_route_metric; return TRUE; } /** * parse_default_route_distance - Parse default-route-distance option * @s: String token * * Reads and assigns the default source metric preference, i.e. routing * protocol distance. This is used if pimd cannot read unicast routing * protocol information from the OS/kernel. * * Syntax: * default-route-distance <1-255> * * Default routing protocol distance and route metric statements should * precede all phyint statements in the config file. * * Returns: * When parsing @s is successful this function returns %TRUE, otherwise %FALSE. */ int parse_default_route_distance(char *s) { char *w; u_int value; vifi_t vifi; struct uvif *v; value = UCAST_DEFAULT_ROUTE_DISTANCE; if (EQUAL((w = next_word(&s)), "")) { WARN("Missing default routing protocol distance; defaulting to %u", UCAST_DEFAULT_ROUTE_DISTANCE); } else if (sscanf(w, "%u", &value) != 1) { WARN("Invalid default routing protocol distance; defaulting to %u", UCAST_DEFAULT_ROUTE_DISTANCE); value = UCAST_DEFAULT_ROUTE_DISTANCE; } default_route_distance = value; logit(LOG_INFO, 0, "default-route-distance is %u", value); for (vifi = 0, v = uvifs; vifi < MAXVIFS; ++vifi, ++v) v->uv_local_pref = default_route_distance; return TRUE; } /** * parse_igmp_query_interval - Parse igmp-query-interval option * @s: String token * * Reads and assigns the default IGMP query interval. If the argument * is missing or invalid the parser defaults to %IGMP_QUERY_INTERVAL * * Syntax: * igmp-query-interval * * Returns: * When parsing @s is successful this function returns %TRUE, otherwise %FALSE. */ static int parse_igmp_query_interval(char *s) { char *w; uint32_t value = IGMP_QUERY_INTERVAL; if (EQUAL((w = next_word(&s)), "")) { WARN("Missing argument to igmp-query-interval; defaulting to %u", IGMP_QUERY_INTERVAL); } else if (sscanf(w, "%u", &value) != 1) { WARN("Invalid default igmp-query-interval; defaulting to %u", IGMP_QUERY_INTERVAL); value = IGMP_QUERY_INTERVAL; } igmp_query_interval = value; /* Calculate new querier timeout, or expect config option after this. */ igmp_querier_timeout = 0; return TRUE; } /** * parse_igmp_querier_timeout - Parse igmp-querier-timeout option * @s: String token * * Reads and assigns default querier timeout for an active IGMP querier. * This is the time it takes before pimd tries to take over as the * active querier. If the argument is missing or invalid the system * will calculate a fallback based on the query interval. * * Syntax: * igmp-querier-timeout * * Returns: * When parsing @s is successful this function returns %TRUE, otherwise %FALSE. */ static int parse_igmp_querier_timeout(char *s) { char *w; uint32_t value = 0; uint32_t recommended = QUERIER_TIMEOUT(igmp_query_interval); if (EQUAL((w = next_word(&s)), "")) { WARN("Missing argument to igmp-querier-timeout!"); } else if (sscanf(w, "%u", &value) != 1) { WARN("Invalid default igmp-querier-timeout!"); value = 0; } /* Do some sanity checks to prevent invalid configuration and to recommend * better settings, see GitHub issue troglobit/pimd#31 for details. */ if (value != 0) { /* 1) Prevent invalid configuration */ if (value <= igmp_query_interval) { WARN("IGMP querier timeout %d must be larger than the query interval %d, forcing default!", value, igmp_query_interval); value = recommended; } /* 2) Warn power user of potentially too low setting. */ if (value < recommended) WARN("The IGMP querier timeout %d is smaller than the recommended value %d, allowing ...", value, recommended); logit(LOG_WARNING, 0, "Recommended querier timeout = Robustness x query-interval + response-time / 2 = %d x %d + %d / 2 = %d", IGMP_ROBUSTNESS_VARIABLE, igmp_query_interval, IGMP_QUERY_RESPONSE_INTERVAL, recommended); } igmp_querier_timeout = value; return TRUE; } static void fallback_config(void) { char buf[LINE_BUFSIZ], *s = buf; logit(LOG_NOTICE, 0, "Using built-in defaults, including RP/BSR candidate."); snprintf(buf, sizeof(buf), "priority 20 time 30"); parse_rp_candidate(s); snprintf(buf, sizeof(buf), "priority 5"); parse_bsr_candidate(s); } void config_vifs_from_file(void) { FILE *fp; char linebuf[LINE_BUFSIZ]; char *w, *s; int option; uint8_t *data_ptr; int error_flag; error_flag = FALSE; lineno = 0; /* TODO: HARDCODING!!! */ cand_rp_adv_message.buffer = calloc(1, 4 + sizeof(pim_encod_uni_addr_t) + 255 * sizeof(pim_encod_grp_addr_t)); if (!cand_rp_adv_message.buffer) logit(LOG_ERR, errno, "Ran out of memory in config_vifs_from_file()"); cand_rp_adv_message.prefix_cnt_ptr = cand_rp_adv_message.buffer; /* By default, if no group-prefix configured, then prefix_cnt == 0 * implies group-prefix = 224.0.0.0 and masklen = 4. */ *cand_rp_adv_message.prefix_cnt_ptr = 0; cand_rp_adv_message.insert_data_ptr = cand_rp_adv_message.buffer; /* TODO: XXX: HARDCODING!!! */ cand_rp_adv_message.insert_data_ptr += (4 + 6); fp = fopen(config_file, "r"); if (!fp) { logit(LOG_WARNING, errno, "Cannot open configuration file %s", config_file); fallback_config(); goto nofile; } while (fgets(linebuf, sizeof(linebuf), fp)) { if (strlen(linebuf) >= (LINE_BUFSIZ - 1)) { WARN("Line length must be shorter than %d", LINE_BUFSIZ); error_flag = TRUE; } lineno++; s = linebuf; w = next_word(&s); option = parse_option(w); switch (option) { case CONF_EMPTY: continue; break; case CONF_PHYINT: parse_phyint(s); break; case CONF_CANDIDATE_RP: parse_rp_candidate(s); break; case CONF_RP_ADDRESS: parse_rp_address(s); break; case CONF_GROUP_PREFIX: parse_group_prefix(s); break; case CONF_BOOTSTRAP_RP: parse_bsr_candidate(s); break; case CONF_COMPAT_THRESHOLD: parse_compat_threshold(s); break; case CONF_SPT_THRESHOLD: parse_spt_threshold(s); break; case CONF_DEFAULT_ROUTE_METRIC: parse_default_route_metric(s); break; case CONF_DEFAULT_ROUTE_DISTANCE: parse_default_route_distance(s); break; case CONF_IGMP_QUERY_INTERVAL: parse_igmp_query_interval(s); break; case CONF_IGMP_QUERIER_TIMEOUT: parse_igmp_querier_timeout(s); break; case CONF_HELLO_INTERVAL: parse_hello_interval(s); break; default: logit(LOG_WARNING, 0, "%s:%u - Unknown command '%s'", config_file, lineno, w); error_flag = TRUE; } } fclose(fp); nofile: /* A static RP address is needed for SSM. We use a link-local * address. It is not required to be configured on any interface. */ strncpy(linebuf, "169.254.0.1 232.0.0.0/8\n", sizeof(linebuf)); s = linebuf; parse_rp_address(s); if (error_flag) logit(LOG_ERR, 0, "%s:%u - Syntax error", config_file, lineno); cand_rp_adv_message.message_size = cand_rp_adv_message.insert_data_ptr - cand_rp_adv_message.buffer; if (cand_rp_flag != FALSE) { /* Prepare the RP info */ my_cand_rp_holdtime = 2.5 * my_cand_rp_adv_period; /* TODO: HARDCODING! */ data_ptr = cand_rp_adv_message.buffer + 1; PUT_BYTE(my_cand_rp_priority, data_ptr); PUT_HOSTSHORT(my_cand_rp_holdtime, data_ptr); PUT_EUADDR(my_cand_rp_address, data_ptr); } /* If no IGMP querier timeout was set, calculate from query interval */ if (!igmp_querier_timeout) igmp_querier_timeout = QUERIER_TIMEOUT(igmp_query_interval); IF_DEBUG(DEBUG_IGMP) { logit(LOG_INFO, 0, "IGMP query interval : %u sec", igmp_query_interval); logit(LOG_INFO, 0, "IGMP querier timeout : %u sec", igmp_querier_timeout); } } static uint32_t ifname2addr(char *s) { vifi_t vifi; struct uvif *v; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { if (!strcmp(v->uv_name, s)) return v->uv_lcl_addr; } return 0; } static char *next_word(char **s) { char *w; w = *s; while (*w == ' ' || *w == '\t') w++; *s = w; while (**s != 0) { switch (**s) { case ' ': case '\t': **s = '\0'; (*s)++; return w; case '\n': case '#': **s = '\0'; return w; default: if (isascii((int)**s) && isupper((int)**s)) **s = tolower((int)**s); (*s)++; } } return w; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */