1a752e82dSGleb Smirnoff /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3fe267a55SPedro F. Giffuni * 45dcd9c10SGleb Smirnoff * Copyright (c) 2010-2011 Alexander V. Chernikov <melifaro@ipfw.ru> 51d03bd16SGleb Smirnoff * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org> 6a752e82dSGleb Smirnoff * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net> 7a752e82dSGleb Smirnoff * All rights reserved. 8a752e82dSGleb Smirnoff * 9a752e82dSGleb Smirnoff * Redistribution and use in source and binary forms, with or without 10a752e82dSGleb Smirnoff * modification, are permitted provided that the following conditions 11a752e82dSGleb Smirnoff * are met: 12a752e82dSGleb Smirnoff * 1. Redistributions of source code must retain the above copyright 13a752e82dSGleb Smirnoff * notice, this list of conditions and the following disclaimer. 14a752e82dSGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright 15a752e82dSGleb Smirnoff * notice, this list of conditions and the following disclaimer in the 16a752e82dSGleb Smirnoff * documentation and/or other materials provided with the distribution. 17a752e82dSGleb Smirnoff * 18a752e82dSGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19a752e82dSGleb Smirnoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20a752e82dSGleb Smirnoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21a752e82dSGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22a752e82dSGleb Smirnoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23a752e82dSGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24a752e82dSGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25a752e82dSGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26a752e82dSGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27a752e82dSGleb Smirnoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28a752e82dSGleb Smirnoff * SUCH DAMAGE. 29a752e82dSGleb Smirnoff * 30a752e82dSGleb Smirnoff * $SourceForge: netflow.c,v 1.41 2004/09/05 11:41:10 glebius Exp $ 31a752e82dSGleb Smirnoff */ 32a752e82dSGleb Smirnoff 3301f6c1eaSDimitry Andric #include <sys/cdefs.h> 344e19e0d9SAlexander V. Chernikov #include "opt_inet.h" 355dcd9c10SGleb Smirnoff #include "opt_inet6.h" 365dcd9c10SGleb Smirnoff #include "opt_route.h" 37a752e82dSGleb Smirnoff #include <sys/param.h> 38a42aa5faSJung-uk Kim #include <sys/bitstring.h> 397ee35ac9SGleb Smirnoff #include <sys/systm.h> 407ee35ac9SGleb Smirnoff #include <sys/counter.h> 41a752e82dSGleb Smirnoff #include <sys/kernel.h> 42ea8d1492SAlexander V. Chernikov #include <sys/ktr.h> 43a752e82dSGleb Smirnoff #include <sys/limits.h> 44a752e82dSGleb Smirnoff #include <sys/mbuf.h> 4518c54fe6SGleb Smirnoff #include <sys/syslog.h> 46a752e82dSGleb Smirnoff #include <sys/socket.h> 478ec07310SGleb Smirnoff #include <vm/uma.h> 48494e177aSGleb Smirnoff 49a752e82dSGleb Smirnoff #include <net/if.h> 506369f51bSAlexander V. Chernikov #include <net/if_dl.h> 5176039bc8SGleb Smirnoff #include <net/if_var.h> 523d0d5b21SJustin Hibbits #include <net/if_private.h> 53a752e82dSGleb Smirnoff #include <net/route.h> 544e19e0d9SAlexander V. Chernikov #include <net/route/nhop.h> 554e19e0d9SAlexander V. Chernikov #include <net/route/route_ctl.h> 565dcd9c10SGleb Smirnoff #include <net/ethernet.h> 57a752e82dSGleb Smirnoff #include <netinet/in.h> 584e19e0d9SAlexander V. Chernikov #include <netinet/in_fib.h> 59a752e82dSGleb Smirnoff #include <netinet/in_systm.h> 60a752e82dSGleb Smirnoff #include <netinet/ip.h> 615dcd9c10SGleb Smirnoff #include <netinet/ip6.h> 62a752e82dSGleb Smirnoff #include <netinet/tcp.h> 63a752e82dSGleb Smirnoff #include <netinet/udp.h> 64a752e82dSGleb Smirnoff 654e19e0d9SAlexander V. Chernikov #include <netinet6/in6_fib.h> 664e19e0d9SAlexander V. Chernikov 67a752e82dSGleb Smirnoff #include <netgraph/ng_message.h> 68a752e82dSGleb Smirnoff #include <netgraph/netgraph.h> 69a752e82dSGleb Smirnoff 70a752e82dSGleb Smirnoff #include <netgraph/netflow/netflow.h> 715dcd9c10SGleb Smirnoff #include <netgraph/netflow/netflow_v9.h> 72a752e82dSGleb Smirnoff #include <netgraph/netflow/ng_netflow.h> 73a752e82dSGleb Smirnoff 74494e177aSGleb Smirnoff #define NBUCKETS (65536) /* must be power of 2 */ 75a752e82dSGleb Smirnoff 7696a0326eSGleb Smirnoff /* This hash is for TCP or UDP packets. */ 77a752e82dSGleb Smirnoff #define FULL_HASH(addr1, addr2, port1, port2) \ 7896a0326eSGleb Smirnoff (((addr1 ^ (addr1 >> 16) ^ \ 7996a0326eSGleb Smirnoff htons(addr2 ^ (addr2 >> 16))) ^ \ 8011e68557SGleb Smirnoff port1 ^ htons(port2)) & \ 81a752e82dSGleb Smirnoff (NBUCKETS - 1)) 82a752e82dSGleb Smirnoff 8396a0326eSGleb Smirnoff /* This hash is for all other IP packets. */ 84a752e82dSGleb Smirnoff #define ADDR_HASH(addr1, addr2) \ 8596a0326eSGleb Smirnoff ((addr1 ^ (addr1 >> 16) ^ \ 8696a0326eSGleb Smirnoff htons(addr2 ^ (addr2 >> 16))) & \ 87a752e82dSGleb Smirnoff (NBUCKETS - 1)) 88a752e82dSGleb Smirnoff 89a752e82dSGleb Smirnoff /* Macros to shorten logical constructions */ 90a752e82dSGleb Smirnoff /* XXX: priv must exist in namespace */ 917ee35ac9SGleb Smirnoff #define INACTIVE(fle) (time_uptime - fle->f.last > priv->nfinfo_inact_t) 927ee35ac9SGleb Smirnoff #define AGED(fle) (time_uptime - fle->f.first > priv->nfinfo_act_t) 93a752e82dSGleb Smirnoff #define ISFREE(fle) (fle->f.packets == 0) 94a752e82dSGleb Smirnoff 95a752e82dSGleb Smirnoff /* 96a752e82dSGleb Smirnoff * 4 is a magical number: statistically number of 4-packet flows is 97a752e82dSGleb Smirnoff * bigger than 5,6,7...-packet flows by an order of magnitude. Most UDP/ICMP 98a752e82dSGleb Smirnoff * scans are 1 packet (~ 90% of flow cache). TCP scans are 2-packet in case 99a752e82dSGleb Smirnoff * of reachable host and 4-packet otherwise. 100a752e82dSGleb Smirnoff */ 101a752e82dSGleb Smirnoff #define SMALL(fle) (fle->f.packets <= 4) 102a752e82dSGleb Smirnoff 1035bb84bc8SRobert Watson MALLOC_DEFINE(M_NETFLOW_HASH, "netflow_hash", "NetFlow hash"); 104a752e82dSGleb Smirnoff 105494e177aSGleb Smirnoff static int export_add(item_p, struct flow_entry *); 1065dcd9c10SGleb Smirnoff static int export_send(priv_p, fib_export_p, item_p, int); 107a752e82dSGleb Smirnoff 1084e19e0d9SAlexander V. Chernikov #ifdef INET 109a23a2dd1SGleb Smirnoff static int hash_insert(priv_p, struct flow_hash_entry *, struct flow_rec *, 110*0fc7bdc9SRichard Scheffenegger int, uint8_t, uint16_t); 1114e19e0d9SAlexander V. Chernikov #endif 1123090c020SBjoern A. Zeeb #ifdef INET6 113a23a2dd1SGleb Smirnoff static int hash6_insert(priv_p, struct flow_hash_entry *, struct flow6_rec *, 114*0fc7bdc9SRichard Scheffenegger int, uint8_t, uint16_t); 1153090c020SBjoern A. Zeeb #endif 1165dcd9c10SGleb Smirnoff 117a23a2dd1SGleb Smirnoff static void expire_flow(priv_p, fib_export_p, struct flow_entry *, int); 1185dcd9c10SGleb Smirnoff 119a3c2c06bSBjoern A. Zeeb #ifdef INET 1205dcd9c10SGleb Smirnoff /* 1215dcd9c10SGleb Smirnoff * Generate hash for a given flow record. 1225dcd9c10SGleb Smirnoff * 1235dcd9c10SGleb Smirnoff * FIB is not used here, because: 1245dcd9c10SGleb Smirnoff * most VRFS will carry public IPv4 addresses which are unique even 1255dcd9c10SGleb Smirnoff * without FIB private addresses can overlap, but this is worked out 1265dcd9c10SGleb Smirnoff * via flow_rec bcmp() containing fib id. In IPv6 world addresses are 1275dcd9c10SGleb Smirnoff * all globally unique (it's not fully true, there is FC00::/7 for example, 1285dcd9c10SGleb Smirnoff * but chances of address overlap are MUCH smaller) 1295dcd9c10SGleb Smirnoff */ 130a23a2dd1SGleb Smirnoff static inline uint32_t 131a752e82dSGleb Smirnoff ip_hash(struct flow_rec *r) 132a752e82dSGleb Smirnoff { 133a23a2dd1SGleb Smirnoff 134a752e82dSGleb Smirnoff switch (r->r_ip_p) { 135a752e82dSGleb Smirnoff case IPPROTO_TCP: 136a752e82dSGleb Smirnoff case IPPROTO_UDP: 137a752e82dSGleb Smirnoff return FULL_HASH(r->r_src.s_addr, r->r_dst.s_addr, 138a752e82dSGleb Smirnoff r->r_sport, r->r_dport); 139a752e82dSGleb Smirnoff default: 140a752e82dSGleb Smirnoff return ADDR_HASH(r->r_src.s_addr, r->r_dst.s_addr); 141a752e82dSGleb Smirnoff } 142a752e82dSGleb Smirnoff } 143a3c2c06bSBjoern A. Zeeb #endif 144a752e82dSGleb Smirnoff 1455dcd9c10SGleb Smirnoff #ifdef INET6 1465dcd9c10SGleb Smirnoff /* Generate hash for a given flow6 record. Use lower 4 octets from v6 addresses */ 147a23a2dd1SGleb Smirnoff static inline uint32_t 1485dcd9c10SGleb Smirnoff ip6_hash(struct flow6_rec *r) 1495dcd9c10SGleb Smirnoff { 150a23a2dd1SGleb Smirnoff 1515dcd9c10SGleb Smirnoff switch (r->r_ip_p) { 1525dcd9c10SGleb Smirnoff case IPPROTO_TCP: 1535dcd9c10SGleb Smirnoff case IPPROTO_UDP: 1545dcd9c10SGleb Smirnoff return FULL_HASH(r->src.r_src6.__u6_addr.__u6_addr32[3], 1555dcd9c10SGleb Smirnoff r->dst.r_dst6.__u6_addr.__u6_addr32[3], r->r_sport, 1565dcd9c10SGleb Smirnoff r->r_dport); 1575dcd9c10SGleb Smirnoff default: 1585dcd9c10SGleb Smirnoff return ADDR_HASH(r->src.r_src6.__u6_addr.__u6_addr32[3], 1595dcd9c10SGleb Smirnoff r->dst.r_dst6.__u6_addr.__u6_addr32[3]); 1605dcd9c10SGleb Smirnoff } 1615dcd9c10SGleb Smirnoff } 162a42aa5faSJung-uk Kim 1635dcd9c10SGleb Smirnoff #endif 1645dcd9c10SGleb Smirnoff 165494e177aSGleb Smirnoff /* 166494e177aSGleb Smirnoff * Detach export datagram from priv, if there is any. 167494e177aSGleb Smirnoff * If there is no, allocate a new one. 168494e177aSGleb Smirnoff */ 169494e177aSGleb Smirnoff static item_p 1705dcd9c10SGleb Smirnoff get_export_dgram(priv_p priv, fib_export_p fe) 171494e177aSGleb Smirnoff { 172494e177aSGleb Smirnoff item_p item = NULL; 173494e177aSGleb Smirnoff 1745dcd9c10SGleb Smirnoff mtx_lock(&fe->export_mtx); 1755dcd9c10SGleb Smirnoff if (fe->exp.item != NULL) { 1765dcd9c10SGleb Smirnoff item = fe->exp.item; 1775dcd9c10SGleb Smirnoff fe->exp.item = NULL; 178494e177aSGleb Smirnoff } 1795dcd9c10SGleb Smirnoff mtx_unlock(&fe->export_mtx); 180494e177aSGleb Smirnoff 181494e177aSGleb Smirnoff if (item == NULL) { 182494e177aSGleb Smirnoff struct netflow_v5_export_dgram *dgram; 183494e177aSGleb Smirnoff struct mbuf *m; 184494e177aSGleb Smirnoff 185eb1b1807SGleb Smirnoff m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 186494e177aSGleb Smirnoff if (m == NULL) 187a752e82dSGleb Smirnoff return (NULL); 188336b3f1eSGleb Smirnoff item = ng_package_data(m, NG_NOFLAGS); 189494e177aSGleb Smirnoff if (item == NULL) 190a752e82dSGleb Smirnoff return (NULL); 191494e177aSGleb Smirnoff dgram = mtod(m, struct netflow_v5_export_dgram *); 192494e177aSGleb Smirnoff dgram->header.count = 0; 193494e177aSGleb Smirnoff dgram->header.version = htons(NETFLOW_V5); 1949da82118SGleb Smirnoff dgram->header.pad = 0; 195a752e82dSGleb Smirnoff } 196a752e82dSGleb Smirnoff 197494e177aSGleb Smirnoff return (item); 198a752e82dSGleb Smirnoff } 199a752e82dSGleb Smirnoff 200494e177aSGleb Smirnoff /* 201494e177aSGleb Smirnoff * Re-attach incomplete datagram back to priv. 202494e177aSGleb Smirnoff * If there is already another one, then send incomplete. */ 203494e177aSGleb Smirnoff static void 2045dcd9c10SGleb Smirnoff return_export_dgram(priv_p priv, fib_export_p fe, item_p item, int flags) 205a752e82dSGleb Smirnoff { 206a23a2dd1SGleb Smirnoff 207494e177aSGleb Smirnoff /* 208494e177aSGleb Smirnoff * It may happen on SMP, that some thread has already 209494e177aSGleb Smirnoff * put its item there, in this case we bail out and 210494e177aSGleb Smirnoff * send what we have to collector. 211494e177aSGleb Smirnoff */ 2125dcd9c10SGleb Smirnoff mtx_lock(&fe->export_mtx); 2135dcd9c10SGleb Smirnoff if (fe->exp.item == NULL) { 2145dcd9c10SGleb Smirnoff fe->exp.item = item; 2155dcd9c10SGleb Smirnoff mtx_unlock(&fe->export_mtx); 216494e177aSGleb Smirnoff } else { 2175dcd9c10SGleb Smirnoff mtx_unlock(&fe->export_mtx); 2185dcd9c10SGleb Smirnoff export_send(priv, fe, item, flags); 219494e177aSGleb Smirnoff } 220a752e82dSGleb Smirnoff } 221a752e82dSGleb Smirnoff 222494e177aSGleb Smirnoff /* 223494e177aSGleb Smirnoff * The flow is over. Call export_add() and free it. If datagram is 224494e177aSGleb Smirnoff * full, then call export_send(). 225494e177aSGleb Smirnoff */ 226a23a2dd1SGleb Smirnoff static void 2275dcd9c10SGleb Smirnoff expire_flow(priv_p priv, fib_export_p fe, struct flow_entry *fle, int flags) 228a752e82dSGleb Smirnoff { 2295dcd9c10SGleb Smirnoff struct netflow_export_item exp; 2305dcd9c10SGleb Smirnoff uint16_t version = fle->f.version; 2315dcd9c10SGleb Smirnoff 2325dcd9c10SGleb Smirnoff if ((priv->export != NULL) && (version == IPVERSION)) { 2335dcd9c10SGleb Smirnoff exp.item = get_export_dgram(priv, fe); 2345dcd9c10SGleb Smirnoff if (exp.item == NULL) { 2357ee35ac9SGleb Smirnoff priv->nfinfo_export_failed++; 2365dcd9c10SGleb Smirnoff if (priv->export9 != NULL) 2377ee35ac9SGleb Smirnoff priv->nfinfo_export9_failed++; 238a23a2dd1SGleb Smirnoff /* fle definitely contains IPv4 flow. */ 239494e177aSGleb Smirnoff uma_zfree_arg(priv->zone, fle, priv); 240494e177aSGleb Smirnoff return; 241494e177aSGleb Smirnoff } 2425dcd9c10SGleb Smirnoff 2435dcd9c10SGleb Smirnoff if (export_add(exp.item, fle) > 0) 2445dcd9c10SGleb Smirnoff export_send(priv, fe, exp.item, flags); 2455dcd9c10SGleb Smirnoff else 2465dcd9c10SGleb Smirnoff return_export_dgram(priv, fe, exp.item, NG_QUEUE); 247494e177aSGleb Smirnoff } 2485dcd9c10SGleb Smirnoff 2495dcd9c10SGleb Smirnoff if (priv->export9 != NULL) { 2505dcd9c10SGleb Smirnoff exp.item9 = get_export9_dgram(priv, fe, &exp.item9_opt); 2515dcd9c10SGleb Smirnoff if (exp.item9 == NULL) { 2527ee35ac9SGleb Smirnoff priv->nfinfo_export9_failed++; 2535dcd9c10SGleb Smirnoff if (version == IPVERSION) 254494e177aSGleb Smirnoff uma_zfree_arg(priv->zone, fle, priv); 2553090c020SBjoern A. Zeeb #ifdef INET6 2565dcd9c10SGleb Smirnoff else if (version == IP6VERSION) 2575dcd9c10SGleb Smirnoff uma_zfree_arg(priv->zone6, fle, priv); 2583090c020SBjoern A. Zeeb #endif 2595dcd9c10SGleb Smirnoff else 260a23a2dd1SGleb Smirnoff panic("ng_netflow: Unknown IP proto: %d", 261a23a2dd1SGleb Smirnoff version); 2625dcd9c10SGleb Smirnoff return; 2635dcd9c10SGleb Smirnoff } 2645dcd9c10SGleb Smirnoff 2655dcd9c10SGleb Smirnoff if (export9_add(exp.item9, exp.item9_opt, fle) > 0) 2665dcd9c10SGleb Smirnoff export9_send(priv, fe, exp.item9, exp.item9_opt, flags); 2675dcd9c10SGleb Smirnoff else 268a23a2dd1SGleb Smirnoff return_export9_dgram(priv, fe, exp.item9, 269a23a2dd1SGleb Smirnoff exp.item9_opt, NG_QUEUE); 2705dcd9c10SGleb Smirnoff } 2715dcd9c10SGleb Smirnoff 2725dcd9c10SGleb Smirnoff if (version == IPVERSION) 2735dcd9c10SGleb Smirnoff uma_zfree_arg(priv->zone, fle, priv); 2743090c020SBjoern A. Zeeb #ifdef INET6 2755dcd9c10SGleb Smirnoff else if (version == IP6VERSION) 2765dcd9c10SGleb Smirnoff uma_zfree_arg(priv->zone6, fle, priv); 2773090c020SBjoern A. Zeeb #endif 278a752e82dSGleb Smirnoff } 279a752e82dSGleb Smirnoff 280a752e82dSGleb Smirnoff /* Get a snapshot of node statistics */ 281a752e82dSGleb Smirnoff void 282a752e82dSGleb Smirnoff ng_netflow_copyinfo(priv_p priv, struct ng_netflow_info *i) 283a752e82dSGleb Smirnoff { 284a23a2dd1SGleb Smirnoff 2857ee35ac9SGleb Smirnoff i->nfinfo_bytes = counter_u64_fetch(priv->nfinfo_bytes); 2867ee35ac9SGleb Smirnoff i->nfinfo_packets = counter_u64_fetch(priv->nfinfo_packets); 2877ee35ac9SGleb Smirnoff i->nfinfo_bytes6 = counter_u64_fetch(priv->nfinfo_bytes6); 2887ee35ac9SGleb Smirnoff i->nfinfo_packets6 = counter_u64_fetch(priv->nfinfo_packets6); 2897ee35ac9SGleb Smirnoff i->nfinfo_sbytes = counter_u64_fetch(priv->nfinfo_sbytes); 2907ee35ac9SGleb Smirnoff i->nfinfo_spackets = counter_u64_fetch(priv->nfinfo_spackets); 2917ee35ac9SGleb Smirnoff i->nfinfo_sbytes6 = counter_u64_fetch(priv->nfinfo_sbytes6); 2927ee35ac9SGleb Smirnoff i->nfinfo_spackets6 = counter_u64_fetch(priv->nfinfo_spackets6); 2937ee35ac9SGleb Smirnoff i->nfinfo_act_exp = counter_u64_fetch(priv->nfinfo_act_exp); 2947ee35ac9SGleb Smirnoff i->nfinfo_inact_exp = counter_u64_fetch(priv->nfinfo_inact_exp); 2957ee35ac9SGleb Smirnoff 2967ee35ac9SGleb Smirnoff i->nfinfo_used = uma_zone_get_cur(priv->zone); 297ccee6fd2SXin LI #ifdef INET6 2987ee35ac9SGleb Smirnoff i->nfinfo_used6 = uma_zone_get_cur(priv->zone6); 299ccee6fd2SXin LI #endif 3007ee35ac9SGleb Smirnoff 3017ee35ac9SGleb Smirnoff i->nfinfo_alloc_failed = priv->nfinfo_alloc_failed; 3027ee35ac9SGleb Smirnoff i->nfinfo_export_failed = priv->nfinfo_export_failed; 3037ee35ac9SGleb Smirnoff i->nfinfo_export9_failed = priv->nfinfo_export9_failed; 3047ee35ac9SGleb Smirnoff i->nfinfo_realloc_mbuf = priv->nfinfo_realloc_mbuf; 3057ee35ac9SGleb Smirnoff i->nfinfo_alloc_fibs = priv->nfinfo_alloc_fibs; 3067ee35ac9SGleb Smirnoff i->nfinfo_inact_t = priv->nfinfo_inact_t; 3077ee35ac9SGleb Smirnoff i->nfinfo_act_t = priv->nfinfo_act_t; 308a752e82dSGleb Smirnoff } 309a752e82dSGleb Smirnoff 310a752e82dSGleb Smirnoff /* 311a752e82dSGleb Smirnoff * Insert a record into defined slot. 312a752e82dSGleb Smirnoff * 313a752e82dSGleb Smirnoff * First we get for us a free flow entry, then fill in all 314494e177aSGleb Smirnoff * possible fields in it. 315494e177aSGleb Smirnoff * 316494e177aSGleb Smirnoff * TODO: consider dropping hash mutex while filling in datagram, 317494e177aSGleb Smirnoff * as this was done in previous version. Need to test & profile 318494e177aSGleb Smirnoff * to be sure. 319a752e82dSGleb Smirnoff */ 3204e19e0d9SAlexander V. Chernikov #ifdef INET 32136374fcfSAlexander V. Chernikov static int 322494e177aSGleb Smirnoff hash_insert(priv_p priv, struct flow_hash_entry *hsh, struct flow_rec *r, 323*0fc7bdc9SRichard Scheffenegger int plen, uint8_t flags, uint16_t tcp_flags) 324a752e82dSGleb Smirnoff { 325a752e82dSGleb Smirnoff struct flow_entry *fle; 326a752e82dSGleb Smirnoff 327494e177aSGleb Smirnoff mtx_assert(&hsh->mtx, MA_OWNED); 328494e177aSGleb Smirnoff 329494e177aSGleb Smirnoff fle = uma_zalloc_arg(priv->zone, priv, M_NOWAIT); 330494e177aSGleb Smirnoff if (fle == NULL) { 3317ee35ac9SGleb Smirnoff priv->nfinfo_alloc_failed++; 332a752e82dSGleb Smirnoff return (ENOMEM); 333494e177aSGleb Smirnoff } 334a752e82dSGleb Smirnoff 335a752e82dSGleb Smirnoff /* 336a752e82dSGleb Smirnoff * Now fle is totally ours. It is detached from all lists, 337a752e82dSGleb Smirnoff * we can safely edit it. 338a752e82dSGleb Smirnoff */ 3395dcd9c10SGleb Smirnoff fle->f.version = IPVERSION; 340a752e82dSGleb Smirnoff bcopy(r, &fle->f.r, sizeof(struct flow_rec)); 341a752e82dSGleb Smirnoff fle->f.bytes = plen; 342a752e82dSGleb Smirnoff fle->f.packets = 1; 343c1249c63SGleb Smirnoff fle->f.tcp_flags = tcp_flags; 344a752e82dSGleb Smirnoff 345a752e82dSGleb Smirnoff fle->f.first = fle->f.last = time_uptime; 346a752e82dSGleb Smirnoff 347a752e82dSGleb Smirnoff /* 348a752e82dSGleb Smirnoff * First we do route table lookup on destination address. So we can 349a752e82dSGleb Smirnoff * fill in out_ifx, dst_mask, nexthop, and dst_as in future releases. 350a752e82dSGleb Smirnoff */ 35136374fcfSAlexander V. Chernikov if ((flags & NG_NETFLOW_CONF_NODSTLOOKUP) == 0) { 3524e19e0d9SAlexander V. Chernikov struct rtentry *rt; 3534e19e0d9SAlexander V. Chernikov struct route_nhop_data rnd; 354a752e82dSGleb Smirnoff 3554e19e0d9SAlexander V. Chernikov rt = fib4_lookup_rt(r->fib, fle->f.r.r_dst, 0, NHR_NONE, &rnd); 3564e19e0d9SAlexander V. Chernikov if (rt != NULL) { 3574e19e0d9SAlexander V. Chernikov struct in_addr addr; 3584e19e0d9SAlexander V. Chernikov uint32_t scopeid; 3594e19e0d9SAlexander V. Chernikov struct nhop_object *nh = nhop_select_func(rnd.rnd_nhop, 0); 3604e19e0d9SAlexander V. Chernikov int plen; 3616369f51bSAlexander V. Chernikov 3624e19e0d9SAlexander V. Chernikov rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid); 3634e19e0d9SAlexander V. Chernikov fle->f.fle_o_ifx = nh->nh_ifp->if_index; 3648e55a80eSAlexander V. Chernikov if (nh->gw_sa.sa_family == AF_INET) 3654e19e0d9SAlexander V. Chernikov fle->f.next_hop = nh->gw4_sa.sin_addr; 36662e1a437SZhenlei Huang /* 36762e1a437SZhenlei Huang * XXX we're leaving an empty gateway here for 36862e1a437SZhenlei Huang * IPv6 nexthops. 36962e1a437SZhenlei Huang */ 3704e19e0d9SAlexander V. Chernikov fle->f.dst_mask = plen; 371a752e82dSGleb Smirnoff } 37236374fcfSAlexander V. Chernikov } 373a752e82dSGleb Smirnoff 374a752e82dSGleb Smirnoff /* Do route lookup on source address, to fill in src_mask. */ 37536374fcfSAlexander V. Chernikov if ((flags & NG_NETFLOW_CONF_NOSRCLOOKUP) == 0) { 3764e19e0d9SAlexander V. Chernikov struct rtentry *rt; 3774e19e0d9SAlexander V. Chernikov struct route_nhop_data rnd; 3786369f51bSAlexander V. Chernikov 3794e19e0d9SAlexander V. Chernikov rt = fib4_lookup_rt(r->fib, fle->f.r.r_src, 0, NHR_NONE, &rnd); 3804e19e0d9SAlexander V. Chernikov if (rt != NULL) { 3814e19e0d9SAlexander V. Chernikov struct in_addr addr; 3824e19e0d9SAlexander V. Chernikov uint32_t scopeid; 3834e19e0d9SAlexander V. Chernikov int plen; 3846369f51bSAlexander V. Chernikov 3854e19e0d9SAlexander V. Chernikov rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid); 3864e19e0d9SAlexander V. Chernikov fle->f.src_mask = plen; 387a752e82dSGleb Smirnoff } 38836374fcfSAlexander V. Chernikov } 389a752e82dSGleb Smirnoff 390494e177aSGleb Smirnoff /* Push new flow at the and of hash. */ 391494e177aSGleb Smirnoff TAILQ_INSERT_TAIL(&hsh->head, fle, fle_hash); 392a752e82dSGleb Smirnoff 393a752e82dSGleb Smirnoff return (0); 394a752e82dSGleb Smirnoff } 3954e19e0d9SAlexander V. Chernikov #endif 396a752e82dSGleb Smirnoff 3975dcd9c10SGleb Smirnoff #ifdef INET6 39836374fcfSAlexander V. Chernikov static int 399ea7e1638SGleb Smirnoff hash6_insert(priv_p priv, struct flow_hash_entry *hsh6, struct flow6_rec *r, 400*0fc7bdc9SRichard Scheffenegger int plen, uint8_t flags, uint16_t tcp_flags) 4015dcd9c10SGleb Smirnoff { 4025dcd9c10SGleb Smirnoff struct flow6_entry *fle6; 4035dcd9c10SGleb Smirnoff 4045dcd9c10SGleb Smirnoff mtx_assert(&hsh6->mtx, MA_OWNED); 4055dcd9c10SGleb Smirnoff 4065dcd9c10SGleb Smirnoff fle6 = uma_zalloc_arg(priv->zone6, priv, M_NOWAIT); 4075dcd9c10SGleb Smirnoff if (fle6 == NULL) { 4087ee35ac9SGleb Smirnoff priv->nfinfo_alloc_failed++; 4095dcd9c10SGleb Smirnoff return (ENOMEM); 4105dcd9c10SGleb Smirnoff } 4115dcd9c10SGleb Smirnoff 4125dcd9c10SGleb Smirnoff /* 4135dcd9c10SGleb Smirnoff * Now fle is totally ours. It is detached from all lists, 4145dcd9c10SGleb Smirnoff * we can safely edit it. 4155dcd9c10SGleb Smirnoff */ 4165dcd9c10SGleb Smirnoff 4175dcd9c10SGleb Smirnoff fle6->f.version = IP6VERSION; 4185dcd9c10SGleb Smirnoff bcopy(r, &fle6->f.r, sizeof(struct flow6_rec)); 4195dcd9c10SGleb Smirnoff fle6->f.bytes = plen; 4205dcd9c10SGleb Smirnoff fle6->f.packets = 1; 4215dcd9c10SGleb Smirnoff fle6->f.tcp_flags = tcp_flags; 4225dcd9c10SGleb Smirnoff 4235dcd9c10SGleb Smirnoff fle6->f.first = fle6->f.last = time_uptime; 4245dcd9c10SGleb Smirnoff 4255dcd9c10SGleb Smirnoff /* 4265dcd9c10SGleb Smirnoff * First we do route table lookup on destination address. So we can 4275dcd9c10SGleb Smirnoff * fill in out_ifx, dst_mask, nexthop, and dst_as in future releases. 4285dcd9c10SGleb Smirnoff */ 429a23a2dd1SGleb Smirnoff if ((flags & NG_NETFLOW_CONF_NODSTLOOKUP) == 0) { 4304e19e0d9SAlexander V. Chernikov struct rtentry *rt; 4314e19e0d9SAlexander V. Chernikov struct route_nhop_data rnd; 4325dcd9c10SGleb Smirnoff 4334e19e0d9SAlexander V. Chernikov rt = fib6_lookup_rt(r->fib, &fle6->f.r.dst.r_dst6, 0, NHR_NONE, &rnd); 4344e19e0d9SAlexander V. Chernikov if (rt != NULL) { 4354e19e0d9SAlexander V. Chernikov struct in6_addr addr; 4364e19e0d9SAlexander V. Chernikov uint32_t scopeid; 4374e19e0d9SAlexander V. Chernikov struct nhop_object *nh = nhop_select_func(rnd.rnd_nhop, 0); 4384e19e0d9SAlexander V. Chernikov int plen; 4395dcd9c10SGleb Smirnoff 4404e19e0d9SAlexander V. Chernikov rt_get_inet6_prefix_plen(rt, &addr, &plen, &scopeid); 4414e19e0d9SAlexander V. Chernikov fle6->f.fle_o_ifx = nh->nh_ifp->if_index; 4428e55a80eSAlexander V. Chernikov if (nh->gw_sa.sa_family == AF_INET6) 4434e19e0d9SAlexander V. Chernikov fle6->f.n.next_hop6 = nh->gw6_sa.sin6_addr; 4444e19e0d9SAlexander V. Chernikov fle6->f.dst_mask = plen; 4455dcd9c10SGleb Smirnoff } 44636374fcfSAlexander V. Chernikov } 4475dcd9c10SGleb Smirnoff 448e971a314SAlexander V. Chernikov if ((flags & NG_NETFLOW_CONF_NOSRCLOOKUP) == 0) { 4495dcd9c10SGleb Smirnoff /* Do route lookup on source address, to fill in src_mask. */ 4504e19e0d9SAlexander V. Chernikov struct rtentry *rt; 4514e19e0d9SAlexander V. Chernikov struct route_nhop_data rnd; 4525dcd9c10SGleb Smirnoff 4534e19e0d9SAlexander V. Chernikov rt = fib6_lookup_rt(r->fib, &fle6->f.r.src.r_src6, 0, NHR_NONE, &rnd); 4544e19e0d9SAlexander V. Chernikov if (rt != NULL) { 4554e19e0d9SAlexander V. Chernikov struct in6_addr addr; 4564e19e0d9SAlexander V. Chernikov uint32_t scopeid; 4574e19e0d9SAlexander V. Chernikov int plen; 4585dcd9c10SGleb Smirnoff 4594e19e0d9SAlexander V. Chernikov rt_get_inet6_prefix_plen(rt, &addr, &plen, &scopeid); 4604e19e0d9SAlexander V. Chernikov fle6->f.src_mask = plen; 4614e19e0d9SAlexander V. Chernikov } 46236374fcfSAlexander V. Chernikov } 4635dcd9c10SGleb Smirnoff 4645dcd9c10SGleb Smirnoff /* Push new flow at the and of hash. */ 465ea7e1638SGleb Smirnoff TAILQ_INSERT_TAIL(&hsh6->head, (struct flow_entry *)fle6, fle_hash); 4665dcd9c10SGleb Smirnoff 4675dcd9c10SGleb Smirnoff return (0); 4685dcd9c10SGleb Smirnoff } 4695dcd9c10SGleb Smirnoff #endif 4705dcd9c10SGleb Smirnoff 471a752e82dSGleb Smirnoff /* 472a752e82dSGleb Smirnoff * Non-static functions called from ng_netflow.c 473a752e82dSGleb Smirnoff */ 474a752e82dSGleb Smirnoff 475a752e82dSGleb Smirnoff /* Allocate memory and set up flow cache */ 476b6770143SGleb Smirnoff void 477a752e82dSGleb Smirnoff ng_netflow_cache_init(priv_p priv) 478a752e82dSGleb Smirnoff { 479494e177aSGleb Smirnoff struct flow_hash_entry *hsh; 480a752e82dSGleb Smirnoff int i; 481a752e82dSGleb Smirnoff 482494e177aSGleb Smirnoff /* Initialize cache UMA zone. */ 483a23a2dd1SGleb Smirnoff priv->zone = uma_zcreate("NetFlow IPv4 cache", 4847ee35ac9SGleb Smirnoff sizeof(struct flow_entry), NULL, NULL, NULL, NULL, 4857ee35ac9SGleb Smirnoff UMA_ALIGN_CACHE, 0); 486494e177aSGleb Smirnoff uma_zone_set_max(priv->zone, CACHESIZE); 4875dcd9c10SGleb Smirnoff #ifdef INET6 488a23a2dd1SGleb Smirnoff priv->zone6 = uma_zcreate("NetFlow IPv6 cache", 4897ee35ac9SGleb Smirnoff sizeof(struct flow6_entry), NULL, NULL, NULL, NULL, 4907ee35ac9SGleb Smirnoff UMA_ALIGN_CACHE, 0); 4915dcd9c10SGleb Smirnoff uma_zone_set_max(priv->zone6, CACHESIZE); 4925dcd9c10SGleb Smirnoff #endif 493a752e82dSGleb Smirnoff 494494e177aSGleb Smirnoff /* Allocate hash. */ 4951ede983cSDag-Erling Smørgrav priv->hash = malloc(NBUCKETS * sizeof(struct flow_hash_entry), 496494e177aSGleb Smirnoff M_NETFLOW_HASH, M_WAITOK | M_ZERO); 497a752e82dSGleb Smirnoff 498494e177aSGleb Smirnoff /* Initialize hash. */ 499494e177aSGleb Smirnoff for (i = 0, hsh = priv->hash; i < NBUCKETS; i++, hsh++) { 500494e177aSGleb Smirnoff mtx_init(&hsh->mtx, "hash mutex", NULL, MTX_DEF); 501494e177aSGleb Smirnoff TAILQ_INIT(&hsh->head); 502494e177aSGleb Smirnoff } 503a752e82dSGleb Smirnoff 5045dcd9c10SGleb Smirnoff #ifdef INET6 5055dcd9c10SGleb Smirnoff /* Allocate hash. */ 506ea7e1638SGleb Smirnoff priv->hash6 = malloc(NBUCKETS * sizeof(struct flow_hash_entry), 5075dcd9c10SGleb Smirnoff M_NETFLOW_HASH, M_WAITOK | M_ZERO); 5085dcd9c10SGleb Smirnoff 5095dcd9c10SGleb Smirnoff /* Initialize hash. */ 510ea7e1638SGleb Smirnoff for (i = 0, hsh = priv->hash6; i < NBUCKETS; i++, hsh++) { 511ea7e1638SGleb Smirnoff mtx_init(&hsh->mtx, "hash mutex", NULL, MTX_DEF); 512ea7e1638SGleb Smirnoff TAILQ_INIT(&hsh->head); 5135dcd9c10SGleb Smirnoff } 5145dcd9c10SGleb Smirnoff #endif 5155dcd9c10SGleb Smirnoff 5167ee35ac9SGleb Smirnoff priv->nfinfo_bytes = counter_u64_alloc(M_WAITOK); 5177ee35ac9SGleb Smirnoff priv->nfinfo_packets = counter_u64_alloc(M_WAITOK); 5187ee35ac9SGleb Smirnoff priv->nfinfo_bytes6 = counter_u64_alloc(M_WAITOK); 5197ee35ac9SGleb Smirnoff priv->nfinfo_packets6 = counter_u64_alloc(M_WAITOK); 5207ee35ac9SGleb Smirnoff priv->nfinfo_sbytes = counter_u64_alloc(M_WAITOK); 5217ee35ac9SGleb Smirnoff priv->nfinfo_spackets = counter_u64_alloc(M_WAITOK); 5227ee35ac9SGleb Smirnoff priv->nfinfo_sbytes6 = counter_u64_alloc(M_WAITOK); 5237ee35ac9SGleb Smirnoff priv->nfinfo_spackets6 = counter_u64_alloc(M_WAITOK); 5247ee35ac9SGleb Smirnoff priv->nfinfo_act_exp = counter_u64_alloc(M_WAITOK); 5257ee35ac9SGleb Smirnoff priv->nfinfo_inact_exp = counter_u64_alloc(M_WAITOK); 5267ee35ac9SGleb Smirnoff 5275dcd9c10SGleb Smirnoff ng_netflow_v9_cache_init(priv); 5285dcd9c10SGleb Smirnoff CTR0(KTR_NET, "ng_netflow startup()"); 5295dcd9c10SGleb Smirnoff } 5305dcd9c10SGleb Smirnoff 5315dcd9c10SGleb Smirnoff /* Initialize new FIB table for v5 and v9 */ 5325dcd9c10SGleb Smirnoff int 5335dcd9c10SGleb Smirnoff ng_netflow_fib_init(priv_p priv, int fib) 5345dcd9c10SGleb Smirnoff { 5355dcd9c10SGleb Smirnoff fib_export_p fe = priv_to_fib(priv, fib); 5365dcd9c10SGleb Smirnoff 5375dcd9c10SGleb Smirnoff CTR1(KTR_NET, "ng_netflow(): fib init: %d", fib); 5385dcd9c10SGleb Smirnoff 5395dcd9c10SGleb Smirnoff if (fe != NULL) 5405dcd9c10SGleb Smirnoff return (0); 5415dcd9c10SGleb Smirnoff 542a23a2dd1SGleb Smirnoff if ((fe = malloc(sizeof(struct fib_export), M_NETGRAPH, 543a23a2dd1SGleb Smirnoff M_NOWAIT | M_ZERO)) == NULL) 544fa75f402SGleb Smirnoff return (ENOMEM); 5455dcd9c10SGleb Smirnoff 5465dcd9c10SGleb Smirnoff mtx_init(&fe->export_mtx, "export dgram lock", NULL, MTX_DEF); 5475dcd9c10SGleb Smirnoff mtx_init(&fe->export9_mtx, "export9 dgram lock", NULL, MTX_DEF); 5485dcd9c10SGleb Smirnoff fe->fib = fib; 5495dcd9c10SGleb Smirnoff fe->domain_id = fib; 5505dcd9c10SGleb Smirnoff 551a23a2dd1SGleb Smirnoff if (atomic_cmpset_ptr((volatile uintptr_t *)&priv->fib_data[fib], 552a23a2dd1SGleb Smirnoff (uintptr_t)NULL, (uintptr_t)fe) == 0) { 5535dcd9c10SGleb Smirnoff /* FIB already set up by other ISR */ 554a23a2dd1SGleb Smirnoff CTR3(KTR_NET, "ng_netflow(): fib init: %d setup %p but got %p", 555a23a2dd1SGleb Smirnoff fib, fe, priv_to_fib(priv, fib)); 5565dcd9c10SGleb Smirnoff mtx_destroy(&fe->export_mtx); 5575dcd9c10SGleb Smirnoff mtx_destroy(&fe->export9_mtx); 5585dcd9c10SGleb Smirnoff free(fe, M_NETGRAPH); 5595dcd9c10SGleb Smirnoff } else { 5605dcd9c10SGleb Smirnoff /* Increase counter for statistics */ 561a23a2dd1SGleb Smirnoff CTR3(KTR_NET, "ng_netflow(): fib %d setup to %p (%p)", 562a23a2dd1SGleb Smirnoff fib, fe, priv_to_fib(priv, fib)); 5637ee35ac9SGleb Smirnoff priv->nfinfo_alloc_fibs++; 5645dcd9c10SGleb Smirnoff } 565a752e82dSGleb Smirnoff 566a752e82dSGleb Smirnoff return (0); 567a752e82dSGleb Smirnoff } 568a752e82dSGleb Smirnoff 569a752e82dSGleb Smirnoff /* Free all flow cache memory. Called from node close method. */ 570a752e82dSGleb Smirnoff void 571a752e82dSGleb Smirnoff ng_netflow_cache_flush(priv_p priv) 572a752e82dSGleb Smirnoff { 573494e177aSGleb Smirnoff struct flow_entry *fle, *fle1; 574494e177aSGleb Smirnoff struct flow_hash_entry *hsh; 5755dcd9c10SGleb Smirnoff struct netflow_export_item exp; 5765dcd9c10SGleb Smirnoff fib_export_p fe; 577a752e82dSGleb Smirnoff int i; 578a752e82dSGleb Smirnoff 5795dcd9c10SGleb Smirnoff bzero(&exp, sizeof(exp)); 5805dcd9c10SGleb Smirnoff 581a752e82dSGleb Smirnoff /* 582a752e82dSGleb Smirnoff * We are going to free probably billable data. 583a752e82dSGleb Smirnoff * Expire everything before freeing it. 584a752e82dSGleb Smirnoff * No locking is required since callout is already drained. 585a752e82dSGleb Smirnoff */ 586494e177aSGleb Smirnoff for (hsh = priv->hash, i = 0; i < NBUCKETS; hsh++, i++) 587494e177aSGleb Smirnoff TAILQ_FOREACH_SAFE(fle, &hsh->head, fle_hash, fle1) { 588494e177aSGleb Smirnoff TAILQ_REMOVE(&hsh->head, fle, fle_hash); 5895dcd9c10SGleb Smirnoff fe = priv_to_fib(priv, fle->f.r.fib); 5905dcd9c10SGleb Smirnoff expire_flow(priv, fe, fle, NG_QUEUE); 591a752e82dSGleb Smirnoff } 5925dcd9c10SGleb Smirnoff #ifdef INET6 593ea7e1638SGleb Smirnoff for (hsh = priv->hash6, i = 0; i < NBUCKETS; hsh++, i++) 594ea7e1638SGleb Smirnoff TAILQ_FOREACH_SAFE(fle, &hsh->head, fle_hash, fle1) { 595ea7e1638SGleb Smirnoff TAILQ_REMOVE(&hsh->head, fle, fle_hash); 596ea7e1638SGleb Smirnoff fe = priv_to_fib(priv, fle->f.r.fib); 597ea7e1638SGleb Smirnoff expire_flow(priv, fe, fle, NG_QUEUE); 5985dcd9c10SGleb Smirnoff } 5995dcd9c10SGleb Smirnoff #endif 600494e177aSGleb Smirnoff 601494e177aSGleb Smirnoff uma_zdestroy(priv->zone); 602494e177aSGleb Smirnoff /* Destroy hash mutexes. */ 603494e177aSGleb Smirnoff for (i = 0, hsh = priv->hash; i < NBUCKETS; i++, hsh++) 604494e177aSGleb Smirnoff mtx_destroy(&hsh->mtx); 605494e177aSGleb Smirnoff 606494e177aSGleb Smirnoff /* Free hash memory. */ 6075dcd9c10SGleb Smirnoff if (priv->hash != NULL) 6081ede983cSDag-Erling Smørgrav free(priv->hash, M_NETFLOW_HASH); 6095dcd9c10SGleb Smirnoff #ifdef INET6 6105dcd9c10SGleb Smirnoff uma_zdestroy(priv->zone6); 6115dcd9c10SGleb Smirnoff /* Destroy hash mutexes. */ 612ea7e1638SGleb Smirnoff for (i = 0, hsh = priv->hash6; i < NBUCKETS; i++, hsh++) 613ea7e1638SGleb Smirnoff mtx_destroy(&hsh->mtx); 614494e177aSGleb Smirnoff 6155dcd9c10SGleb Smirnoff /* Free hash memory. */ 6165dcd9c10SGleb Smirnoff if (priv->hash6 != NULL) 6175dcd9c10SGleb Smirnoff free(priv->hash6, M_NETFLOW_HASH); 6185dcd9c10SGleb Smirnoff #endif 6195dcd9c10SGleb Smirnoff 62014797255SAlexander V. Chernikov for (i = 0; i < priv->maxfibs; i++) { 6215dcd9c10SGleb Smirnoff if ((fe = priv_to_fib(priv, i)) == NULL) 6225dcd9c10SGleb Smirnoff continue; 6235dcd9c10SGleb Smirnoff 6245dcd9c10SGleb Smirnoff if (fe->exp.item != NULL) 6255dcd9c10SGleb Smirnoff export_send(priv, fe, fe->exp.item, NG_QUEUE); 6265dcd9c10SGleb Smirnoff 6275dcd9c10SGleb Smirnoff if (fe->exp.item9 != NULL) 628a23a2dd1SGleb Smirnoff export9_send(priv, fe, fe->exp.item9, 629a23a2dd1SGleb Smirnoff fe->exp.item9_opt, NG_QUEUE); 6305dcd9c10SGleb Smirnoff 6315dcd9c10SGleb Smirnoff mtx_destroy(&fe->export_mtx); 6325dcd9c10SGleb Smirnoff mtx_destroy(&fe->export9_mtx); 6335dcd9c10SGleb Smirnoff free(fe, M_NETGRAPH); 6345dcd9c10SGleb Smirnoff } 6355dcd9c10SGleb Smirnoff 6367ee35ac9SGleb Smirnoff counter_u64_free(priv->nfinfo_bytes); 6377ee35ac9SGleb Smirnoff counter_u64_free(priv->nfinfo_packets); 6387ee35ac9SGleb Smirnoff counter_u64_free(priv->nfinfo_bytes6); 6397ee35ac9SGleb Smirnoff counter_u64_free(priv->nfinfo_packets6); 6407ee35ac9SGleb Smirnoff counter_u64_free(priv->nfinfo_sbytes); 6417ee35ac9SGleb Smirnoff counter_u64_free(priv->nfinfo_spackets); 6427ee35ac9SGleb Smirnoff counter_u64_free(priv->nfinfo_sbytes6); 6437ee35ac9SGleb Smirnoff counter_u64_free(priv->nfinfo_spackets6); 6447ee35ac9SGleb Smirnoff counter_u64_free(priv->nfinfo_act_exp); 6457ee35ac9SGleb Smirnoff counter_u64_free(priv->nfinfo_inact_exp); 6467ee35ac9SGleb Smirnoff 6475dcd9c10SGleb Smirnoff ng_netflow_v9_cache_flush(priv); 648494e177aSGleb Smirnoff } 649494e177aSGleb Smirnoff 6504e19e0d9SAlexander V. Chernikov #ifdef INET 651494e177aSGleb Smirnoff /* Insert packet from into flow cache. */ 652a752e82dSGleb Smirnoff int 653a23a2dd1SGleb Smirnoff ng_netflow_flow_add(priv_p priv, fib_export_p fe, struct ip *ip, 654a23a2dd1SGleb Smirnoff caddr_t upper_ptr, uint8_t upper_proto, uint8_t flags, 655a23a2dd1SGleb Smirnoff unsigned int src_if_index) 656a752e82dSGleb Smirnoff { 657a23a2dd1SGleb Smirnoff struct flow_entry *fle, *fle1; 658494e177aSGleb Smirnoff struct flow_hash_entry *hsh; 659a752e82dSGleb Smirnoff struct flow_rec r; 6601d03bd16SGleb Smirnoff int hlen, plen; 661494e177aSGleb Smirnoff int error = 0; 662*0fc7bdc9SRichard Scheffenegger uint16_t tcp_flags = 0; 663a752e82dSGleb Smirnoff 664a752e82dSGleb Smirnoff bzero(&r, sizeof(r)); 665a23a2dd1SGleb Smirnoff 6661d03bd16SGleb Smirnoff if (ip->ip_v != IPVERSION) 6671d03bd16SGleb Smirnoff return (EINVAL); 6681d03bd16SGleb Smirnoff 6691d03bd16SGleb Smirnoff hlen = ip->ip_hl << 2; 6701d03bd16SGleb Smirnoff if (hlen < sizeof(struct ip)) 6711d03bd16SGleb Smirnoff return (EINVAL); 6721d03bd16SGleb Smirnoff 6735dcd9c10SGleb Smirnoff /* Assume L4 template by default */ 6745dcd9c10SGleb Smirnoff r.flow_type = NETFLOW_V9_FLOW_V4_L4; 6755dcd9c10SGleb Smirnoff 6761d03bd16SGleb Smirnoff r.r_src = ip->ip_src; 6771d03bd16SGleb Smirnoff r.r_dst = ip->ip_dst; 6785dcd9c10SGleb Smirnoff r.fib = fe->fib; 6791d03bd16SGleb Smirnoff 6801d03bd16SGleb Smirnoff plen = ntohs(ip->ip_len); 6811d03bd16SGleb Smirnoff 6821d03bd16SGleb Smirnoff r.r_ip_p = ip->ip_p; 6831d03bd16SGleb Smirnoff r.r_tos = ip->ip_tos; 6841d03bd16SGleb Smirnoff 6851a6dd095SAlexander Motin r.r_i_ifx = src_if_index; 6861d03bd16SGleb Smirnoff 6871d03bd16SGleb Smirnoff /* 6881d03bd16SGleb Smirnoff * XXX NOTE: only first fragment of fragmented TCP, UDP and 6891d03bd16SGleb Smirnoff * ICMP packet will be recorded with proper s_port and d_port. 6901d03bd16SGleb Smirnoff * Following fragments will be recorded simply as IP packet with 6911d03bd16SGleb Smirnoff * ip_proto = ip->ip_p and s_port, d_port set to zero. 6921d03bd16SGleb Smirnoff * I know, it looks like bug. But I don't want to re-implement 6931d03bd16SGleb Smirnoff * ip packet assebmling here. Anyway, (in)famous trafd works this way - 6941d03bd16SGleb Smirnoff * and nobody complains yet :) 6951d03bd16SGleb Smirnoff */ 696d6bd5ec9SGleb Smirnoff if ((ip->ip_off & htons(IP_OFFMASK)) == 0) 6971d03bd16SGleb Smirnoff switch(r.r_ip_p) { 6981d03bd16SGleb Smirnoff case IPPROTO_TCP: 6991d03bd16SGleb Smirnoff { 700a23a2dd1SGleb Smirnoff struct tcphdr *tcp; 7011d03bd16SGleb Smirnoff 7021d03bd16SGleb Smirnoff tcp = (struct tcphdr *)((caddr_t )ip + hlen); 7031d03bd16SGleb Smirnoff r.r_sport = tcp->th_sport; 7041d03bd16SGleb Smirnoff r.r_dport = tcp->th_dport; 705*0fc7bdc9SRichard Scheffenegger tcp_flags = tcp_get_flags(tcp); 7061d03bd16SGleb Smirnoff break; 7071d03bd16SGleb Smirnoff } 7081d03bd16SGleb Smirnoff case IPPROTO_UDP: 7091d03bd16SGleb Smirnoff r.r_ports = *(uint32_t *)((caddr_t )ip + hlen); 7101d03bd16SGleb Smirnoff break; 7111d03bd16SGleb Smirnoff } 7121d03bd16SGleb Smirnoff 7137ee35ac9SGleb Smirnoff counter_u64_add(priv->nfinfo_packets, 1); 7147ee35ac9SGleb Smirnoff counter_u64_add(priv->nfinfo_bytes, plen); 71592d7d5b4SGleb Smirnoff 716494e177aSGleb Smirnoff /* Find hash slot. */ 717494e177aSGleb Smirnoff hsh = &priv->hash[ip_hash(&r)]; 718a752e82dSGleb Smirnoff 719494e177aSGleb Smirnoff mtx_lock(&hsh->mtx); 720a752e82dSGleb Smirnoff 721494e177aSGleb Smirnoff /* 722494e177aSGleb Smirnoff * Go through hash and find our entry. If we encounter an 723494e177aSGleb Smirnoff * entry, that should be expired, purge it. We do a reverse 724494e177aSGleb Smirnoff * search since most active entries are first, and most 725494e177aSGleb Smirnoff * searches are done on most active entries. 726494e177aSGleb Smirnoff */ 727494e177aSGleb Smirnoff TAILQ_FOREACH_REVERSE_SAFE(fle, &hsh->head, fhead, fle_hash, fle1) { 728494e177aSGleb Smirnoff if (bcmp(&r, &fle->f.r, sizeof(struct flow_rec)) == 0) 729494e177aSGleb Smirnoff break; 730494e177aSGleb Smirnoff if ((INACTIVE(fle) && SMALL(fle)) || AGED(fle)) { 731494e177aSGleb Smirnoff TAILQ_REMOVE(&hsh->head, fle, fle_hash); 732a23a2dd1SGleb Smirnoff expire_flow(priv, priv_to_fib(priv, fle->f.r.fib), 733a23a2dd1SGleb Smirnoff fle, NG_QUEUE); 7347ee35ac9SGleb Smirnoff counter_u64_add(priv->nfinfo_act_exp, 1); 735494e177aSGleb Smirnoff } 736494e177aSGleb Smirnoff } 737494e177aSGleb Smirnoff 738494e177aSGleb Smirnoff if (fle) { /* An existent entry. */ 739a752e82dSGleb Smirnoff 740a752e82dSGleb Smirnoff fle->f.bytes += plen; 741a752e82dSGleb Smirnoff fle->f.packets ++; 742a752e82dSGleb Smirnoff fle->f.tcp_flags |= tcp_flags; 743a752e82dSGleb Smirnoff fle->f.last = time_uptime; 744a752e82dSGleb Smirnoff 745a752e82dSGleb Smirnoff /* 746a752e82dSGleb Smirnoff * We have the following reasons to expire flow in active way: 747a752e82dSGleb Smirnoff * - it hit active timeout 748a752e82dSGleb Smirnoff * - a TCP connection closed 749a752e82dSGleb Smirnoff * - it is going to overflow counter 750a752e82dSGleb Smirnoff */ 751a752e82dSGleb Smirnoff if (tcp_flags & TH_FIN || tcp_flags & TH_RST || AGED(fle) || 7525dcd9c10SGleb Smirnoff (fle->f.bytes >= (CNTR_MAX - IF_MAXMTU)) ) { 753494e177aSGleb Smirnoff TAILQ_REMOVE(&hsh->head, fle, fle_hash); 754a23a2dd1SGleb Smirnoff expire_flow(priv, priv_to_fib(priv, fle->f.r.fib), 755a23a2dd1SGleb Smirnoff fle, NG_QUEUE); 7567ee35ac9SGleb Smirnoff counter_u64_add(priv->nfinfo_act_exp, 1); 757494e177aSGleb Smirnoff } else { 758494e177aSGleb Smirnoff /* 759494e177aSGleb Smirnoff * It is the newest, move it to the tail, 760494e177aSGleb Smirnoff * if it isn't there already. Next search will 761494e177aSGleb Smirnoff * locate it quicker. 762494e177aSGleb Smirnoff */ 763494e177aSGleb Smirnoff if (fle != TAILQ_LAST(&hsh->head, fhead)) { 764494e177aSGleb Smirnoff TAILQ_REMOVE(&hsh->head, fle, fle_hash); 765494e177aSGleb Smirnoff TAILQ_INSERT_TAIL(&hsh->head, fle, fle_hash); 766a752e82dSGleb Smirnoff } 767494e177aSGleb Smirnoff } 768494e177aSGleb Smirnoff } else /* A new flow entry. */ 76936374fcfSAlexander V. Chernikov error = hash_insert(priv, hsh, &r, plen, flags, tcp_flags); 770a752e82dSGleb Smirnoff 771494e177aSGleb Smirnoff mtx_unlock(&hsh->mtx); 77292d7d5b4SGleb Smirnoff 7735dcd9c10SGleb Smirnoff return (error); 7745dcd9c10SGleb Smirnoff } 7754e19e0d9SAlexander V. Chernikov #endif 7765dcd9c10SGleb Smirnoff 7775dcd9c10SGleb Smirnoff #ifdef INET6 7785dcd9c10SGleb Smirnoff /* Insert IPv6 packet from into flow cache. */ 7795dcd9c10SGleb Smirnoff int 780a23a2dd1SGleb Smirnoff ng_netflow_flow6_add(priv_p priv, fib_export_p fe, struct ip6_hdr *ip6, 781a23a2dd1SGleb Smirnoff caddr_t upper_ptr, uint8_t upper_proto, uint8_t flags, 782a23a2dd1SGleb Smirnoff unsigned int src_if_index) 7835dcd9c10SGleb Smirnoff { 784a23a2dd1SGleb Smirnoff struct flow_entry *fle = NULL, *fle1; 785a23a2dd1SGleb Smirnoff struct flow6_entry *fle6; 786ea7e1638SGleb Smirnoff struct flow_hash_entry *hsh; 7875dcd9c10SGleb Smirnoff struct flow6_rec r; 7885dcd9c10SGleb Smirnoff int plen; 7895dcd9c10SGleb Smirnoff int error = 0; 790*0fc7bdc9SRichard Scheffenegger uint16_t tcp_flags = 0; 7915dcd9c10SGleb Smirnoff 7925dcd9c10SGleb Smirnoff /* check version */ 7935dcd9c10SGleb Smirnoff if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) 7945dcd9c10SGleb Smirnoff return (EINVAL); 7955dcd9c10SGleb Smirnoff 7965dcd9c10SGleb Smirnoff bzero(&r, sizeof(r)); 7975dcd9c10SGleb Smirnoff 7985dcd9c10SGleb Smirnoff r.src.r_src6 = ip6->ip6_src; 7995dcd9c10SGleb Smirnoff r.dst.r_dst6 = ip6->ip6_dst; 8005dcd9c10SGleb Smirnoff r.fib = fe->fib; 8015dcd9c10SGleb Smirnoff 8025dcd9c10SGleb Smirnoff /* Assume L4 template by default */ 8035dcd9c10SGleb Smirnoff r.flow_type = NETFLOW_V9_FLOW_V6_L4; 8045dcd9c10SGleb Smirnoff 8055dcd9c10SGleb Smirnoff plen = ntohs(ip6->ip6_plen) + sizeof(struct ip6_hdr); 8065dcd9c10SGleb Smirnoff 8075dcd9c10SGleb Smirnoff #if 0 808a23a2dd1SGleb Smirnoff /* XXX: set DSCP/CoS value */ 8095dcd9c10SGleb Smirnoff r.r_tos = ip->ip_tos; 8105dcd9c10SGleb Smirnoff #endif 81136374fcfSAlexander V. Chernikov if ((flags & NG_NETFLOW_IS_FRAG) == 0) { 8125dcd9c10SGleb Smirnoff switch(upper_proto) { 8135dcd9c10SGleb Smirnoff case IPPROTO_TCP: 8145dcd9c10SGleb Smirnoff { 815a23a2dd1SGleb Smirnoff struct tcphdr *tcp; 8165dcd9c10SGleb Smirnoff 8175dcd9c10SGleb Smirnoff tcp = (struct tcphdr *)upper_ptr; 8185dcd9c10SGleb Smirnoff r.r_ports = *(uint32_t *)upper_ptr; 819*0fc7bdc9SRichard Scheffenegger tcp_flags = tcp_get_flags(tcp); 8205dcd9c10SGleb Smirnoff break; 8215dcd9c10SGleb Smirnoff } 8225dcd9c10SGleb Smirnoff case IPPROTO_UDP: 8235dcd9c10SGleb Smirnoff case IPPROTO_SCTP: 8245dcd9c10SGleb Smirnoff r.r_ports = *(uint32_t *)upper_ptr; 8255dcd9c10SGleb Smirnoff break; 8265dcd9c10SGleb Smirnoff } 8275dcd9c10SGleb Smirnoff } 8285dcd9c10SGleb Smirnoff 8295dcd9c10SGleb Smirnoff r.r_ip_p = upper_proto; 8305dcd9c10SGleb Smirnoff r.r_i_ifx = src_if_index; 8315dcd9c10SGleb Smirnoff 8327ee35ac9SGleb Smirnoff counter_u64_add(priv->nfinfo_packets6, 1); 8337ee35ac9SGleb Smirnoff counter_u64_add(priv->nfinfo_bytes6, plen); 8345dcd9c10SGleb Smirnoff 8355dcd9c10SGleb Smirnoff /* Find hash slot. */ 836ea7e1638SGleb Smirnoff hsh = &priv->hash6[ip6_hash(&r)]; 8375dcd9c10SGleb Smirnoff 838ea7e1638SGleb Smirnoff mtx_lock(&hsh->mtx); 8395dcd9c10SGleb Smirnoff 8405dcd9c10SGleb Smirnoff /* 8415dcd9c10SGleb Smirnoff * Go through hash and find our entry. If we encounter an 8425dcd9c10SGleb Smirnoff * entry, that should be expired, purge it. We do a reverse 8435dcd9c10SGleb Smirnoff * search since most active entries are first, and most 8445dcd9c10SGleb Smirnoff * searches are done on most active entries. 8455dcd9c10SGleb Smirnoff */ 846ea7e1638SGleb Smirnoff TAILQ_FOREACH_REVERSE_SAFE(fle, &hsh->head, fhead, fle_hash, fle1) { 847ea7e1638SGleb Smirnoff if (fle->f.version != IP6VERSION) 8485dcd9c10SGleb Smirnoff continue; 849ea7e1638SGleb Smirnoff fle6 = (struct flow6_entry *)fle; 8505dcd9c10SGleb Smirnoff if (bcmp(&r, &fle6->f.r, sizeof(struct flow6_rec)) == 0) 8515dcd9c10SGleb Smirnoff break; 8525dcd9c10SGleb Smirnoff if ((INACTIVE(fle6) && SMALL(fle6)) || AGED(fle6)) { 853ea7e1638SGleb Smirnoff TAILQ_REMOVE(&hsh->head, fle, fle_hash); 854ea7e1638SGleb Smirnoff expire_flow(priv, priv_to_fib(priv, fle->f.r.fib), fle, 855ea7e1638SGleb Smirnoff NG_QUEUE); 8567ee35ac9SGleb Smirnoff counter_u64_add(priv->nfinfo_act_exp, 1); 8575dcd9c10SGleb Smirnoff } 8585dcd9c10SGleb Smirnoff } 8595dcd9c10SGleb Smirnoff 860ea7e1638SGleb Smirnoff if (fle != NULL) { /* An existent entry. */ 861ea7e1638SGleb Smirnoff fle6 = (struct flow6_entry *)fle; 8625dcd9c10SGleb Smirnoff 8635dcd9c10SGleb Smirnoff fle6->f.bytes += plen; 8645dcd9c10SGleb Smirnoff fle6->f.packets ++; 8655dcd9c10SGleb Smirnoff fle6->f.tcp_flags |= tcp_flags; 8665dcd9c10SGleb Smirnoff fle6->f.last = time_uptime; 8675dcd9c10SGleb Smirnoff 8685dcd9c10SGleb Smirnoff /* 8695dcd9c10SGleb Smirnoff * We have the following reasons to expire flow in active way: 8705dcd9c10SGleb Smirnoff * - it hit active timeout 8715dcd9c10SGleb Smirnoff * - a TCP connection closed 8725dcd9c10SGleb Smirnoff * - it is going to overflow counter 8735dcd9c10SGleb Smirnoff */ 8745dcd9c10SGleb Smirnoff if (tcp_flags & TH_FIN || tcp_flags & TH_RST || AGED(fle6) || 8755dcd9c10SGleb Smirnoff (fle6->f.bytes >= (CNTR_MAX - IF_MAXMTU)) ) { 876ea7e1638SGleb Smirnoff TAILQ_REMOVE(&hsh->head, fle, fle_hash); 877ea7e1638SGleb Smirnoff expire_flow(priv, priv_to_fib(priv, fle->f.r.fib), fle, 878ea7e1638SGleb Smirnoff NG_QUEUE); 8797ee35ac9SGleb Smirnoff counter_u64_add(priv->nfinfo_act_exp, 1); 8805dcd9c10SGleb Smirnoff } else { 8815dcd9c10SGleb Smirnoff /* 8825dcd9c10SGleb Smirnoff * It is the newest, move it to the tail, 8835dcd9c10SGleb Smirnoff * if it isn't there already. Next search will 8845dcd9c10SGleb Smirnoff * locate it quicker. 8855dcd9c10SGleb Smirnoff */ 886ea7e1638SGleb Smirnoff if (fle != TAILQ_LAST(&hsh->head, fhead)) { 887ea7e1638SGleb Smirnoff TAILQ_REMOVE(&hsh->head, fle, fle_hash); 888ea7e1638SGleb Smirnoff TAILQ_INSERT_TAIL(&hsh->head, fle, fle_hash); 8895dcd9c10SGleb Smirnoff } 8905dcd9c10SGleb Smirnoff } 8915dcd9c10SGleb Smirnoff } else /* A new flow entry. */ 89236374fcfSAlexander V. Chernikov error = hash6_insert(priv, hsh, &r, plen, flags, tcp_flags); 8935dcd9c10SGleb Smirnoff 894ea7e1638SGleb Smirnoff mtx_unlock(&hsh->mtx); 895494e177aSGleb Smirnoff 896494e177aSGleb Smirnoff return (error); 897a752e82dSGleb Smirnoff } 8985dcd9c10SGleb Smirnoff #endif 899a752e82dSGleb Smirnoff 900a752e82dSGleb Smirnoff /* 901494e177aSGleb Smirnoff * Return records from cache to userland. 902a752e82dSGleb Smirnoff * 903a752e82dSGleb Smirnoff * TODO: matching particular IP should be done in kernel, here. 904a752e82dSGleb Smirnoff */ 905a752e82dSGleb Smirnoff int 906ea7e1638SGleb Smirnoff ng_netflow_flow_show(priv_p priv, struct ngnf_show_header *req, 907ea7e1638SGleb Smirnoff struct ngnf_show_header *resp) 908a752e82dSGleb Smirnoff { 909494e177aSGleb Smirnoff struct flow_hash_entry *hsh; 910a752e82dSGleb Smirnoff struct flow_entry *fle; 911ea7e1638SGleb Smirnoff struct flow_entry_data *data = (struct flow_entry_data *)(resp + 1); 912ea7e1638SGleb Smirnoff #ifdef INET6 913ea7e1638SGleb Smirnoff struct flow6_entry_data *data6 = (struct flow6_entry_data *)(resp + 1); 914ea7e1638SGleb Smirnoff #endif 915ea7e1638SGleb Smirnoff int i, max; 916a752e82dSGleb Smirnoff 917ea7e1638SGleb Smirnoff i = req->hash_id; 918ea7e1638SGleb Smirnoff if (i > NBUCKETS-1) 919a752e82dSGleb Smirnoff return (EINVAL); 920ea7e1638SGleb Smirnoff 921ea7e1638SGleb Smirnoff #ifdef INET6 922ea7e1638SGleb Smirnoff if (req->version == 6) { 923ea7e1638SGleb Smirnoff resp->version = 6; 924ea7e1638SGleb Smirnoff hsh = priv->hash6 + i; 925ea7e1638SGleb Smirnoff max = NREC6_AT_ONCE; 926ea7e1638SGleb Smirnoff } else 927ea7e1638SGleb Smirnoff #endif 928ea7e1638SGleb Smirnoff if (req->version == 4) { 929ea7e1638SGleb Smirnoff resp->version = 4; 930ea7e1638SGleb Smirnoff hsh = priv->hash + i; 931ea7e1638SGleb Smirnoff max = NREC_AT_ONCE; 932ea7e1638SGleb Smirnoff } else 933ea7e1638SGleb Smirnoff return (EINVAL); 934a752e82dSGleb Smirnoff 935a752e82dSGleb Smirnoff /* 936a752e82dSGleb Smirnoff * We will transfer not more than NREC_AT_ONCE. More data 937a752e82dSGleb Smirnoff * will come in next message. 938ea7e1638SGleb Smirnoff * We send current hash index and current record number in list 939ea7e1638SGleb Smirnoff * to userland, and userland should return it back to us. 940ea7e1638SGleb Smirnoff * Then, we will restart with new entry. 941494e177aSGleb Smirnoff * 942ea7e1638SGleb Smirnoff * The resulting cache snapshot can be inaccurate if flow expiration 943ea7e1638SGleb Smirnoff * is taking place on hash item between userland data requests for 944ea7e1638SGleb Smirnoff * this hash item id. 945a752e82dSGleb Smirnoff */ 946ea7e1638SGleb Smirnoff resp->nentries = 0; 947494e177aSGleb Smirnoff for (; i < NBUCKETS; hsh++, i++) { 948ea7e1638SGleb Smirnoff int list_id; 949ea7e1638SGleb Smirnoff 950ea7e1638SGleb Smirnoff if (mtx_trylock(&hsh->mtx) == 0) { 951ea7e1638SGleb Smirnoff /* 952ea7e1638SGleb Smirnoff * Requested hash index is not available, 953ea7e1638SGleb Smirnoff * relay decision to skip or re-request data 954ea7e1638SGleb Smirnoff * to userland. 955ea7e1638SGleb Smirnoff */ 956ea7e1638SGleb Smirnoff resp->hash_id = i; 957ea7e1638SGleb Smirnoff resp->list_id = 0; 958ea7e1638SGleb Smirnoff return (0); 959ea7e1638SGleb Smirnoff } 960ea7e1638SGleb Smirnoff 961ea7e1638SGleb Smirnoff list_id = 0; 962ea7e1638SGleb Smirnoff TAILQ_FOREACH(fle, &hsh->head, fle_hash) { 963ea7e1638SGleb Smirnoff if (hsh->mtx.mtx_lock & MTX_CONTESTED) { 964ea7e1638SGleb Smirnoff resp->hash_id = i; 965ea7e1638SGleb Smirnoff resp->list_id = list_id; 966acfc0709SGleb Smirnoff mtx_unlock(&hsh->mtx); 967ea7e1638SGleb Smirnoff return (0); 968ea7e1638SGleb Smirnoff } 969ea7e1638SGleb Smirnoff 970ea7e1638SGleb Smirnoff list_id++; 971ea7e1638SGleb Smirnoff /* Search for particular record in list. */ 972ea7e1638SGleb Smirnoff if (req->list_id > 0) { 973ea7e1638SGleb Smirnoff if (list_id < req->list_id) 974a752e82dSGleb Smirnoff continue; 975494e177aSGleb Smirnoff 976ea7e1638SGleb Smirnoff /* Requested list position found. */ 977ea7e1638SGleb Smirnoff req->list_id = 0; 978ea7e1638SGleb Smirnoff } 979ea7e1638SGleb Smirnoff #ifdef INET6 980ea7e1638SGleb Smirnoff if (req->version == 6) { 981ea7e1638SGleb Smirnoff struct flow6_entry *fle6; 982494e177aSGleb Smirnoff 983ea7e1638SGleb Smirnoff fle6 = (struct flow6_entry *)fle; 984ea7e1638SGleb Smirnoff bcopy(&fle6->f, data6 + resp->nentries, 985ea7e1638SGleb Smirnoff sizeof(fle6->f)); 986ea7e1638SGleb Smirnoff } else 987ea7e1638SGleb Smirnoff #endif 988ea7e1638SGleb Smirnoff bcopy(&fle->f, data + resp->nentries, 989020d3f61SGleb Smirnoff sizeof(fle->f)); 990ea7e1638SGleb Smirnoff resp->nentries++; 991ea7e1638SGleb Smirnoff if (resp->nentries == max) { 992ea7e1638SGleb Smirnoff resp->hash_id = i; 993ea7e1638SGleb Smirnoff /* 994ea7e1638SGleb Smirnoff * If it was the last item in list 995ea7e1638SGleb Smirnoff * we simply skip to next hash_id. 996ea7e1638SGleb Smirnoff */ 997ea7e1638SGleb Smirnoff resp->list_id = list_id + 1; 998acfc0709SGleb Smirnoff mtx_unlock(&hsh->mtx); 999a752e82dSGleb Smirnoff return (0); 1000a752e82dSGleb Smirnoff } 1001a752e82dSGleb Smirnoff } 1002494e177aSGleb Smirnoff mtx_unlock(&hsh->mtx); 1003494e177aSGleb Smirnoff } 1004a752e82dSGleb Smirnoff 1005ea7e1638SGleb Smirnoff resp->hash_id = resp->list_id = 0; 1006ea7e1638SGleb Smirnoff 1007a752e82dSGleb Smirnoff return (0); 1008a752e82dSGleb Smirnoff } 1009a752e82dSGleb Smirnoff 1010a752e82dSGleb Smirnoff /* We have full datagram in privdata. Send it to export hook. */ 1011a752e82dSGleb Smirnoff static int 10125dcd9c10SGleb Smirnoff export_send(priv_p priv, fib_export_p fe, item_p item, int flags) 1013a752e82dSGleb Smirnoff { 1014494e177aSGleb Smirnoff struct mbuf *m = NGI_M(item); 1015494e177aSGleb Smirnoff struct netflow_v5_export_dgram *dgram = mtod(m, 1016494e177aSGleb Smirnoff struct netflow_v5_export_dgram *); 1017494e177aSGleb Smirnoff struct netflow_v5_header *header = &dgram->header; 1018a752e82dSGleb Smirnoff struct timespec ts; 1019a752e82dSGleb Smirnoff int error = 0; 1020a752e82dSGleb Smirnoff 1021494e177aSGleb Smirnoff /* Fill mbuf header. */ 1022494e177aSGleb Smirnoff m->m_len = m->m_pkthdr.len = sizeof(struct netflow_v5_record) * 1023494e177aSGleb Smirnoff header->count + sizeof(struct netflow_v5_header); 1024494e177aSGleb Smirnoff 1025494e177aSGleb Smirnoff /* Fill export header. */ 10265fac4ee9SGleb Smirnoff header->sys_uptime = htonl(MILLIUPTIME(time_uptime)); 1027a752e82dSGleb Smirnoff getnanotime(&ts); 1028a752e82dSGleb Smirnoff header->unix_secs = htonl(ts.tv_sec); 1029a752e82dSGleb Smirnoff header->unix_nsecs = htonl(ts.tv_nsec); 1030f17f8231SMaxim Konovalov header->engine_type = 0; 10315dcd9c10SGleb Smirnoff header->engine_id = fe->domain_id; 1032f17f8231SMaxim Konovalov header->pad = 0; 10335dcd9c10SGleb Smirnoff header->flow_seq = htonl(atomic_fetchadd_32(&fe->flow_seq, 103414379bfbSGleb Smirnoff header->count)); 1035a752e82dSGleb Smirnoff header->count = htons(header->count); 1036a752e82dSGleb Smirnoff 1037494e177aSGleb Smirnoff if (priv->export != NULL) 103873189791SGleb Smirnoff NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export, flags); 1039cfcb2a4cSAlexander Motin else 1040cfcb2a4cSAlexander Motin NG_FREE_ITEM(item); 1041a752e82dSGleb Smirnoff 1042a752e82dSGleb Smirnoff return (error); 1043a752e82dSGleb Smirnoff } 1044a752e82dSGleb Smirnoff 1045494e177aSGleb Smirnoff /* Add export record to dgram. */ 1046a752e82dSGleb Smirnoff static int 1047494e177aSGleb Smirnoff export_add(item_p item, struct flow_entry *fle) 1048a752e82dSGleb Smirnoff { 1049494e177aSGleb Smirnoff struct netflow_v5_export_dgram *dgram = mtod(NGI_M(item), 1050494e177aSGleb Smirnoff struct netflow_v5_export_dgram *); 1051494e177aSGleb Smirnoff struct netflow_v5_header *header = &dgram->header; 1052a752e82dSGleb Smirnoff struct netflow_v5_record *rec; 1053a752e82dSGleb Smirnoff 1054494e177aSGleb Smirnoff rec = &dgram->r[header->count]; 1055a752e82dSGleb Smirnoff header->count ++; 1056a752e82dSGleb Smirnoff 1057494e177aSGleb Smirnoff KASSERT(header->count <= NETFLOW_V5_MAX_RECORDS, 1058494e177aSGleb Smirnoff ("ng_netflow: export too big")); 1059494e177aSGleb Smirnoff 1060494e177aSGleb Smirnoff /* Fill in export record. */ 1061a752e82dSGleb Smirnoff rec->src_addr = fle->f.r.r_src.s_addr; 1062a752e82dSGleb Smirnoff rec->dst_addr = fle->f.r.r_dst.s_addr; 1063a752e82dSGleb Smirnoff rec->next_hop = fle->f.next_hop.s_addr; 1064a752e82dSGleb Smirnoff rec->i_ifx = htons(fle->f.fle_i_ifx); 1065a752e82dSGleb Smirnoff rec->o_ifx = htons(fle->f.fle_o_ifx); 1066a752e82dSGleb Smirnoff rec->packets = htonl(fle->f.packets); 1067a752e82dSGleb Smirnoff rec->octets = htonl(fle->f.bytes); 10685fac4ee9SGleb Smirnoff rec->first = htonl(MILLIUPTIME(fle->f.first)); 10695fac4ee9SGleb Smirnoff rec->last = htonl(MILLIUPTIME(fle->f.last)); 1070a752e82dSGleb Smirnoff rec->s_port = fle->f.r.r_sport; 1071a752e82dSGleb Smirnoff rec->d_port = fle->f.r.r_dport; 1072a752e82dSGleb Smirnoff rec->flags = fle->f.tcp_flags; 1073a752e82dSGleb Smirnoff rec->prot = fle->f.r.r_ip_p; 1074a752e82dSGleb Smirnoff rec->tos = fle->f.r.r_tos; 1075a752e82dSGleb Smirnoff rec->dst_mask = fle->f.dst_mask; 1076a752e82dSGleb Smirnoff rec->src_mask = fle->f.src_mask; 10779da82118SGleb Smirnoff rec->pad1 = 0; 10789da82118SGleb Smirnoff rec->pad2 = 0; 1079a752e82dSGleb Smirnoff 1080494e177aSGleb Smirnoff /* Not supported fields. */ 1081494e177aSGleb Smirnoff rec->src_as = rec->dst_as = 0; 1082a752e82dSGleb Smirnoff 1083494e177aSGleb Smirnoff if (header->count == NETFLOW_V5_MAX_RECORDS) 1084494e177aSGleb Smirnoff return (1); /* end of datagram */ 1085494e177aSGleb Smirnoff else 1086a752e82dSGleb Smirnoff return (0); 1087a752e82dSGleb Smirnoff } 1088a752e82dSGleb Smirnoff 1089a752e82dSGleb Smirnoff /* Periodic flow expiry run. */ 1090a752e82dSGleb Smirnoff void 1091a752e82dSGleb Smirnoff ng_netflow_expire(void *arg) 1092a752e82dSGleb Smirnoff { 1093494e177aSGleb Smirnoff struct flow_entry *fle, *fle1; 1094494e177aSGleb Smirnoff struct flow_hash_entry *hsh; 1095a752e82dSGleb Smirnoff priv_p priv = (priv_p )arg; 10967ee35ac9SGleb Smirnoff int used, i; 1097a752e82dSGleb Smirnoff 1098a752e82dSGleb Smirnoff /* 1099494e177aSGleb Smirnoff * Going through all the cache. 1100a752e82dSGleb Smirnoff */ 11017ee35ac9SGleb Smirnoff used = uma_zone_get_cur(priv->zone); 1102494e177aSGleb Smirnoff for (hsh = priv->hash, i = 0; i < NBUCKETS; hsh++, i++) { 1103a752e82dSGleb Smirnoff /* 1104494e177aSGleb Smirnoff * Skip entries, that are already being worked on. 1105a752e82dSGleb Smirnoff */ 1106494e177aSGleb Smirnoff if (mtx_trylock(&hsh->mtx) == 0) 1107494e177aSGleb Smirnoff continue; 1108a752e82dSGleb Smirnoff 1109494e177aSGleb Smirnoff TAILQ_FOREACH_SAFE(fle, &hsh->head, fle_hash, fle1) { 1110494e177aSGleb Smirnoff /* 1111494e177aSGleb Smirnoff * Interrupt thread wants this entry! 1112494e177aSGleb Smirnoff * Quick! Quick! Bail out! 1113494e177aSGleb Smirnoff */ 1114494e177aSGleb Smirnoff if (hsh->mtx.mtx_lock & MTX_CONTESTED) 1115494e177aSGleb Smirnoff break; 1116494e177aSGleb Smirnoff 1117494e177aSGleb Smirnoff /* 1118494e177aSGleb Smirnoff * Don't expire aggressively while hash collision 1119494e177aSGleb Smirnoff * ratio is predicted small. 1120494e177aSGleb Smirnoff */ 1121494e177aSGleb Smirnoff if (used <= (NBUCKETS*2) && !INACTIVE(fle)) 1122494e177aSGleb Smirnoff break; 1123494e177aSGleb Smirnoff 1124006725baSGleb Smirnoff if ((INACTIVE(fle) && (SMALL(fle) || 11253b9c2997SGleb Smirnoff (used > (NBUCKETS*2)))) || AGED(fle)) { 1126494e177aSGleb Smirnoff TAILQ_REMOVE(&hsh->head, fle, fle_hash); 1127a23a2dd1SGleb Smirnoff expire_flow(priv, priv_to_fib(priv, 1128a23a2dd1SGleb Smirnoff fle->f.r.fib), fle, NG_NOFLAGS); 1129494e177aSGleb Smirnoff used--; 11307ee35ac9SGleb Smirnoff counter_u64_add(priv->nfinfo_inact_exp, 1); 1131a752e82dSGleb Smirnoff } 1132a752e82dSGleb Smirnoff } 1133494e177aSGleb Smirnoff mtx_unlock(&hsh->mtx); 1134494e177aSGleb Smirnoff } 1135a752e82dSGleb Smirnoff 11365dcd9c10SGleb Smirnoff #ifdef INET6 11377ee35ac9SGleb Smirnoff used = uma_zone_get_cur(priv->zone6); 1138ea7e1638SGleb Smirnoff for (hsh = priv->hash6, i = 0; i < NBUCKETS; hsh++, i++) { 1139ea7e1638SGleb Smirnoff struct flow6_entry *fle6; 1140ea7e1638SGleb Smirnoff 11415dcd9c10SGleb Smirnoff /* 11425dcd9c10SGleb Smirnoff * Skip entries, that are already being worked on. 11435dcd9c10SGleb Smirnoff */ 1144ea7e1638SGleb Smirnoff if (mtx_trylock(&hsh->mtx) == 0) 11455dcd9c10SGleb Smirnoff continue; 11465dcd9c10SGleb Smirnoff 1147ea7e1638SGleb Smirnoff TAILQ_FOREACH_SAFE(fle, &hsh->head, fle_hash, fle1) { 1148ea7e1638SGleb Smirnoff fle6 = (struct flow6_entry *)fle; 11495dcd9c10SGleb Smirnoff /* 11505dcd9c10SGleb Smirnoff * Interrupt thread wants this entry! 11515dcd9c10SGleb Smirnoff * Quick! Quick! Bail out! 11525dcd9c10SGleb Smirnoff */ 1153ea7e1638SGleb Smirnoff if (hsh->mtx.mtx_lock & MTX_CONTESTED) 11545dcd9c10SGleb Smirnoff break; 11555dcd9c10SGleb Smirnoff 11565dcd9c10SGleb Smirnoff /* 11575dcd9c10SGleb Smirnoff * Don't expire aggressively while hash collision 11585dcd9c10SGleb Smirnoff * ratio is predicted small. 11595dcd9c10SGleb Smirnoff */ 11605dcd9c10SGleb Smirnoff if (used <= (NBUCKETS*2) && !INACTIVE(fle6)) 11615dcd9c10SGleb Smirnoff break; 11625dcd9c10SGleb Smirnoff 11635dcd9c10SGleb Smirnoff if ((INACTIVE(fle6) && (SMALL(fle6) || 11645dcd9c10SGleb Smirnoff (used > (NBUCKETS*2)))) || AGED(fle6)) { 1165ea7e1638SGleb Smirnoff TAILQ_REMOVE(&hsh->head, fle, fle_hash); 1166ea7e1638SGleb Smirnoff expire_flow(priv, priv_to_fib(priv, 1167ea7e1638SGleb Smirnoff fle->f.r.fib), fle, NG_NOFLAGS); 11685dcd9c10SGleb Smirnoff used--; 11697ee35ac9SGleb Smirnoff counter_u64_add(priv->nfinfo_inact_exp, 1); 11705dcd9c10SGleb Smirnoff } 11715dcd9c10SGleb Smirnoff } 1172ea7e1638SGleb Smirnoff mtx_unlock(&hsh->mtx); 11735dcd9c10SGleb Smirnoff } 11745dcd9c10SGleb Smirnoff #endif 1175a752e82dSGleb Smirnoff 1176494e177aSGleb Smirnoff /* Schedule next expire. */ 1177a752e82dSGleb Smirnoff callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire, 1178a752e82dSGleb Smirnoff (void *)priv); 1179a752e82dSGleb Smirnoff } 1180