Index: head/lib/libkvm/kvm_getswapinfo.c =================================================================== --- head/lib/libkvm/kvm_getswapinfo.c (revision 333474) +++ head/lib/libkvm/kvm_getswapinfo.c (revision 333475) @@ -1,271 +1,272 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1999, Matthew Dillon. All Rights Reserved. * Copyright (c) 2001, Thomas Moestl. 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kvm_private.h" static struct nlist kvm_swap_nl[] = { { .n_name = "_swtailq" }, /* list of swap devices and sizes */ { .n_name = "_dmmax" }, /* maximum size of a swap block */ { .n_name = NULL } }; #define NL_SWTAILQ 0 #define NL_DMMAX 1 static int kvm_swap_nl_cached = 0; static int unswdev; /* number of found swap dev's */ static int dmmax; static int kvm_getswapinfo_kvm(kvm_t *, struct kvm_swap *, int, int); static int kvm_getswapinfo_sysctl(kvm_t *, struct kvm_swap *, int, int); static int nlist_init(kvm_t *); static int getsysctl(kvm_t *, const char *, void *, size_t); #define KREAD(kd, addr, obj) \ (kvm_read(kd, addr, (char *)(obj), sizeof(*obj)) != sizeof(*obj)) #define KGET(idx, var) \ KGET2(kvm_swap_nl[(idx)].n_value, var, kvm_swap_nl[(idx)].n_name) #define KGET2(addr, var, msg) \ if (KREAD(kd, (u_long)(addr), (var))) { \ _kvm_err(kd, kd->program, "cannot read %s", msg); \ return (-1); \ } #define GETSWDEVNAME(dev, str, flags) \ if (dev == NODEV) { \ strlcpy(str, "[NFS swap]", sizeof(str)); \ } else { \ snprintf( \ str, sizeof(str),"%s%s", \ ((flags & SWIF_DEV_PREFIX) ? _PATH_DEV : ""), \ devname(dev, S_IFCHR) \ ); \ } int kvm_getswapinfo(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max, int flags) { /* * clear cache */ if (kd == NULL) { kvm_swap_nl_cached = 0; return(0); } if (ISALIVE(kd)) { return kvm_getswapinfo_sysctl(kd, swap_ary, swap_max, flags); } else { return kvm_getswapinfo_kvm(kd, swap_ary, swap_max, flags); } } int kvm_getswapinfo_kvm(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max, int flags) { int i; swblk_t ttl; TAILQ_HEAD(, swdevt) swtailq; struct swdevt *sp, swinfo; struct kvm_swap tot; if (!kd->arch->ka_native(kd)) { _kvm_err(kd, kd->program, "cannot read swapinfo from non-native core"); return (-1); } if (!nlist_init(kd)) return (-1); bzero(&tot, sizeof(tot)); KGET(NL_SWTAILQ, &swtailq); sp = TAILQ_FIRST(&swtailq); for (i = 0; sp != NULL; i++) { KGET2(sp, &swinfo, "swinfo"); ttl = swinfo.sw_nblks - dmmax; if (i < swap_max - 1) { bzero(&swap_ary[i], sizeof(swap_ary[i])); swap_ary[i].ksw_total = ttl; swap_ary[i].ksw_used = swinfo.sw_used; swap_ary[i].ksw_flags = swinfo.sw_flags; GETSWDEVNAME(swinfo.sw_dev, swap_ary[i].ksw_devname, flags); } tot.ksw_total += ttl; tot.ksw_used += swinfo.sw_used; sp = TAILQ_NEXT(&swinfo, sw_list); } if (i >= swap_max) i = swap_max - 1; if (i >= 0) swap_ary[i] = tot; return(i); } #define GETSYSCTL(kd, name, var) \ getsysctl(kd, name, &(var), sizeof(var)) /* The maximum MIB length for vm.swap_info and an additional device number */ #define SWI_MAXMIB 3 int kvm_getswapinfo_sysctl(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max, int flags) { int ti; swblk_t ttl; size_t mibi, len; int soid[SWI_MAXMIB]; struct xswdev xsd; struct kvm_swap tot; if (!GETSYSCTL(kd, "vm.dmmax", dmmax)) return -1; mibi = SWI_MAXMIB - 1; if (sysctlnametomib("vm.swap_info", soid, &mibi) == -1) { _kvm_err(kd, kd->program, "sysctlnametomib failed: %s", strerror(errno)); return -1; } bzero(&tot, sizeof(tot)); for (unswdev = 0;; unswdev++) { soid[mibi] = unswdev; len = sizeof(xsd); if (sysctl(soid, mibi + 1, &xsd, &len, NULL, 0) == -1) { if (errno == ENOENT) break; _kvm_err(kd, kd->program, "cannot read sysctl: %s.", strerror(errno)); return -1; } if (len != sizeof(xsd)) { _kvm_err(kd, kd->program, "struct xswdev has unexpected " "size; kernel and libkvm out of sync?"); return -1; } if (xsd.xsw_version != XSWDEV_VERSION) { _kvm_err(kd, kd->program, "struct xswdev version " "mismatch; kernel and libkvm out of sync?"); return -1; } ttl = xsd.xsw_nblks - dmmax; if (unswdev < swap_max - 1) { bzero(&swap_ary[unswdev], sizeof(swap_ary[unswdev])); swap_ary[unswdev].ksw_total = ttl; swap_ary[unswdev].ksw_used = xsd.xsw_used; swap_ary[unswdev].ksw_flags = xsd.xsw_flags; GETSWDEVNAME(xsd.xsw_dev, swap_ary[unswdev].ksw_devname, flags); } tot.ksw_total += ttl; tot.ksw_used += xsd.xsw_used; } ti = unswdev; if (ti >= swap_max) ti = swap_max - 1; if (ti >= 0) swap_ary[ti] = tot; return(ti); } static int nlist_init(kvm_t *kd) { if (kvm_swap_nl_cached) return (1); if (kvm_nlist(kd, kvm_swap_nl) < 0) return (0); /* Required entries */ if (kvm_swap_nl[NL_SWTAILQ].n_value == 0) { _kvm_err(kd, kd->program, "unable to find swtailq"); return (0); } if (kvm_swap_nl[NL_DMMAX].n_value == 0) { _kvm_err(kd, kd->program, "unable to find dmmax"); return (0); } /* Get globals, type of swap */ KGET(NL_DMMAX, &dmmax); kvm_swap_nl_cached = 1; return (1); } static int getsysctl(kvm_t *kd, const char *name, void *ptr, size_t len) { size_t nlen = len; if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) { _kvm_err(kd, kd->program, "cannot read sysctl %s:%s", name, strerror(errno)); return (0); } if (nlen != len) { _kvm_err(kd, kd->program, "sysctl %s has unexpected size", name); return (0); } return (1); } Index: head/lib/libmemstat/memstat.c =================================================================== --- head/lib/libmemstat/memstat.c (revision 333474) +++ head/lib/libmemstat/memstat.c (revision 333475) @@ -1,442 +1,443 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005 Robert N. M. Watson * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #include +#include #include #include #include #include #include #include #include "memstat.h" #include "memstat_internal.h" const char * memstat_strerror(int error) { switch (error) { case MEMSTAT_ERROR_NOMEMORY: return ("Cannot allocate memory"); case MEMSTAT_ERROR_VERSION: return ("Version mismatch"); case MEMSTAT_ERROR_PERMISSION: return ("Permission denied"); case MEMSTAT_ERROR_DATAERROR: return ("Data format error"); case MEMSTAT_ERROR_KVM: return ("KVM error"); case MEMSTAT_ERROR_KVM_NOSYMBOL: return ("KVM unable to find symbol"); case MEMSTAT_ERROR_KVM_SHORTREAD: return ("KVM short read"); case MEMSTAT_ERROR_UNDEFINED: default: return ("Unknown error"); } } struct memory_type_list * memstat_mtl_alloc(void) { struct memory_type_list *mtlp; mtlp = malloc(sizeof(*mtlp)); if (mtlp == NULL) return (NULL); LIST_INIT(&mtlp->mtl_list); mtlp->mtl_error = MEMSTAT_ERROR_UNDEFINED; return (mtlp); } struct memory_type * memstat_mtl_first(struct memory_type_list *list) { return (LIST_FIRST(&list->mtl_list)); } struct memory_type * memstat_mtl_next(struct memory_type *mtp) { return (LIST_NEXT(mtp, mt_list)); } void _memstat_mtl_empty(struct memory_type_list *list) { struct memory_type *mtp; while ((mtp = LIST_FIRST(&list->mtl_list))) { free(mtp->mt_percpu_alloc); free(mtp->mt_percpu_cache); LIST_REMOVE(mtp, mt_list); free(mtp); } } void memstat_mtl_free(struct memory_type_list *list) { _memstat_mtl_empty(list); free(list); } int memstat_mtl_geterror(struct memory_type_list *list) { return (list->mtl_error); } /* * Look for an existing memory_type entry in a memory_type list, based on the * allocator and name of the type. If not found, return NULL. No errno or * memstat error. */ struct memory_type * memstat_mtl_find(struct memory_type_list *list, int allocator, const char *name) { struct memory_type *mtp; LIST_FOREACH(mtp, &list->mtl_list, mt_list) { if ((mtp->mt_allocator == allocator || allocator == ALLOCATOR_ANY) && strcmp(mtp->mt_name, name) == 0) return (mtp); } return (NULL); } /* * Allocate a new memory_type with the specificed allocator type and name, * then insert into the list. The structure will be zero'd. * * libmemstat(3) internal function. */ struct memory_type * _memstat_mt_allocate(struct memory_type_list *list, int allocator, const char *name, int maxcpus) { struct memory_type *mtp; mtp = malloc(sizeof(*mtp)); if (mtp == NULL) return (NULL); bzero(mtp, sizeof(*mtp)); mtp->mt_allocator = allocator; mtp->mt_percpu_alloc = malloc(sizeof(struct mt_percpu_alloc_s) * maxcpus); mtp->mt_percpu_cache = malloc(sizeof(struct mt_percpu_cache_s) * maxcpus); strlcpy(mtp->mt_name, name, MEMTYPE_MAXNAME); LIST_INSERT_HEAD(&list->mtl_list, mtp, mt_list); return (mtp); } /* * Reset any libmemstat(3)-owned statistics in a memory_type record so that * it can be reused without incremental addition problems. Caller-owned * memory is left "as-is", and must be updated by the caller if desired. * * libmemstat(3) internal function. */ void _memstat_mt_reset_stats(struct memory_type *mtp, int maxcpus) { int i; mtp->mt_countlimit = 0; mtp->mt_byteslimit = 0; mtp->mt_sizemask = 0; mtp->mt_size = 0; mtp->mt_memalloced = 0; mtp->mt_memfreed = 0; mtp->mt_numallocs = 0; mtp->mt_numfrees = 0; mtp->mt_bytes = 0; mtp->mt_count = 0; mtp->mt_free = 0; mtp->mt_failures = 0; mtp->mt_sleeps = 0; mtp->mt_zonefree = 0; mtp->mt_kegfree = 0; for (i = 0; i < maxcpus; i++) { mtp->mt_percpu_alloc[i].mtp_memalloced = 0; mtp->mt_percpu_alloc[i].mtp_memfreed = 0; mtp->mt_percpu_alloc[i].mtp_numallocs = 0; mtp->mt_percpu_alloc[i].mtp_numfrees = 0; mtp->mt_percpu_alloc[i].mtp_sizemask = 0; mtp->mt_percpu_cache[i].mtp_free = 0; } } /* * Accessor methods for struct memory_type. Avoids encoding the structure * ABI into the application. */ const char * memstat_get_name(const struct memory_type *mtp) { return (mtp->mt_name); } int memstat_get_allocator(const struct memory_type *mtp) { return (mtp->mt_allocator); } uint64_t memstat_get_countlimit(const struct memory_type *mtp) { return (mtp->mt_countlimit); } uint64_t memstat_get_byteslimit(const struct memory_type *mtp) { return (mtp->mt_byteslimit); } uint64_t memstat_get_sizemask(const struct memory_type *mtp) { return (mtp->mt_sizemask); } uint64_t memstat_get_size(const struct memory_type *mtp) { return (mtp->mt_size); } uint64_t memstat_get_rsize(const struct memory_type *mtp) { return (mtp->mt_rsize); } uint64_t memstat_get_memalloced(const struct memory_type *mtp) { return (mtp->mt_memalloced); } uint64_t memstat_get_memfreed(const struct memory_type *mtp) { return (mtp->mt_memfreed); } uint64_t memstat_get_numallocs(const struct memory_type *mtp) { return (mtp->mt_numallocs); } uint64_t memstat_get_numfrees(const struct memory_type *mtp) { return (mtp->mt_numfrees); } uint64_t memstat_get_bytes(const struct memory_type *mtp) { return (mtp->mt_bytes); } uint64_t memstat_get_count(const struct memory_type *mtp) { return (mtp->mt_count); } uint64_t memstat_get_free(const struct memory_type *mtp) { return (mtp->mt_free); } uint64_t memstat_get_failures(const struct memory_type *mtp) { return (mtp->mt_failures); } uint64_t memstat_get_sleeps(const struct memory_type *mtp) { return (mtp->mt_sleeps); } void * memstat_get_caller_pointer(const struct memory_type *mtp, int index) { return (mtp->mt_caller_pointer[index]); } void memstat_set_caller_pointer(struct memory_type *mtp, int index, void *value) { mtp->mt_caller_pointer[index] = value; } uint64_t memstat_get_caller_uint64(const struct memory_type *mtp, int index) { return (mtp->mt_caller_uint64[index]); } void memstat_set_caller_uint64(struct memory_type *mtp, int index, uint64_t value) { mtp->mt_caller_uint64[index] = value; } uint64_t memstat_get_zonefree(const struct memory_type *mtp) { return (mtp->mt_zonefree); } uint64_t memstat_get_kegfree(const struct memory_type *mtp) { return (mtp->mt_kegfree); } uint64_t memstat_get_percpu_memalloced(const struct memory_type *mtp, int cpu) { return (mtp->mt_percpu_alloc[cpu].mtp_memalloced); } uint64_t memstat_get_percpu_memfreed(const struct memory_type *mtp, int cpu) { return (mtp->mt_percpu_alloc[cpu].mtp_memfreed); } uint64_t memstat_get_percpu_numallocs(const struct memory_type *mtp, int cpu) { return (mtp->mt_percpu_alloc[cpu].mtp_numallocs); } uint64_t memstat_get_percpu_numfrees(const struct memory_type *mtp, int cpu) { return (mtp->mt_percpu_alloc[cpu].mtp_numfrees); } uint64_t memstat_get_percpu_sizemask(const struct memory_type *mtp, int cpu) { return (mtp->mt_percpu_alloc[cpu].mtp_sizemask); } void * memstat_get_percpu_caller_pointer(const struct memory_type *mtp, int cpu, int index) { return (mtp->mt_percpu_alloc[cpu].mtp_caller_pointer[index]); } void memstat_set_percpu_caller_pointer(struct memory_type *mtp, int cpu, int index, void *value) { mtp->mt_percpu_alloc[cpu].mtp_caller_pointer[index] = value; } uint64_t memstat_get_percpu_caller_uint64(const struct memory_type *mtp, int cpu, int index) { return (mtp->mt_percpu_alloc[cpu].mtp_caller_uint64[index]); } void memstat_set_percpu_caller_uint64(struct memory_type *mtp, int cpu, int index, uint64_t value) { mtp->mt_percpu_alloc[cpu].mtp_caller_uint64[index] = value; } uint64_t memstat_get_percpu_free(const struct memory_type *mtp, int cpu) { return (mtp->mt_percpu_cache[cpu].mtp_free); } Index: head/sys/sys/sysctl.h =================================================================== --- head/sys/sys/sysctl.h (revision 333474) +++ head/sys/sys/sysctl.h (revision 333475) @@ -1,1066 +1,1068 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Karels at Berkeley Software Design, Inc. * * 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 University 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 REGENTS 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 REGENTS 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. * * @(#)sysctl.h 8.1 (Berkeley) 6/2/93 * $FreeBSD$ */ #ifndef _SYS_SYSCTL_H_ #define _SYS_SYSCTL_H_ +#ifdef _KERNEL #include +#endif struct thread; /* * Definitions for sysctl call. The sysctl call uses a hierarchical name * for objects that can be examined or modified. The name is expressed as * a sequence of integers. Like a file path name, the meaning of each * component depends on its place in the hierarchy. The top-level and kern * identifiers are defined here, and other identifiers are defined in the * respective subsystem header files. */ #define CTL_MAXNAME 24 /* largest number of components supported */ /* * Each subsystem defined by sysctl defines a list of variables * for that subsystem. Each name is either a node with further * levels defined below it, or it is a leaf of some particular * type given below. Each sysctl level defines a set of name/type * pairs to be used by sysctl(8) in manipulating the subsystem. */ struct ctlname { char *ctl_name; /* subsystem name */ int ctl_type; /* type of name */ }; #define CTLTYPE 0xf /* mask for the type */ #define CTLTYPE_NODE 1 /* name is a node */ #define CTLTYPE_INT 2 /* name describes an integer */ #define CTLTYPE_STRING 3 /* name describes a string */ #define CTLTYPE_S64 4 /* name describes a signed 64-bit number */ #define CTLTYPE_OPAQUE 5 /* name describes a structure */ #define CTLTYPE_STRUCT CTLTYPE_OPAQUE /* name describes a structure */ #define CTLTYPE_UINT 6 /* name describes an unsigned integer */ #define CTLTYPE_LONG 7 /* name describes a long */ #define CTLTYPE_ULONG 8 /* name describes an unsigned long */ #define CTLTYPE_U64 9 /* name describes an unsigned 64-bit number */ #define CTLTYPE_U8 0xa /* name describes an unsigned 8-bit number */ #define CTLTYPE_U16 0xb /* name describes an unsigned 16-bit number */ #define CTLTYPE_S8 0xc /* name describes a signed 8-bit number */ #define CTLTYPE_S16 0xd /* name describes a signed 16-bit number */ #define CTLTYPE_S32 0xe /* name describes a signed 32-bit number */ #define CTLTYPE_U32 0xf /* name describes an unsigned 32-bit number */ #define CTLFLAG_RD 0x80000000 /* Allow reads of variable */ #define CTLFLAG_WR 0x40000000 /* Allow writes to the variable */ #define CTLFLAG_RW (CTLFLAG_RD|CTLFLAG_WR) #define CTLFLAG_DORMANT 0x20000000 /* This sysctl is not active yet */ #define CTLFLAG_ANYBODY 0x10000000 /* All users can set this var */ #define CTLFLAG_SECURE 0x08000000 /* Permit set only if securelevel<=0 */ #define CTLFLAG_PRISON 0x04000000 /* Prisoned roots can fiddle */ #define CTLFLAG_DYN 0x02000000 /* Dynamic oid - can be freed */ #define CTLFLAG_SKIP 0x01000000 /* Skip this sysctl when listing */ #define CTLMASK_SECURE 0x00F00000 /* Secure level */ #define CTLFLAG_TUN 0x00080000 /* Default value is loaded from getenv() */ #define CTLFLAG_RDTUN (CTLFLAG_RD|CTLFLAG_TUN) #define CTLFLAG_RWTUN (CTLFLAG_RW|CTLFLAG_TUN) #define CTLFLAG_MPSAFE 0x00040000 /* Handler is MP safe */ #define CTLFLAG_VNET 0x00020000 /* Prisons with vnet can fiddle */ #define CTLFLAG_DYING 0x00010000 /* Oid is being removed */ #define CTLFLAG_CAPRD 0x00008000 /* Can be read in capability mode */ #define CTLFLAG_CAPWR 0x00004000 /* Can be written in capability mode */ #define CTLFLAG_STATS 0x00002000 /* Statistics, not a tuneable */ #define CTLFLAG_NOFETCH 0x00001000 /* Don't fetch tunable from getenv() */ #define CTLFLAG_CAPRW (CTLFLAG_CAPRD|CTLFLAG_CAPWR) /* * Secure level. Note that CTLFLAG_SECURE == CTLFLAG_SECURE1. * * Secure when the securelevel is raised to at least N. */ #define CTLSHIFT_SECURE 20 #define CTLFLAG_SECURE1 (CTLFLAG_SECURE | (0 << CTLSHIFT_SECURE)) #define CTLFLAG_SECURE2 (CTLFLAG_SECURE | (1 << CTLSHIFT_SECURE)) #define CTLFLAG_SECURE3 (CTLFLAG_SECURE | (2 << CTLSHIFT_SECURE)) /* * USE THIS instead of a hardwired number from the categories below * to get dynamically assigned sysctl entries using the linker-set * technology. This is the way nearly all new sysctl variables should * be implemented. * e.g. SYSCTL_INT(_parent, OID_AUTO, name, CTLFLAG_RW, &variable, 0, ""); */ #define OID_AUTO (-1) /* * The starting number for dynamically-assigned entries. WARNING! * ALL static sysctl entries should have numbers LESS than this! */ #define CTL_AUTO_START 0x100 #ifdef _KERNEL #include #ifdef KLD_MODULE /* XXX allow overspecification of type in external kernel modules */ #define SYSCTL_CT_ASSERT_MASK CTLTYPE #else #define SYSCTL_CT_ASSERT_MASK 0 #endif #define SYSCTL_HANDLER_ARGS struct sysctl_oid *oidp, void *arg1, \ intmax_t arg2, struct sysctl_req *req /* definitions for sysctl_req 'lock' member */ #define REQ_UNWIRED 1 #define REQ_WIRED 2 /* definitions for sysctl_req 'flags' member */ #if defined(__aarch64__) || defined(__amd64__) || defined(__powerpc64__) ||\ (defined(__mips__) && defined(__mips_n64)) #define SCTL_MASK32 1 /* 32 bit emulation */ #endif /* * This describes the access space for a sysctl request. This is needed * so that we can use the interface from the kernel or from user-space. */ struct sysctl_req { struct thread *td; /* used for access checking */ int lock; /* wiring state */ void *oldptr; size_t oldlen; size_t oldidx; int (*oldfunc)(struct sysctl_req *, const void *, size_t); void *newptr; size_t newlen; size_t newidx; int (*newfunc)(struct sysctl_req *, void *, size_t); size_t validlen; int flags; }; SLIST_HEAD(sysctl_oid_list, sysctl_oid); /* * This describes one "oid" in the MIB tree. Potentially more nodes can * be hidden behind it, expanded by the handler. */ struct sysctl_oid { struct sysctl_oid_list oid_children; struct sysctl_oid_list *oid_parent; SLIST_ENTRY(sysctl_oid) oid_link; int oid_number; u_int oid_kind; void *oid_arg1; intmax_t oid_arg2; const char *oid_name; int (*oid_handler)(SYSCTL_HANDLER_ARGS); const char *oid_fmt; int oid_refcnt; u_int oid_running; const char *oid_descr; const char *oid_label; }; #define SYSCTL_IN(r, p, l) (r->newfunc)(r, p, l) #define SYSCTL_OUT(r, p, l) (r->oldfunc)(r, p, l) #define SYSCTL_OUT_STR(r, p) (r->oldfunc)(r, p, strlen(p) + 1) int sysctl_handle_bool(SYSCTL_HANDLER_ARGS); int sysctl_handle_8(SYSCTL_HANDLER_ARGS); int sysctl_handle_16(SYSCTL_HANDLER_ARGS); int sysctl_handle_32(SYSCTL_HANDLER_ARGS); int sysctl_handle_64(SYSCTL_HANDLER_ARGS); int sysctl_handle_int(SYSCTL_HANDLER_ARGS); int sysctl_msec_to_ticks(SYSCTL_HANDLER_ARGS); int sysctl_handle_long(SYSCTL_HANDLER_ARGS); int sysctl_handle_string(SYSCTL_HANDLER_ARGS); int sysctl_handle_opaque(SYSCTL_HANDLER_ARGS); int sysctl_handle_counter_u64(SYSCTL_HANDLER_ARGS); int sysctl_handle_counter_u64_array(SYSCTL_HANDLER_ARGS); int sysctl_handle_uma_zone_max(SYSCTL_HANDLER_ARGS); int sysctl_handle_uma_zone_cur(SYSCTL_HANDLER_ARGS); int sysctl_dpcpu_int(SYSCTL_HANDLER_ARGS); int sysctl_dpcpu_long(SYSCTL_HANDLER_ARGS); int sysctl_dpcpu_quad(SYSCTL_HANDLER_ARGS); /* * These functions are used to add/remove an oid from the mib. */ void sysctl_register_oid(struct sysctl_oid *oidp); void sysctl_register_disabled_oid(struct sysctl_oid *oidp); void sysctl_enable_oid(struct sysctl_oid *oidp); void sysctl_unregister_oid(struct sysctl_oid *oidp); /* Declare a static oid to allow child oids to be added to it. */ #define SYSCTL_DECL(name) \ extern struct sysctl_oid sysctl__##name /* Hide these in macros. */ #define SYSCTL_CHILDREN(oid_ptr) (&(oid_ptr)->oid_children) #define SYSCTL_PARENT(oid_ptr) \ (((oid_ptr)->oid_parent != &sysctl__children) ? \ __containerof((oid_ptr)->oid_parent, struct sysctl_oid, \ oid_children) : (struct sysctl_oid *)NULL) #define SYSCTL_STATIC_CHILDREN(oid_name) (&sysctl__##oid_name.oid_children) /* === Structs and macros related to context handling. === */ /* All dynamically created sysctls can be tracked in a context list. */ struct sysctl_ctx_entry { struct sysctl_oid *entry; TAILQ_ENTRY(sysctl_ctx_entry) link; }; TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry); #define SYSCTL_NODE_CHILDREN(parent, name) \ sysctl__##parent##_##name.oid_children #ifndef NO_SYSCTL_DESCR #define __DESCR(d) d #else #define __DESCR(d) "" #endif /* This macro is only for internal use */ #define SYSCTL_OID_RAW(id, parent_child_head, nbr, name, kind, a1, a2, handler, fmt, descr, label) \ struct sysctl_oid id = { \ .oid_parent = (parent_child_head), \ .oid_children = SLIST_HEAD_INITIALIZER(&id.oid_children), \ .oid_number = (nbr), \ .oid_kind = (kind), \ .oid_arg1 = (a1), \ .oid_arg2 = (a2), \ .oid_name = (name), \ .oid_handler = (handler), \ .oid_fmt = (fmt), \ .oid_descr = __DESCR(descr), \ .oid_label = (label), \ }; \ DATA_SET(sysctl_set, id) /* This constructs a static "raw" MIB oid. */ #define SYSCTL_OID(parent, nbr, name, kind, a1, a2, handler, fmt, descr) \ SYSCTL_OID_WITH_LABEL(parent, nbr, name, kind, a1, a2, \ handler, fmt, descr, NULL) #define SYSCTL_OID_WITH_LABEL(parent, nbr, name, kind, a1, a2, handler, fmt, descr, label) \ static SYSCTL_OID_RAW(sysctl__##parent##_##name, \ SYSCTL_CHILDREN(&sysctl__##parent), \ nbr, #name, kind, a1, a2, handler, fmt, descr, label) /* This constructs a global "raw" MIB oid. */ #define SYSCTL_OID_GLOBAL(parent, nbr, name, kind, a1, a2, handler, fmt, descr, label) \ SYSCTL_OID_RAW(sysctl__##parent##_##name, \ SYSCTL_CHILDREN(&sysctl__##parent), \ nbr, #name, kind, a1, a2, handler, fmt, descr, label) #define SYSCTL_ADD_OID(ctx, parent, nbr, name, kind, a1, a2, handler, fmt, descr) \ sysctl_add_oid(ctx, parent, nbr, name, kind, a1, a2, handler, fmt, __DESCR(descr), NULL) /* This constructs a root node from which other nodes can hang. */ #define SYSCTL_ROOT_NODE(nbr, name, access, handler, descr) \ SYSCTL_OID_RAW(sysctl___##name, &sysctl__children, \ nbr, #name, CTLTYPE_NODE|(access), NULL, 0, \ handler, "N", descr, NULL); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_NODE) /* This constructs a node from which other oids can hang. */ #define SYSCTL_NODE(parent, nbr, name, access, handler, descr) \ SYSCTL_NODE_WITH_LABEL(parent, nbr, name, access, handler, descr, NULL) #define SYSCTL_NODE_WITH_LABEL(parent, nbr, name, access, handler, descr, label) \ SYSCTL_OID_GLOBAL(parent, nbr, name, CTLTYPE_NODE|(access), \ NULL, 0, handler, "N", descr, label); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_NODE) #define SYSCTL_ADD_NODE(ctx, parent, nbr, name, access, handler, descr) \ SYSCTL_ADD_NODE_WITH_LABEL(ctx, parent, nbr, name, access, \ handler, descr, NULL) #define SYSCTL_ADD_NODE_WITH_LABEL(ctx, parent, nbr, name, access, handler, descr, label) \ ({ \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_NODE); \ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_NODE|(access), \ NULL, 0, handler, "N", __DESCR(descr), label); \ }) #define SYSCTL_ADD_ROOT_NODE(ctx, nbr, name, access, handler, descr) \ ({ \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_NODE); \ sysctl_add_oid(ctx, &sysctl__children, nbr, name, \ CTLTYPE_NODE|(access), \ NULL, 0, handler, "N", __DESCR(descr), NULL); \ }) /* Oid for a string. len can be 0 to indicate '\0' termination. */ #define SYSCTL_STRING(parent, nbr, name, access, arg, len, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_STRING|(access), \ arg, len, sysctl_handle_string, "A", descr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_STRING) #define SYSCTL_ADD_STRING(ctx, parent, nbr, name, access, arg, len, descr) \ ({ \ char *__arg = (arg); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_STRING); \ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_STRING|(access), \ __arg, len, sysctl_handle_string, "A", __DESCR(descr), \ NULL); \ }) /* Oid for a bool. If ptr is NULL, val is returned. */ #define SYSCTL_NULL_BOOL_PTR ((bool *)NULL) #define SYSCTL_BOOL(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_U8 | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_bool, "CU", descr); \ CTASSERT(((access) & CTLTYPE) == 0 && \ sizeof(bool) == sizeof(*(ptr))) #define SYSCTL_ADD_BOOL(ctx, parent, nbr, name, access, ptr, val, descr) \ ({ \ bool *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_U8 | CTLFLAG_MPSAFE | (access), \ __ptr, val, sysctl_handle_bool, "CU", __DESCR(descr), \ NULL); \ }) /* Oid for a signed 8-bit int. If ptr is NULL, val is returned. */ #define SYSCTL_NULL_S8_PTR ((int8_t *)NULL) #define SYSCTL_S8(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_S8 | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_8, "C", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S8) && \ sizeof(int8_t) == sizeof(*(ptr))) #define SYSCTL_ADD_S8(ctx, parent, nbr, name, access, ptr, val, descr) \ ({ \ int8_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S8); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_S8 | CTLFLAG_MPSAFE | (access), \ __ptr, val, sysctl_handle_8, "C", __DESCR(descr), NULL); \ }) /* Oid for an unsigned 8-bit int. If ptr is NULL, val is returned. */ #define SYSCTL_NULL_U8_PTR ((uint8_t *)NULL) #define SYSCTL_U8(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_U8 | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_8, "CU", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U8) && \ sizeof(uint8_t) == sizeof(*(ptr))) #define SYSCTL_ADD_U8(ctx, parent, nbr, name, access, ptr, val, descr) \ ({ \ uint8_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U8); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_U8 | CTLFLAG_MPSAFE | (access), \ __ptr, val, sysctl_handle_8, "CU", __DESCR(descr), NULL); \ }) /* Oid for a signed 16-bit int. If ptr is NULL, val is returned. */ #define SYSCTL_NULL_S16_PTR ((int16_t *)NULL) #define SYSCTL_S16(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_S16 | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_16, "S", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S16) && \ sizeof(int16_t) == sizeof(*(ptr))) #define SYSCTL_ADD_S16(ctx, parent, nbr, name, access, ptr, val, descr) \ ({ \ int16_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S16); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_S16 | CTLFLAG_MPSAFE | (access), \ __ptr, val, sysctl_handle_16, "S", __DESCR(descr), NULL); \ }) /* Oid for an unsigned 16-bit int. If ptr is NULL, val is returned. */ #define SYSCTL_NULL_U16_PTR ((uint16_t *)NULL) #define SYSCTL_U16(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_U16 | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_16, "SU", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U16) && \ sizeof(uint16_t) == sizeof(*(ptr))) #define SYSCTL_ADD_U16(ctx, parent, nbr, name, access, ptr, val, descr) \ ({ \ uint16_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U16); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_U16 | CTLFLAG_MPSAFE | (access), \ __ptr, val, sysctl_handle_16, "SU", __DESCR(descr), NULL); \ }) /* Oid for a signed 32-bit int. If ptr is NULL, val is returned. */ #define SYSCTL_NULL_S32_PTR ((int32_t *)NULL) #define SYSCTL_S32(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_S32 | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_32, "I", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S32) && \ sizeof(int32_t) == sizeof(*(ptr))) #define SYSCTL_ADD_S32(ctx, parent, nbr, name, access, ptr, val, descr) \ ({ \ int32_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S32); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_S32 | CTLFLAG_MPSAFE | (access), \ __ptr, val, sysctl_handle_32, "I", __DESCR(descr), NULL); \ }) /* Oid for an unsigned 32-bit int. If ptr is NULL, val is returned. */ #define SYSCTL_NULL_U32_PTR ((uint32_t *)NULL) #define SYSCTL_U32(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_U32 | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_32, "IU", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U32) && \ sizeof(uint32_t) == sizeof(*(ptr))) #define SYSCTL_ADD_U32(ctx, parent, nbr, name, access, ptr, val, descr) \ ({ \ uint32_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U32); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_U32 | CTLFLAG_MPSAFE | (access), \ __ptr, val, sysctl_handle_32, "IU", __DESCR(descr), NULL); \ }) /* Oid for a signed 64-bit int. If ptr is NULL, val is returned. */ #define SYSCTL_NULL_S64_PTR ((int64_t *)NULL) #define SYSCTL_S64(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_S64 | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_64, "Q", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S64) && \ sizeof(int64_t) == sizeof(*(ptr))) #define SYSCTL_ADD_S64(ctx, parent, nbr, name, access, ptr, val, descr) \ ({ \ int64_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S64); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_S64 | CTLFLAG_MPSAFE | (access), \ __ptr, val, sysctl_handle_64, "Q", __DESCR(descr), NULL); \ }) /* Oid for an unsigned 64-bit int. If ptr is NULL, val is returned. */ #define SYSCTL_NULL_U64_PTR ((uint64_t *)NULL) #define SYSCTL_U64(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_U64 | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_64, "QU", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U64) && \ sizeof(uint64_t) == sizeof(*(ptr))) #define SYSCTL_ADD_U64(ctx, parent, nbr, name, access, ptr, val, descr) \ ({ \ uint64_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U64); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_U64 | CTLFLAG_MPSAFE | (access), \ __ptr, val, sysctl_handle_64, "QU", __DESCR(descr), NULL); \ }) /* Oid for an int. If ptr is SYSCTL_NULL_INT_PTR, val is returned. */ #define SYSCTL_NULL_INT_PTR ((int *)NULL) #define SYSCTL_INT(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_INT_WITH_LABEL(parent, nbr, name, access, ptr, val, descr, NULL) #define SYSCTL_INT_WITH_LABEL(parent, nbr, name, access, ptr, val, descr, label) \ SYSCTL_OID_WITH_LABEL(parent, nbr, name, \ CTLTYPE_INT | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_int, "I", descr, label); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_INT) && \ sizeof(int) == sizeof(*(ptr))) #define SYSCTL_ADD_INT(ctx, parent, nbr, name, access, ptr, val, descr) \ ({ \ int *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_INT); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_INT | CTLFLAG_MPSAFE | (access), \ __ptr, val, sysctl_handle_int, "I", __DESCR(descr), NULL); \ }) /* Oid for an unsigned int. If ptr is NULL, val is returned. */ #define SYSCTL_NULL_UINT_PTR ((unsigned *)NULL) #define SYSCTL_UINT(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_UINT | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_int, "IU", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_UINT) && \ sizeof(unsigned) == sizeof(*(ptr))) #define SYSCTL_ADD_UINT(ctx, parent, nbr, name, access, ptr, val, descr) \ ({ \ unsigned *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_UINT); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_UINT | CTLFLAG_MPSAFE | (access), \ __ptr, val, sysctl_handle_int, "IU", __DESCR(descr), NULL); \ }) /* Oid for a long. The pointer must be non NULL. */ #define SYSCTL_NULL_LONG_PTR ((long *)NULL) #define SYSCTL_LONG(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_LONG | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_long, "L", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_LONG) && \ sizeof(long) == sizeof(*(ptr))) #define SYSCTL_ADD_LONG(ctx, parent, nbr, name, access, ptr, descr) \ ({ \ long *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_LONG); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_LONG | CTLFLAG_MPSAFE | (access), \ __ptr, 0, sysctl_handle_long, "L", __DESCR(descr), NULL); \ }) /* Oid for an unsigned long. The pointer must be non NULL. */ #define SYSCTL_NULL_ULONG_PTR ((unsigned long *)NULL) #define SYSCTL_ULONG(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_ULONG | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_long, "LU", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_ULONG) && \ sizeof(unsigned long) == sizeof(*(ptr))) #define SYSCTL_ADD_ULONG(ctx, parent, nbr, name, access, ptr, descr) \ ({ \ unsigned long *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_ULONG); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_ULONG | CTLFLAG_MPSAFE | (access), \ __ptr, 0, sysctl_handle_long, "LU", __DESCR(descr), NULL); \ }) /* Oid for a quad. The pointer must be non NULL. */ #define SYSCTL_NULL_QUAD_PTR ((int64_t *)NULL) #define SYSCTL_QUAD(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_S64 | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_64, "Q", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S64) && \ sizeof(int64_t) == sizeof(*(ptr))) #define SYSCTL_ADD_QUAD(ctx, parent, nbr, name, access, ptr, descr) \ ({ \ int64_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S64); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_S64 | CTLFLAG_MPSAFE | (access), \ __ptr, 0, sysctl_handle_64, "Q", __DESCR(descr), NULL); \ }) #define SYSCTL_NULL_UQUAD_PTR ((uint64_t *)NULL) #define SYSCTL_UQUAD(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_U64 | CTLFLAG_MPSAFE | (access), \ ptr, val, sysctl_handle_64, "QU", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U64) && \ sizeof(uint64_t) == sizeof(*(ptr))) #define SYSCTL_ADD_UQUAD(ctx, parent, nbr, name, access, ptr, descr) \ ({ \ uint64_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U64); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_U64 | CTLFLAG_MPSAFE | (access), \ __ptr, 0, sysctl_handle_64, "QU", __DESCR(descr), NULL); \ }) /* Oid for a CPU dependent variable */ #define SYSCTL_ADD_UAUTO(ctx, parent, nbr, name, access, ptr, descr) \ ({ \ struct sysctl_oid *__ret; \ CTASSERT((sizeof(uint64_t) == sizeof(*(ptr)) || \ sizeof(unsigned) == sizeof(*(ptr))) && \ ((access) & CTLTYPE) == 0); \ if (sizeof(uint64_t) == sizeof(*(ptr))) { \ __ret = sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_U64 | CTLFLAG_MPSAFE | (access), \ (ptr), 0, sysctl_handle_64, "QU", \ __DESCR(descr), NULL); \ } else { \ __ret = sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_UINT | CTLFLAG_MPSAFE | (access), \ (ptr), 0, sysctl_handle_int, "IU", \ __DESCR(descr), NULL); \ } \ __ret; \ }) /* Oid for a 64-bit unsigned counter(9). The pointer must be non NULL. */ #define SYSCTL_COUNTER_U64(parent, nbr, name, access, ptr, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_U64 | CTLFLAG_MPSAFE | (access), \ (ptr), 0, sysctl_handle_counter_u64, "QU", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U64) && \ sizeof(counter_u64_t) == sizeof(*(ptr)) && \ sizeof(uint64_t) == sizeof(**(ptr))) #define SYSCTL_ADD_COUNTER_U64(ctx, parent, nbr, name, access, ptr, descr) \ ({ \ counter_u64_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U64); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_U64 | CTLFLAG_MPSAFE | (access), \ __ptr, 0, sysctl_handle_counter_u64, "QU", __DESCR(descr), \ NULL); \ }) /* Oid for an array of counter(9)s. The pointer and length must be non zero. */ #define SYSCTL_COUNTER_U64_ARRAY(parent, nbr, name, access, ptr, len, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_OPAQUE | CTLFLAG_MPSAFE | (access), \ (ptr), (len), sysctl_handle_counter_u64_array, "S", descr); \ CTASSERT((((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE) && \ sizeof(counter_u64_t) == sizeof(*(ptr)) && \ sizeof(uint64_t) == sizeof(**(ptr))) #define SYSCTL_ADD_COUNTER_U64_ARRAY(ctx, parent, nbr, name, access, \ ptr, len, descr) \ ({ \ counter_u64_t *__ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_OPAQUE | CTLFLAG_MPSAFE | (access), \ __ptr, len, sysctl_handle_counter_u64_array, "S", \ __DESCR(descr), NULL); \ }) /* Oid for an opaque object. Specified by a pointer and a length. */ #define SYSCTL_OPAQUE(parent, nbr, name, access, ptr, len, fmt, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_OPAQUE|(access), \ ptr, len, sysctl_handle_opaque, fmt, descr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE) #define SYSCTL_ADD_OPAQUE(ctx, parent, nbr, name, access, ptr, len, fmt, descr) \ ({ \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE); \ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_OPAQUE|(access), \ ptr, len, sysctl_handle_opaque, fmt, __DESCR(descr), NULL); \ }) /* Oid for a struct. Specified by a pointer and a type. */ #define SYSCTL_STRUCT(parent, nbr, name, access, ptr, type, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_OPAQUE|(access), \ ptr, sizeof(struct type), sysctl_handle_opaque, \ "S," #type, descr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE) #define SYSCTL_ADD_STRUCT(ctx, parent, nbr, name, access, ptr, type, descr) \ ({ \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE); \ sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_OPAQUE|(access), \ (ptr), sizeof(struct type), \ sysctl_handle_opaque, "S," #type, __DESCR(descr), NULL); \ }) /* Oid for a procedure. Specified by a pointer and an arg. */ #define SYSCTL_PROC(parent, nbr, name, access, ptr, arg, handler, fmt, descr) \ SYSCTL_OID(parent, nbr, name, (access), \ ptr, arg, handler, fmt, descr); \ CTASSERT(((access) & CTLTYPE) != 0) #define SYSCTL_ADD_PROC(ctx, parent, nbr, name, access, ptr, arg, handler, fmt, descr) \ ({ \ CTASSERT(((access) & CTLTYPE) != 0); \ sysctl_add_oid(ctx, parent, nbr, name, (access), \ (ptr), (arg), (handler), (fmt), __DESCR(descr), NULL); \ }) /* Oid to handle limits on uma(9) zone specified by pointer. */ #define SYSCTL_UMA_MAX(parent, nbr, name, access, ptr, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_INT | CTLFLAG_MPSAFE | (access), \ (ptr), 0, sysctl_handle_uma_zone_max, "I", descr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_INT) #define SYSCTL_ADD_UMA_MAX(ctx, parent, nbr, name, access, ptr, descr) \ ({ \ uma_zone_t __ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_INT); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_INT | CTLFLAG_MPSAFE | (access), \ __ptr, 0, sysctl_handle_uma_zone_max, "I", __DESCR(descr), \ NULL); \ }) /* Oid to obtain current use of uma(9) zone specified by pointer. */ #define SYSCTL_UMA_CUR(parent, nbr, name, access, ptr, descr) \ SYSCTL_OID(parent, nbr, name, \ CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RD | (access), \ (ptr), 0, sysctl_handle_uma_zone_cur, "I", descr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_INT) #define SYSCTL_ADD_UMA_CUR(ctx, parent, nbr, name, access, ptr, descr) \ ({ \ uma_zone_t __ptr = (ptr); \ CTASSERT(((access) & CTLTYPE) == 0 || \ ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_INT); \ sysctl_add_oid(ctx, parent, nbr, name, \ CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RD | (access), \ __ptr, 0, sysctl_handle_uma_zone_cur, "I", __DESCR(descr), \ NULL); \ }) /* * A macro to generate a read-only sysctl to indicate the presence of optional * kernel features. */ #define FEATURE(name, desc) \ SYSCTL_INT_WITH_LABEL(_kern_features, OID_AUTO, name, \ CTLFLAG_RD | CTLFLAG_CAPRD, SYSCTL_NULL_INT_PTR, 1, desc, "feature") #endif /* _KERNEL */ /* * Top-level identifiers */ #define CTL_UNSPEC 0 /* unused */ #define CTL_KERN 1 /* "high kernel": proc, limits */ #define CTL_VM 2 /* virtual memory */ #define CTL_VFS 3 /* filesystem, mount type is next */ #define CTL_NET 4 /* network, see socket.h */ #define CTL_DEBUG 5 /* debugging parameters */ #define CTL_HW 6 /* generic cpu/io */ #define CTL_MACHDEP 7 /* machine dependent */ #define CTL_USER 8 /* user-level */ #define CTL_P1003_1B 9 /* POSIX 1003.1B */ /* * CTL_KERN identifiers */ #define KERN_OSTYPE 1 /* string: system version */ #define KERN_OSRELEASE 2 /* string: system release */ #define KERN_OSREV 3 /* int: system revision */ #define KERN_VERSION 4 /* string: compile time info */ #define KERN_MAXVNODES 5 /* int: max vnodes */ #define KERN_MAXPROC 6 /* int: max processes */ #define KERN_MAXFILES 7 /* int: max open files */ #define KERN_ARGMAX 8 /* int: max arguments to exec */ #define KERN_SECURELVL 9 /* int: system security level */ #define KERN_HOSTNAME 10 /* string: hostname */ #define KERN_HOSTID 11 /* int: host identifier */ #define KERN_CLOCKRATE 12 /* struct: struct clockrate */ #define KERN_VNODE 13 /* struct: vnode structures */ #define KERN_PROC 14 /* struct: process entries */ #define KERN_FILE 15 /* struct: file entries */ #define KERN_PROF 16 /* node: kernel profiling info */ #define KERN_POSIX1 17 /* int: POSIX.1 version */ #define KERN_NGROUPS 18 /* int: # of supplemental group ids */ #define KERN_JOB_CONTROL 19 /* int: is job control available */ #define KERN_SAVED_IDS 20 /* int: saved set-user/group-ID */ #define KERN_BOOTTIME 21 /* struct: time kernel was booted */ #define KERN_NISDOMAINNAME 22 /* string: YP domain name */ #define KERN_UPDATEINTERVAL 23 /* int: update process sleep time */ #define KERN_OSRELDATE 24 /* int: kernel release date */ #define KERN_NTP_PLL 25 /* node: NTP PLL control */ #define KERN_BOOTFILE 26 /* string: name of booted kernel */ #define KERN_MAXFILESPERPROC 27 /* int: max open files per proc */ #define KERN_MAXPROCPERUID 28 /* int: max processes per uid */ #define KERN_DUMPDEV 29 /* struct cdev *: device to dump on */ #define KERN_IPC 30 /* node: anything related to IPC */ #define KERN_DUMMY 31 /* unused */ #define KERN_PS_STRINGS 32 /* int: address of PS_STRINGS */ #define KERN_USRSTACK 33 /* int: address of USRSTACK */ #define KERN_LOGSIGEXIT 34 /* int: do we log sigexit procs? */ #define KERN_IOV_MAX 35 /* int: value of UIO_MAXIOV */ #define KERN_HOSTUUID 36 /* string: host UUID identifier */ #define KERN_ARND 37 /* int: from arc4rand() */ #define KERN_MAXPHYS 38 /* int: MAXPHYS value */ /* * KERN_PROC subtypes */ #define KERN_PROC_ALL 0 /* everything */ #define KERN_PROC_PID 1 /* by process id */ #define KERN_PROC_PGRP 2 /* by process group id */ #define KERN_PROC_SESSION 3 /* by session of pid */ #define KERN_PROC_TTY 4 /* by controlling tty */ #define KERN_PROC_UID 5 /* by effective uid */ #define KERN_PROC_RUID 6 /* by real uid */ #define KERN_PROC_ARGS 7 /* get/set arguments/proctitle */ #define KERN_PROC_PROC 8 /* only return procs */ #define KERN_PROC_SV_NAME 9 /* get syscall vector name */ #define KERN_PROC_RGID 10 /* by real group id */ #define KERN_PROC_GID 11 /* by effective group id */ #define KERN_PROC_PATHNAME 12 /* path to executable */ #define KERN_PROC_OVMMAP 13 /* Old VM map entries for process */ #define KERN_PROC_OFILEDESC 14 /* Old file descriptors for process */ #define KERN_PROC_KSTACK 15 /* Kernel stacks for process */ #define KERN_PROC_INC_THREAD 0x10 /* * modifier for pid, pgrp, tty, * uid, ruid, gid, rgid and proc * This effectively uses 16-31 */ #define KERN_PROC_VMMAP 32 /* VM map entries for process */ #define KERN_PROC_FILEDESC 33 /* File descriptors for process */ #define KERN_PROC_GROUPS 34 /* process groups */ #define KERN_PROC_ENV 35 /* get environment */ #define KERN_PROC_AUXV 36 /* get ELF auxiliary vector */ #define KERN_PROC_RLIMIT 37 /* process resource limits */ #define KERN_PROC_PS_STRINGS 38 /* get ps_strings location */ #define KERN_PROC_UMASK 39 /* process umask */ #define KERN_PROC_OSREL 40 /* osreldate for process binary */ #define KERN_PROC_SIGTRAMP 41 /* signal trampoline location */ #define KERN_PROC_CWD 42 /* process current working directory */ #define KERN_PROC_NFDS 43 /* number of open file descriptors */ /* * KERN_IPC identifiers */ #define KIPC_MAXSOCKBUF 1 /* int: max size of a socket buffer */ #define KIPC_SOCKBUF_WASTE 2 /* int: wastage factor in sockbuf */ #define KIPC_SOMAXCONN 3 /* int: max length of connection q */ #define KIPC_MAX_LINKHDR 4 /* int: max length of link header */ #define KIPC_MAX_PROTOHDR 5 /* int: max length of network header */ #define KIPC_MAX_HDR 6 /* int: max total length of headers */ #define KIPC_MAX_DATALEN 7 /* int: max length of data? */ /* * CTL_HW identifiers */ #define HW_MACHINE 1 /* string: machine class */ #define HW_MODEL 2 /* string: specific machine model */ #define HW_NCPU 3 /* int: number of cpus */ #define HW_BYTEORDER 4 /* int: machine byte order */ #define HW_PHYSMEM 5 /* int: total memory */ #define HW_USERMEM 6 /* int: non-kernel memory */ #define HW_PAGESIZE 7 /* int: software page size */ #define HW_DISKNAMES 8 /* strings: disk drive names */ #define HW_DISKSTATS 9 /* struct: diskstats[] */ #define HW_FLOATINGPT 10 /* int: has HW floating point? */ #define HW_MACHINE_ARCH 11 /* string: machine architecture */ #define HW_REALMEM 12 /* int: 'real' memory */ /* * CTL_USER definitions */ #define USER_CS_PATH 1 /* string: _CS_PATH */ #define USER_BC_BASE_MAX 2 /* int: BC_BASE_MAX */ #define USER_BC_DIM_MAX 3 /* int: BC_DIM_MAX */ #define USER_BC_SCALE_MAX 4 /* int: BC_SCALE_MAX */ #define USER_BC_STRING_MAX 5 /* int: BC_STRING_MAX */ #define USER_COLL_WEIGHTS_MAX 6 /* int: COLL_WEIGHTS_MAX */ #define USER_EXPR_NEST_MAX 7 /* int: EXPR_NEST_MAX */ #define USER_LINE_MAX 8 /* int: LINE_MAX */ #define USER_RE_DUP_MAX 9 /* int: RE_DUP_MAX */ #define USER_POSIX2_VERSION 10 /* int: POSIX2_VERSION */ #define USER_POSIX2_C_BIND 11 /* int: POSIX2_C_BIND */ #define USER_POSIX2_C_DEV 12 /* int: POSIX2_C_DEV */ #define USER_POSIX2_CHAR_TERM 13 /* int: POSIX2_CHAR_TERM */ #define USER_POSIX2_FORT_DEV 14 /* int: POSIX2_FORT_DEV */ #define USER_POSIX2_FORT_RUN 15 /* int: POSIX2_FORT_RUN */ #define USER_POSIX2_LOCALEDEF 16 /* int: POSIX2_LOCALEDEF */ #define USER_POSIX2_SW_DEV 17 /* int: POSIX2_SW_DEV */ #define USER_POSIX2_UPE 18 /* int: POSIX2_UPE */ #define USER_STREAM_MAX 19 /* int: POSIX2_STREAM_MAX */ #define USER_TZNAME_MAX 20 /* int: POSIX2_TZNAME_MAX */ #define CTL_P1003_1B_ASYNCHRONOUS_IO 1 /* boolean */ #define CTL_P1003_1B_MAPPED_FILES 2 /* boolean */ #define CTL_P1003_1B_MEMLOCK 3 /* boolean */ #define CTL_P1003_1B_MEMLOCK_RANGE 4 /* boolean */ #define CTL_P1003_1B_MEMORY_PROTECTION 5 /* boolean */ #define CTL_P1003_1B_MESSAGE_PASSING 6 /* boolean */ #define CTL_P1003_1B_PRIORITIZED_IO 7 /* boolean */ #define CTL_P1003_1B_PRIORITY_SCHEDULING 8 /* boolean */ #define CTL_P1003_1B_REALTIME_SIGNALS 9 /* boolean */ #define CTL_P1003_1B_SEMAPHORES 10 /* boolean */ #define CTL_P1003_1B_FSYNC 11 /* boolean */ #define CTL_P1003_1B_SHARED_MEMORY_OBJECTS 12 /* boolean */ #define CTL_P1003_1B_SYNCHRONIZED_IO 13 /* boolean */ #define CTL_P1003_1B_TIMERS 14 /* boolean */ #define CTL_P1003_1B_AIO_LISTIO_MAX 15 /* int */ #define CTL_P1003_1B_AIO_MAX 16 /* int */ #define CTL_P1003_1B_AIO_PRIO_DELTA_MAX 17 /* int */ #define CTL_P1003_1B_DELAYTIMER_MAX 18 /* int */ #define CTL_P1003_1B_MQ_OPEN_MAX 19 /* int */ #define CTL_P1003_1B_PAGESIZE 20 /* int */ #define CTL_P1003_1B_RTSIG_MAX 21 /* int */ #define CTL_P1003_1B_SEM_NSEMS_MAX 22 /* int */ #define CTL_P1003_1B_SEM_VALUE_MAX 23 /* int */ #define CTL_P1003_1B_SIGQUEUE_MAX 24 /* int */ #define CTL_P1003_1B_TIMER_MAX 25 /* int */ #define CTL_P1003_1B_MAXID 26 #ifdef _KERNEL /* * Declare some common oids. */ extern struct sysctl_oid_list sysctl__children; SYSCTL_DECL(_kern); SYSCTL_DECL(_kern_features); SYSCTL_DECL(_kern_ipc); SYSCTL_DECL(_kern_proc); SYSCTL_DECL(_kern_sched); SYSCTL_DECL(_kern_sched_stats); SYSCTL_DECL(_sysctl); SYSCTL_DECL(_vm); SYSCTL_DECL(_vm_stats); SYSCTL_DECL(_vm_stats_misc); SYSCTL_DECL(_vfs); SYSCTL_DECL(_net); SYSCTL_DECL(_debug); SYSCTL_DECL(_debug_sizeof); SYSCTL_DECL(_dev); SYSCTL_DECL(_hw); SYSCTL_DECL(_hw_bus); SYSCTL_DECL(_hw_bus_devices); SYSCTL_DECL(_hw_bus_info); SYSCTL_DECL(_machdep); SYSCTL_DECL(_user); SYSCTL_DECL(_compat); SYSCTL_DECL(_regression); SYSCTL_DECL(_security); SYSCTL_DECL(_security_bsd); extern char machine[]; extern char osrelease[]; extern char ostype[]; extern char kern_ident[]; /* Dynamic oid handling */ struct sysctl_oid *sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent, int nbr, const char *name, int kind, void *arg1, intmax_t arg2, int (*handler)(SYSCTL_HANDLER_ARGS), const char *fmt, const char *descr, const char *label); int sysctl_remove_name(struct sysctl_oid *parent, const char *name, int del, int recurse); void sysctl_rename_oid(struct sysctl_oid *oidp, const char *name); int sysctl_move_oid(struct sysctl_oid *oidp, struct sysctl_oid_list *parent); int sysctl_remove_oid(struct sysctl_oid *oidp, int del, int recurse); int sysctl_ctx_init(struct sysctl_ctx_list *clist); int sysctl_ctx_free(struct sysctl_ctx_list *clist); struct sysctl_ctx_entry *sysctl_ctx_entry_add(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp); struct sysctl_ctx_entry *sysctl_ctx_entry_find(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp); int sysctl_ctx_entry_del(struct sysctl_ctx_list *clist, struct sysctl_oid *oidp); int kernel_sysctl(struct thread *td, int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen, size_t *retval, int flags); int kernel_sysctlbyname(struct thread *td, char *name, void *old, size_t *oldlenp, void *new, size_t newlen, size_t *retval, int flags); int userland_sysctl(struct thread *td, int *name, u_int namelen, void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen, size_t *retval, int flags); int sysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid, int *nindx, struct sysctl_req *req); void sysctl_wlock(void); void sysctl_wunlock(void); int sysctl_wire_old_buffer(struct sysctl_req *req, size_t len); struct sbuf; struct sbuf *sbuf_new_for_sysctl(struct sbuf *, char *, int, struct sysctl_req *); #else /* !_KERNEL */ #include __BEGIN_DECLS int sysctl(const int *, u_int, void *, size_t *, const void *, size_t); int sysctlbyname(const char *, void *, size_t *, const void *, size_t); int sysctlnametomib(const char *, int *, size_t *); __END_DECLS #endif /* _KERNEL */ #endif /* !_SYS_SYSCTL_H_ */ Index: head/usr.bin/systat/ifstat.c =================================================================== --- head/usr.bin/systat/ifstat.c (revision 333474) +++ head/usr.bin/systat/ifstat.c (revision 333475) @@ -1,498 +1,499 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2003, Trent Nelson, . * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 INTIFSTAT_ERRUPTION) * 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. * * $FreeBSD$ */ #include #include +#include #include #include #include #include #include #include #include #include #include "systat.h" #include "extern.h" #include "convtbl.h" /* Column numbers */ #define C1 0 /* 0-19 */ #define C2 20 /* 20-39 */ #define C3 40 /* 40-59 */ #define C4 60 /* 60-80 */ #define C5 80 /* Used for label positioning. */ static const int col0 = 0; static const int col1 = C1; static const int col2 = C2; static const int col3 = C3; static const int col4 = C4; static const int col5 = C5; SLIST_HEAD(, if_stat) curlist; SLIST_HEAD(, if_stat_disp) displist; struct if_stat { SLIST_ENTRY(if_stat) link; char if_name[IF_NAMESIZE]; struct ifmibdata if_mib; struct timeval tv; struct timeval tv_lastchanged; uint64_t if_in_curtraffic; uint64_t if_out_curtraffic; uint64_t if_in_traffic_peak; uint64_t if_out_traffic_peak; uint64_t if_in_curpps; uint64_t if_out_curpps; uint64_t if_in_pps_peak; uint64_t if_out_pps_peak; u_int if_row; /* Index into ifmib sysctl */ int if_ypos; /* -1 if not being displayed */ u_int display; u_int match; }; extern int curscale; extern char *matchline; extern int showpps; extern int needsort; static int needclear = 0; static void right_align_string(struct if_stat *); static void getifmibdata(const int, struct ifmibdata *); static void sort_interface_list(void); static u_int getifnum(void); #define IFSTAT_ERR(n, s) do { \ putchar('\014'); \ closeifstat(wnd); \ err((n), (s)); \ } while (0) #define TOPLINE 3 #define TOPLABEL \ " Interface Traffic Peak Total" #define STARTING_ROW (TOPLINE + 1) #define ROW_SPACING (3) #define IN_col2 (showpps ? ifp->if_in_curpps : ifp->if_in_curtraffic) #define OUT_col2 (showpps ? ifp->if_out_curpps : ifp->if_out_curtraffic) #define IN_col3 (showpps ? \ ifp->if_in_pps_peak : ifp->if_in_traffic_peak) #define OUT_col3 (showpps ? \ ifp->if_out_pps_peak : ifp->if_out_traffic_peak) #define IN_col4 (showpps ? \ ifp->if_mib.ifmd_data.ifi_ipackets : ifp->if_mib.ifmd_data.ifi_ibytes) #define OUT_col4 (showpps ? \ ifp->if_mib.ifmd_data.ifi_opackets : ifp->if_mib.ifmd_data.ifi_obytes) #define EMPTY_COLUMN " " #define CLEAR_COLUMN(y, x) mvprintw((y), (x), "%20s", EMPTY_COLUMN); #define DOPUTRATE(c, r, d) do { \ CLEAR_COLUMN(r, c); \ if (showpps) { \ mvprintw(r, (c), "%10.3f %cp%s ", \ convert(d##_##c, curscale), \ *get_string(d##_##c, curscale), \ "/s"); \ } \ else { \ mvprintw(r, (c), "%10.3f %s%s ", \ convert(d##_##c, curscale), \ get_string(d##_##c, curscale), \ "/s"); \ } \ } while (0) #define DOPUTTOTAL(c, r, d) do { \ CLEAR_COLUMN((r), (c)); \ if (showpps) { \ mvprintw((r), (c), "%12.3f %cp ", \ convert(d##_##c, SC_AUTO), \ *get_string(d##_##c, SC_AUTO)); \ } \ else { \ mvprintw((r), (c), "%12.3f %s ", \ convert(d##_##c, SC_AUTO), \ get_string(d##_##c, SC_AUTO)); \ } \ } while (0) #define PUTRATE(c, r) do { \ DOPUTRATE(c, (r), IN); \ DOPUTRATE(c, (r)+1, OUT); \ } while (0) #define PUTTOTAL(c, r) do { \ DOPUTTOTAL(c, (r), IN); \ DOPUTTOTAL(c, (r)+1, OUT); \ } while (0) #define PUTNAME(p) do { \ mvprintw(p->if_ypos, 0, "%s", p->if_name); \ mvprintw(p->if_ypos, col2-3, "%s", (const char *)"in"); \ mvprintw(p->if_ypos+1, col2-3, "%s", (const char *)"out"); \ } while (0) WINDOW * openifstat(void) { return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0)); } void closeifstat(WINDOW *w) { struct if_stat *node = NULL; while (!SLIST_EMPTY(&curlist)) { node = SLIST_FIRST(&curlist); SLIST_REMOVE_HEAD(&curlist, link); free(node); } if (w != NULL) { wclear(w); wrefresh(w); delwin(w); } return; } void labelifstat(void) { wmove(wnd, TOPLINE, 0); wclrtoeol(wnd); mvprintw(TOPLINE, 0, "%s", TOPLABEL); return; } void showifstat(void) { struct if_stat *ifp = NULL; SLIST_FOREACH(ifp, &curlist, link) { if (ifp->if_ypos < LINES - 3 && ifp->if_ypos != -1) if (ifp->display == 0 || ifp->match == 0) { wmove(wnd, ifp->if_ypos, 0); wclrtoeol(wnd); wmove(wnd, ifp->if_ypos + 1, 0); wclrtoeol(wnd); } else { PUTNAME(ifp); PUTRATE(col2, ifp->if_ypos); PUTRATE(col3, ifp->if_ypos); PUTTOTAL(col4, ifp->if_ypos); } } return; } int initifstat(void) { struct if_stat *p = NULL; u_int n = 0, i = 0; n = getifnum(); if (n <= 0) return (-1); SLIST_INIT(&curlist); for (i = 0; i < n; i++) { p = (struct if_stat *)calloc(1, sizeof(struct if_stat)); if (p == NULL) IFSTAT_ERR(1, "out of memory"); SLIST_INSERT_HEAD(&curlist, p, link); p->if_row = i+1; getifmibdata(p->if_row, &p->if_mib); right_align_string(p); p->match = 1; /* * Initially, we only display interfaces that have * received some traffic. */ if (p->if_mib.ifmd_data.ifi_ibytes != 0) p->display = 1; } sort_interface_list(); return (1); } void fetchifstat(void) { struct if_stat *ifp = NULL; struct timeval tv, new_tv, old_tv; double elapsed = 0.0; uint64_t new_inb, new_outb, old_inb, old_outb = 0; uint64_t new_inp, new_outp, old_inp, old_outp = 0; SLIST_FOREACH(ifp, &curlist, link) { /* * Grab a copy of the old input/output values before we * call getifmibdata(). */ old_inb = ifp->if_mib.ifmd_data.ifi_ibytes; old_outb = ifp->if_mib.ifmd_data.ifi_obytes; old_inp = ifp->if_mib.ifmd_data.ifi_ipackets; old_outp = ifp->if_mib.ifmd_data.ifi_opackets; ifp->tv_lastchanged = ifp->if_mib.ifmd_data.ifi_lastchange; (void)gettimeofday(&new_tv, NULL); (void)getifmibdata(ifp->if_row, &ifp->if_mib); new_inb = ifp->if_mib.ifmd_data.ifi_ibytes; new_outb = ifp->if_mib.ifmd_data.ifi_obytes; new_inp = ifp->if_mib.ifmd_data.ifi_ipackets; new_outp = ifp->if_mib.ifmd_data.ifi_opackets; /* Display interface if it's received some traffic. */ if (new_inb > 0 && old_inb == 0) { ifp->display = 1; needsort = 1; } /* * The rest is pretty trivial. Calculate the new values * for our current traffic rates, and while we're there, * see if we have new peak rates. */ old_tv = ifp->tv; timersub(&new_tv, &old_tv, &tv); elapsed = tv.tv_sec + (tv.tv_usec * 1e-6); ifp->if_in_curtraffic = new_inb - old_inb; ifp->if_out_curtraffic = new_outb - old_outb; ifp->if_in_curpps = new_inp - old_inp; ifp->if_out_curpps = new_outp - old_outp; /* * Rather than divide by the time specified on the comm- * and line, we divide by ``elapsed'' as this is likely * to be more accurate. */ ifp->if_in_curtraffic /= elapsed; ifp->if_out_curtraffic /= elapsed; ifp->if_in_curpps /= elapsed; ifp->if_out_curpps /= elapsed; if (ifp->if_in_curtraffic > ifp->if_in_traffic_peak) ifp->if_in_traffic_peak = ifp->if_in_curtraffic; if (ifp->if_out_curtraffic > ifp->if_out_traffic_peak) ifp->if_out_traffic_peak = ifp->if_out_curtraffic; if (ifp->if_in_curpps > ifp->if_in_pps_peak) ifp->if_in_pps_peak = ifp->if_in_curpps; if (ifp->if_out_curpps > ifp->if_out_pps_peak) ifp->if_out_pps_peak = ifp->if_out_curpps; ifp->tv.tv_sec = new_tv.tv_sec; ifp->tv.tv_usec = new_tv.tv_usec; } if (needsort) sort_interface_list(); return; } /* * We want to right justify our interface names against the first column * (first sixteen or so characters), so we need to do some alignment. */ static void right_align_string(struct if_stat *ifp) { int str_len = 0, pad_len = 0; char *newstr = NULL, *ptr = NULL; if (ifp == NULL || ifp->if_mib.ifmd_name == NULL) return; else { /* string length + '\0' */ str_len = strlen(ifp->if_mib.ifmd_name)+1; pad_len = IF_NAMESIZE-(str_len); newstr = ifp->if_name; ptr = newstr + pad_len; (void)memset((void *)newstr, (int)' ', IF_NAMESIZE); (void)strncpy(ptr, (const char *)&ifp->if_mib.ifmd_name, str_len); } return; } static int check_match(const char *ifname) { char *p = matchline, *c, t; int match = 0, mlen; if (matchline == NULL) return (0); /* Strip leading whitespaces */ while (*p == ' ') p ++; c = p; while ((mlen = strcspn(c, " ;,")) != 0) { p = c + mlen; t = *p; if (p - c > 0) { *p = '\0'; if (fnmatch(c, ifname, FNM_CASEFOLD) == 0) { *p = t; return (1); } *p = t; c = p + strspn(p, " ;,"); } else { c = p + strspn(p, " ;,"); } } return (match); } /* * This function iterates through our list of interfaces, identifying * those that are to be displayed (ifp->display = 1). For each interf- * rface that we're displaying, we generate an appropriate position for * it on the screen (ifp->if_ypos). * * This function is called any time a change is made to an interface's * ``display'' state. */ void sort_interface_list(void) { struct if_stat *ifp = NULL; u_int y = 0; y = STARTING_ROW; SLIST_FOREACH(ifp, &curlist, link) { if (matchline && !check_match(ifp->if_mib.ifmd_name)) ifp->match = 0; else ifp->match = 1; if (ifp->display && ifp->match) { ifp->if_ypos = y; y += ROW_SPACING; } else ifp->if_ypos = -1; } needsort = 0; needclear = 1; } static unsigned int getifnum(void) { u_int data = 0; size_t datalen = 0; static int name[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_SYSTEM, IFMIB_IFCOUNT }; datalen = sizeof(data); if (sysctl(name, 5, (void *)&data, (size_t *)&datalen, (void *)NULL, (size_t)0) != 0) IFSTAT_ERR(1, "sysctl error"); return (data); } static void getifmibdata(int row, struct ifmibdata *data) { size_t datalen = 0; static int name[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, 0, IFDATA_GENERAL }; datalen = sizeof(*data); name[4] = row; if ((sysctl(name, 6, (void *)data, (size_t *)&datalen, (void *)NULL, (size_t)0) != 0) && (errno != ENOENT)) IFSTAT_ERR(2, "sysctl error getting interface data"); } int cmdifstat(const char *cmd, const char *args) { int retval = 0; retval = ifcmd(cmd, args); /* ifcmd() returns 1 on success */ if (retval == 1) { if (needclear) { showifstat(); refresh(); werase(wnd); labelifstat(); needclear = 0; } } return (retval); } Index: head/usr.bin/systat/tcp.c =================================================================== --- head/usr.bin/systat/tcp.c (revision 333474) +++ head/usr.bin/systat/tcp.c (revision 333475) @@ -1,328 +1,329 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1992, 1993 * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. */ #if 0 #ifndef lint /* From: */ static char sccsid[] = "@(#)mbufs.c 8.1 (Berkeley) 6/6/93"; static const char rcsid[] = "Id: mbufs.c,v 1.5 1997/02/24 20:59:03 wollman Exp"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "systat.h" #include "extern.h" #include "mode.h" static struct tcpstat curstat, initstat, oldstat; /*- --0 1 2 3 4 5 6 7 --0123456789012345678901234567890123456789012345678901234567890123456789012345 00 TCP Connections TCP Packets 01999999999999 connections initiated 999999999999 total packets sent 02999999999999 connections accepted 999999999999 - data 03999999999999 connections established 999999999999 - data (retransmit by dupack) 04999999999999 connections dropped 999999999999 - data (retransmit by sack) 05999999999999 - in embryonic state 999999999999 - ack-only 06999999999999 - on retransmit timeout 999999999999 - window probes 07999999999999 - by keepalive 999999999999 - window updates 08999999999999 - from listen queue 999999999999 - urgent data only 09 999999999999 - control 10 999999999999 - resends by PMTU discovery 11 TCP Timers 999999999999 total packets received 12999999999999 potential rtt updates 999999999999 - in sequence 13999999999999 - successful 999999999999 - completely duplicate 14999999999999 delayed acks sent 999999999999 - with some duplicate data 15999999999999 retransmit timeouts 999999999999 - out-of-order 16999999999999 persist timeouts 999999999999 - duplicate acks 17999999999999 keepalive probes 999999999999 - acks 18999999999999 - timeouts 999999999999 - window probes 19 999999999999 - window updates 20 999999999999 - bad checksum --0123456789012345678901234567890123456789012345678901234567890123456789012345 --0 1 2 3 4 5 6 7 */ WINDOW * opentcp(void) { return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0)); } void closetcp(WINDOW *w) { if (w == NULL) return; wclear(w); wrefresh(w); delwin(w); } void labeltcp(void) { wmove(wnd, 0, 0); wclrtoeol(wnd); #define L(row, str) mvwprintw(wnd, row, 13, str) #define R(row, str) mvwprintw(wnd, row, 51, str); L(0, "TCP Connections"); R(0, "TCP Packets"); L(1, "connections initiated"); R(1, "total packets sent"); L(2, "connections accepted"); R(2, "- data"); L(3, "connections established"); R(3, "- data (retransmit by dupack)"); L(4, "connections dropped"); R(4, "- data (retransmit by sack)"); L(5, "- in embryonic state"); R(5, "- ack-only"); L(6, "- on retransmit timeout"); R(6, "- window probes"); L(7, "- by keepalive"); R(7, "- window updates"); L(8, "- from listen queue"); R(8, "- urgent data only"); R(9, "- control"); R(10, "- resends by PMTU discovery"); L(11, "TCP Timers"); R(11, "total packets received"); L(12, "potential rtt updates"); R(12, "- in sequence"); L(13, "- successful"); R(13, "- completely duplicate"); L(14, "delayed acks sent"); R(14, "- with some duplicate data"); L(15, "retransmit timeouts"); R(15, "- out-of-order"); L(16, "persist timeouts"); R(16, "- duplicate acks"); L(17, "keepalive probes"); R(17, "- acks"); L(18, "- timeouts"); R(18, "- window probes"); R(19, "- window updates"); R(20, "- bad checksum"); #undef L #undef R } static void domode(struct tcpstat *ret) { const struct tcpstat *sub; int divisor = 1; switch(currentmode) { case display_RATE: sub = &oldstat; divisor = (delay > 1000000) ? delay / 1000000 : 1; break; case display_DELTA: sub = &oldstat; break; case display_SINCE: sub = &initstat; break; default: *ret = curstat; return; } #define DO(stat) ret->stat = (curstat.stat - sub->stat) / divisor DO(tcps_connattempt); DO(tcps_accepts); DO(tcps_connects); DO(tcps_drops); DO(tcps_conndrops); DO(tcps_closed); DO(tcps_segstimed); DO(tcps_rttupdated); DO(tcps_delack); DO(tcps_timeoutdrop); DO(tcps_rexmttimeo); DO(tcps_persisttimeo); DO(tcps_keeptimeo); DO(tcps_keepprobe); DO(tcps_keepdrops); DO(tcps_sndtotal); DO(tcps_sndpack); DO(tcps_sndbyte); DO(tcps_sndrexmitpack); DO(tcps_sack_rexmits); DO(tcps_sndacks); DO(tcps_sndprobe); DO(tcps_sndurg); DO(tcps_sndwinup); DO(tcps_sndctrl); DO(tcps_rcvtotal); DO(tcps_rcvpack); DO(tcps_rcvbyte); DO(tcps_rcvbadsum); DO(tcps_rcvbadoff); DO(tcps_rcvshort); DO(tcps_rcvduppack); DO(tcps_rcvdupbyte); DO(tcps_rcvpartduppack); DO(tcps_rcvpartdupbyte); DO(tcps_rcvoopack); DO(tcps_rcvoobyte); DO(tcps_rcvpackafterwin); DO(tcps_rcvbyteafterwin); DO(tcps_rcvafterclose); DO(tcps_rcvwinprobe); DO(tcps_rcvdupack); DO(tcps_rcvacktoomuch); DO(tcps_rcvackpack); DO(tcps_rcvackbyte); DO(tcps_rcvwinupd); DO(tcps_pawsdrop); DO(tcps_predack); DO(tcps_preddat); DO(tcps_pcbcachemiss); DO(tcps_cachedrtt); DO(tcps_cachedrttvar); DO(tcps_cachedssthresh); DO(tcps_usedrtt); DO(tcps_usedrttvar); DO(tcps_usedssthresh); DO(tcps_persistdrop); DO(tcps_badsyn); DO(tcps_mturesent); DO(tcps_listendrop); #undef DO } void showtcp(void) { struct tcpstat stats; memset(&stats, 0, sizeof stats); domode(&stats); #define DO(stat, row, col) \ mvwprintw(wnd, row, col, "%12lu", stats.stat) #define L(row, stat) DO(stat, row, 0) #define R(row, stat) DO(stat, row, 38) L(1, tcps_connattempt); R(1, tcps_sndtotal); L(2, tcps_accepts); R(2, tcps_sndpack); L(3, tcps_connects); R(3, tcps_sndrexmitpack); L(4, tcps_drops); R(4, tcps_sack_rexmits); L(5, tcps_conndrops); R(5, tcps_sndacks); L(6, tcps_timeoutdrop); R(6, tcps_sndprobe); L(7, tcps_keepdrops); R(7, tcps_sndwinup); L(8, tcps_listendrop); R(8, tcps_sndurg); R(9, tcps_sndctrl); R(10, tcps_mturesent); R(11, tcps_rcvtotal); L(12, tcps_segstimed); R(12, tcps_rcvpack); L(13, tcps_rttupdated); R(13, tcps_rcvduppack); L(14, tcps_delack); R(14, tcps_rcvpartduppack); L(15, tcps_rexmttimeo); R(15, tcps_rcvoopack); L(16, tcps_persisttimeo); R(16, tcps_rcvdupack); L(17, tcps_keepprobe); R(17, tcps_rcvackpack); L(18, tcps_keeptimeo); R(18, tcps_rcvwinprobe); R(19, tcps_rcvwinupd); R(20, tcps_rcvbadsum); #undef DO #undef L #undef R } int inittcp(void) { size_t len; int name[4]; name[0] = CTL_NET; name[1] = PF_INET; name[2] = IPPROTO_TCP; name[3] = TCPCTL_STATS; len = 0; if (sysctl(name, 4, 0, &len, 0, 0) < 0) { error("sysctl getting tcpstat size failed"); return 0; } if (len > sizeof curstat) { error("tcpstat structure has grown--recompile systat!"); return 0; } if (sysctl(name, 4, &initstat, &len, 0, 0) < 0) { error("sysctl getting tcpstat failed"); return 0; } oldstat = initstat; return 1; } void resettcp(void) { size_t len; int name[4]; name[0] = CTL_NET; name[1] = PF_INET; name[2] = IPPROTO_TCP; name[3] = TCPCTL_STATS; len = sizeof initstat; if (sysctl(name, 4, &initstat, &len, 0, 0) < 0) { error("sysctl getting tcpstat failed"); } oldstat = initstat; } void fetchtcp(void) { int name[4]; size_t len; oldstat = curstat; name[0] = CTL_NET; name[1] = PF_INET; name[2] = IPPROTO_TCP; name[3] = TCPCTL_STATS; len = sizeof curstat; if (sysctl(name, 4, &curstat, &len, 0, 0) < 0) return; } Index: head/usr.sbin/route6d/route6d.c =================================================================== --- head/usr.sbin/route6d/route6d.c (revision 333474) +++ head/usr.sbin/route6d/route6d.c (revision 333475) @@ -1,3581 +1,3582 @@ /* $FreeBSD$ */ /* $KAME: route6d.c,v 1.104 2003/10/31 00:30:20 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * 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. */ #ifndef lint static const char _rcsid[] = "$KAME: route6d.c,v 1.104 2003/10/31 00:30:20 itojun Exp $"; #endif #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_POLL_H #include #endif #include #include #include #include #include #include #include #include #include #include "route6d.h" #define MAXFILTER 40 #define RT_DUMP_MAXRETRY 15 #ifdef DEBUG #define INIT_INTERVAL6 6 #else #define INIT_INTERVAL6 10 /* Wait to submit an initial riprequest */ #endif /* alignment constraint for routing socket */ #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) struct ifc { /* Configuration of an interface */ TAILQ_ENTRY(ifc) ifc_next; char ifc_name[IFNAMSIZ]; /* if name */ int ifc_index; /* if index */ int ifc_mtu; /* if mtu */ int ifc_metric; /* if metric */ u_int ifc_flags; /* flags */ short ifc_cflags; /* IFC_XXX */ struct in6_addr ifc_mylladdr; /* my link-local address */ struct sockaddr_in6 ifc_ripsin; /* rip multicast address */ TAILQ_HEAD(, ifac) ifc_ifac_head; /* list of AF_INET6 addrs */ TAILQ_HEAD(, iff) ifc_iff_head; /* list of filters */ int ifc_joined; /* joined to ff02::9 */ }; static TAILQ_HEAD(, ifc) ifc_head = TAILQ_HEAD_INITIALIZER(ifc_head); struct ifac { /* Adddress associated to an interface */ TAILQ_ENTRY(ifac) ifac_next; struct ifc *ifac_ifc; /* back pointer */ struct in6_addr ifac_addr; /* address */ struct in6_addr ifac_raddr; /* remote address, valid in p2p */ int ifac_scope_id; /* scope id */ int ifac_plen; /* prefix length */ }; struct iff { /* Filters for an interface */ TAILQ_ENTRY(iff) iff_next; int iff_type; struct in6_addr iff_addr; int iff_plen; }; static struct ifc **index2ifc; static unsigned int nindex2ifc; static struct ifc *loopifcp = NULL; /* pointing to loopback */ #ifdef HAVE_POLL_H static struct pollfd set[2]; #else static fd_set *sockvecp; /* vector to select() for receiving */ static fd_set *recvecp; static int fdmasks; static int maxfd; /* maximum fd for select() */ #endif static int rtsock; /* the routing socket */ static int ripsock; /* socket to send/receive RIP datagram */ static struct rip6 *ripbuf; /* packet buffer for sending */ /* * Maintain the routes in a linked list. When the number of the routes * grows, somebody would like to introduce a hash based or a radix tree * based structure. I believe the number of routes handled by RIP is * limited and I don't have to manage a complex data structure, however. * * One of the major drawbacks of the linear linked list is the difficulty * of representing the relationship between a couple of routes. This may * be a significant problem when we have to support route aggregation with * suppressing the specifics covered by the aggregate. */ struct riprt { TAILQ_ENTRY(riprt) rrt_next; /* next destination */ struct riprt *rrt_same; /* same destination - future use */ struct netinfo6 rrt_info; /* network info */ struct in6_addr rrt_gw; /* gateway */ u_long rrt_flags; /* kernel routing table flags */ u_long rrt_rflags; /* route6d routing table flags */ time_t rrt_t; /* when the route validated */ int rrt_index; /* ifindex from which this route got */ }; static TAILQ_HEAD(, riprt) riprt_head = TAILQ_HEAD_INITIALIZER(riprt_head); static int dflag = 0; /* debug flag */ static int qflag = 0; /* quiet flag */ static int nflag = 0; /* don't update kernel routing table */ static int aflag = 0; /* age out even the statically defined routes */ static int hflag = 0; /* don't split horizon */ static int lflag = 0; /* exchange site local routes */ static int Pflag = 0; /* don't age out routes with RTF_PROTO[123] */ static int Qflag = RTF_PROTO2; /* set RTF_PROTO[123] flag to routes by RIPng */ static int sflag = 0; /* announce static routes w/ split horizon */ static int Sflag = 0; /* announce static routes to every interface */ static unsigned long routetag = 0; /* route tag attached on originating case */ static char *filter[MAXFILTER]; static int filtertype[MAXFILTER]; static int nfilter = 0; static pid_t pid; static struct sockaddr_storage ripsin; static int interval = 1; static time_t nextalarm = 0; #if 0 static time_t sup_trig_update = 0; #endif static FILE *rtlog = NULL; static int logopened = 0; static int seq = 0; static volatile sig_atomic_t seenalrm; static volatile sig_atomic_t seenquit; static volatile sig_atomic_t seenusr1; #define RRTF_AGGREGATE 0x08000000 #define RRTF_NOADVERTISE 0x10000000 #define RRTF_NH_NOT_LLADDR 0x20000000 #define RRTF_SENDANYWAY 0x40000000 #define RRTF_CHANGED 0x80000000 static void sighandler(int); static void ripalarm(void); static void riprecv(void); static void ripsend(struct ifc *, struct sockaddr_in6 *, int); static int out_filter(struct riprt *, struct ifc *); static void init(void); static void ifconfig(void); static int ifconfig1(const char *, const struct sockaddr *, struct ifc *, int); static void rtrecv(void); static int rt_del(const struct sockaddr_in6 *, const struct sockaddr_in6 *, const struct sockaddr_in6 *); static int rt_deladdr(struct ifc *, const struct sockaddr_in6 *, const struct sockaddr_in6 *); static void filterconfig(void); static int getifmtu(int); static const char *rttypes(struct rt_msghdr *); static const char *rtflags(struct rt_msghdr *); static const char *ifflags(int); static int ifrt(struct ifc *, int); static void ifrt_p2p(struct ifc *, int); static void applyplen(struct in6_addr *, int); static void ifrtdump(int); static void ifdump(int); static void ifdump0(FILE *, const struct ifc *); static void ifremove(int); static void rtdump(int); static void rt_entry(struct rt_msghdr *, int); static void rtdexit(void); static void riprequest(struct ifc *, struct netinfo6 *, int, struct sockaddr_in6 *); static void ripflush(struct ifc *, struct sockaddr_in6 *, int, struct netinfo6 *np); static void sendrequest(struct ifc *); static int sin6mask2len(const struct sockaddr_in6 *); static int mask2len(const struct in6_addr *, int); static int sendpacket(struct sockaddr_in6 *, int); static int addroute(struct riprt *, const struct in6_addr *, struct ifc *); static int delroute(struct netinfo6 *, struct in6_addr *); #if 0 static struct in6_addr *getroute(struct netinfo6 *, struct in6_addr *); #endif static void krtread(int); static int tobeadv(struct riprt *, struct ifc *); static char *allocopy(char *); static char *hms(void); static const char *inet6_n2p(const struct in6_addr *); static struct ifac *ifa_match(const struct ifc *, const struct in6_addr *, int); static struct in6_addr *plen2mask(int); static struct riprt *rtsearch(struct netinfo6 *); static int ripinterval(int); #if 0 static time_t ripsuptrig(void); #endif static void fatal(const char *, ...) __attribute__((__format__(__printf__, 1, 2))); static void trace(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); static void tracet(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); static struct ifc *ifc_find(char *); static struct iff *iff_find(struct ifc *, int); static void setindex2ifc(int, struct ifc *); #define MALLOC(type) ((type *)malloc(sizeof(type))) #define IFIL_TYPE_ANY 0x0 #define IFIL_TYPE_A 'A' #define IFIL_TYPE_N 'N' #define IFIL_TYPE_T 'T' #define IFIL_TYPE_O 'O' #define IFIL_TYPE_L 'L' int main(int argc, char *argv[]) { int ch; int error = 0; unsigned long proto; struct ifc *ifcp; sigset_t mask, omask; const char *pidfile = ROUTE6D_PID; FILE *pidfh; char *progname; char *ep; progname = strrchr(*argv, '/'); if (progname) progname++; else progname = *argv; pid = getpid(); while ((ch = getopt(argc, argv, "A:N:O:R:T:L:t:adDhlnp:P:Q:qsS")) != -1) { switch (ch) { case 'A': case 'N': case 'O': case 'T': case 'L': if (nfilter >= MAXFILTER) { fatal("Exceeds MAXFILTER"); /*NOTREACHED*/ } filtertype[nfilter] = ch; filter[nfilter++] = allocopy(optarg); break; case 't': ep = NULL; routetag = strtoul(optarg, &ep, 0); if (!ep || *ep != '\0' || (routetag & ~0xffff) != 0) { fatal("invalid route tag"); /*NOTREACHED*/ } break; case 'p': pidfile = optarg; break; case 'P': ep = NULL; proto = strtoul(optarg, &ep, 0); if (!ep || *ep != '\0' || 3 < proto) { fatal("invalid P flag"); /*NOTREACHED*/ } if (proto == 0) Pflag = 0; if (proto == 1) Pflag |= RTF_PROTO1; if (proto == 2) Pflag |= RTF_PROTO2; if (proto == 3) Pflag |= RTF_PROTO3; break; case 'Q': ep = NULL; proto = strtoul(optarg, &ep, 0); if (!ep || *ep != '\0' || 3 < proto) { fatal("invalid Q flag"); /*NOTREACHED*/ } if (proto == 0) Qflag = 0; if (proto == 1) Qflag |= RTF_PROTO1; if (proto == 2) Qflag |= RTF_PROTO2; if (proto == 3) Qflag |= RTF_PROTO3; break; case 'R': if ((rtlog = fopen(optarg, "w")) == NULL) { fatal("Can not write to routelog"); /*NOTREACHED*/ } break; #define FLAG(c, flag, n) case c: do { flag = n; break; } while(0) FLAG('a', aflag, 1); break; FLAG('d', dflag, 1); break; FLAG('D', dflag, 2); break; FLAG('h', hflag, 1); break; FLAG('l', lflag, 1); break; FLAG('n', nflag, 1); break; FLAG('q', qflag, 1); break; FLAG('s', sflag, 1); break; FLAG('S', Sflag, 1); break; #undef FLAG default: fatal("Invalid option specified, terminating"); /*NOTREACHED*/ } } argc -= optind; argv += optind; if (argc > 0) { fatal("bogus extra arguments"); /*NOTREACHED*/ } if (geteuid()) { nflag = 1; fprintf(stderr, "No kernel update is allowed\n"); } if (dflag == 0) { if (daemon(0, 0) < 0) { fatal("daemon"); /*NOTREACHED*/ } } openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); logopened++; if ((ripbuf = (struct rip6 *)malloc(RIP6_MAXMTU)) == NULL) fatal("malloc"); memset(ripbuf, 0, RIP6_MAXMTU); ripbuf->rip6_cmd = RIP6_RESPONSE; ripbuf->rip6_vers = RIP6_VERSION; ripbuf->rip6_res1[0] = 0; ripbuf->rip6_res1[1] = 0; init(); ifconfig(); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (ifcp->ifc_index < 0) { fprintf(stderr, "No ifindex found at %s " "(no link-local address?)\n", ifcp->ifc_name); error++; } } if (error) exit(1); if (loopifcp == NULL) { fatal("No loopback found"); /*NOTREACHED*/ } TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { ifrt(ifcp, 0); } filterconfig(); krtread(0); if (dflag) ifrtdump(0); pid = getpid(); if ((pidfh = fopen(pidfile, "w")) != NULL) { fprintf(pidfh, "%d\n", pid); fclose(pidfh); } if ((ripbuf = (struct rip6 *)malloc(RIP6_MAXMTU)) == NULL) { fatal("malloc"); /*NOTREACHED*/ } memset(ripbuf, 0, RIP6_MAXMTU); ripbuf->rip6_cmd = RIP6_RESPONSE; ripbuf->rip6_vers = RIP6_VERSION; ripbuf->rip6_res1[0] = 0; ripbuf->rip6_res1[1] = 0; if (signal(SIGALRM, sighandler) == SIG_ERR || signal(SIGQUIT, sighandler) == SIG_ERR || signal(SIGTERM, sighandler) == SIG_ERR || signal(SIGUSR1, sighandler) == SIG_ERR || signal(SIGHUP, sighandler) == SIG_ERR || signal(SIGINT, sighandler) == SIG_ERR) { fatal("signal"); /*NOTREACHED*/ } /* * To avoid rip packet congestion (not on a cable but in this * process), wait for a moment to send the first RIP6_RESPONSE * packets. */ alarm(ripinterval(INIT_INTERVAL6)); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (iff_find(ifcp, IFIL_TYPE_N) != NULL) continue; if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP)) sendrequest(ifcp); } syslog(LOG_INFO, "**** Started ****"); sigemptyset(&mask); sigaddset(&mask, SIGALRM); while (1) { if (seenalrm) { ripalarm(); seenalrm = 0; continue; } if (seenquit) { rtdexit(); seenquit = 0; continue; } if (seenusr1) { ifrtdump(SIGUSR1); seenusr1 = 0; continue; } #ifdef HAVE_POLL_H switch (poll(set, 2, INFTIM)) #else memcpy(recvecp, sockvecp, fdmasks); switch (select(maxfd + 1, recvecp, 0, 0, 0)) #endif { case -1: if (errno != EINTR) { fatal("select"); /*NOTREACHED*/ } continue; case 0: continue; default: #ifdef HAVE_POLL_H if (set[0].revents & POLLIN) #else if (FD_ISSET(ripsock, recvecp)) #endif { sigprocmask(SIG_BLOCK, &mask, &omask); riprecv(); sigprocmask(SIG_SETMASK, &omask, NULL); } #ifdef HAVE_POLL_H if (set[1].revents & POLLIN) #else if (FD_ISSET(rtsock, recvecp)) #endif { sigprocmask(SIG_BLOCK, &mask, &omask); rtrecv(); sigprocmask(SIG_SETMASK, &omask, NULL); } } } } static void sighandler(int signo) { switch (signo) { case SIGALRM: seenalrm++; break; case SIGQUIT: case SIGTERM: seenquit++; break; case SIGUSR1: case SIGHUP: case SIGINT: seenusr1++; break; } } /* * gracefully exits after resetting sockopts. */ /* ARGSUSED */ static void rtdexit(void) { struct riprt *rrt; alarm(0); TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_rflags & RRTF_AGGREGATE) { delroute(&rrt->rrt_info, &rrt->rrt_gw); } } close(ripsock); close(rtsock); syslog(LOG_INFO, "**** Terminated ****"); closelog(); exit(1); } /* * Called periodically: * 1. age out the learned route. remove it if necessary. * 2. submit RIP6_RESPONSE packets. * Invoked in every SUPPLY_INTERVAL6 (30) seconds. I believe we don't have * to invoke this function in every 1 or 5 or 10 seconds only to age the * routes more precisely. */ /* ARGSUSED */ static void ripalarm(void) { struct ifc *ifcp; struct riprt *rrt, *rrt_tmp; time_t t_lifetime, t_holddown; /* age the RIP routes */ t_lifetime = time(NULL) - RIP_LIFETIME; t_holddown = t_lifetime - RIP_HOLDDOWN; TAILQ_FOREACH_SAFE(rrt, &riprt_head, rrt_next, rrt_tmp) { if (rrt->rrt_t == 0) continue; else if (rrt->rrt_t < t_holddown) { TAILQ_REMOVE(&riprt_head, rrt, rrt_next); delroute(&rrt->rrt_info, &rrt->rrt_gw); free(rrt); } else if (rrt->rrt_t < t_lifetime) rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; } /* Supply updates */ TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP)) ripsend(ifcp, &ifcp->ifc_ripsin, 0); } alarm(ripinterval(SUPPLY_INTERVAL6)); } static void init(void) { int error; const int int0 = 0, int1 = 1, int255 = 255; struct addrinfo hints, *res; char port[NI_MAXSERV]; TAILQ_INIT(&ifc_head); nindex2ifc = 0; /*initial guess*/ index2ifc = NULL; snprintf(port, sizeof(port), "%u", RIP6_PORT); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_PASSIVE; error = getaddrinfo(NULL, port, &hints, &res); if (error) { fatal("%s", gai_strerror(error)); /*NOTREACHED*/ } if (res->ai_next) { fatal(":: resolved to multiple address"); /*NOTREACHED*/ } ripsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (ripsock < 0) { fatal("rip socket"); /*NOTREACHED*/ } #ifdef IPV6_V6ONLY if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_V6ONLY, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_V6ONLY"); /*NOTREACHED*/ } #endif if (bind(ripsock, res->ai_addr, res->ai_addrlen) < 0) { fatal("rip bind"); /*NOTREACHED*/ } if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &int255, sizeof(int255)) < 0) { fatal("rip IPV6_MULTICAST_HOPS"); /*NOTREACHED*/ } if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &int0, sizeof(int0)) < 0) { fatal("rip IPV6_MULTICAST_LOOP"); /*NOTREACHED*/ } #ifdef IPV6_RECVPKTINFO if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_RECVPKTINFO"); /*NOTREACHED*/ } #else /* old adv. API */ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_PKTINFO, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_PKTINFO"); /*NOTREACHED*/ } #endif #ifdef IPV6_RECVPKTINFO if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_RECVHOPLIMIT"); /*NOTREACHED*/ } #else /* old adv. API */ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_HOPLIMIT, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_HOPLIMIT"); /*NOTREACHED*/ } #endif freeaddrinfo(res); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; error = getaddrinfo(RIP6_DEST, port, &hints, &res); if (error) { fatal("%s", gai_strerror(error)); /*NOTREACHED*/ } if (res->ai_next) { fatal("%s resolved to multiple address", RIP6_DEST); /*NOTREACHED*/ } memcpy(&ripsin, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); #ifdef HAVE_POLL_H set[0].fd = ripsock; set[0].events = POLLIN; #else maxfd = ripsock; #endif if (nflag == 0) { if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { fatal("route socket"); /*NOTREACHED*/ } #ifdef HAVE_POLL_H set[1].fd = rtsock; set[1].events = POLLIN; #else if (rtsock > maxfd) maxfd = rtsock; #endif } else { #ifdef HAVE_POLL_H set[1].fd = -1; #else rtsock = -1; /*just for safety */ #endif } #ifndef HAVE_POLL_H fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask); if ((sockvecp = malloc(fdmasks)) == NULL) { fatal("malloc"); /*NOTREACHED*/ } if ((recvecp = malloc(fdmasks)) == NULL) { fatal("malloc"); /*NOTREACHED*/ } memset(sockvecp, 0, fdmasks); FD_SET(ripsock, sockvecp); if (rtsock >= 0) FD_SET(rtsock, sockvecp); #endif } #define RIPSIZE(n) \ (sizeof(struct rip6) + ((n)-1) * sizeof(struct netinfo6)) /* * ripflush flushes the rip datagram stored in the rip buffer */ static void ripflush(struct ifc *ifcp, struct sockaddr_in6 *sin6, int nrt, struct netinfo6 *np) { int i; int error; if (ifcp) tracet(1, "Send(%s): info(%d) to %s.%d\n", ifcp->ifc_name, nrt, inet6_n2p(&sin6->sin6_addr), ntohs(sin6->sin6_port)); else tracet(1, "Send: info(%d) to %s.%d\n", nrt, inet6_n2p(&sin6->sin6_addr), ntohs(sin6->sin6_port)); if (dflag >= 2) { np = ripbuf->rip6_nets; for (i = 0; i < nrt; i++, np++) { if (np->rip6_metric == NEXTHOP_METRIC) { if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest)) trace(2, " NextHop reset"); else { trace(2, " NextHop %s", inet6_n2p(&np->rip6_dest)); } } else { trace(2, " %s/%d[%d]", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); } if (np->rip6_tag) { trace(2, " tag=0x%04x", ntohs(np->rip6_tag) & 0xffff); } trace(2, "\n"); } } error = sendpacket(sin6, RIPSIZE(nrt)); if (error == EAFNOSUPPORT) { /* Protocol not supported */ if (ifcp != NULL) { tracet(1, "Could not send info to %s (%s): " "set IFF_UP to 0\n", ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr)); /* As if down for AF_INET6 */ ifcp->ifc_flags &= ~IFF_UP; } else { tracet(1, "Could not send info to %s\n", inet6_n2p(&sin6->sin6_addr)); } } } /* * Generate RIP6_RESPONSE packets and send them. */ static void ripsend(struct ifc *ifcp, struct sockaddr_in6 *sin6, int flag) { struct riprt *rrt; struct in6_addr *nh; /* next hop */ struct netinfo6 *np; int maxrte; int nrt; if (qflag) return; if (ifcp == NULL) { /* * Request from non-link local address is not * a regular route6d update. */ maxrte = (IFMINMTU - sizeof(struct ip6_hdr) - sizeof(struct udphdr) - sizeof(struct rip6) + sizeof(struct netinfo6)) / sizeof(struct netinfo6); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_rflags & RRTF_NOADVERTISE) continue; /* Put the route to the buffer */ *np = rrt->rrt_info; np++; nrt++; if (nrt == maxrte) { ripflush(NULL, sin6, nrt, np); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; } } if (nrt) /* Send last packet */ ripflush(NULL, sin6, nrt, np); return; } if ((flag & RRTF_SENDANYWAY) == 0 && (qflag || (ifcp->ifc_flags & IFF_LOOPBACK))) return; /* -N: no use */ if (iff_find(ifcp, IFIL_TYPE_N) != NULL) return; /* -T: generate default route only */ if (iff_find(ifcp, IFIL_TYPE_T) != NULL) { struct netinfo6 rrt_info; memset(&rrt_info, 0, sizeof(struct netinfo6)); rrt_info.rip6_dest = in6addr_any; rrt_info.rip6_plen = 0; rrt_info.rip6_metric = 1; rrt_info.rip6_metric += ifcp->ifc_metric; rrt_info.rip6_tag = htons(routetag & 0xffff); np = ripbuf->rip6_nets; *np = rrt_info; nrt = 1; ripflush(ifcp, sin6, nrt, np); return; } maxrte = (ifcp->ifc_mtu - sizeof(struct ip6_hdr) - sizeof(struct udphdr) - sizeof(struct rip6) + sizeof(struct netinfo6)) / sizeof(struct netinfo6); nrt = 0; np = ripbuf->rip6_nets; nh = NULL; TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_rflags & RRTF_NOADVERTISE) continue; /* Need to check filter here */ if (out_filter(rrt, ifcp) == 0) continue; /* Check split horizon and other conditions */ if (tobeadv(rrt, ifcp) == 0) continue; /* Only considers the routes with flag if specified */ if ((flag & RRTF_CHANGED) && (rrt->rrt_rflags & RRTF_CHANGED) == 0) continue; /* Check nexthop */ if (rrt->rrt_index == ifcp->ifc_index && !IN6_IS_ADDR_UNSPECIFIED(&rrt->rrt_gw) && (rrt->rrt_rflags & RRTF_NH_NOT_LLADDR) == 0) { if (nh == NULL || !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw)) { if (nrt == maxrte - 2) { ripflush(ifcp, sin6, nrt, np); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; } np->rip6_dest = rrt->rrt_gw; np->rip6_plen = 0; np->rip6_tag = 0; np->rip6_metric = NEXTHOP_METRIC; nh = &rrt->rrt_gw; np++; nrt++; } } else if (nh && (rrt->rrt_index != ifcp->ifc_index || !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw) || rrt->rrt_rflags & RRTF_NH_NOT_LLADDR)) { /* Reset nexthop */ if (nrt == maxrte - 2) { ripflush(ifcp, sin6, nrt, np); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; } memset(np, 0, sizeof(struct netinfo6)); np->rip6_metric = NEXTHOP_METRIC; nh = NULL; np++; nrt++; } /* Put the route to the buffer */ *np = rrt->rrt_info; np++; nrt++; if (nrt == maxrte) { ripflush(ifcp, sin6, nrt, np); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; } } if (nrt) /* Send last packet */ ripflush(ifcp, sin6, nrt, np); } /* * outbound filter logic, per-route/interface. */ static int out_filter(struct riprt *rrt, struct ifc *ifcp) { struct iff *iffp; struct in6_addr ia; int ok; /* * -A: filter out less specific routes, if we have aggregated * route configured. */ TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (iffp->iff_type != 'A') continue; if (rrt->rrt_info.rip6_plen <= iffp->iff_plen) continue; ia = rrt->rrt_info.rip6_dest; applyplen(&ia, iffp->iff_plen); if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) return 0; } /* * if it is an aggregated route, advertise it only to the * interfaces specified on -A. */ if ((rrt->rrt_rflags & RRTF_AGGREGATE) != 0) { ok = 0; TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (iffp->iff_type != 'A') continue; if (rrt->rrt_info.rip6_plen == iffp->iff_plen && IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest, &iffp->iff_addr)) { ok = 1; break; } } if (!ok) return 0; } /* * -O: advertise only if prefix matches the configured prefix. */ if (iff_find(ifcp, IFIL_TYPE_O) != NULL) { ok = 0; TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (iffp->iff_type != 'O') continue; if (rrt->rrt_info.rip6_plen < iffp->iff_plen) continue; ia = rrt->rrt_info.rip6_dest; applyplen(&ia, iffp->iff_plen); if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) { ok = 1; break; } } if (!ok) return 0; } /* the prefix should be advertised */ return 1; } /* * Determine if the route is to be advertised on the specified interface. * It checks options specified in the arguments and the split horizon rule. */ static int tobeadv(struct riprt *rrt, struct ifc *ifcp) { /* Special care for static routes */ if (rrt->rrt_flags & RTF_STATIC) { /* XXX don't advertise reject/blackhole routes */ if (rrt->rrt_flags & (RTF_REJECT | RTF_BLACKHOLE)) return 0; if (Sflag) /* Yes, advertise it anyway */ return 1; if (sflag && rrt->rrt_index != ifcp->ifc_index) return 1; return 0; } /* Regular split horizon */ if (hflag == 0 && rrt->rrt_index == ifcp->ifc_index) return 0; return 1; } /* * Send a rip packet actually. */ static int sendpacket(struct sockaddr_in6 *sin6, int len) { struct msghdr m; struct cmsghdr *cm; struct iovec iov[2]; struct in6_pktinfo *pi; u_char cmsgbuf[256]; int idx; struct sockaddr_in6 sincopy; /* do not overwrite the given sin */ sincopy = *sin6; sin6 = &sincopy; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) idx = sin6->sin6_scope_id; else idx = 0; m.msg_name = (caddr_t)sin6; m.msg_namelen = sizeof(*sin6); iov[0].iov_base = (caddr_t)ripbuf; iov[0].iov_len = len; m.msg_iov = iov; m.msg_iovlen = 1; m.msg_flags = 0; if (!idx) { m.msg_control = NULL; m.msg_controllen = 0; } else { memset(cmsgbuf, 0, sizeof(cmsgbuf)); cm = (struct cmsghdr *)(void *)cmsgbuf; m.msg_control = (caddr_t)cm; m.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); cm->cmsg_level = IPPROTO_IPV6; cm->cmsg_type = IPV6_PKTINFO; pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*::*/ pi->ipi6_ifindex = idx; } if (sendmsg(ripsock, &m, 0 /*MSG_DONTROUTE*/) < 0) { trace(1, "sendmsg: %s\n", strerror(errno)); return errno; } return 0; } /* * Receive and process RIP packets. Update the routes/kernel forwarding * table if necessary. */ static void riprecv(void) { struct ifc *ifcp, *ic; struct sockaddr_in6 fsock; struct in6_addr nh; /* next hop */ struct rip6 *rp; struct netinfo6 *np, *nq; struct riprt *rrt; ssize_t len, nn; unsigned int need_trigger, idx; char buf[4 * RIP6_MAXMTU]; time_t t; struct msghdr m; struct cmsghdr *cm; struct iovec iov[2]; u_char cmsgbuf[256]; struct in6_pktinfo *pi = NULL; int *hlimp = NULL; struct iff *iffp; struct in6_addr ia; int ok; time_t t_half_lifetime; need_trigger = 0; m.msg_name = (caddr_t)&fsock; m.msg_namelen = sizeof(fsock); iov[0].iov_base = (caddr_t)buf; iov[0].iov_len = sizeof(buf); m.msg_iov = iov; m.msg_iovlen = 1; cm = (struct cmsghdr *)(void *)cmsgbuf; m.msg_control = (caddr_t)cm; m.msg_controllen = sizeof(cmsgbuf); m.msg_flags = 0; if ((len = recvmsg(ripsock, &m, 0)) < 0) { fatal("recvmsg"); /*NOTREACHED*/ } idx = 0; for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m); cm; cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) { if (cm->cmsg_level != IPPROTO_IPV6) continue; switch (cm->cmsg_type) { case IPV6_PKTINFO: if (cm->cmsg_len != CMSG_LEN(sizeof(*pi))) { trace(1, "invalid cmsg length for IPV6_PKTINFO\n"); return; } pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); idx = pi->ipi6_ifindex; break; case IPV6_HOPLIMIT: if (cm->cmsg_len != CMSG_LEN(sizeof(int))) { trace(1, "invalid cmsg length for IPV6_HOPLIMIT\n"); return; } hlimp = (int *)(void *)CMSG_DATA(cm); break; } } if ((size_t)len < sizeof(struct rip6)) { trace(1, "Packet too short\n"); return; } if (pi == NULL || hlimp == NULL) { /* * This can happen when the kernel failed to allocate memory * for the ancillary data. Although we might be able to handle * some cases without this info, those are minor and not so * important, so it's better to discard the packet for safer * operation. */ trace(1, "IPv6 packet information cannot be retrieved\n"); return; } nh = fsock.sin6_addr; nn = (len - sizeof(struct rip6) + sizeof(struct netinfo6)) / sizeof(struct netinfo6); rp = (struct rip6 *)(void *)buf; np = rp->rip6_nets; if (rp->rip6_vers != RIP6_VERSION) { trace(1, "Incorrect RIP version %d\n", rp->rip6_vers); return; } if (rp->rip6_cmd == RIP6_REQUEST) { if (idx && idx < nindex2ifc) { ifcp = index2ifc[idx]; riprequest(ifcp, np, nn, &fsock); } else { riprequest(NULL, np, nn, &fsock); } return; } if (!IN6_IS_ADDR_LINKLOCAL(&fsock.sin6_addr)) { trace(1, "Response from non-ll addr: %s\n", inet6_n2p(&fsock.sin6_addr)); return; /* Ignore packets from non-link-local addr */ } if (ntohs(fsock.sin6_port) != RIP6_PORT) { trace(1, "Response from non-rip port from %s\n", inet6_n2p(&fsock.sin6_addr)); return; } if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && *hlimp != 255) { trace(1, "Response packet with a smaller hop limit (%d) from %s\n", *hlimp, inet6_n2p(&fsock.sin6_addr)); return; } /* * Further validation: since this program does not send off-link * requests, an incoming response must always come from an on-link * node. Although this is normally ensured by the source address * check above, it may not 100% be safe because there are router * implementations that (invalidly) allow a packet with a link-local * source address to be forwarded to a different link. * So we also check whether the destination address is a link-local * address or the hop limit is 255. Note that RFC2080 does not require * the specific hop limit for a unicast response, so we cannot assume * the limitation. */ if (!IN6_IS_ADDR_LINKLOCAL(&pi->ipi6_addr) && *hlimp != 255) { trace(1, "Response packet possibly from an off-link node: " "from %s to %s hlim=%d\n", inet6_n2p(&fsock.sin6_addr), inet6_n2p(&pi->ipi6_addr), *hlimp); return; } idx = fsock.sin6_scope_id; ifcp = (idx < nindex2ifc) ? index2ifc[idx] : NULL; if (!ifcp) { trace(1, "Packets to unknown interface index %d\n", idx); return; /* Ignore it */ } if (IN6_ARE_ADDR_EQUAL(&ifcp->ifc_mylladdr, &fsock.sin6_addr)) return; /* The packet is from me; ignore */ if (rp->rip6_cmd != RIP6_RESPONSE) { trace(1, "Invalid command %d\n", rp->rip6_cmd); return; } /* -N: no use */ if (iff_find(ifcp, IFIL_TYPE_N) != NULL) return; tracet(1, "Recv(%s): from %s.%d info(%zd)\n", ifcp->ifc_name, inet6_n2p(&nh), ntohs(fsock.sin6_port), nn); t = time(NULL); t_half_lifetime = t - (RIP_LIFETIME/2); for (; nn; nn--, np++) { if (np->rip6_metric == NEXTHOP_METRIC) { /* modify neighbor address */ if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) { nh = np->rip6_dest; trace(1, "\tNexthop: %s\n", inet6_n2p(&nh)); } else if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest)) { nh = fsock.sin6_addr; trace(1, "\tNexthop: %s\n", inet6_n2p(&nh)); } else { nh = fsock.sin6_addr; trace(1, "\tInvalid Nexthop: %s\n", inet6_n2p(&np->rip6_dest)); } continue; } if (IN6_IS_ADDR_MULTICAST(&np->rip6_dest)) { trace(1, "\tMulticast netinfo6: %s/%d [%d]\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); continue; } if (IN6_IS_ADDR_LOOPBACK(&np->rip6_dest)) { trace(1, "\tLoopback netinfo6: %s/%d [%d]\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); continue; } if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) { trace(1, "\tLink Local netinfo6: %s/%d [%d]\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); continue; } /* may need to pass sitelocal prefix in some case, however*/ if (IN6_IS_ADDR_SITELOCAL(&np->rip6_dest) && !lflag) { trace(1, "\tSite Local netinfo6: %s/%d [%d]\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); continue; } trace(2, "\tnetinfo6: %s/%d [%d]", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); if (np->rip6_tag) trace(2, " tag=0x%04x", ntohs(np->rip6_tag) & 0xffff); if (dflag >= 2) { ia = np->rip6_dest; applyplen(&ia, np->rip6_plen); if (!IN6_ARE_ADDR_EQUAL(&ia, &np->rip6_dest)) trace(2, " [junk outside prefix]"); } /* * -L: listen only if the prefix matches the configuration */ ok = 1; /* if there's no L filter, it is ok */ TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (iffp->iff_type != IFIL_TYPE_L) continue; ok = 0; if (np->rip6_plen < iffp->iff_plen) continue; /* special rule: ::/0 means default, not "in /0" */ if (iffp->iff_plen == 0 && np->rip6_plen > 0) continue; ia = np->rip6_dest; applyplen(&ia, iffp->iff_plen); if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) { ok = 1; break; } } if (!ok) { trace(2, " (filtered)\n"); continue; } trace(2, "\n"); np->rip6_metric++; np->rip6_metric += ifcp->ifc_metric; if (np->rip6_metric > HOPCNT_INFINITY6) np->rip6_metric = HOPCNT_INFINITY6; applyplen(&np->rip6_dest, np->rip6_plen); if ((rrt = rtsearch(np)) != NULL) { if (rrt->rrt_t == 0) continue; /* Intf route has priority */ nq = &rrt->rrt_info; if (nq->rip6_metric > np->rip6_metric) { if (rrt->rrt_index == ifcp->ifc_index && IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) { /* Small metric from the same gateway */ nq->rip6_metric = np->rip6_metric; } else { /* Better route found */ rrt->rrt_index = ifcp->ifc_index; /* Update routing table */ delroute(nq, &rrt->rrt_gw); rrt->rrt_gw = nh; *nq = *np; addroute(rrt, &nh, ifcp); } rrt->rrt_rflags |= RRTF_CHANGED; rrt->rrt_t = t; need_trigger = 1; } else if (nq->rip6_metric < np->rip6_metric && rrt->rrt_index == ifcp->ifc_index && IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) { /* Got worse route from same gw */ nq->rip6_metric = np->rip6_metric; rrt->rrt_t = t; rrt->rrt_rflags |= RRTF_CHANGED; need_trigger = 1; } else if (nq->rip6_metric == np->rip6_metric && np->rip6_metric < HOPCNT_INFINITY6) { if (rrt->rrt_index == ifcp->ifc_index && IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) { /* same metric, same route from same gw */ rrt->rrt_t = t; } else if (rrt->rrt_t < t_half_lifetime) { /* Better route found */ rrt->rrt_index = ifcp->ifc_index; /* Update routing table */ delroute(nq, &rrt->rrt_gw); rrt->rrt_gw = nh; *nq = *np; addroute(rrt, &nh, ifcp); rrt->rrt_rflags |= RRTF_CHANGED; rrt->rrt_t = t; } } /* * if nq->rip6_metric == HOPCNT_INFINITY6 then * do not update age value. Do nothing. */ } else if (np->rip6_metric < HOPCNT_INFINITY6) { /* Got a new valid route */ if ((rrt = MALLOC(struct riprt)) == NULL) { fatal("malloc: struct riprt"); /*NOTREACHED*/ } memset(rrt, 0, sizeof(*rrt)); nq = &rrt->rrt_info; rrt->rrt_same = NULL; rrt->rrt_index = ifcp->ifc_index; rrt->rrt_flags = RTF_UP|RTF_GATEWAY; rrt->rrt_gw = nh; *nq = *np; applyplen(&nq->rip6_dest, nq->rip6_plen); if (nq->rip6_plen == sizeof(struct in6_addr) * 8) rrt->rrt_flags |= RTF_HOST; /* Update routing table */ addroute(rrt, &nh, ifcp); rrt->rrt_rflags |= RRTF_CHANGED; need_trigger = 1; rrt->rrt_t = t; /* Put the route to the list */ TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); } } /* XXX need to care the interval between triggered updates */ if (need_trigger) { if (nextalarm > time(NULL) + RIP_TRIG_INT6_MAX) { TAILQ_FOREACH(ic, &ifc_head, ifc_next) { if (ifcp->ifc_index == ic->ifc_index) continue; if (ic->ifc_flags & IFF_UP) ripsend(ic, &ic->ifc_ripsin, RRTF_CHANGED); } } /* Reset the flag */ TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { rrt->rrt_rflags &= ~RRTF_CHANGED; } } } /* * Send all routes request packet to the specified interface. */ static void sendrequest(struct ifc *ifcp) { struct netinfo6 *np; int error; if (ifcp->ifc_flags & IFF_LOOPBACK) return; ripbuf->rip6_cmd = RIP6_REQUEST; np = ripbuf->rip6_nets; memset(np, 0, sizeof(struct netinfo6)); np->rip6_metric = HOPCNT_INFINITY6; tracet(1, "Send rtdump Request to %s (%s)\n", ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr)); error = sendpacket(&ifcp->ifc_ripsin, RIPSIZE(1)); if (error == EAFNOSUPPORT) { /* Protocol not supported */ tracet(1, "Could not send rtdump Request to %s (%s): " "set IFF_UP to 0\n", ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr)); ifcp->ifc_flags &= ~IFF_UP; /* As if down for AF_INET6 */ } ripbuf->rip6_cmd = RIP6_RESPONSE; } /* * Process a RIP6_REQUEST packet. */ static void riprequest(struct ifc *ifcp, struct netinfo6 *np, int nn, struct sockaddr_in6 *sin6) { int i; struct riprt *rrt; if (!(nn == 1 && IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest) && np->rip6_plen == 0 && np->rip6_metric == HOPCNT_INFINITY6)) { /* Specific response, don't split-horizon */ trace(1, "\tRIP Request\n"); for (i = 0; i < nn; i++, np++) { rrt = rtsearch(np); if (rrt) np->rip6_metric = rrt->rrt_info.rip6_metric; else np->rip6_metric = HOPCNT_INFINITY6; } (void)sendpacket(sin6, RIPSIZE(nn)); return; } /* Whole routing table dump */ trace(1, "\tRIP Request -- whole routing table\n"); ripsend(ifcp, sin6, RRTF_SENDANYWAY); } /* * Get information of each interface. */ static void ifconfig(void) { struct ifaddrs *ifap, *ifa; struct ifc *ifcp; struct ipv6_mreq mreq; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { fatal("socket"); /*NOTREACHED*/ } if (getifaddrs(&ifap) != 0) { fatal("getifaddrs"); /*NOTREACHED*/ } for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifcp = ifc_find(ifa->ifa_name); /* we are interested in multicast-capable interfaces */ if ((ifa->ifa_flags & IFF_MULTICAST) == 0) continue; if (!ifcp) { /* new interface */ if ((ifcp = MALLOC(struct ifc)) == NULL) { fatal("malloc: struct ifc"); /*NOTREACHED*/ } memset(ifcp, 0, sizeof(*ifcp)); ifcp->ifc_index = -1; strlcpy(ifcp->ifc_name, ifa->ifa_name, sizeof(ifcp->ifc_name)); TAILQ_INIT(&ifcp->ifc_ifac_head); TAILQ_INIT(&ifcp->ifc_iff_head); ifcp->ifc_flags = ifa->ifa_flags; TAILQ_INSERT_HEAD(&ifc_head, ifcp, ifc_next); trace(1, "newif %s <%s>\n", ifcp->ifc_name, ifflags(ifcp->ifc_flags)); if (!strcmp(ifcp->ifc_name, LOOPBACK_IF)) loopifcp = ifcp; } else { /* update flag, this may be up again */ if (ifcp->ifc_flags != ifa->ifa_flags) { trace(1, "%s: <%s> -> ", ifcp->ifc_name, ifflags(ifcp->ifc_flags)); trace(1, "<%s>\n", ifflags(ifa->ifa_flags)); ifcp->ifc_cflags |= IFC_CHANGED; } ifcp->ifc_flags = ifa->ifa_flags; } if (ifconfig1(ifa->ifa_name, ifa->ifa_addr, ifcp, s) < 0) { /* maybe temporary failure */ continue; } if ((ifcp->ifc_flags & (IFF_LOOPBACK | IFF_UP)) == IFF_UP && 0 < ifcp->ifc_index && !ifcp->ifc_joined) { mreq.ipv6mr_multiaddr = ifcp->ifc_ripsin.sin6_addr; mreq.ipv6mr_interface = ifcp->ifc_index; if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) { fatal("IPV6_JOIN_GROUP"); /*NOTREACHED*/ } trace(1, "join %s %s\n", ifcp->ifc_name, RIP6_DEST); ifcp->ifc_joined++; } } close(s); freeifaddrs(ifap); } static int ifconfig1(const char *name, const struct sockaddr *sa, struct ifc *ifcp, int s) { struct in6_ifreq ifr; const struct sockaddr_in6 *sin6; struct ifac *ifac; int plen; char buf[BUFSIZ]; sin6 = (const struct sockaddr_in6 *)(const void *)sa; if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) && !lflag) return (-1); ifr.ifr_addr = *sin6; strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCGIFNETMASK_IN6, (char *)&ifr) < 0) { syslog(LOG_INFO, "ioctl: SIOCGIFNETMASK_IN6"); return (-1); } plen = sin6mask2len(&ifr.ifr_addr); if ((ifac = ifa_match(ifcp, &sin6->sin6_addr, plen)) != NULL) { /* same interface found */ /* need check if something changed */ /* XXX not yet implemented */ return (-1); } /* * New address is found */ if ((ifac = MALLOC(struct ifac)) == NULL) { fatal("malloc: struct ifac"); /*NOTREACHED*/ } memset(ifac, 0, sizeof(*ifac)); ifac->ifac_ifc = ifcp; ifac->ifac_addr = sin6->sin6_addr; ifac->ifac_plen = plen; ifac->ifac_scope_id = sin6->sin6_scope_id; if (ifcp->ifc_flags & IFF_POINTOPOINT) { ifr.ifr_addr = *sin6; if (ioctl(s, SIOCGIFDSTADDR_IN6, (char *)&ifr) < 0) { fatal("ioctl: SIOCGIFDSTADDR_IN6"); /*NOTREACHED*/ } ifac->ifac_raddr = ifr.ifr_dstaddr.sin6_addr; inet_ntop(AF_INET6, (void *)&ifac->ifac_raddr, buf, sizeof(buf)); trace(1, "found address %s/%d -- %s\n", inet6_n2p(&ifac->ifac_addr), ifac->ifac_plen, buf); } else { trace(1, "found address %s/%d\n", inet6_n2p(&ifac->ifac_addr), ifac->ifac_plen); } if (ifcp->ifc_index < 0 && IN6_IS_ADDR_LINKLOCAL(&ifac->ifac_addr)) { ifcp->ifc_mylladdr = ifac->ifac_addr; ifcp->ifc_index = ifac->ifac_scope_id; memcpy(&ifcp->ifc_ripsin, &ripsin, ripsin.ss_len); ifcp->ifc_ripsin.sin6_scope_id = ifcp->ifc_index; setindex2ifc(ifcp->ifc_index, ifcp); ifcp->ifc_mtu = getifmtu(ifcp->ifc_index); if (ifcp->ifc_mtu > RIP6_MAXMTU) ifcp->ifc_mtu = RIP6_MAXMTU; if (ioctl(s, SIOCGIFMETRIC, (char *)&ifr) < 0) { fatal("ioctl: SIOCGIFMETRIC"); /*NOTREACHED*/ } ifcp->ifc_metric = ifr.ifr_metric; trace(1, "\tindex: %d, mtu: %d, metric: %d\n", ifcp->ifc_index, ifcp->ifc_mtu, ifcp->ifc_metric); } else ifcp->ifc_cflags |= IFC_CHANGED; TAILQ_INSERT_HEAD(&ifcp->ifc_ifac_head, ifac, ifac_next); return 0; } static void ifremove(int ifindex) { struct ifc *ifcp; struct riprt *rrt; TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (ifcp->ifc_index == ifindex) break; } if (ifcp == NULL) return; tracet(1, "ifremove: %s is departed.\n", ifcp->ifc_name); TAILQ_REMOVE(&ifc_head, ifcp, ifc_next); TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_index == ifcp->ifc_index && rrt->rrt_rflags & RRTF_AGGREGATE) delroute(&rrt->rrt_info, &rrt->rrt_gw); } free(ifcp); } /* * Receive and process routing messages. * Update interface information as necesssary. */ static void rtrecv(void) { char buf[BUFSIZ]; char *p, *q = NULL; struct rt_msghdr *rtm; struct ifa_msghdr *ifam; struct if_msghdr *ifm; struct if_announcemsghdr *ifan; int len; struct ifc *ifcp, *ic; int iface = 0, rtable = 0; struct sockaddr_in6 *rta[RTAX_MAX]; struct sockaddr_in6 mask; int i, addrs = 0; struct riprt *rrt; if ((len = read(rtsock, buf, sizeof(buf))) < 0) { perror("read from rtsock"); exit(1); } if (len == 0) return; #if 0 if (len < sizeof(*rtm)) { trace(1, "short read from rtsock: %d (should be > %lu)\n", len, (u_long)sizeof(*rtm)); return; } #endif if (dflag >= 2) { fprintf(stderr, "rtmsg:\n"); for (i = 0; i < len; i++) { fprintf(stderr, "%02x ", buf[i] & 0xff); if (i % 16 == 15) fprintf(stderr, "\n"); } fprintf(stderr, "\n"); } for (p = buf; p - buf < len; p += ((struct rt_msghdr *)(void *)p)->rtm_msglen) { if (((struct rt_msghdr *)(void *)p)->rtm_version != RTM_VERSION) continue; /* safety against bogus message */ if (((struct rt_msghdr *)(void *)p)->rtm_msglen <= 0) { trace(1, "bogus rtmsg: length=%d\n", ((struct rt_msghdr *)(void *)p)->rtm_msglen); break; } rtm = NULL; ifam = NULL; ifm = NULL; switch (((struct rt_msghdr *)(void *)p)->rtm_type) { case RTM_NEWADDR: case RTM_DELADDR: ifam = (struct ifa_msghdr *)(void *)p; addrs = ifam->ifam_addrs; q = (char *)(ifam + 1); break; case RTM_IFINFO: ifm = (struct if_msghdr *)(void *)p; addrs = ifm->ifm_addrs; q = (char *)(ifm + 1); break; case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *)(void *)p; switch (ifan->ifan_what) { case IFAN_ARRIVAL: iface++; break; case IFAN_DEPARTURE: ifremove(ifan->ifan_index); iface++; break; } break; default: rtm = (struct rt_msghdr *)(void *)p; if (rtm->rtm_version != RTM_VERSION) { trace(1, "unexpected rtmsg version %d " "(should be %d)\n", rtm->rtm_version, RTM_VERSION); continue; } /* * Only messages that use the struct rt_msghdr * format are allowed beyond this point. */ if (rtm->rtm_type > RTM_RESOLVE) { trace(1, "rtmsg type %d ignored\n", rtm->rtm_type); continue; } addrs = rtm->rtm_addrs; q = (char *)(rtm + 1); if (rtm->rtm_pid == pid) { #if 0 trace(1, "rtmsg looped back to me, ignored\n"); #endif continue; } break; } memset(&rta, 0, sizeof(rta)); for (i = 0; i < RTAX_MAX; i++) { if (addrs & (1 << i)) { rta[i] = (struct sockaddr_in6 *)(void *)q; q += ROUNDUP(rta[i]->sin6_len); } } trace(1, "rtsock: %s (addrs=%x)\n", rttypes((struct rt_msghdr *)(void *)p), addrs); if (dflag >= 2) { for (i = 0; i < ((struct rt_msghdr *)(void *)p)->rtm_msglen; i++) { fprintf(stderr, "%02x ", p[i] & 0xff); if (i % 16 == 15) fprintf(stderr, "\n"); } fprintf(stderr, "\n"); } /* * Easy ones first. * * We may be able to optimize by using ifm->ifm_index or * ifam->ifam_index. For simplicity we don't do that here. */ switch (((struct rt_msghdr *)(void *)p)->rtm_type) { case RTM_NEWADDR: case RTM_IFINFO: iface++; continue; case RTM_ADD: rtable++; continue; case RTM_LOSING: case RTM_MISS: case RTM_GET: case RTM_LOCK: /* nothing to be done here */ trace(1, "\tnothing to be done, ignored\n"); continue; } #if 0 if (rta[RTAX_DST] == NULL) { trace(1, "\tno destination, ignored\n"); continue; } if (rta[RTAX_DST]->sin6_family != AF_INET6) { trace(1, "\taf mismatch, ignored\n"); continue; } if (IN6_IS_ADDR_LINKLOCAL(&rta[RTAX_DST]->sin6_addr)) { trace(1, "\tlinklocal destination, ignored\n"); continue; } if (IN6_ARE_ADDR_EQUAL(&rta[RTAX_DST]->sin6_addr, &in6addr_loopback)) { trace(1, "\tloopback destination, ignored\n"); continue; /* Loopback */ } if (IN6_IS_ADDR_MULTICAST(&rta[RTAX_DST]->sin6_addr)) { trace(1, "\tmulticast destination, ignored\n"); continue; } #endif /* hard ones */ switch (((struct rt_msghdr *)(void *)p)->rtm_type) { case RTM_NEWADDR: case RTM_IFINFO: case RTM_ADD: case RTM_LOSING: case RTM_MISS: case RTM_GET: case RTM_LOCK: /* should already be handled */ fatal("rtrecv: never reach here"); /*NOTREACHED*/ case RTM_DELETE: if (!rta[RTAX_DST] || !rta[RTAX_GATEWAY]) { trace(1, "\tsome of dst/gw/netamsk are " "unavailable, ignored\n"); break; } if ((rtm->rtm_flags & RTF_HOST) != 0) { mask.sin6_len = sizeof(mask); memset(&mask.sin6_addr, 0xff, sizeof(mask.sin6_addr)); rta[RTAX_NETMASK] = &mask; } else if (!rta[RTAX_NETMASK]) { trace(1, "\tsome of dst/gw/netamsk are " "unavailable, ignored\n"); break; } if (rt_del(rta[RTAX_DST], rta[RTAX_GATEWAY], rta[RTAX_NETMASK]) == 0) { rtable++; /*just to be sure*/ } break; case RTM_CHANGE: case RTM_REDIRECT: trace(1, "\tnot supported yet, ignored\n"); break; case RTM_DELADDR: if (!rta[RTAX_NETMASK] || !rta[RTAX_IFA]) { trace(1, "\tno netmask or ifa given, ignored\n"); break; } if (ifam->ifam_index < nindex2ifc) ifcp = index2ifc[ifam->ifam_index]; else ifcp = NULL; if (!ifcp) { trace(1, "\tinvalid ifam_index %d, ignored\n", ifam->ifam_index); break; } if (!rt_deladdr(ifcp, rta[RTAX_IFA], rta[RTAX_NETMASK])) iface++; break; } } if (iface) { trace(1, "rtsock: reconfigure interfaces, refresh interface routes\n"); ifconfig(); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (ifcp->ifc_cflags & IFC_CHANGED) { if (ifrt(ifcp, 1)) { TAILQ_FOREACH(ic, &ifc_head, ifc_next) { if (ifcp->ifc_index == ic->ifc_index) continue; if (ic->ifc_flags & IFF_UP) ripsend(ic, &ic->ifc_ripsin, RRTF_CHANGED); } /* Reset the flag */ TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { rrt->rrt_rflags &= ~RRTF_CHANGED; } } ifcp->ifc_cflags &= ~IFC_CHANGED; } } } if (rtable) { trace(1, "rtsock: read routing table again\n"); krtread(1); } } /* * remove specified route from the internal routing table. */ static int rt_del(const struct sockaddr_in6 *sdst, const struct sockaddr_in6 *sgw, const struct sockaddr_in6 *smask) { const struct in6_addr *dst = NULL; const struct in6_addr *gw = NULL; int prefix; struct netinfo6 ni6; struct riprt *rrt = NULL; time_t t_lifetime; if (sdst->sin6_family != AF_INET6) { trace(1, "\tother AF, ignored\n"); return -1; } if (IN6_IS_ADDR_LINKLOCAL(&sdst->sin6_addr) || IN6_ARE_ADDR_EQUAL(&sdst->sin6_addr, &in6addr_loopback) || IN6_IS_ADDR_MULTICAST(&sdst->sin6_addr)) { trace(1, "\taddress %s not interesting, ignored\n", inet6_n2p(&sdst->sin6_addr)); return -1; } dst = &sdst->sin6_addr; if (sgw->sin6_family == AF_INET6) { /* easy case */ gw = &sgw->sin6_addr; prefix = sin6mask2len(smask); } else if (sgw->sin6_family == AF_LINK) { /* * Interface route... a hard case. We need to get the prefix * length from the kernel, but we now are parsing rtmsg. * We'll purge matching routes from my list, then get the * fresh list. */ struct riprt *longest; trace(1, "\t%s is an interface route, guessing prefixlen\n", inet6_n2p(dst)); longest = NULL; TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest, &sdst->sin6_addr) && IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw)) { if (!longest || longest->rrt_info.rip6_plen < rrt->rrt_info.rip6_plen) { longest = rrt; } } } rrt = longest; if (!rrt) { trace(1, "\tno matching interface route found\n"); return -1; } gw = &in6addr_loopback; prefix = rrt->rrt_info.rip6_plen; } else { trace(1, "\tunsupported af: (gw=%d)\n", sgw->sin6_family); return -1; } trace(1, "\tdeleting %s/%d ", inet6_n2p(dst), prefix); trace(1, "gw %s\n", inet6_n2p(gw)); t_lifetime = time(NULL) - RIP_LIFETIME; /* age route for interface address */ memset(&ni6, 0, sizeof(ni6)); ni6.rip6_dest = *dst; ni6.rip6_plen = prefix; applyplen(&ni6.rip6_dest, ni6.rip6_plen); /*to be sure*/ trace(1, "\tfind route %s/%d\n", inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen); if (!rrt && (rrt = rtsearch(&ni6)) == NULL) { trace(1, "\tno route found\n"); return -1; } #if 0 if ((rrt->rrt_flags & RTF_STATIC) == 0) { trace(1, "\tyou can delete static routes only\n"); } else #endif if (!IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, gw)) { trace(1, "\tgw mismatch: %s <-> ", inet6_n2p(&rrt->rrt_gw)); trace(1, "%s\n", inet6_n2p(gw)); } else { trace(1, "\troute found, age it\n"); if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) { rrt->rrt_t = t_lifetime; rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; } } return 0; } /* * remove specified address from internal interface/routing table. */ static int rt_deladdr(struct ifc *ifcp, const struct sockaddr_in6 *sifa, const struct sockaddr_in6 *smask) { const struct in6_addr *addr = NULL; int prefix; struct ifac *ifac = NULL; struct netinfo6 ni6; struct riprt *rrt = NULL; time_t t_lifetime; int updated = 0; if (sifa->sin6_family != AF_INET6) { trace(1, "\tother AF, ignored\n"); return -1; } addr = &sifa->sin6_addr; prefix = sin6mask2len(smask); trace(1, "\tdeleting %s/%d from %s\n", inet6_n2p(addr), prefix, ifcp->ifc_name); ifac = ifa_match(ifcp, addr, prefix); if (!ifac) { trace(1, "\tno matching ifa found for %s/%d on %s\n", inet6_n2p(addr), prefix, ifcp->ifc_name); return -1; } if (ifac->ifac_ifc != ifcp) { trace(1, "\taddress table corrupt: back pointer does not match " "(%s != %s)\n", ifcp->ifc_name, ifac->ifac_ifc->ifc_name); return -1; } TAILQ_REMOVE(&ifcp->ifc_ifac_head, ifac, ifac_next); t_lifetime = time(NULL) - RIP_LIFETIME; /* age route for interface address */ memset(&ni6, 0, sizeof(ni6)); ni6.rip6_dest = ifac->ifac_addr; ni6.rip6_plen = ifac->ifac_plen; applyplen(&ni6.rip6_dest, ni6.rip6_plen); trace(1, "\tfind interface route %s/%d on %d\n", inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen, ifcp->ifc_index); if ((rrt = rtsearch(&ni6)) != NULL) { struct in6_addr none; memset(&none, 0, sizeof(none)); if (rrt->rrt_index == ifcp->ifc_index && (IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, &none) || IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw))) { trace(1, "\troute found, age it\n"); if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) { rrt->rrt_t = t_lifetime; rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; } updated++; } else { trace(1, "\tnon-interface route found: %s/%d on %d\n", inet6_n2p(&rrt->rrt_info.rip6_dest), rrt->rrt_info.rip6_plen, rrt->rrt_index); } } else trace(1, "\tno interface route found\n"); /* age route for p2p destination */ if (ifcp->ifc_flags & IFF_POINTOPOINT) { memset(&ni6, 0, sizeof(ni6)); ni6.rip6_dest = ifac->ifac_raddr; ni6.rip6_plen = 128; applyplen(&ni6.rip6_dest, ni6.rip6_plen); /*to be sure*/ trace(1, "\tfind p2p route %s/%d on %d\n", inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen, ifcp->ifc_index); if ((rrt = rtsearch(&ni6)) != NULL) { if (rrt->rrt_index == ifcp->ifc_index && IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, &ifac->ifac_addr)) { trace(1, "\troute found, age it\n"); if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) { rrt->rrt_t = t_lifetime; rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; updated++; } } else { trace(1, "\tnon-p2p route found: %s/%d on %d\n", inet6_n2p(&rrt->rrt_info.rip6_dest), rrt->rrt_info.rip6_plen, rrt->rrt_index); } } else trace(1, "\tno p2p route found\n"); } free(ifac); return ((updated) ? 0 : -1); } /* * Get each interface address and put those interface routes to the route * list. */ static int ifrt(struct ifc *ifcp, int again) { struct ifac *ifac; struct riprt *rrt = NULL, *search_rrt, *loop_rrt; struct netinfo6 *np; time_t t_lifetime; int need_trigger = 0; #if 0 if (ifcp->ifc_flags & IFF_LOOPBACK) return 0; /* ignore loopback */ #endif if (ifcp->ifc_flags & IFF_POINTOPOINT) { ifrt_p2p(ifcp, again); return 0; } TAILQ_FOREACH(ifac, &ifcp->ifc_ifac_head, ifac_next) { if (IN6_IS_ADDR_LINKLOCAL(&ifac->ifac_addr)) { #if 0 trace(1, "route: %s on %s: " "skip linklocal interface address\n", inet6_n2p(&ifac->ifac_addr), ifcp->ifc_name); #endif continue; } if (IN6_IS_ADDR_UNSPECIFIED(&ifac->ifac_addr)) { #if 0 trace(1, "route: %s: skip unspec interface address\n", ifcp->ifc_name); #endif continue; } if (IN6_IS_ADDR_LOOPBACK(&ifac->ifac_addr)) { #if 0 trace(1, "route: %s: skip loopback address\n", ifcp->ifc_name); #endif continue; } if (ifcp->ifc_flags & IFF_UP) { if ((rrt = MALLOC(struct riprt)) == NULL) fatal("malloc: struct riprt"); memset(rrt, 0, sizeof(*rrt)); rrt->rrt_same = NULL; rrt->rrt_index = ifcp->ifc_index; rrt->rrt_t = 0; /* don't age */ rrt->rrt_info.rip6_dest = ifac->ifac_addr; rrt->rrt_info.rip6_tag = htons(routetag & 0xffff); rrt->rrt_info.rip6_metric = 1 + ifcp->ifc_metric; rrt->rrt_info.rip6_plen = ifac->ifac_plen; rrt->rrt_flags = RTF_HOST; rrt->rrt_rflags |= RRTF_CHANGED; applyplen(&rrt->rrt_info.rip6_dest, ifac->ifac_plen); memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr)); rrt->rrt_gw = ifac->ifac_addr; np = &rrt->rrt_info; search_rrt = rtsearch(np); if (search_rrt != NULL) { if (search_rrt->rrt_info.rip6_metric <= rrt->rrt_info.rip6_metric) { /* Already have better route */ if (!again) { trace(1, "route: %s/%d: " "already registered (%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, ifcp->ifc_name); } goto next; } TAILQ_REMOVE(&riprt_head, rrt, rrt_next); delroute(&rrt->rrt_info, &rrt->rrt_gw); } /* Attach the route to the list */ trace(1, "route: %s/%d: register route (%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, ifcp->ifc_name); TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); addroute(rrt, &rrt->rrt_gw, ifcp); rrt = NULL; sendrequest(ifcp); ripsend(ifcp, &ifcp->ifc_ripsin, 0); need_trigger = 1; } else { TAILQ_FOREACH(loop_rrt, &riprt_head, rrt_next) { if (loop_rrt->rrt_index == ifcp->ifc_index) { t_lifetime = time(NULL) - RIP_LIFETIME; if (loop_rrt->rrt_t == 0 || loop_rrt->rrt_t > t_lifetime) { loop_rrt->rrt_t = t_lifetime; loop_rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; loop_rrt->rrt_rflags |= RRTF_CHANGED; need_trigger = 1; } } } } next: if (rrt) free(rrt); } return need_trigger; } /* * there are couple of p2p interface routing models. "behavior" lets * you pick one. it looks that gated behavior fits best with BSDs, * since BSD kernels do not look at prefix length on p2p interfaces. */ static void ifrt_p2p(struct ifc *ifcp, int again) { struct ifac *ifac; struct riprt *rrt, *orrt; struct netinfo6 *np; struct in6_addr addr, dest; int advert, ignore, i; #define P2PADVERT_NETWORK 1 #define P2PADVERT_ADDR 2 #define P2PADVERT_DEST 4 #define P2PADVERT_MAX 4 const enum { CISCO, GATED, ROUTE6D } behavior = GATED; const char *category = ""; const char *noadv; TAILQ_FOREACH(ifac, &ifcp->ifc_ifac_head, ifac_next) { addr = ifac->ifac_addr; dest = ifac->ifac_raddr; applyplen(&addr, ifac->ifac_plen); applyplen(&dest, ifac->ifac_plen); advert = ignore = 0; switch (behavior) { case CISCO: /* * honor addr/plen, just like normal shared medium * interface. this may cause trouble if you reuse * addr/plen on other interfaces. * * advertise addr/plen. */ advert |= P2PADVERT_NETWORK; break; case GATED: /* * prefixlen on p2p interface is meaningless. * advertise addr/128 and dest/128. * * do not install network route to route6d routing * table (if we do, it would prevent route installation * for other p2p interface that shares addr/plen). * * XXX what should we do if dest is ::? it will not * get announced anyways (see following filter), * but we need to think. */ advert |= P2PADVERT_ADDR; advert |= P2PADVERT_DEST; ignore |= P2PADVERT_NETWORK; break; case ROUTE6D: /* * just for testing. actually the code is redundant * given the current p2p interface address assignment * rule for kame kernel. * * intent: * A/n -> announce A/n * A B/n, A and B share prefix -> A/n (= B/n) * A B/n, do not share prefix -> A/128 and B/128 * actually, A/64 and A B/128 are the only cases * permitted by the kernel: * A/64 -> A/64 * A B/128 -> A/128 and B/128 */ if (!IN6_IS_ADDR_UNSPECIFIED(&ifac->ifac_raddr)) { if (IN6_ARE_ADDR_EQUAL(&addr, &dest)) advert |= P2PADVERT_NETWORK; else { advert |= P2PADVERT_ADDR; advert |= P2PADVERT_DEST; ignore |= P2PADVERT_NETWORK; } } else advert |= P2PADVERT_NETWORK; break; } for (i = 1; i <= P2PADVERT_MAX; i *= 2) { if ((ignore & i) != 0) continue; if ((rrt = MALLOC(struct riprt)) == NULL) { fatal("malloc: struct riprt"); /*NOTREACHED*/ } memset(rrt, 0, sizeof(*rrt)); rrt->rrt_same = NULL; rrt->rrt_index = ifcp->ifc_index; rrt->rrt_t = 0; /* don't age */ switch (i) { case P2PADVERT_NETWORK: rrt->rrt_info.rip6_dest = ifac->ifac_addr; rrt->rrt_info.rip6_plen = ifac->ifac_plen; applyplen(&rrt->rrt_info.rip6_dest, ifac->ifac_plen); category = "network"; break; case P2PADVERT_ADDR: rrt->rrt_info.rip6_dest = ifac->ifac_addr; rrt->rrt_info.rip6_plen = 128; rrt->rrt_gw = in6addr_loopback; category = "addr"; break; case P2PADVERT_DEST: rrt->rrt_info.rip6_dest = ifac->ifac_raddr; rrt->rrt_info.rip6_plen = 128; rrt->rrt_gw = ifac->ifac_addr; category = "dest"; break; } if (IN6_IS_ADDR_UNSPECIFIED(&rrt->rrt_info.rip6_dest) || IN6_IS_ADDR_LINKLOCAL(&rrt->rrt_info.rip6_dest)) { #if 0 trace(1, "route: %s: skip unspec/linklocal " "(%s on %s)\n", category, ifcp->ifc_name); #endif free(rrt); continue; } if ((advert & i) == 0) { rrt->rrt_rflags |= RRTF_NOADVERTISE; noadv = ", NO-ADV"; } else noadv = ""; rrt->rrt_info.rip6_tag = htons(routetag & 0xffff); rrt->rrt_info.rip6_metric = 1 + ifcp->ifc_metric; np = &rrt->rrt_info; orrt = rtsearch(np); if (!orrt) { /* Attach the route to the list */ trace(1, "route: %s/%d: register route " "(%s on %s%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, category, ifcp->ifc_name, noadv); TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); } else if (rrt->rrt_index != orrt->rrt_index || rrt->rrt_info.rip6_metric != orrt->rrt_info.rip6_metric) { /* replace route */ TAILQ_INSERT_BEFORE(orrt, rrt, rrt_next); TAILQ_REMOVE(&riprt_head, orrt, rrt_next); free(orrt); trace(1, "route: %s/%d: update (%s on %s%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, category, ifcp->ifc_name, noadv); } else { /* Already found */ if (!again) { trace(1, "route: %s/%d: " "already registered (%s on %s%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, category, ifcp->ifc_name, noadv); } free(rrt); } } } #undef P2PADVERT_NETWORK #undef P2PADVERT_ADDR #undef P2PADVERT_DEST #undef P2PADVERT_MAX } static int getifmtu(int ifindex) { int mib[6]; char *buf; size_t msize; struct if_msghdr *ifm; int mtu; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET6; mib[4] = NET_RT_IFLIST; mib[5] = ifindex; if (sysctl(mib, nitems(mib), NULL, &msize, NULL, 0) < 0) { fatal("sysctl estimate NET_RT_IFLIST"); /*NOTREACHED*/ } if ((buf = malloc(msize)) == NULL) { fatal("malloc"); /*NOTREACHED*/ } if (sysctl(mib, nitems(mib), buf, &msize, NULL, 0) < 0) { fatal("sysctl NET_RT_IFLIST"); /*NOTREACHED*/ } ifm = (struct if_msghdr *)(void *)buf; mtu = ifm->ifm_data.ifi_mtu; if (ifindex != ifm->ifm_index) { fatal("ifindex does not match with ifm_index"); /*NOTREACHED*/ } free(buf); return mtu; } static const char * rttypes(struct rt_msghdr *rtm) { #define RTTYPE(s, f) \ do { \ if (rtm->rtm_type == (f)) \ return (s); \ } while (0) RTTYPE("ADD", RTM_ADD); RTTYPE("DELETE", RTM_DELETE); RTTYPE("CHANGE", RTM_CHANGE); RTTYPE("GET", RTM_GET); RTTYPE("LOSING", RTM_LOSING); RTTYPE("REDIRECT", RTM_REDIRECT); RTTYPE("MISS", RTM_MISS); RTTYPE("LOCK", RTM_LOCK); RTTYPE("NEWADDR", RTM_NEWADDR); RTTYPE("DELADDR", RTM_DELADDR); RTTYPE("IFINFO", RTM_IFINFO); #ifdef RTM_OIFINFO RTTYPE("OIFINFO", RTM_OIFINFO); #endif #ifdef RTM_IFANNOUNCE RTTYPE("IFANNOUNCE", RTM_IFANNOUNCE); #endif #ifdef RTM_NEWMADDR RTTYPE("NEWMADDR", RTM_NEWMADDR); #endif #ifdef RTM_DELMADDR RTTYPE("DELMADDR", RTM_DELMADDR); #endif #undef RTTYPE return NULL; } static const char * rtflags(struct rt_msghdr *rtm) { static char buf[BUFSIZ]; /* * letter conflict should be okay. painful when *BSD diverges... */ strlcpy(buf, "", sizeof(buf)); #define RTFLAG(s, f) \ do { \ if (rtm->rtm_flags & (f)) \ strlcat(buf, (s), sizeof(buf)); \ } while (0) RTFLAG("U", RTF_UP); RTFLAG("G", RTF_GATEWAY); RTFLAG("H", RTF_HOST); RTFLAG("R", RTF_REJECT); RTFLAG("D", RTF_DYNAMIC); RTFLAG("M", RTF_MODIFIED); RTFLAG("d", RTF_DONE); #ifdef RTF_MASK RTFLAG("m", RTF_MASK); #endif #ifdef RTF_CLONED RTFLAG("c", RTF_CLONED); #endif RTFLAG("X", RTF_XRESOLVE); #ifdef RTF_LLINFO RTFLAG("L", RTF_LLINFO); #endif RTFLAG("S", RTF_STATIC); RTFLAG("B", RTF_BLACKHOLE); #ifdef RTF_PROTO3 RTFLAG("3", RTF_PROTO3); #endif RTFLAG("2", RTF_PROTO2); RTFLAG("1", RTF_PROTO1); #ifdef RTF_BROADCAST RTFLAG("b", RTF_BROADCAST); #endif #ifdef RTF_DEFAULT RTFLAG("d", RTF_DEFAULT); #endif #ifdef RTF_ISAROUTER RTFLAG("r", RTF_ISAROUTER); #endif #ifdef RTF_TUNNEL RTFLAG("T", RTF_TUNNEL); #endif #ifdef RTF_AUTH RTFLAG("A", RTF_AUTH); #endif #ifdef RTF_CRYPT RTFLAG("E", RTF_CRYPT); #endif #undef RTFLAG return buf; } static const char * ifflags(int flags) { static char buf[BUFSIZ]; strlcpy(buf, "", sizeof(buf)); #define IFFLAG(s, f) \ do { \ if (flags & (f)) { \ if (buf[0]) \ strlcat(buf, ",", sizeof(buf)); \ strlcat(buf, (s), sizeof(buf)); \ } \ } while (0) IFFLAG("UP", IFF_UP); IFFLAG("BROADCAST", IFF_BROADCAST); IFFLAG("DEBUG", IFF_DEBUG); IFFLAG("LOOPBACK", IFF_LOOPBACK); IFFLAG("POINTOPOINT", IFF_POINTOPOINT); #ifdef IFF_NOTRAILERS IFFLAG("NOTRAILERS", IFF_NOTRAILERS); #endif IFFLAG("RUNNING", IFF_RUNNING); IFFLAG("NOARP", IFF_NOARP); IFFLAG("PROMISC", IFF_PROMISC); IFFLAG("ALLMULTI", IFF_ALLMULTI); IFFLAG("OACTIVE", IFF_OACTIVE); IFFLAG("SIMPLEX", IFF_SIMPLEX); IFFLAG("LINK0", IFF_LINK0); IFFLAG("LINK1", IFF_LINK1); IFFLAG("LINK2", IFF_LINK2); IFFLAG("MULTICAST", IFF_MULTICAST); #undef IFFLAG return buf; } static void krtread(int again) { int mib[6]; size_t msize; char *buf, *p, *lim; struct rt_msghdr *rtm; int retry; const char *errmsg; retry = 0; buf = NULL; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET6; /* Address family */ mib[4] = NET_RT_DUMP; /* Dump the kernel routing table */ mib[5] = 0; /* No flags */ do { if (retry) sleep(1); retry++; errmsg = NULL; if (buf) { free(buf); buf = NULL; } if (sysctl(mib, nitems(mib), NULL, &msize, NULL, 0) < 0) { errmsg = "sysctl estimate"; continue; } if ((buf = malloc(msize)) == NULL) { errmsg = "malloc"; continue; } if (sysctl(mib, nitems(mib), buf, &msize, NULL, 0) < 0) { errmsg = "sysctl NET_RT_DUMP"; continue; } } while (retry < RT_DUMP_MAXRETRY && errmsg != NULL); if (errmsg) { fatal("%s (with %d retries, msize=%lu)", errmsg, retry, (u_long)msize); /*NOTREACHED*/ } else if (1 < retry) syslog(LOG_INFO, "NET_RT_DUMP %d retires", retry); lim = buf + msize; for (p = buf; p < lim; p += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)p; rt_entry(rtm, again); } free(buf); } static void rt_entry(struct rt_msghdr *rtm, int again) { struct sockaddr_in6 *sin6_dst, *sin6_gw, *sin6_mask; struct sockaddr_in6 *sin6_genmask, *sin6_ifp; char *rtmp, *ifname = NULL; struct riprt *rrt, *orrt; struct netinfo6 *np; int ifindex; sin6_dst = sin6_gw = sin6_mask = sin6_genmask = sin6_ifp = 0; if ((rtm->rtm_flags & RTF_UP) == 0 || rtm->rtm_flags & (RTF_XRESOLVE|RTF_BLACKHOLE)) { return; /* not interested in the link route */ } /* do not look at cloned routes */ #ifdef RTF_WASCLONED if (rtm->rtm_flags & RTF_WASCLONED) return; #endif #ifdef RTF_CLONED if (rtm->rtm_flags & RTF_CLONED) return; #endif /* XXX: Ignore connected routes. */ if (!(rtm->rtm_flags & (RTF_GATEWAY|RTF_HOST|RTF_STATIC))) return; /* * do not look at dynamic routes. * netbsd/openbsd cloned routes have UGHD. */ if (rtm->rtm_flags & RTF_DYNAMIC) return; rtmp = (char *)(rtm + 1); /* Destination */ if ((rtm->rtm_addrs & RTA_DST) == 0) return; /* ignore routes without destination address */ sin6_dst = (struct sockaddr_in6 *)(void *)rtmp; rtmp += ROUNDUP(sin6_dst->sin6_len); if (rtm->rtm_addrs & RTA_GATEWAY) { sin6_gw = (struct sockaddr_in6 *)(void *)rtmp; rtmp += ROUNDUP(sin6_gw->sin6_len); } if (rtm->rtm_addrs & RTA_NETMASK) { sin6_mask = (struct sockaddr_in6 *)(void *)rtmp; rtmp += ROUNDUP(sin6_mask->sin6_len); } if (rtm->rtm_addrs & RTA_GENMASK) { sin6_genmask = (struct sockaddr_in6 *)(void *)rtmp; rtmp += ROUNDUP(sin6_genmask->sin6_len); } if (rtm->rtm_addrs & RTA_IFP) { sin6_ifp = (struct sockaddr_in6 *)(void *)rtmp; rtmp += ROUNDUP(sin6_ifp->sin6_len); } /* Destination */ if (sin6_dst->sin6_family != AF_INET6) return; if (IN6_IS_ADDR_LINKLOCAL(&sin6_dst->sin6_addr)) return; /* Link-local */ if (IN6_ARE_ADDR_EQUAL(&sin6_dst->sin6_addr, &in6addr_loopback)) return; /* Loopback */ if (IN6_IS_ADDR_MULTICAST(&sin6_dst->sin6_addr)) return; if ((rrt = MALLOC(struct riprt)) == NULL) { fatal("malloc: struct riprt"); /*NOTREACHED*/ } memset(rrt, 0, sizeof(*rrt)); np = &rrt->rrt_info; rrt->rrt_same = NULL; rrt->rrt_t = time(NULL); if (aflag == 0 && (rtm->rtm_flags & RTF_STATIC)) rrt->rrt_t = 0; /* Don't age static routes */ if (rtm->rtm_flags & Pflag) rrt->rrt_t = 0; /* Don't age PROTO[123] routes */ if ((rtm->rtm_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST) rrt->rrt_t = 0; /* Don't age non-gateway host routes */ np->rip6_tag = 0; np->rip6_metric = rtm->rtm_rmx.rmx_hopcount; if (np->rip6_metric < 1) np->rip6_metric = 1; rrt->rrt_flags = rtm->rtm_flags; np->rip6_dest = sin6_dst->sin6_addr; /* Mask or plen */ if (rtm->rtm_flags & RTF_HOST) np->rip6_plen = 128; /* Host route */ else if (sin6_mask) np->rip6_plen = sin6mask2len(sin6_mask); else np->rip6_plen = 0; orrt = rtsearch(np); if (orrt && orrt->rrt_info.rip6_metric != HOPCNT_INFINITY6) { /* Already found */ if (!again) { trace(1, "route: %s/%d flags %s: already registered\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, rtflags(rtm)); } free(rrt); return; } /* Gateway */ if (!sin6_gw) memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr)); else { if (sin6_gw->sin6_family == AF_INET6) rrt->rrt_gw = sin6_gw->sin6_addr; else if (sin6_gw->sin6_family == AF_LINK) { /* XXX in case ppp link? */ rrt->rrt_gw = in6addr_loopback; } else memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr)); } trace(1, "route: %s/%d flags %s", inet6_n2p(&np->rip6_dest), np->rip6_plen, rtflags(rtm)); trace(1, " gw %s", inet6_n2p(&rrt->rrt_gw)); /* Interface */ ifindex = rtm->rtm_index; if ((unsigned int)ifindex < nindex2ifc && index2ifc[ifindex]) ifname = index2ifc[ifindex]->ifc_name; else { trace(1, " not configured\n"); free(rrt); return; } trace(1, " if %s sock %d", ifname, ifindex); rrt->rrt_index = ifindex; trace(1, "\n"); /* Check gateway */ if (!IN6_IS_ADDR_LINKLOCAL(&rrt->rrt_gw) && !IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw) && (rrt->rrt_flags & RTF_LOCAL) == 0) { trace(0, "***** Gateway %s is not a link-local address.\n", inet6_n2p(&rrt->rrt_gw)); trace(0, "***** dest(%s) if(%s) -- Not optimized.\n", inet6_n2p(&rrt->rrt_info.rip6_dest), ifname); rrt->rrt_rflags |= RRTF_NH_NOT_LLADDR; } /* Put it to the route list */ if (orrt && orrt->rrt_info.rip6_metric == HOPCNT_INFINITY6) { /* replace route list */ TAILQ_INSERT_BEFORE(orrt, rrt, rrt_next); TAILQ_REMOVE(&riprt_head, orrt, rrt_next); trace(1, "route: %s/%d flags %s: replace new route\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, rtflags(rtm)); free(orrt); } else TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); } static int addroute(struct riprt *rrt, const struct in6_addr *gw, struct ifc *ifcp) { struct netinfo6 *np; u_char buf[BUFSIZ], buf1[BUFSIZ], buf2[BUFSIZ]; struct rt_msghdr *rtm; struct sockaddr_in6 *sin6; int len; np = &rrt->rrt_info; inet_ntop(AF_INET6, (const void *)gw, (char *)buf1, sizeof(buf1)); inet_ntop(AF_INET6, (void *)&ifcp->ifc_mylladdr, (char *)buf2, sizeof(buf2)); tracet(1, "ADD: %s/%d gw %s [%d] ifa %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1, np->rip6_metric - 1, buf2); if (rtlog) fprintf(rtlog, "%s: ADD: %s/%d gw %s [%d] ifa %s\n", hms(), inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1, np->rip6_metric - 1, buf2); if (nflag) return 0; memset(buf, 0, sizeof(buf)); rtm = (struct rt_msghdr *)(void *)buf; rtm->rtm_type = RTM_ADD; rtm->rtm_version = RTM_VERSION; rtm->rtm_seq = ++seq; rtm->rtm_pid = pid; rtm->rtm_flags = rrt->rrt_flags; rtm->rtm_flags |= Qflag; rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; rtm->rtm_rmx.rmx_hopcount = np->rip6_metric - 1; rtm->rtm_inits = RTV_HOPCOUNT; sin6 = (struct sockaddr_in6 *)(void *)&buf[sizeof(struct rt_msghdr)]; /* Destination */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = np->rip6_dest; sin6 = (struct sockaddr_in6 *)(void *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); /* Gateway */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = *gw; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) sin6->sin6_scope_id = ifcp->ifc_index; sin6 = (struct sockaddr_in6 *)(void *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); /* Netmask */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = *(plen2mask(np->rip6_plen)); sin6 = (struct sockaddr_in6 *)(void *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); len = (char *)sin6 - (char *)buf; rtm->rtm_msglen = len; if (write(rtsock, buf, len) > 0) return 0; if (errno == EEXIST) { trace(0, "ADD: Route already exists %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1); if (rtlog) fprintf(rtlog, "ADD: Route already exists %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1); } else { trace(0, "Can not write to rtsock (addroute): %s\n", strerror(errno)); if (rtlog) fprintf(rtlog, "\tCan not write to rtsock: %s\n", strerror(errno)); } return -1; } static int delroute(struct netinfo6 *np, struct in6_addr *gw) { u_char buf[BUFSIZ], buf2[BUFSIZ]; struct rt_msghdr *rtm; struct sockaddr_in6 *sin6; int len; inet_ntop(AF_INET6, (void *)gw, (char *)buf2, sizeof(buf2)); tracet(1, "DEL: %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2); if (rtlog) fprintf(rtlog, "%s: DEL: %s/%d gw %s\n", hms(), inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2); if (nflag) return 0; memset(buf, 0, sizeof(buf)); rtm = (struct rt_msghdr *)(void *)buf; rtm->rtm_type = RTM_DELETE; rtm->rtm_version = RTM_VERSION; rtm->rtm_seq = ++seq; rtm->rtm_pid = pid; rtm->rtm_flags = RTF_UP | RTF_GATEWAY; rtm->rtm_flags |= Qflag; if (np->rip6_plen == sizeof(struct in6_addr) * 8) rtm->rtm_flags |= RTF_HOST; rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; sin6 = (struct sockaddr_in6 *)(void *)&buf[sizeof(struct rt_msghdr)]; /* Destination */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = np->rip6_dest; sin6 = (struct sockaddr_in6 *)(void *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); /* Gateway */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = *gw; sin6 = (struct sockaddr_in6 *)(void *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); /* Netmask */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = *(plen2mask(np->rip6_plen)); sin6 = (struct sockaddr_in6 *)(void *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); len = (char *)sin6 - (char *)buf; rtm->rtm_msglen = len; if (write(rtsock, buf, len) >= 0) return 0; if (errno == ESRCH) { trace(0, "RTDEL: Route does not exist: %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2); if (rtlog) fprintf(rtlog, "RTDEL: Route does not exist: %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2); } else { trace(0, "Can not write to rtsock (delroute): %s\n", strerror(errno)); if (rtlog) fprintf(rtlog, "\tCan not write to rtsock: %s\n", strerror(errno)); } return -1; } #if 0 static struct in6_addr * getroute(struct netinfo6 *np, struct in6_addr *gw) { u_char buf[BUFSIZ]; int myseq; int len; struct rt_msghdr *rtm; struct sockaddr_in6 *sin6; rtm = (struct rt_msghdr *)(void *)buf; len = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in6); memset(rtm, 0, len); rtm->rtm_type = RTM_GET; rtm->rtm_version = RTM_VERSION; myseq = ++seq; rtm->rtm_seq = myseq; rtm->rtm_addrs = RTA_DST; rtm->rtm_msglen = len; sin6 = (struct sockaddr_in6 *)(void *)&buf[sizeof(struct rt_msghdr)]; sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = np->rip6_dest; if (write(rtsock, buf, len) < 0) { if (errno == ESRCH) /* No such route found */ return NULL; perror("write to rtsock"); exit(1); } do { if ((len = read(rtsock, buf, sizeof(buf))) < 0) { perror("read from rtsock"); exit(1); } rtm = (struct rt_msghdr *)(void *)buf; } while (rtm->rtm_type != RTM_GET || rtm->rtm_seq != myseq || rtm->rtm_pid != pid); sin6 = (struct sockaddr_in6 *)(void *)&buf[sizeof(struct rt_msghdr)]; if (rtm->rtm_addrs & RTA_DST) { sin6 = (struct sockaddr_in6 *)(void *) ((char *)sin6 + ROUNDUP(sin6->sin6_len)); } if (rtm->rtm_addrs & RTA_GATEWAY) { *gw = sin6->sin6_addr; return gw; } return NULL; } #endif static const char * inet6_n2p(const struct in6_addr *p) { static char buf[BUFSIZ]; return inet_ntop(AF_INET6, (const void *)p, buf, sizeof(buf)); } static void ifrtdump(int sig) { ifdump(sig); rtdump(sig); } static void ifdump(int sig) { struct ifc *ifcp; FILE *dump; int nifc = 0; if (sig == 0) dump = stderr; else if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL) dump = stderr; fprintf(dump, "%s: Interface Table Dump\n", hms()); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) nifc++; fprintf(dump, " Number of interfaces: %d\n", nifc); fprintf(dump, " advertising interfaces:\n"); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if ((ifcp->ifc_flags & IFF_UP) == 0) continue; if (iff_find(ifcp, IFIL_TYPE_N) != NULL) continue; ifdump0(dump, ifcp); } fprintf(dump, "\n"); fprintf(dump, " non-advertising interfaces:\n"); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if ((ifcp->ifc_flags & IFF_UP) && (iff_find(ifcp, IFIL_TYPE_N) == NULL)) continue; ifdump0(dump, ifcp); } fprintf(dump, "\n"); if (dump != stderr) fclose(dump); } static void ifdump0(FILE *dump, const struct ifc *ifcp) { struct ifac *ifac; struct iff *iffp; char buf[BUFSIZ]; const char *ft; int addr; fprintf(dump, " %s: index(%d) flags(%s) addr(%s) mtu(%d) metric(%d)\n", ifcp->ifc_name, ifcp->ifc_index, ifflags(ifcp->ifc_flags), inet6_n2p(&ifcp->ifc_mylladdr), ifcp->ifc_mtu, ifcp->ifc_metric); TAILQ_FOREACH(ifac, &ifcp->ifc_ifac_head, ifac_next) { if (ifcp->ifc_flags & IFF_POINTOPOINT) { inet_ntop(AF_INET6, (void *)&ifac->ifac_raddr, buf, sizeof(buf)); fprintf(dump, "\t%s/%d -- %s\n", inet6_n2p(&ifac->ifac_addr), ifac->ifac_plen, buf); } else { fprintf(dump, "\t%s/%d\n", inet6_n2p(&ifac->ifac_addr), ifac->ifac_plen); } } fprintf(dump, "\tFilter:\n"); TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { addr = 0; switch (iffp->iff_type) { case IFIL_TYPE_A: ft = "Aggregate"; addr++; break; case IFIL_TYPE_N: ft = "No-use"; break; case IFIL_TYPE_O: ft = "Advertise-only"; addr++; break; case IFIL_TYPE_T: ft = "Default-only"; break; case IFIL_TYPE_L: ft = "Listen-only"; addr++; break; default: snprintf(buf, sizeof(buf), "Unknown-%c", iffp->iff_type); ft = buf; addr++; break; } fprintf(dump, "\t\t%s", ft); if (addr) fprintf(dump, "(%s/%d)", inet6_n2p(&iffp->iff_addr), iffp->iff_plen); fprintf(dump, "\n"); } fprintf(dump, "\n"); } static void rtdump(int sig) { struct riprt *rrt; char buf[BUFSIZ]; FILE *dump; time_t t, age; if (sig == 0) dump = stderr; else if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL) dump = stderr; t = time(NULL); fprintf(dump, "\n%s: Routing Table Dump\n", hms()); TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_t == 0) age = 0; else age = t - rrt->rrt_t; inet_ntop(AF_INET6, (void *)&rrt->rrt_info.rip6_dest, buf, sizeof(buf)); fprintf(dump, " %s/%d if(%d:%s) gw(%s) [%d] age(%ld)", buf, rrt->rrt_info.rip6_plen, rrt->rrt_index, index2ifc[rrt->rrt_index]->ifc_name, inet6_n2p(&rrt->rrt_gw), rrt->rrt_info.rip6_metric, (long)age); if (rrt->rrt_info.rip6_tag) { fprintf(dump, " tag(0x%04x)", ntohs(rrt->rrt_info.rip6_tag) & 0xffff); } if (rrt->rrt_rflags & RRTF_NH_NOT_LLADDR) fprintf(dump, " NOT-LL"); if (rrt->rrt_rflags & RRTF_NOADVERTISE) fprintf(dump, " NO-ADV"); fprintf(dump, "\n"); } fprintf(dump, "\n"); if (dump != stderr) fclose(dump); } /* * Parse the -A (and -O) options and put corresponding filter object to the * specified interface structures. Each of the -A/O option has the following * syntax: -A 5f09:c400::/32,ef0,ef1 (aggregate) * -O 5f09:c400::/32,ef0,ef1 (only when match) */ static void filterconfig(void) { int i; char *p, *ap, *iflp, *ifname, *ep; struct iff iff, *iffp; struct ifc *ifcp; struct riprt *rrt; #if 0 struct in6_addr gw; #endif u_long plen; for (i = 0; i < nfilter; i++) { ap = filter[i]; iflp = NULL; iffp = ⇔ memset(iffp, 0, sizeof(*iffp)); if (filtertype[i] == 'N' || filtertype[i] == 'T') { iflp = ap; goto ifonly; } if ((p = strchr(ap, ',')) != NULL) { *p++ = '\0'; iflp = p; } if ((p = strchr(ap, '/')) == NULL) { fatal("no prefixlen specified for '%s'", ap); /*NOTREACHED*/ } *p++ = '\0'; if (inet_pton(AF_INET6, ap, &iffp->iff_addr) != 1) { fatal("invalid prefix specified for '%s'", ap); /*NOTREACHED*/ } errno = 0; ep = NULL; plen = strtoul(p, &ep, 10); if (errno || !*p || *ep || plen > sizeof(iffp->iff_addr) * 8) { fatal("invalid prefix length specified for '%s'", ap); /*NOTREACHED*/ } iffp->iff_plen = plen; applyplen(&iffp->iff_addr, iffp->iff_plen); ifonly: iffp->iff_type = filtertype[i]; if (iflp == NULL || *iflp == '\0') { fatal("no interface specified for '%s'", ap); /*NOTREACHED*/ } /* parse the interface listing portion */ while (iflp) { ifname = iflp; if ((iflp = strchr(iflp, ',')) != NULL) *iflp++ = '\0'; TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (fnmatch(ifname, ifcp->ifc_name, 0) != 0) continue; iffp = malloc(sizeof(*iffp)); if (iffp == NULL) { fatal("malloc of iff"); /*NOTREACHED*/ } memcpy(iffp, &iff, sizeof(*iffp)); #if 0 syslog(LOG_INFO, "Add filter: type %d, ifname %s.", iffp->iff_type, ifname); #endif TAILQ_INSERT_HEAD(&ifcp->ifc_iff_head, iffp, iff_next); } } /* * -A: aggregate configuration. */ if (filtertype[i] != IFIL_TYPE_A) continue; /* put the aggregate to the kernel routing table */ rrt = (struct riprt *)malloc(sizeof(struct riprt)); if (rrt == NULL) { fatal("malloc: rrt"); /*NOTREACHED*/ } memset(rrt, 0, sizeof(struct riprt)); rrt->rrt_info.rip6_dest = iff.iff_addr; rrt->rrt_info.rip6_plen = iff.iff_plen; rrt->rrt_info.rip6_metric = 1; rrt->rrt_info.rip6_tag = htons(routetag & 0xffff); rrt->rrt_gw = in6addr_loopback; rrt->rrt_flags = RTF_UP | RTF_REJECT; rrt->rrt_rflags = RRTF_AGGREGATE; rrt->rrt_t = 0; rrt->rrt_index = loopifcp->ifc_index; #if 0 if (getroute(&rrt->rrt_info, &gw)) { #if 0 /* * When the address has already been registered in the * kernel routing table, it should be removed */ delroute(&rrt->rrt_info, &gw); #else /* it is safer behavior */ errno = EINVAL; fatal("%s/%u already in routing table, " "cannot aggregate", inet6_n2p(&rrt->rrt_info.rip6_dest), rrt->rrt_info.rip6_plen); /*NOTREACHED*/ #endif } #endif /* Put the route to the list */ TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); trace(1, "Aggregate: %s/%d for %s\n", inet6_n2p(&iff.iff_addr), iff.iff_plen, loopifcp->ifc_name); /* Add this route to the kernel */ if (nflag) /* do not modify kernel routing table */ continue; addroute(rrt, &in6addr_loopback, loopifcp); } } /***************** utility functions *****************/ /* * Returns a pointer to ifac whose address and prefix length matches * with the address and prefix length specified in the arguments. */ static struct ifac * ifa_match(const struct ifc *ifcp, const struct in6_addr *ia, int plen) { struct ifac *ifac; TAILQ_FOREACH(ifac, &ifcp->ifc_ifac_head, ifac_next) { if (IN6_ARE_ADDR_EQUAL(&ifac->ifac_addr, ia) && ifac->ifac_plen == plen) break; } return (ifac); } /* * Return a pointer to riprt structure whose address and prefix length * matches with the address and prefix length found in the argument. * Note: This is not a rtalloc(). Therefore exact match is necessary. */ static struct riprt * rtsearch(struct netinfo6 *np) { struct riprt *rrt; TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_info.rip6_plen == np->rip6_plen && IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest, &np->rip6_dest)) break; } return (rrt); } static int sin6mask2len(const struct sockaddr_in6 *sin6) { return mask2len(&sin6->sin6_addr, sin6->sin6_len - offsetof(struct sockaddr_in6, sin6_addr)); } static int mask2len(const struct in6_addr *addr, int lenlim) { int i = 0, j; const u_char *p = (const u_char *)addr; for (j = 0; j < lenlim; j++, p++) { if (*p != 0xff) break; i += 8; } if (j < lenlim) { switch (*p) { #define MASKLEN(m, l) case m: do { i += l; break; } while (0) MASKLEN(0xfe, 7); break; MASKLEN(0xfc, 6); break; MASKLEN(0xf8, 5); break; MASKLEN(0xf0, 4); break; MASKLEN(0xe0, 3); break; MASKLEN(0xc0, 2); break; MASKLEN(0x80, 1); break; #undef MASKLEN } } return i; } static const u_char plent[8] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe }; static void applyplen(struct in6_addr *ia, int plen) { u_char *p; int i; p = ia->s6_addr; for (i = 0; i < 16; i++) { if (plen <= 0) *p = 0; else if (plen < 8) *p &= plent[plen]; p++, plen -= 8; } } static const int pl2m[9] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; static struct in6_addr * plen2mask(int n) { static struct in6_addr ia; u_char *p; int i; memset(&ia, 0, sizeof(struct in6_addr)); p = (u_char *)&ia; for (i = 0; i < 16; i++, p++, n -= 8) { if (n >= 8) { *p = 0xff; continue; } *p = pl2m[n]; break; } return &ia; } static char * allocopy(char *p) { int len = strlen(p) + 1; char *q = (char *)malloc(len); if (!q) { fatal("malloc"); /*NOTREACHED*/ } strlcpy(q, p, len); return q; } static char * hms(void) { static char buf[BUFSIZ]; time_t t; struct tm *tm; t = time(NULL); if ((tm = localtime(&t)) == 0) { fatal("localtime"); /*NOTREACHED*/ } snprintf(buf, sizeof(buf), "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); return buf; } #define RIPRANDDEV 1.0 /* 30 +- 15, max - min = 30 */ static int ripinterval(int timer) { double r = rand(); interval = (int)(timer + timer * RIPRANDDEV * (r / RAND_MAX - 0.5)); nextalarm = time(NULL) + interval; return interval; } #if 0 static time_t ripsuptrig(void) { time_t t; double r = rand(); t = (int)(RIP_TRIG_INT6_MIN + (RIP_TRIG_INT6_MAX - RIP_TRIG_INT6_MIN) * (r / RAND_MAX)); sup_trig_update = time(NULL) + t; return t; } #endif static void fatal(const char *fmt, ...) { va_list ap; char buf[1024]; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); perror(buf); if (errno) syslog(LOG_ERR, "%s: %s", buf, strerror(errno)); else syslog(LOG_ERR, "%s", buf); rtdexit(); } static void tracet(int level, const char *fmt, ...) { va_list ap; if (level <= dflag) { va_start(ap, fmt); fprintf(stderr, "%s: ", hms()); vfprintf(stderr, fmt, ap); va_end(ap); } if (dflag) { va_start(ap, fmt); if (level > 0) vsyslog(LOG_DEBUG, fmt, ap); else vsyslog(LOG_WARNING, fmt, ap); va_end(ap); } } static void trace(int level, const char *fmt, ...) { va_list ap; if (level <= dflag) { va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } if (dflag) { va_start(ap, fmt); if (level > 0) vsyslog(LOG_DEBUG, fmt, ap); else vsyslog(LOG_WARNING, fmt, ap); va_end(ap); } } static struct ifc * ifc_find(char *name) { struct ifc *ifcp; TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (strcmp(name, ifcp->ifc_name) == 0) break; } return (ifcp); } static struct iff * iff_find(struct ifc *ifcp, int type) { struct iff *iffp; TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (type == IFIL_TYPE_ANY || type == iffp->iff_type) break; } return (iffp); } static void setindex2ifc(int idx, struct ifc *ifcp) { int n, nsize; struct ifc **p; if (!index2ifc) { nindex2ifc = 5; /*initial guess*/ index2ifc = (struct ifc **) malloc(sizeof(*index2ifc) * nindex2ifc); if (index2ifc == NULL) { fatal("malloc"); /*NOTREACHED*/ } memset(index2ifc, 0, sizeof(*index2ifc) * nindex2ifc); } n = nindex2ifc; for (nsize = nindex2ifc; nsize <= idx; nsize *= 2) ; if (n != nsize) { p = (struct ifc **)realloc(index2ifc, sizeof(*index2ifc) * nsize); if (p == NULL) { fatal("realloc"); /*NOTREACHED*/ } memset(p + n, 0, sizeof(*index2ifc) * (nindex2ifc - n)); index2ifc = p; nindex2ifc = nsize; } index2ifc[idx] = ifcp; } Index: head/usr.sbin/rtadvd/rrenum.c =================================================================== --- head/usr.sbin/rtadvd/rrenum.c (revision 333474) +++ head/usr.sbin/rtadvd/rrenum.c (revision 333475) @@ -1,504 +1,505 @@ /* $FreeBSD$ */ /* $KAME: rrenum.c,v 1.12 2002/06/10 19:59:47 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * 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. */ #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rtadvd.h" #include "rrenum.h" #include "if.h" #define RR_ISSET_SEGNUM(segnum_bits, segnum) \ ((((segnum_bits)[(segnum) >> 5]) & (1 << ((segnum) & 31))) != 0) #define RR_SET_SEGNUM(segnum_bits, segnum) \ (((segnum_bits)[(segnum) >> 5]) |= (1 << ((segnum) & 31))) struct rr_operation { u_long rro_seqnum; u_long rro_segnum_bits[8]; }; static struct rr_operation rro; static int rr_rcvifindex; static int rrcmd2pco[RPM_PCO_MAX] = { 0, SIOCAIFPREFIX_IN6, SIOCCIFPREFIX_IN6, SIOCSGIFPREFIX_IN6 }; static int s = -1; /* * Check validity of a Prefix Control Operation(PCO). * return 0 on success, 1 on failure. */ static int rr_pco_check(int len, struct rr_pco_match *rpm) { struct rr_pco_use *rpu, *rpulim; int checklen; /* rpm->rpm_len must be (4N * 3) as router-renum-05.txt */ if ((rpm->rpm_len - 3) < 0 || /* must be at least 3 */ (rpm->rpm_len - 3) & 0x3) { /* must be multiple of 4 */ syslog(LOG_WARNING, "<%s> rpm_len %d is not 4N * 3", __func__, rpm->rpm_len); return (1); } /* rpm->rpm_code must be valid value */ switch (rpm->rpm_code) { case RPM_PCO_ADD: case RPM_PCO_CHANGE: case RPM_PCO_SETGLOBAL: break; default: syslog(LOG_WARNING, "<%s> unknown rpm_code %d", __func__, rpm->rpm_code); return (1); } /* rpm->rpm_matchlen must be 0 to 128 inclusive */ if (rpm->rpm_matchlen > 128) { syslog(LOG_WARNING, "<%s> rpm_matchlen %d is over 128", __func__, rpm->rpm_matchlen); return (1); } /* * rpu->rpu_uselen, rpu->rpu_keeplen, and sum of them must be * between 0 and 128 inclusive */ for (rpu = (struct rr_pco_use *)(rpm + 1), rpulim = (struct rr_pco_use *)((char *)rpm + len); rpu < rpulim; rpu += 1) { checklen = rpu->rpu_uselen; checklen += rpu->rpu_keeplen; /* * omit these check, because either of rpu_uselen * and rpu_keeplen is unsigned char * (128 > rpu_uselen > 0) * (128 > rpu_keeplen > 0) * (rpu_uselen + rpu_keeplen > 0) */ if (checklen > 128) { syslog(LOG_WARNING, "<%s> sum of rpu_uselen %d and" " rpu_keeplen %d is %d(over 128)", __func__, rpu->rpu_uselen, rpu->rpu_keeplen, rpu->rpu_uselen + rpu->rpu_keeplen); return (1); } } return (0); } static void do_use_prefix(int len, struct rr_pco_match *rpm, struct in6_rrenumreq *irr, int ifindex) { struct rr_pco_use *rpu, *rpulim; struct rainfo *rai; struct ifinfo *ifi; struct prefix *pfx; rpu = (struct rr_pco_use *)(rpm + 1); rpulim = (struct rr_pco_use *)((char *)rpm + len); if (rpu == rpulim) { /* no use prefix */ if (rpm->rpm_code == RPM_PCO_ADD) return; irr->irr_u_uselen = 0; irr->irr_u_keeplen = 0; irr->irr_raf_mask_onlink = 0; irr->irr_raf_mask_auto = 0; irr->irr_vltime = 0; irr->irr_pltime = 0; memset(&irr->irr_flags, 0, sizeof(irr->irr_flags)); irr->irr_useprefix.sin6_len = 0; /* let it mean, no addition */ irr->irr_useprefix.sin6_family = 0; irr->irr_useprefix.sin6_addr = in6addr_any; if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 && errno != EADDRNOTAVAIL) syslog(LOG_ERR, "<%s> ioctl: %s", __func__, strerror(errno)); return; } for (rpu = (struct rr_pco_use *)(rpm + 1), rpulim = (struct rr_pco_use *)((char *)rpm + len); rpu < rpulim; rpu += 1) { /* init in6_rrenumreq fields */ irr->irr_u_uselen = rpu->rpu_uselen; irr->irr_u_keeplen = rpu->rpu_keeplen; irr->irr_raf_mask_onlink = !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK); irr->irr_raf_mask_auto = !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_AUTO); irr->irr_vltime = ntohl(rpu->rpu_vltime); irr->irr_pltime = ntohl(rpu->rpu_pltime); irr->irr_raf_onlink = (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK) == 0 ? 0 : 1; irr->irr_raf_auto = (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_AUTO) == 0 ? 0 : 1; irr->irr_rrf_decrvalid = (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME) == 0 ? 0 : 1; irr->irr_rrf_decrprefd = (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME) == 0 ? 0 : 1; irr->irr_useprefix.sin6_len = sizeof(irr->irr_useprefix); irr->irr_useprefix.sin6_family = AF_INET6; irr->irr_useprefix.sin6_addr = rpu->rpu_prefix; if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 && errno != EADDRNOTAVAIL) syslog(LOG_ERR, "<%s> ioctl: %s", __func__, strerror(errno)); /* very adhoc: should be rewritten */ if (rpm->rpm_code == RPM_PCO_CHANGE && IN6_ARE_ADDR_EQUAL(&rpm->rpm_prefix, &rpu->rpu_prefix) && rpm->rpm_matchlen == rpu->rpu_uselen && rpu->rpu_uselen == rpu->rpu_keeplen) { ifi = if_indextoifinfo(ifindex); if (ifi == NULL || ifi->ifi_rainfo == NULL) continue; /* non-advertising IF */ rai = ifi->ifi_rainfo; TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { struct timespec now; if (prefix_match(&pfx->pfx_prefix, pfx->pfx_prefixlen, &rpm->rpm_prefix, rpm->rpm_matchlen)) { /* change parameters */ pfx->pfx_validlifetime = ntohl(rpu->rpu_vltime); pfx->pfx_preflifetime = ntohl(rpu->rpu_pltime); if (irr->irr_rrf_decrvalid) { clock_gettime(CLOCK_MONOTONIC_FAST, &now); pfx->pfx_vltimeexpire = now.tv_sec + pfx->pfx_validlifetime; } else pfx->pfx_vltimeexpire = 0; if (irr->irr_rrf_decrprefd) { clock_gettime(CLOCK_MONOTONIC_FAST, &now); pfx->pfx_pltimeexpire = now.tv_sec + pfx->pfx_preflifetime; } else pfx->pfx_pltimeexpire = 0; } } } } } /* * process a Prefix Control Operation(PCO). * return 0 on success, 1 on failure */ static int do_pco(struct icmp6_router_renum *rr, int len, struct rr_pco_match *rpm) { int ifindex = 0; struct in6_rrenumreq irr; struct ifinfo *ifi; if ((rr_pco_check(len, rpm) != 0)) return (1); if (s == -1 && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "<%s> socket: %s", __func__, strerror(errno)); exit(1); } memset(&irr, 0, sizeof(irr)); irr.irr_origin = PR_ORIG_RR; irr.irr_m_len = rpm->rpm_matchlen; irr.irr_m_minlen = rpm->rpm_minlen; irr.irr_m_maxlen = rpm->rpm_maxlen; irr.irr_matchprefix.sin6_len = sizeof(irr.irr_matchprefix); irr.irr_matchprefix.sin6_family = AF_INET6; irr.irr_matchprefix.sin6_addr = rpm->rpm_prefix; while (if_indextoname(++ifindex, irr.irr_name)) { ifi = if_indextoifinfo(ifindex); if (ifi == NULL) { syslog(LOG_ERR, "<%s> ifindex not found.", __func__); return (1); } /* * if ICMP6_RR_FLAGS_FORCEAPPLY(A flag) is 0 and * IFF_UP is off, the interface is not applied */ if ((rr->rr_flags & ICMP6_RR_FLAGS_FORCEAPPLY) == 0 && (ifi->ifi_flags & IFF_UP) == 0) continue; /* TODO: interface scope check */ do_use_prefix(len, rpm, &irr, ifindex); } if (errno == ENXIO) return (0); else if (errno) { syslog(LOG_ERR, "<%s> if_indextoname: %s", __func__, strerror(errno)); return (1); } return (0); } /* * call do_pco() for each Prefix Control Operations(PCOs) in a received * Router Renumbering Command packet. * return 0 on success, 1 on failure */ static int do_rr(int len, struct icmp6_router_renum *rr) { struct rr_pco_match *rpm; char *cp, *lim; lim = (char *)rr + len; cp = (char *)(rr + 1); len -= sizeof(struct icmp6_router_renum); update_ifinfo(&ifilist, UPDATE_IFINFO_ALL); while (cp < lim) { int rpmlen; rpm = (struct rr_pco_match *)cp; if ((size_t)len < sizeof(struct rr_pco_match)) { tooshort: syslog(LOG_ERR, "<%s> pkt too short. left len = %d. " "garbage at end of pkt?", __func__, len); return (1); } rpmlen = rpm->rpm_len << 3; if (len < rpmlen) goto tooshort; if (do_pco(rr, rpmlen, rpm)) { syslog(LOG_WARNING, "<%s> invalid PCO", __func__); goto next; } next: cp += rpmlen; len -= rpmlen; } return (0); } /* * check validity of a router renumbering command packet * return 0 on success, 1 on failure */ static int rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from, struct in6_addr *dst) { u_char ntopbuf[INET6_ADDRSTRLEN]; /* omit rr minimal length check. hope kernel have done it. */ /* rr_command length check */ if ((size_t)len < (sizeof(struct icmp6_router_renum) + sizeof(struct rr_pco_match))) { syslog(LOG_ERR, "<%s> rr_command len %d is too short", __func__, len); return (1); } /* destination check. only for multicast. omit unicast check. */ if (IN6_IS_ADDR_MULTICAST(dst) && !IN6_IS_ADDR_MC_LINKLOCAL(dst) && !IN6_IS_ADDR_MC_SITELOCAL(dst)) { syslog(LOG_ERR, "<%s> dst mcast addr %s is illegal", __func__, inet_ntop(AF_INET6, dst, ntopbuf, sizeof(ntopbuf))); return (1); } /* seqnum and segnum check */ if (rro.rro_seqnum > rr->rr_seqnum) { syslog(LOG_WARNING, "<%s> rcvd old seqnum %d from %s", __func__, (u_int32_t)ntohl(rr->rr_seqnum), inet_ntop(AF_INET6, from, ntopbuf, sizeof(ntopbuf))); return (1); } if (rro.rro_seqnum == rr->rr_seqnum && (rr->rr_flags & ICMP6_RR_FLAGS_TEST) == 0 && RR_ISSET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum)) { if ((rr->rr_flags & ICMP6_RR_FLAGS_REQRESULT) != 0) syslog(LOG_WARNING, "<%s> rcvd duped segnum %d from %s", __func__, rr->rr_segnum, inet_ntop(AF_INET6, from, ntopbuf, sizeof(ntopbuf))); return (0); } /* update seqnum */ if (rro.rro_seqnum != rr->rr_seqnum) { /* then must be "<" */ /* init rro_segnum_bits */ memset(rro.rro_segnum_bits, 0, sizeof(rro.rro_segnum_bits)); } rro.rro_seqnum = rr->rr_seqnum; return (0); } static void rr_command_input(int len, struct icmp6_router_renum *rr, struct in6_addr *from, struct in6_addr *dst) { /* rr_command validity check */ if (rr_command_check(len, rr, from, dst)) goto failed; if ((rr->rr_flags & (ICMP6_RR_FLAGS_TEST|ICMP6_RR_FLAGS_REQRESULT)) == ICMP6_RR_FLAGS_TEST) return; /* do router renumbering */ if (do_rr(len, rr)) goto failed; /* update segnum */ RR_SET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum); return; failed: syslog(LOG_ERR, "<%s> received RR was invalid", __func__); return; } void rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi, struct sockaddr_in6 *from, struct in6_addr *dst) { u_char ntopbuf[2][INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; syslog(LOG_DEBUG, "<%s> RR received from %s to %s on %s", __func__, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[0] ,sizeof(ntopbuf[0])), inet_ntop(AF_INET6, &dst, ntopbuf[1], sizeof(ntopbuf[1])), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); /* packet validation based on Section 4.1 of RFC2894 */ if ((size_t)len < sizeof(struct icmp6_router_renum)) { syslog(LOG_NOTICE, "<%s>: RR short message (size %d) from %s to %s on %s", __func__, len, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[0], sizeof(ntopbuf[0])), inet_ntop(AF_INET6, &dst, ntopbuf[1], sizeof(ntopbuf[1])), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } /* * If the IPv6 destination address is neither an All Routers multicast * address [AARCH] nor one of the receiving router's unicast addresses, * the message MUST be discarded and SHOULD be logged to network * management. * We rely on the kernel input routine for unicast addresses, and thus * check multicast destinations only. */ if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && !IN6_ARE_ADDR_EQUAL( &sin6_sitelocal_allrouters.sin6_addr, &pi->ipi6_addr)) { syslog(LOG_NOTICE, "<%s>: RR message with invalid destination (%s) " "from %s on %s", __func__, inet_ntop(AF_INET6, &dst, ntopbuf[0], sizeof(ntopbuf[0])), inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[1], sizeof(ntopbuf[1])), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } rr_rcvifindex = pi->ipi6_ifindex; switch (rr->rr_code) { case ICMP6_ROUTER_RENUMBERING_COMMAND: rr_command_input(len, rr, &from->sin6_addr, dst); /* TODO: send reply msg */ break; case ICMP6_ROUTER_RENUMBERING_RESULT: /* RESULT will be processed by rrenumd */ break; case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET: /* TODO: sequence number reset */ break; default: syslog(LOG_ERR, "<%s> received unknown code %d", __func__, rr->rr_code); break; } return; }