summaryrefslogtreecommitdiffstats
path: root/usr.sbin/pim6dd/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/pim6dd/config.c')
-rw-r--r--usr.sbin/pim6dd/config.c681
1 files changed, 681 insertions, 0 deletions
diff --git a/usr.sbin/pim6dd/config.c b/usr.sbin/pim6dd/config.c
new file mode 100644
index 0000000..c52e06f
--- /dev/null
+++ b/usr.sbin/pim6dd/config.c
@@ -0,0 +1,681 @@
+/*
+ * Copyright (c) 1998 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
+ * 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.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+/*
+ * Questions concerning this software should be directed to
+ * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
+ *
+ * $Id: config.c,v 1.3 1999/12/30 09:23:08 itojun Exp $
+ */
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "defs.h"
+
+
+/*
+ * Forward declarations.
+ */
+static char *next_word __P((char **));
+static int parse_phyint __P((char *s));
+static void add_phaddr __P((struct uvif *v, struct sockaddr_in6 *addr,
+ struct in6_addr *mask));
+static mifi_t ifname2mifi __P((char *ifname));
+static int wordToOption __P((char *));
+static int parse_filter __P((char *s));
+#if 0 /* not used */
+static int parse_default_source_metric __P((char *));
+static int parse_default_source_preference __P((char *));
+#endif
+
+/*
+ * Query the kernel to find network interfaces that are multicast-capable
+ * and install them in the uvifs array.
+ */
+void
+config_vifs_from_kernel()
+{
+ struct ifreq *ifrp, *ifend;
+ register struct uvif *v;
+ register vifi_t vifi;
+ int n;
+ struct sockaddr_in6 addr;
+ struct in6_addr mask, prefix;
+ short flags;
+ int num_ifreq = 64;
+ struct ifconf ifc;
+
+ 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) {
+ caddr_t newbuf;
+
+ if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
+
+ /*
+ * 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)
+ log(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;
+ struct in6_ifreq ifr6;
+#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 IPv6.
+ */
+ if (ifrp->ifr_addr.sa_family != AF_INET6) {
+ total_interfaces++; /* Eventually may have IPv6 address later */
+ continue;
+ }
+
+ memcpy(&addr, &ifrp->ifr_addr, sizeof(struct sockaddr_in6));
+
+ /*
+ * 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));
+ memcpy(ifr6.ifr_name, ifrp->ifr_name, sizeof(ifr6.ifr_name));
+
+ /*
+ * Ignore loopback interfaces and interfaces that do not
+ * support multicast.
+ */
+ if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
+ flags = ifr.ifr_flags;
+ if ((flags & (IFF_LOOPBACK | IFF_MULTICAST)) != IFF_MULTICAST)
+ continue;
+
+ /*
+ * Get netmask of the address.
+ */
+ ifr6.ifr_addr = *(struct sockaddr_in6 *)&ifrp->ifr_addr;
+ if (ioctl(udp_socket, SIOCGIFNETMASK_IN6, (char *)&ifr6) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK_IN6 for %s",
+ ifr6.ifr_name);
+ memcpy(&mask, &ifr6.ifr_addr.sin6_addr, sizeof(mask));
+
+ if (IN6_IS_ADDR_LINKLOCAL(&addr.sin6_addr)) {
+ addr.sin6_scope_id = if_nametoindex(ifrp->ifr_name);
+#ifdef __KAME__
+ /*
+ * Hack for KAME kernel. Set sin6_scope_id field of a
+ * link local address and clear the index embedded in
+ * the address.
+ */
+ /* clear interface index */
+ addr.sin6_addr.s6_addr[2] = 0;
+ addr.sin6_addr.s6_addr[3] = 0;
+#endif
+ }
+
+ /*
+ * If the address is connected to the same subnet as one already
+ * installed in the uvifs array, just add the address to the list
+ * of addresses of the uvif.
+ */
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (strcmp(v->uv_name, ifr.ifr_name) == 0) {
+ add_phaddr(v, &addr, &mask);
+ break;
+ }
+ }
+ if (vifi != numvifs)
+ continue;
+
+ /*
+ * If there is room in the uvifs array, install this interface.
+ */
+ if (numvifs == MAXMIFS) {
+ log(LOG_WARNING, 0, "too many ifs, ignoring %s", ifr.ifr_name);
+ continue;
+ }
+
+ /*
+ * Everyone below is a potential vif interface.
+ * We don't care if it has wrong configuration or not configured
+ * at all.
+ */
+ total_interfaces++;
+
+ v = &uvifs[numvifs];
+ v->uv_flags = 0;
+ v->uv_metric = DEFAULT_METRIC;
+ v->uv_admetric = 0;
+#if 0
+ v->uv_threshold = DEFAULT_THRESHOLD;
+#endif
+ v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT;
+#if 0
+ v->uv_rmt_addr = INADDR_ANY_N;
+#endif
+ v->uv_dst_addr = allpim6routers_group;
+ v->uv_prefix.sin6_addr = prefix;
+ v->uv_subnetmask = mask;
+ strncpy(v->uv_name, ifr.ifr_name, IFNAMSIZ);
+ v->uv_ifindex = if_nametoindex(v->uv_name);
+ v->uv_groups = (struct listaddr *)NULL;
+ v->uv_dvmrp_neighbors = (struct listaddr *)NULL;
+ NBRM_CLRALL(v->uv_nbrmap);
+ v->uv_querier = (struct listaddr *)NULL;
+ v->uv_prune_lifetime = 0;
+ v->uv_acl = (struct vif_acl *)NULL;
+ v->uv_leaf_timer = 0;
+ v->uv_addrs = (struct phaddr *)NULL;
+ v->uv_filter = (struct vif_filter *)NULL;
+ v->uv_pim_hello_timer = 0;
+ v->uv_gq_timer = 0;
+ v->uv_pim_neighbors = (struct pim_nbr_entry *)NULL;
+ v->uv_local_pref = default_source_preference;
+ v->uv_local_metric = default_source_metric;
+ add_phaddr(v, &addr, &mask);
+
+ if (flags & IFF_POINTOPOINT)
+ v->uv_flags |= (VIFF_REXMIT_PRUNES | VIFF_POINT_TO_POINT);
+ log(LOG_INFO, 0,
+ "installing %s as if #%u - rate=%d",
+ v->uv_name, numvifs, v->uv_rate_limit);
+ ++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 void
+add_phaddr(v, addr, mask)
+ struct uvif *v;
+ struct sockaddr_in6 *addr;
+ struct in6_addr *mask;
+{
+ struct phaddr *pa;
+ int i;
+
+ if ((pa = malloc(sizeof(*pa))) == NULL)
+ log(LOG_ERR, 0, "add_phaddr: memory exhausted");
+
+ memset(pa, 0, sizeof(*pa));
+ pa->pa_addr = *addr;
+ pa->pa_subnetmask = *mask;
+
+ /*
+ * install the prefix of the address derived from the address
+ * and the mask.
+ */
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ pa->pa_prefix.sin6_addr.s6_addr[i] =
+ addr->sin6_addr.s6_addr[i] & mask->s6_addr[i];
+ pa->pa_prefix.sin6_scope_id = addr->sin6_scope_id;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) {
+ if (v->uv_linklocal) {
+ log(LOG_WARNING, 0,
+ "add_phaddr: found more than one link-local "
+ "address on %s",
+ v->uv_name);
+ }
+ v->uv_linklocal = pa; /* relace anyway */
+ }
+
+ /* link into chain */
+ pa->pa_next = v->uv_addrs;
+ v->uv_addrs = pa;
+}
+
+static mifi_t
+ifname2mifi(ifname)
+ char *ifname;
+{
+ mifi_t mifi;
+ struct uvif *v;
+
+ for (mifi = 0, v = uvifs; mifi < numvifs; ++mifi, ++v) {
+ if (strcmp(v->uv_name, ifname) == 0)
+ break;
+ }
+ return(mifi);
+}
+
+#define UNKNOWN -1
+#define EMPTY 1
+#define PHYINT 2
+#define DEFAULT_SOURCE_METRIC 3
+#define DEFAULT_SOURCE_PREFERENCE 4
+#define FILTER 5
+
+/*
+ * function name: wordToOption
+ * input: char *word, a pointer to the word
+ * output: int; a number corresponding to the code of the word
+ * operation: converts the result of the string comparisons into numerics.
+ * comments: called by config_vifs_from_file()
+ */
+static int
+wordToOption(word)
+ char *word;
+{
+ if (EQUAL(word, ""))
+ return EMPTY;
+ if (EQUAL(word, "phyint"))
+ return PHYINT;
+ if (EQUAL(word, "default_source_metric"))
+ return DEFAULT_SOURCE_METRIC;
+ if (EQUAL(word, "default_source_preference"))
+ return DEFAULT_SOURCE_PREFERENCE;
+ if (EQUAL(word, "filter"))
+ return FILTER;
+ return UNKNOWN;
+}
+
+/*
+ * function name: parse_phyint
+ * input: char *s, pointing to the parsing point of the file
+ * output: int (TRUE if the parsing was successful, o.w. FALSE)
+ * operation: parses the physical interface file configurations, if any.
+ * The general form is:
+ * phyint <ifname> [disable]
+ */
+static int
+parse_phyint(s)
+ char *s;
+{
+ char *w, c, *ifname;
+ vifi_t vifi;
+ struct uvif *v;
+ u_int n;
+
+ if (EQUAL((w = next_word(&s)), "")) {
+ log(LOG_WARNING, 0, "Missing phyint in %s", configfilename);
+ return(FALSE);
+ } /* if empty */
+ ifname = w;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (vifi == numvifs) {
+ log(LOG_WARNING, 0,
+ "phyint %s in %s is not a configured interface",
+ ifname, configfilename);
+ return(FALSE);
+ } /* if vifi == numvifs */
+
+ if (strcmp(v->uv_name, ifname))
+ continue;
+
+ while (!EQUAL((w = next_word(&s)), "")) {
+ if (EQUAL(w, "disable"))
+ v->uv_flags |= VIFF_DISABLED;
+ else if (EQUAL(w, "nolistener"))
+ v->uv_flags |= VIFF_NOLISTENER;
+ else if(EQUAL(w, "preference")) {
+ if(EQUAL((w = next_word(&s)), ""))
+ log(LOG_WARNING, 0,
+ "Missing preference for phyint %s in %s",
+ ifname, configfilename);
+ else if (sscanf(w, "%u%c", &n, &c) != 1 ||
+ n < 1 || n > 255 )
+ log(LOG_WARNING, 0,
+ "Invalid preference '%s' for phyint %s in %s",
+ w, ifname,
+ configfilename);
+ else {
+ IF_DEBUG(DEBUG_ASSERT)
+ log(LOG_DEBUG, 0,
+ "Config setting default local preference on %s to %d.",
+ ifname, n);
+ v->uv_local_pref = n;
+ }
+
+ } else if(EQUAL(w, "metric")) {
+ if(EQUAL((w = next_word(&s)), ""))
+ log(LOG_WARNING, 0,
+ "Missing metric for phyint %s in %s",
+ ifname, configfilename);
+ else if (sscanf(w, "%u%c", &n, &c) != 1 ||
+ n < 1 || n > 1024 )
+ log(LOG_WARNING, 0,
+ "Invalid metric '%s' for phyint %s in %s",
+ w, ifname,
+ configfilename);
+ else {
+ IF_DEBUG(DEBUG_ASSERT)
+ log(LOG_DEBUG, 0,
+ "Config setting default local metric on %s to %d.",
+ ifname, n);
+ v->uv_local_metric = n;
+ }
+ }
+ } /* if not empty */
+ break;
+ }
+ return(TRUE);
+}
+
+static int
+parse_filter(s)
+ char *s;
+{
+ char *w, *groups, *p;
+ mifi_t mifi;
+ struct in6_addr grp1, grp2;
+ if_set filterset;
+ struct mrtfilter *filter;
+ int plen = 0, filtertype;
+
+ if (EQUAL((groups = next_word(&s)), "")) {
+ log(LOG_WARNING, 0, "Missing multicast group in %s",
+ configfilename);
+ return(FALSE);
+ }
+
+ /*
+ * Group address specification. Valid format are the followings.
+ * - Group1-Group2: specifies a numerical range of a scope.
+ * - GroupPrefix/Prefixlen: specifies a prefix of a scope. If the
+ * Prefixlen is omitted, it means the exact match.
+ */
+ if ((p = strchr(groups, '-')) != NULL) { /* Group1-Group2 */
+ char *maddr1, *maddr2;
+
+ maddr1 = groups;
+ maddr2 = p + 1;
+ *p = '\0';
+ if (inet_pton(AF_INET6, maddr1, (void *)&grp1) != 1 ||
+ !IN6_IS_ADDR_MULTICAST(&grp1)) {
+ log(LOG_WARNING, 0, "invalid group address %s", maddr1);
+ return(FALSE);
+ }
+ if (inet_pton(AF_INET6, maddr2, (void *)&grp2) != 1 ||
+ !IN6_IS_ADDR_MULTICAST(&grp2)) {
+ log(LOG_WARNING, 0, "invalid group address %s", maddr2);
+ return(FALSE);
+ }
+ filtertype = FILTER_RANGE;
+ }
+ else if ((p = strchr(groups, '/')) != NULL) { /* GroupPrefix/Plen */
+ char *mprefix = groups;
+ int plen = atoi(p + 1);
+ *p = '\0';
+ if (inet_pton(AF_INET6, mprefix, (void *)&grp1) != 1 ||
+ !IN6_IS_ADDR_MULTICAST(&grp1)) {
+ log(LOG_WARNING, 0, "invalid group prefix %s", mprefix);
+ return(FALSE);
+ }
+ if (plen < 0 || plen > 128) {
+ log(LOG_WARNING, 0, "invalid prefix length %s", p + 1);
+ return(FALSE);
+ }
+ filtertype = FILTER_PREFIX;
+ }
+ else {
+ if (inet_pton(AF_INET6, groups, (void *)&grp1) != 1) {
+ log(LOG_WARNING, 0, "invalid group address %s", groups);
+ return(FALSE);
+ }
+ plen = 128; /* exact match */
+ filtertype = FILTER_PREFIX;
+ }
+
+ IF_ZERO(&filterset);
+ while (!EQUAL((w = next_word(&s)), "")) {
+ if ((mifi = ifname2mifi(w)) == MAXMIFS) {
+ /* XXX: scope consideration?? */
+ log(LOG_WARNING, 0,
+ "phyint %s in %s is not a configured interface",
+ w, configfilename);
+ return(FALSE);
+ }
+
+ IF_SET(mifi, &filterset);
+ }
+ if (IF_ISEMPTY(&filterset)) {
+ log(LOG_WARNING, 0,
+ "filter set is empty. ignore it.");
+ return(FALSE);
+ }
+
+ filter = add_filter(filtertype, &grp1, &grp2, plen);
+ IF_COPY(&filterset, &filter->ifset);
+
+ return(TRUE);
+}
+
+#if 0 /* not used */
+/*
+ * function name: parse_default_source_metric
+ * input: char *s
+ * output: int
+ * operation: reads and assigns the default source metric, if no reliable
+ * unicast routing information available.
+ * General form:
+ * 'default_source_metric <number>'.
+ * default pref and metric statements should precede all phyint
+ * statements in the config file.
+ */
+static int
+parse_default_source_metric(s)
+ char *s;
+{
+ char *w;
+ u_int value;
+ vifi_t vifi;
+ struct uvif *v;
+
+ value = DEFAULT_LOCAL_METRIC;
+ if (EQUAL((w = next_word(&s)), "")) {
+ log(LOG_WARNING, 0,
+ "Missing default source metric; set to default %u",
+ DEFAULT_LOCAL_METRIC);
+ } else if (sscanf(w, "%u", &value) != 1) {
+ log(LOG_WARNING, 0,
+ "Invalid default source metric; set to default %u",
+ DEFAULT_LOCAL_METRIC);
+ value = DEFAULT_LOCAL_METRIC;
+ }
+ default_source_metric = value;
+ log(LOG_INFO, 0, "default_source_metric is %u", value);
+
+ for (vifi = 0, v = uvifs; vifi < MAXMIFS; ++vifi, ++v) {
+ v->uv_local_metric = default_source_metric;
+ }
+
+ return(TRUE);
+}
+
+
+/*
+ * function name: parse_default_source_preference
+ * input: char *s
+ * output: int
+ * operation: reads and assigns the default source preference, if no reliable
+ * unicast routing information available.
+ * General form:
+ * 'default_source_preference <number>'.
+ * default pref and metric statements should precede all phyint
+ * statements in the config file.
+*/
+static int
+parse_default_source_preference(s)
+ char *s;
+{
+ char *w;
+ u_int value;
+ vifi_t vifi;
+ struct uvif *v;
+
+ value = DEFAULT_LOCAL_PREF;
+ if (EQUAL((w = next_word(&s)), "")) {
+ log(LOG_WARNING, 0,
+ "Missing default source preference; set to default %u",
+ DEFAULT_LOCAL_PREF);
+ } else if (sscanf(w, "%u", &value) != 1) {
+ log(LOG_WARNING, 0,
+ "Invalid default source preference; set to default %u",
+ DEFAULT_LOCAL_PREF);
+ value = DEFAULT_LOCAL_PREF;
+ }
+ default_source_preference = value;
+ log(LOG_INFO, 0, "default_source_preference is %u", value);
+
+ for (vifi = 0, v = uvifs; vifi < MAXMIFS; ++vifi, ++v) {
+ v->uv_local_pref = default_source_preference;
+ }
+
+ return(TRUE);
+}
+#endif
+
+void
+config_vifs_from_file()
+{
+ FILE *f;
+ char linebuf[100];
+ char *w, *s;
+ struct ifconf ifc;
+ int option;
+ char ifbuf[BUFSIZ];
+
+ if ((f = fopen(configfilename, "r")) == NULL) {
+ if (errno != ENOENT) log(LOG_WARNING, errno, "can't open %s",
+ configfilename);
+ return;
+ }
+
+ ifc.ifc_buf = ifbuf;
+ ifc.ifc_len = sizeof(ifbuf);
+ if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
+
+ while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
+ s = linebuf;
+ w = next_word(&s);
+ option = wordToOption(w);
+ switch(option) {
+ case EMPTY:
+ continue;
+ break;
+ case PHYINT:
+ parse_phyint(s);
+ break;
+ case FILTER:
+ parse_filter(s);
+ break;
+ default:
+ log(LOG_WARNING, 0, "unknown command '%s' in %s",
+ w, configfilename);
+ }
+ }
+ fclose(f);
+}
+
+static char *
+next_word(s)
+ char **s;
+{
+ char *w;
+
+ w = *s;
+ while (*w == ' ' || *w == '\t')
+ w++;
+
+ *s = w;
+ for(;;) {
+ switch (**s) {
+ case ' ' :
+ case '\t' :
+ **s = '\0';
+ (*s)++;
+ return(w);
+ case '\n' :
+ case '#' :
+ **s = '\0';
+ return(w);
+ case '\0' :
+ return(w);
+ default :
+ if (isascii(**s) && isupper(**s))
+ **s = tolower(**s);
+ (*s)++;
+ }
+ }
+}
+
OpenPOWER on IntegriCloud