Index: head/sys/conf/NOTES =================================================================== --- head/sys/conf/NOTES +++ head/sys/conf/NOTES @@ -646,9 +646,6 @@ # libalias library, performing NAT options LIBALIAS -# flowtable cache -options FLOWTABLE - # # SCTP is a NEW transport protocol defined by # RFC2960 updated by RFC3309 and RFC3758.. and Index: head/sys/conf/options =================================================================== --- head/sys/conf/options +++ head/sys/conf/options @@ -454,8 +454,6 @@ TCP_SIGNATURE opt_ipsec.h VLAN_ARRAY opt_vlan.h XBONEHACK -FLOWTABLE opt_route.h -FLOWTABLE_HASH_ALL opt_route.h # # SCTP Index: head/sys/net/flowtable.h =================================================================== --- head/sys/net/flowtable.h +++ head/sys/net/flowtable.h @@ -1,56 +0,0 @@ -/*- - * Copyright (c) 2014 Gleb Smirnoff - * Copyright (c) 2008-2010, BitGravity Inc. - * 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. Neither the name of the BitGravity Corporation 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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$ - * - */ - -#ifndef _NET_FLOWTABLE_H_ -#define _NET_FLOWTABLE_H_ - -struct flowtable_stat { - uint64_t ft_collisions; - uint64_t ft_misses; - uint64_t ft_free_checks; - uint64_t ft_frees; - uint64_t ft_hits; - uint64_t ft_lookups; - uint64_t ft_fail_lle_invalid; - uint64_t ft_inserts; -}; - -#ifdef _KERNEL - -/* - * Given a flow table, look up the L3 and L2 information - * and return it in the route. - */ -int flowtable_lookup(sa_family_t, struct mbuf *, struct route *); -void flowtable_route_flush(sa_family_t, struct rtentry *); - -#endif /* _KERNEL */ -#endif /* !_NET_FLOWTABLE_H_ */ Index: head/sys/net/flowtable.c =================================================================== --- head/sys/net/flowtable.c +++ head/sys/net/flowtable.c @@ -1,1185 +0,0 @@ -/*- - * Copyright (c) 2014 Gleb Smirnoff - * Copyright (c) 2008-2010, BitGravity Inc. - * 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. Neither the name of the BitGravity Corporation 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "opt_route.h" -#include "opt_mpath.h" -#include "opt_ddb.h" -#include "opt_inet.h" -#include "opt_inet6.h" - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#ifdef INET6 -#include -#endif -#ifdef FLOWTABLE_HASH_ALL -#include -#include -#include -#endif - -#include - -#ifdef FLOWTABLE_HASH_ALL -#define KEY_PORTS (sizeof(uint16_t) * 2) -#define KEY_ADDRS 2 -#else -#define KEY_PORTS 0 -#define KEY_ADDRS 1 -#endif - -#ifdef INET6 -#define KEY_ADDR_LEN sizeof(struct in6_addr) -#else -#define KEY_ADDR_LEN sizeof(struct in_addr) -#endif - -#define KEYLEN ((KEY_ADDR_LEN * KEY_ADDRS + KEY_PORTS) / sizeof(uint32_t)) - -struct flentry { - uint32_t f_hash; /* hash flowing forward */ - uint32_t f_key[KEYLEN]; /* address(es and ports) */ - uint32_t f_uptime; /* uptime at last access */ - uint16_t f_fibnum; /* fib index */ -#ifdef FLOWTABLE_HASH_ALL - uint8_t f_proto; /* protocol */ - uint8_t f_flags; /* stale? */ -#define FL_STALE 1 -#endif - SLIST_ENTRY(flentry) f_next; /* pointer to collision entry */ - struct rtentry *f_rt; /* rtentry for flow */ - struct llentry *f_lle; /* llentry for flow */ -}; -#undef KEYLEN - -SLIST_HEAD(flist, flentry); -/* Make sure we can use pcpu_zone_ptr for struct flist. */ -CTASSERT(sizeof(struct flist) == sizeof(void *)); - -struct flowtable { - counter_u64_t *ft_stat; - int ft_size; - /* - * ft_table is a malloc(9)ed array of pointers. Pointers point to - * memory from UMA_ZONE_PCPU zone. - * ft_masks is per-cpu pointer itself. Each instance points - * to a malloc(9)ed bitset, that is private to corresponding CPU. - */ - struct flist **ft_table; - bitstr_t **ft_masks; - bitstr_t *ft_tmpmask; -}; - -#define FLOWSTAT_ADD(ft, name, v) \ - counter_u64_add((ft)->ft_stat[offsetof(struct flowtable_stat, name) / sizeof(uint64_t)], (v)) -#define FLOWSTAT_INC(ft, name) FLOWSTAT_ADD(ft, name, 1) - -static struct proc *flowcleanerproc; -static uint32_t flow_hashjitter; - -static struct cv flowclean_f_cv; -static struct cv flowclean_c_cv; -static struct mtx flowclean_lock; -static uint32_t flowclean_cycles; - -/* - * TODO: - * - add sysctls to resize && flush flow tables - * - Add per flowtable sysctls for statistics and configuring timeouts - * - add saturation counter to rtentry to support per-packet load-balancing - * add flag to indicate round-robin flow, add list lookup from head - for flows - * - add sysctl / device node / syscall to support exporting and importing - * of flows with flag to indicate that a flow was imported so should - * not be considered for auto-cleaning - * - support explicit connection state (currently only ad-hoc for DSR) - * - idetach() cleanup for options VIMAGE builds. - */ -#ifdef INET -static VNET_DEFINE(struct flowtable, ip4_ft); -#define V_ip4_ft VNET(ip4_ft) -#endif -#ifdef INET6 -static VNET_DEFINE(struct flowtable, ip6_ft); -#define V_ip6_ft VNET(ip6_ft) -#endif - -static uma_zone_t flow_zone; - -static VNET_DEFINE(int, flowtable_enable) = 1; -#define V_flowtable_enable VNET(flowtable_enable) - -static SYSCTL_NODE(_net, OID_AUTO, flowtable, CTLFLAG_RD, NULL, - "flowtable"); -SYSCTL_INT(_net_flowtable, OID_AUTO, enable, CTLFLAG_VNET | CTLFLAG_RW, - &VNET_NAME(flowtable_enable), 0, "enable flowtable caching."); -SYSCTL_UMA_MAX(_net_flowtable, OID_AUTO, maxflows, CTLFLAG_RW, - &flow_zone, "Maximum number of flows allowed"); - -static MALLOC_DEFINE(M_FTABLE, "flowtable", "flowtable hashes and bitstrings"); - -static struct flentry * -flowtable_lookup_common(struct flowtable *, uint32_t *, int, uint32_t); - -#ifdef INET -static struct flentry * -flowtable_lookup_ipv4(struct mbuf *m, struct route *ro) -{ - struct flentry *fle; - struct sockaddr_in *sin; - struct ip *ip; - uint32_t fibnum; -#ifdef FLOWTABLE_HASH_ALL - uint32_t key[3]; - int iphlen; - uint16_t sport, dport; - uint8_t proto; -#endif - - ip = mtod(m, struct ip *); - - if (ip->ip_src.s_addr == ip->ip_dst.s_addr || - (ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || - (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) - return (NULL); - - fibnum = M_GETFIB(m); - -#ifdef FLOWTABLE_HASH_ALL - iphlen = ip->ip_hl << 2; - proto = ip->ip_p; - - switch (proto) { - case IPPROTO_TCP: { - struct tcphdr *th; - - th = (struct tcphdr *)((char *)ip + iphlen); - sport = th->th_sport; - dport = th->th_dport; - if (th->th_flags & (TH_RST|TH_FIN)) - fibnum |= (FL_STALE << 24); - break; - } - case IPPROTO_UDP: { - struct udphdr *uh; - - uh = (struct udphdr *)((char *)ip + iphlen); - sport = uh->uh_sport; - dport = uh->uh_dport; - break; - } - case IPPROTO_SCTP: { - struct sctphdr *sh; - - sh = (struct sctphdr *)((char *)ip + iphlen); - sport = sh->src_port; - dport = sh->dest_port; - /* XXXGL: handle stale? */ - break; - } - default: - sport = dport = 0; - break; - } - - key[0] = ip->ip_dst.s_addr; - key[1] = ip->ip_src.s_addr; - key[2] = (dport << 16) | sport; - fibnum |= proto << 16; - - fle = flowtable_lookup_common(&V_ip4_ft, key, 3 * sizeof(uint32_t), - fibnum); - -#else /* !FLOWTABLE_HASH_ALL */ - - fle = flowtable_lookup_common(&V_ip4_ft, (uint32_t *)&ip->ip_dst, - sizeof(struct in_addr), fibnum); - -#endif /* FLOWTABLE_HASH_ALL */ - - if (fle == NULL) - return (NULL); - - sin = (struct sockaddr_in *)&ro->ro_dst; - sin->sin_family = AF_INET; - sin->sin_len = sizeof(*sin); - sin->sin_addr = ip->ip_dst; - - return (fle); -} -#endif /* INET */ - -#ifdef INET6 -/* - * PULLUP_TO(len, p, T) makes sure that len + sizeof(T) is contiguous, - * then it sets p to point at the offset "len" in the mbuf. WARNING: the - * pointer might become stale after other pullups (but we never use it - * this way). - */ -#define PULLUP_TO(_len, p, T) \ -do { \ - int x = (_len) + sizeof(T); \ - if ((m)->m_len < x) \ - return (NULL); \ - p = (mtod(m, char *) + (_len)); \ -} while (0) - -#define TCP(p) ((struct tcphdr *)(p)) -#define SCTP(p) ((struct sctphdr *)(p)) -#define UDP(p) ((struct udphdr *)(p)) - -static struct flentry * -flowtable_lookup_ipv6(struct mbuf *m, struct route *ro) -{ - struct flentry *fle; - struct sockaddr_in6 *sin6; - struct ip6_hdr *ip6; - uint32_t fibnum; -#ifdef FLOWTABLE_HASH_ALL - uint32_t key[9]; - void *ulp; - int hlen; - uint16_t sport, dport; - u_short offset; - uint8_t proto; -#else - uint32_t key[4]; -#endif - - ip6 = mtod(m, struct ip6_hdr *); - if (in6_localaddr(&ip6->ip6_dst)) - return (NULL); - - fibnum = M_GETFIB(m); - -#ifdef FLOWTABLE_HASH_ALL - hlen = sizeof(struct ip6_hdr); - proto = ip6->ip6_nxt; - offset = sport = dport = 0; - ulp = NULL; - while (ulp == NULL) { - switch (proto) { - case IPPROTO_ICMPV6: - case IPPROTO_OSPFIGP: - case IPPROTO_PIM: - case IPPROTO_CARP: - case IPPROTO_ESP: - case IPPROTO_NONE: - ulp = ip6; - break; - case IPPROTO_TCP: - PULLUP_TO(hlen, ulp, struct tcphdr); - dport = TCP(ulp)->th_dport; - sport = TCP(ulp)->th_sport; - if (TCP(ulp)->th_flags & (TH_RST|TH_FIN)) - fibnum |= (FL_STALE << 24); - break; - case IPPROTO_SCTP: - PULLUP_TO(hlen, ulp, struct sctphdr); - dport = SCTP(ulp)->src_port; - sport = SCTP(ulp)->dest_port; - /* XXXGL: handle stale? */ - break; - case IPPROTO_UDP: - PULLUP_TO(hlen, ulp, struct udphdr); - dport = UDP(ulp)->uh_dport; - sport = UDP(ulp)->uh_sport; - break; - case IPPROTO_HOPOPTS: /* RFC 2460 */ - PULLUP_TO(hlen, ulp, struct ip6_hbh); - hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; - proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; - ulp = NULL; - break; - case IPPROTO_ROUTING: /* RFC 2460 */ - PULLUP_TO(hlen, ulp, struct ip6_rthdr); - hlen += (((struct ip6_rthdr *)ulp)->ip6r_len + 1) << 3; - proto = ((struct ip6_rthdr *)ulp)->ip6r_nxt; - ulp = NULL; - break; - case IPPROTO_FRAGMENT: /* RFC 2460 */ - PULLUP_TO(hlen, ulp, struct ip6_frag); - hlen += sizeof (struct ip6_frag); - proto = ((struct ip6_frag *)ulp)->ip6f_nxt; - offset = ((struct ip6_frag *)ulp)->ip6f_offlg & - IP6F_OFF_MASK; - ulp = NULL; - break; - case IPPROTO_DSTOPTS: /* RFC 2460 */ - PULLUP_TO(hlen, ulp, struct ip6_hbh); - hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; - proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; - ulp = NULL; - break; - case IPPROTO_AH: /* RFC 2402 */ - PULLUP_TO(hlen, ulp, struct ip6_ext); - hlen += (((struct ip6_ext *)ulp)->ip6e_len + 2) << 2; - proto = ((struct ip6_ext *)ulp)->ip6e_nxt; - ulp = NULL; - break; - default: - PULLUP_TO(hlen, ulp, struct ip6_ext); - break; - } - } - - bcopy(&ip6->ip6_dst, &key[0], sizeof(struct in6_addr)); - bcopy(&ip6->ip6_src, &key[4], sizeof(struct in6_addr)); - key[8] = (dport << 16) | sport; - fibnum |= proto << 16; - - fle = flowtable_lookup_common(&V_ip6_ft, key, 9 * sizeof(uint32_t), - fibnum); -#else /* !FLOWTABLE_HASH_ALL */ - bcopy(&ip6->ip6_dst, &key[0], sizeof(struct in6_addr)); - fle = flowtable_lookup_common(&V_ip6_ft, key, sizeof(struct in6_addr), - fibnum); -#endif /* FLOWTABLE_HASH_ALL */ - - if (fle == NULL) - return (NULL); - - sin6 = (struct sockaddr_in6 *)&ro->ro_dst; - sin6->sin6_family = AF_INET6; - sin6->sin6_len = sizeof(*sin6); - bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(struct in6_addr)); - - return (fle); -} -#endif /* INET6 */ - -static bitstr_t * -flowtable_mask(struct flowtable *ft) -{ - - /* - * flowtable_free_stale() calls w/o critical section, but - * with sched_bind(). Since pointer is stable throughout - * ft lifetime, it is safe, otherwise... - * - * CRITICAL_ASSERT(curthread); - */ - - return (*(bitstr_t **)zpcpu_get(ft->ft_masks)); -} - -static struct flist * -flowtable_list(struct flowtable *ft, uint32_t hash) -{ - - CRITICAL_ASSERT(curthread); - return (zpcpu_get(ft->ft_table[hash % ft->ft_size])); -} - -static int -flow_stale(struct flowtable *ft, struct flentry *fle, int maxidle) -{ - - if (((fle->f_rt->rt_flags & RTF_UP) == 0) || - (fle->f_rt->rt_ifp == NULL) || - !RT_LINK_IS_UP(fle->f_rt->rt_ifp) || - (fle->f_lle->la_flags & LLE_VALID) == 0) - return (1); - - if (time_uptime - fle->f_uptime > maxidle) - return (1); - -#ifdef FLOWTABLE_HASH_ALL - if (fle->f_flags & FL_STALE) - return (1); -#endif - - return (0); -} - -static int -flow_full(void) -{ - int count, max; - - count = uma_zone_get_cur(flow_zone); - max = uma_zone_get_max(flow_zone); - - return (count > (max - (max >> 3))); -} - -static int -flow_matches(struct flentry *fle, uint32_t *key, int keylen, uint32_t fibnum) -{ -#ifdef FLOWTABLE_HASH_ALL - uint8_t proto; - - proto = (fibnum >> 16) & 0xff; - fibnum &= 0xffff; -#endif - - CRITICAL_ASSERT(curthread); - - /* Microoptimization for IPv4: don't use bcmp(). */ - if (((keylen == sizeof(uint32_t) && (fle->f_key[0] == key[0])) || - (bcmp(fle->f_key, key, keylen) == 0)) && - fibnum == fle->f_fibnum && -#ifdef FLOWTABLE_HASH_ALL - proto == fle->f_proto && -#endif - (fle->f_rt->rt_flags & RTF_UP) && - fle->f_rt->rt_ifp != NULL && - (fle->f_lle->la_flags & LLE_VALID)) - return (1); - - return (0); -} - -static struct flentry * -flowtable_insert(struct flowtable *ft, uint32_t hash, uint32_t *key, - int keylen, uint32_t fibnum0) -{ -#ifdef INET6 - struct route_in6 sro6; -#endif -#ifdef INET - struct route sro; -#endif - struct route *ro = NULL; - struct rtentry *rt; - struct lltable *lt = NULL; - struct llentry *lle; - struct sockaddr_storage *l3addr; - struct ifnet *ifp; - struct flist *flist; - struct flentry *fle, *iter; - bitstr_t *mask; - uint16_t fibnum = fibnum0; -#ifdef FLOWTABLE_HASH_ALL - uint8_t proto; - - proto = (fibnum0 >> 16) & 0xff; - fibnum = fibnum0 & 0xffff; -#endif - - /* - * This bit of code ends up locking the - * same route 3 times (just like ip_output + ether_output) - * - at lookup - * - in rt_check when called by arpresolve - * - dropping the refcount for the rtentry - * - * This could be consolidated to one if we wrote a variant - * of arpresolve with an rt_check variant that expected to - * receive the route locked - */ -#ifdef INET - if (ft == &V_ip4_ft) { - struct sockaddr_in *sin; - - ro = &sro; - bzero(&sro.ro_dst, sizeof(sro.ro_dst)); - - sin = (struct sockaddr_in *)&sro.ro_dst; - sin->sin_family = AF_INET; - sin->sin_len = sizeof(*sin); - sin->sin_addr.s_addr = key[0]; - } -#endif -#ifdef INET6 - if (ft == &V_ip6_ft) { - struct sockaddr_in6 *sin6; - - ro = (struct route *)&sro6; - sin6 = &sro6.ro_dst; - - bzero(sin6, sizeof(*sin6)); - sin6->sin6_family = AF_INET6; - sin6->sin6_len = sizeof(*sin6); - bcopy(key, &sin6->sin6_addr, sizeof(struct in6_addr)); - } -#endif - - ro->ro_rt = NULL; -#ifdef RADIX_MPATH - rtalloc_mpath_fib(ro, hash, fibnum); -#else - rtalloc_ign_fib(ro, 0, fibnum); -#endif - if (ro->ro_rt == NULL) - return (NULL); - - rt = ro->ro_rt; - ifp = rt->rt_ifp; - - if (ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) { - RTFREE(rt); - return (NULL); - } - -#ifdef INET - if (ft == &V_ip4_ft) - lt = LLTABLE(ifp); -#endif -#ifdef INET6 - if (ft == &V_ip6_ft) - lt = LLTABLE6(ifp); -#endif - - if (rt->rt_flags & RTF_GATEWAY) - l3addr = (struct sockaddr_storage *)rt->rt_gateway; - else - l3addr = (struct sockaddr_storage *)&ro->ro_dst; - lle = llentry_alloc(ifp, lt, l3addr); - - if (lle == NULL) { - RTFREE(rt); - return (NULL); - } - - /* Don't insert the entry if the ARP hasn't yet finished resolving. */ - if ((lle->la_flags & LLE_VALID) == 0) { - RTFREE(rt); - LLE_FREE(lle); - FLOWSTAT_INC(ft, ft_fail_lle_invalid); - return (NULL); - } - - fle = uma_zalloc(flow_zone, M_NOWAIT | M_ZERO); - if (fle == NULL) { - RTFREE(rt); - LLE_FREE(lle); - return (NULL); - } - - fle->f_hash = hash; - bcopy(key, &fle->f_key, keylen); - fle->f_rt = rt; - fle->f_lle = lle; - fle->f_fibnum = fibnum; - fle->f_uptime = time_uptime; -#ifdef FLOWTABLE_HASH_ALL - fle->f_proto = proto; - fle->f_flags = fibnum0 >> 24; -#endif - - critical_enter(); - mask = flowtable_mask(ft); - flist = flowtable_list(ft, hash); - - if (SLIST_EMPTY(flist)) { - bit_set(mask, (hash % ft->ft_size)); - SLIST_INSERT_HEAD(flist, fle, f_next); - goto skip; - } - - /* - * find end of list and make sure that we were not - * preempted by another thread handling this flow - */ - SLIST_FOREACH(iter, flist, f_next) { - KASSERT(iter->f_hash % ft->ft_size == hash % ft->ft_size, - ("%s: wrong hash", __func__)); - if (flow_matches(iter, key, keylen, fibnum)) { - /* - * We probably migrated to an other CPU after - * lookup in flowtable_lookup_common() failed. - * It appeared that this CPU already has flow - * entry. - */ - iter->f_uptime = time_uptime; -#ifdef FLOWTABLE_HASH_ALL - iter->f_flags |= fibnum >> 24; -#endif - critical_exit(); - FLOWSTAT_INC(ft, ft_collisions); - uma_zfree(flow_zone, fle); - return (iter); - } - } - - SLIST_INSERT_HEAD(flist, fle, f_next); -skip: - critical_exit(); - FLOWSTAT_INC(ft, ft_inserts); - - return (fle); -} - -int -flowtable_lookup(sa_family_t sa, struct mbuf *m, struct route *ro) -{ - struct flentry *fle; - struct llentry *lle; - - if (V_flowtable_enable == 0) - return (ENXIO); - - switch (sa) { -#ifdef INET - case AF_INET: - fle = flowtable_lookup_ipv4(m, ro); - break; -#endif -#ifdef INET6 - case AF_INET6: - fle = flowtable_lookup_ipv6(m, ro); - break; -#endif - default: - panic("%s: sa %d", __func__, sa); - } - - if (fle == NULL) - return (EHOSTUNREACH); - - if (M_HASHTYPE_GET(m) == M_HASHTYPE_NONE) { - M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE_HASH); - m->m_pkthdr.flowid = fle->f_hash; - } - - ro->ro_rt = fle->f_rt; - ro->ro_flags |= RT_NORTREF; - lle = fle->f_lle; - if (lle != NULL && (lle->la_flags & LLE_VALID)) - ro->ro_lle = lle; /* share ref with fle->f_lle */ - - return (0); -} - -static struct flentry * -flowtable_lookup_common(struct flowtable *ft, uint32_t *key, int keylen, - uint32_t fibnum) -{ - struct flist *flist; - struct flentry *fle; - uint32_t hash; - - FLOWSTAT_INC(ft, ft_lookups); - - hash = jenkins_hash32(key, keylen / sizeof(uint32_t), flow_hashjitter); - - critical_enter(); - flist = flowtable_list(ft, hash); - SLIST_FOREACH(fle, flist, f_next) { - KASSERT(fle->f_hash % ft->ft_size == hash % ft->ft_size, - ("%s: wrong hash", __func__)); - if (flow_matches(fle, key, keylen, fibnum)) { - fle->f_uptime = time_uptime; -#ifdef FLOWTABLE_HASH_ALL - fle->f_flags |= fibnum >> 24; -#endif - critical_exit(); - FLOWSTAT_INC(ft, ft_hits); - return (fle); - } - } - critical_exit(); - - FLOWSTAT_INC(ft, ft_misses); - - return (flowtable_insert(ft, hash, key, keylen, fibnum)); -} - -static void -flowtable_alloc(struct flowtable *ft) -{ - int i; - - ft->ft_table = malloc(ft->ft_size * sizeof(struct flist), - M_FTABLE, M_WAITOK); - for (int i = 0; i < ft->ft_size; i++) - ft->ft_table[i] = uma_zalloc(pcpu_zone_ptr, M_WAITOK | M_ZERO); - - ft->ft_masks = uma_zalloc(pcpu_zone_ptr, M_WAITOK); - CPU_FOREACH(i) { - bitstr_t **b; - - b = zpcpu_get_cpu(ft->ft_masks, i); - *b = bit_alloc(ft->ft_size, M_FTABLE, M_WAITOK); - } - ft->ft_tmpmask = bit_alloc(ft->ft_size, M_FTABLE, M_WAITOK); -} - -static void -flowtable_free_stale(struct flowtable *ft, struct rtentry *rt, int maxidle) -{ - struct flist *flist, freelist; - struct flentry *fle, *fle1, *fleprev; - bitstr_t *mask, *tmpmask; - int curbit, tmpsize; - - SLIST_INIT(&freelist); - mask = flowtable_mask(ft); - tmpmask = ft->ft_tmpmask; - tmpsize = ft->ft_size; - memcpy(tmpmask, mask, ft->ft_size/8); - curbit = 0; - fleprev = NULL; /* pacify gcc */ - /* - * XXX Note to self, bit_ffs operates at the byte level - * and thus adds gratuitous overhead - */ - bit_ffs(tmpmask, ft->ft_size, &curbit); - while (curbit != -1) { - if (curbit >= ft->ft_size || curbit < -1) { - log(LOG_ALERT, - "warning: bad curbit value %d \n", - curbit); - break; - } - - FLOWSTAT_INC(ft, ft_free_checks); - - critical_enter(); - flist = flowtable_list(ft, curbit); -#ifdef DIAGNOSTIC - if (SLIST_EMPTY(flist) && curbit > 0) { - log(LOG_ALERT, - "warning bit=%d set, but no fle found\n", - curbit); - } -#endif - SLIST_FOREACH_SAFE(fle, flist, f_next, fle1) { - if (rt != NULL && fle->f_rt != rt) { - fleprev = fle; - continue; - } - if (!flow_stale(ft, fle, maxidle)) { - fleprev = fle; - continue; - } - - if (fle == SLIST_FIRST(flist)) - SLIST_REMOVE_HEAD(flist, f_next); - else - SLIST_REMOVE_AFTER(fleprev, f_next); - SLIST_INSERT_HEAD(&freelist, fle, f_next); - } - if (SLIST_EMPTY(flist)) - bit_clear(mask, curbit); - critical_exit(); - - bit_clear(tmpmask, curbit); - bit_ffs(tmpmask, tmpsize, &curbit); - } - - SLIST_FOREACH_SAFE(fle, &freelist, f_next, fle1) { - FLOWSTAT_INC(ft, ft_frees); - if (fle->f_rt != NULL) - RTFREE(fle->f_rt); - if (fle->f_lle != NULL) - LLE_FREE(fle->f_lle); - uma_zfree(flow_zone, fle); - } -} - -static void -flowtable_clean_vnet(struct flowtable *ft, struct rtentry *rt, int maxidle) -{ - int i; - - CPU_FOREACH(i) { - if (smp_started == 1) { - thread_lock(curthread); - sched_bind(curthread, i); - thread_unlock(curthread); - } - - flowtable_free_stale(ft, rt, maxidle); - - if (smp_started == 1) { - thread_lock(curthread); - sched_unbind(curthread); - thread_unlock(curthread); - } - } -} - -void -flowtable_route_flush(sa_family_t sa, struct rtentry *rt) -{ - struct flowtable *ft; - - switch (sa) { -#ifdef INET - case AF_INET: - ft = &V_ip4_ft; - break; -#endif -#ifdef INET6 - case AF_INET6: - ft = &V_ip6_ft; - break; -#endif - default: - panic("%s: sa %d", __func__, sa); - } - - flowtable_clean_vnet(ft, rt, 0); -} - -static void -flowtable_cleaner(void) -{ - VNET_ITERATOR_DECL(vnet_iter); - struct thread *td; - - if (bootverbose) - log(LOG_INFO, "flowtable cleaner started\n"); - td = curthread; - while (1) { - uint32_t flowclean_freq, maxidle; - - /* - * The maximum idle time, as well as frequency are arbitrary. - */ - if (flow_full()) - maxidle = 5; - else - maxidle = 30; - - VNET_LIST_RLOCK(); - VNET_FOREACH(vnet_iter) { - CURVNET_SET(vnet_iter); -#ifdef INET - flowtable_clean_vnet(&V_ip4_ft, NULL, maxidle); -#endif -#ifdef INET6 - flowtable_clean_vnet(&V_ip6_ft, NULL, maxidle); -#endif - CURVNET_RESTORE(); - } - VNET_LIST_RUNLOCK(); - - if (flow_full()) - flowclean_freq = 4*hz; - else - flowclean_freq = 20*hz; - mtx_lock(&flowclean_lock); - thread_lock(td); - sched_prio(td, PPAUSE); - thread_unlock(td); - flowclean_cycles++; - cv_broadcast(&flowclean_f_cv); - cv_timedwait(&flowclean_c_cv, &flowclean_lock, flowclean_freq); - mtx_unlock(&flowclean_lock); - } -} - -static void -flowtable_flush(void *unused __unused) -{ - uint64_t start; - - mtx_lock(&flowclean_lock); - start = flowclean_cycles; - while (start == flowclean_cycles) { - cv_broadcast(&flowclean_c_cv); - cv_wait(&flowclean_f_cv, &flowclean_lock); - } - mtx_unlock(&flowclean_lock); -} - -static struct kproc_desc flow_kp = { - "flowcleaner", - flowtable_cleaner, - &flowcleanerproc -}; -SYSINIT(flowcleaner, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, kproc_start, &flow_kp); - -static int -flowtable_get_size(char *name) -{ - int size; - - if (TUNABLE_INT_FETCH(name, &size)) { - if (size < 256) - size = 256; - if (!powerof2(size)) { - printf("%s must be power of 2\n", name); - size = 2048; - } - } else { - /* - * round up to the next power of 2 - */ - size = 1 << fls((1024 + maxusers * 64) - 1); - } - - return (size); -} - -static void -flowtable_init(const void *unused __unused) -{ - - flow_hashjitter = arc4random(); - - flow_zone = uma_zcreate("flows", sizeof(struct flentry), - NULL, NULL, NULL, NULL, (64-1), UMA_ZONE_MAXBUCKET); - uma_zone_set_max(flow_zone, 1024 + maxusers * 64 * mp_ncpus); - - cv_init(&flowclean_c_cv, "c_flowcleanwait"); - cv_init(&flowclean_f_cv, "f_flowcleanwait"); - mtx_init(&flowclean_lock, "flowclean lock", NULL, MTX_DEF); - EVENTHANDLER_REGISTER(ifnet_departure_event, flowtable_flush, NULL, - EVENTHANDLER_PRI_ANY); -} -SYSINIT(flowtable_init, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST, - flowtable_init, NULL); - -#ifdef INET -static SYSCTL_NODE(_net_flowtable, OID_AUTO, ip4, CTLFLAG_RD, NULL, - "Flowtable for IPv4"); - -static VNET_PCPUSTAT_DEFINE(struct flowtable_stat, ip4_ftstat); -VNET_PCPUSTAT_SYSINIT(ip4_ftstat); -VNET_PCPUSTAT_SYSUNINIT(ip4_ftstat); -SYSCTL_VNET_PCPUSTAT(_net_flowtable_ip4, OID_AUTO, stat, struct flowtable_stat, - ip4_ftstat, "Flowtable statistics for IPv4 " - "(struct flowtable_stat, net/flowtable.h)"); - -static void -flowtable_init_vnet_v4(const void *unused __unused) -{ - - V_ip4_ft.ft_size = flowtable_get_size("net.flowtable.ip4.size"); - V_ip4_ft.ft_stat = VNET(ip4_ftstat); - flowtable_alloc(&V_ip4_ft); -} -VNET_SYSINIT(ft_vnet_v4, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, - flowtable_init_vnet_v4, NULL); -#endif /* INET */ - -#ifdef INET6 -static SYSCTL_NODE(_net_flowtable, OID_AUTO, ip6, CTLFLAG_RD, NULL, - "Flowtable for IPv6"); - -static VNET_PCPUSTAT_DEFINE(struct flowtable_stat, ip6_ftstat); -VNET_PCPUSTAT_SYSINIT(ip6_ftstat); -VNET_PCPUSTAT_SYSUNINIT(ip6_ftstat); -SYSCTL_VNET_PCPUSTAT(_net_flowtable_ip6, OID_AUTO, stat, struct flowtable_stat, - ip6_ftstat, "Flowtable statistics for IPv6 " - "(struct flowtable_stat, net/flowtable.h)"); - -static void -flowtable_init_vnet_v6(const void *unused __unused) -{ - - V_ip6_ft.ft_size = flowtable_get_size("net.flowtable.ip6.size"); - V_ip6_ft.ft_stat = VNET(ip6_ftstat); - flowtable_alloc(&V_ip6_ft); -} -VNET_SYSINIT(flowtable_init_vnet_v6, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, - flowtable_init_vnet_v6, NULL); -#endif /* INET6 */ - -#ifdef DDB -static bitstr_t * -flowtable_mask_pcpu(struct flowtable *ft, int cpuid) -{ - - return (zpcpu_get_cpu(*ft->ft_masks, cpuid)); -} - -static struct flist * -flowtable_list_pcpu(struct flowtable *ft, uint32_t hash, int cpuid) -{ - - return (zpcpu_get_cpu(&ft->ft_table[hash % ft->ft_size], cpuid)); -} - -static void -flow_show(struct flowtable *ft, struct flentry *fle) -{ - int idle_time; - int rt_valid, ifp_valid; - volatile struct rtentry *rt; - struct ifnet *ifp = NULL; - uint32_t *hashkey = fle->f_key; - - idle_time = (int)(time_uptime - fle->f_uptime); - rt = fle->f_rt; - rt_valid = rt != NULL; - if (rt_valid) - ifp = rt->rt_ifp; - ifp_valid = ifp != NULL; - -#ifdef INET - if (ft == &V_ip4_ft) { - char daddr[4*sizeof "123"]; -#ifdef FLOWTABLE_HASH_ALL - char saddr[4*sizeof "123"]; - uint16_t sport, dport; -#endif - - inet_ntoa_r(*(struct in_addr *) &hashkey[0], daddr); -#ifdef FLOWTABLE_HASH_ALL - inet_ntoa_r(*(struct in_addr *) &hashkey[1], saddr); - dport = ntohs((uint16_t)(hashkey[2] >> 16)); - sport = ntohs((uint16_t)(hashkey[2] & 0xffff)); - db_printf("%s:%d->%s:%d", saddr, sport, daddr, dport); -#else - db_printf("%s ", daddr); -#endif - } -#endif /* INET */ -#ifdef INET6 - if (ft == &V_ip6_ft) { -#ifdef FLOWTABLE_HASH_ALL - db_printf("\n\tkey=%08x:%08x:%08x%08x:%08x:%08x%08x:%08x:%08x", - hashkey[0], hashkey[1], hashkey[2], - hashkey[3], hashkey[4], hashkey[5], - hashkey[6], hashkey[7], hashkey[8]); -#else - db_printf("\n\tkey=%08x:%08x:%08x ", - hashkey[0], hashkey[1], hashkey[2]); -#endif - } -#endif /* INET6 */ - - db_printf("hash=%08x idle_time=%03d" - "\n\tfibnum=%02d rt=%p", - fle->f_hash, idle_time, fle->f_fibnum, fle->f_rt); - -#ifdef FLOWTABLE_HASH_ALL - if (fle->f_flags & FL_STALE) - db_printf(" FL_STALE "); -#endif - if (rt_valid) { - if (rt->rt_flags & RTF_UP) - db_printf(" RTF_UP "); - } - if (ifp_valid) { - if (ifp->if_flags & IFF_LOOPBACK) - db_printf(" IFF_LOOPBACK "); - if (ifp->if_flags & IFF_UP) - db_printf(" IFF_UP "); - if (ifp->if_flags & IFF_POINTOPOINT) - db_printf(" IFF_POINTOPOINT "); - } - db_printf("\n"); -} - -static void -flowtable_show(struct flowtable *ft, int cpuid) -{ - int curbit = 0; - bitstr_t *mask, *tmpmask; - - if (cpuid != -1) - db_printf("cpu: %d\n", cpuid); - mask = flowtable_mask_pcpu(ft, cpuid); - tmpmask = ft->ft_tmpmask; - memcpy(tmpmask, mask, ft->ft_size/8); - /* - * XXX Note to self, bit_ffs operates at the byte level - * and thus adds gratuitous overhead - */ - bit_ffs(tmpmask, ft->ft_size, &curbit); - while (curbit != -1) { - struct flist *flist; - struct flentry *fle; - - if (curbit >= ft->ft_size || curbit < -1) { - db_printf("warning: bad curbit value %d \n", - curbit); - break; - } - - flist = flowtable_list_pcpu(ft, curbit, cpuid); - - SLIST_FOREACH(fle, flist, f_next) - flow_show(ft, fle); - bit_clear(tmpmask, curbit); - bit_ffs(tmpmask, ft->ft_size, &curbit); - } -} - -static void -flowtable_show_vnet(struct flowtable *ft) -{ - - int i; - - CPU_FOREACH(i) - flowtable_show(ft, i); -} - -DB_SHOW_COMMAND(flowtables, db_show_flowtables) -{ - VNET_ITERATOR_DECL(vnet_iter); - - VNET_FOREACH(vnet_iter) { - CURVNET_SET(vnet_iter); -#ifdef VIMAGE - db_printf("vnet %p\n", vnet_iter); -#endif -#ifdef INET - printf("IPv4:\n"); - flowtable_show_vnet(&V_ip4_ft); -#endif -#ifdef INET6 - printf("IPv6:\n"); - flowtable_show_vnet(&V_ip6_ft); -#endif - CURVNET_RESTORE(); - } -} -#endif Index: head/sys/net/route.c =================================================================== --- head/sys/net/route.c +++ head/sys/net/route.c @@ -59,7 +59,6 @@ #include #include #include -#include #ifdef RADIX_MPATH #include @@ -1504,79 +1503,12 @@ } #endif -#ifdef FLOWTABLE -static struct rtentry * -rt_flowtable_check_route(struct rib_head *rnh, struct rt_addrinfo *info) -{ -#if defined(INET6) || defined(INET) - struct radix_node *rn; -#endif - struct rtentry *rt0; - - rt0 = NULL; - /* "flow-table" only supports IPv6 and IPv4 at the moment. */ - switch (dst->sa_family) { -#ifdef INET6 - case AF_INET6: -#endif -#ifdef INET - case AF_INET: -#endif -#if defined(INET6) || defined(INET) - rn = rnh->rnh_matchaddr(dst, &rnh->head); - if (rn && ((rn->rn_flags & RNF_ROOT) == 0)) { - struct sockaddr *mask; - u_char *m, *n; - int len; - - /* - * compare mask to see if the new route is - * more specific than the existing one - */ - rt0 = RNTORT(rn); - RT_LOCK(rt0); - RT_ADDREF(rt0); - RT_UNLOCK(rt0); - /* - * A host route is already present, so - * leave the flow-table entries as is. - */ - if (rt0->rt_flags & RTF_HOST) { - RTFREE(rt0); - rt0 = NULL; - } else if (!(flags & RTF_HOST) && netmask) { - mask = rt_mask(rt0); - len = mask->sa_len; - m = (u_char *)mask; - n = (u_char *)netmask; - while (len-- > 0) { - if (*n != *m) - break; - n++; - m++; - } - if (len == 0 || (*n < *m)) { - RTFREE(rt0); - rt0 = NULL; - } - } - } -#endif/* INET6 || INET */ - } - - return (rt0); -} -#endif - int rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, u_int fibnum) { int error = 0; struct rtentry *rt, *rt_old; -#ifdef FLOWTABLE - struct rtentry *rt0; -#endif struct radix_node *rn; struct rib_head *rnh; struct ifaddr *ifa; @@ -1710,10 +1642,6 @@ } #endif -#ifdef FLOWTABLE - rt0 = rt_flowtable_check_route(rnh, info); -#endif /* FLOWTABLE */ - /* XXX mtu manipulation will be done in rnh_addaddr -- itojun */ rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, rt->rt_nodes); @@ -1748,18 +1676,8 @@ ifa_free(rt->rt_ifa); R_Free(rt_key(rt)); uma_zfree(V_rtzone, rt); -#ifdef FLOWTABLE - if (rt0 != NULL) - RTFREE(rt0); -#endif return (EEXIST); } -#ifdef FLOWTABLE - else if (rt0 != NULL) { - flowtable_route_flush(dst->sa_family, rt0); - RTFREE(rt0); - } -#endif if (rt_old != NULL) { rt_notifydelete(rt_old, info); Index: head/sys/netinet/ip_output.c =================================================================== --- head/sys/netinet/ip_output.c +++ head/sys/netinet/ip_output.c @@ -63,7 +63,6 @@ #include #include #include -#include #ifdef RADIX_MPATH #include #endif @@ -243,11 +242,6 @@ ro = &iproute; bzero(ro, sizeof (*ro)); } - -#ifdef FLOWTABLE - if (ro->ro_rt == NULL) - (void )flowtable_lookup(AF_INET, m, ro); -#endif if (opt) { int len = 0; Index: head/sys/netinet6/ip6_output.c =================================================================== --- head/sys/netinet6/ip6_output.c +++ head/sys/netinet6/ip6_output.c @@ -117,10 +117,6 @@ #include #include -#ifdef FLOWTABLE -#include -#endif - extern int in6_mcast_loop; struct ip6_exthdrs { @@ -502,10 +498,6 @@ if (opt && opt->ip6po_rthdr) ro = &opt->ip6po_route; dst = (struct sockaddr_in6 *)&ro->ro_dst; -#ifdef FLOWTABLE - if (ro->ro_rt == NULL) - (void )flowtable_lookup(AF_INET6, m, (struct route *)ro); -#endif fibnum = (inp != NULL) ? inp->inp_inc.inc_fibnum : M_GETFIB(m); again: /* Index: head/usr.bin/netstat/Makefile =================================================================== --- head/usr.bin/netstat/Makefile +++ head/usr.bin/netstat/Makefile @@ -6,7 +6,7 @@ PROG= netstat SRCS= if.c inet.c main.c mbuf.c mroute.c netisr.c nl_symbols.c route.c \ unix.c mroute6.c ipsec.c bpf.c pfkey.c sctp.c \ - flowtable.c nl_defs.h + nl_defs.h nl_symbols.c: nlist_symbols awk '\ Index: head/usr.bin/netstat/flowtable.c =================================================================== --- head/usr.bin/netstat/flowtable.c +++ head/usr.bin/netstat/flowtable.c @@ -1,88 +0,0 @@ -/*- - * Copyright (c) 2014 Gleb Smirnoff - * - * 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. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include - -#include - -#include -#include -#include - -#include "netstat.h" - -/* - * Print flowtable statistics. - */ - -static void -print_stats(struct flowtable_stat *stat) -{ - -#define p(f, m) if (stat->f || sflag <= 1) \ - printf(m, (uintmax_t)stat->f, plural(stat->f)) -#define p2(f, m) if (stat->f || sflag <= 1) \ - printf(m, (uintmax_t)stat->f, plurales(stat->f)) - - p(ft_lookups, "\t%ju lookup%s\n"); - p(ft_hits, "\t%ju hit%s\n"); - p2(ft_misses, "\t%ju miss%s\n"); - p(ft_inserts, "\t%ju insert%s\n"); - p(ft_collisions, "\t%ju collision%s\n"); - p(ft_free_checks, "\t%ju free check%s\n"); - p(ft_frees, "\t%ju free%s\n"); - p(ft_fail_lle_invalid, - "\t%ju lookup%s with not resolved Layer 2 address\n"); - -#undef p2 -#undef p -} - -void -flowtable_stats(void) -{ - struct flowtable_stat stat; - - if (!live) - return; - - if (fetch_stats("net.flowtable.ip4.stat", 0, &stat, - sizeof(stat), NULL) == 0) { - printf("flowtable for IPv4:\n"); - print_stats(&stat); - } - - if (fetch_stats("net.flowtable.ip6.stat", 0, &stat, - sizeof(stat), NULL) == 0) { - printf("flowtable for IPv6:\n"); - print_stats(&stat); - } -} Index: head/usr.bin/netstat/main.c =================================================================== --- head/usr.bin/netstat/main.c +++ head/usr.bin/netstat/main.c @@ -480,7 +480,6 @@ xo_open_container("statistics"); if (sflag) { rt_stats(); - flowtable_stats(); } else routepr(fib, af); xo_close_container("statistics"); Index: head/usr.bin/netstat/netstat.h =================================================================== --- head/usr.bin/netstat/netstat.h +++ head/usr.bin/netstat/netstat.h @@ -140,7 +140,6 @@ void pr_family(int); void rt_stats(void); -void flowtable_stats(void); char *routename(struct sockaddr *, int); const char *netname(struct sockaddr *, struct sockaddr *);