1219182Sglebius/*- 2219182Sglebius * Copyright (c) 2010 Alexander V. Chernikov <melifaro@ipfw.ru> 3219182Sglebius * All rights reserved. 4219182Sglebius * 5219182Sglebius * Redistribution and use in source and binary forms, with or without 6219182Sglebius * modification, are permitted provided that the following conditions 7219182Sglebius * are met: 8219182Sglebius * 1. Redistributions of source code must retain the above copyright 9219182Sglebius * notice, this list of conditions and the following disclaimer. 10219182Sglebius * 2. Redistributions in binary form must reproduce the above copyright 11219182Sglebius * notice, this list of conditions and the following disclaimer in the 12219182Sglebius * documentation and/or other materials provided with the distribution. 13219182Sglebius * 14219182Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15219182Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16219182Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17219182Sglebius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18219182Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19219182Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20219182Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21219182Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22219182Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23219182Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24219182Sglebius * SUCH DAMAGE. 25219182Sglebius * 26219182Sglebius * $FreeBSD$ 27219182Sglebius */ 28219182Sglebius 29260278Sdim#include <sys/cdefs.h> 30260278Sdim__FBSDID("$FreeBSD$"); 31219182Sglebius 32219182Sglebius#include "opt_inet6.h" 33219182Sglebius#include "opt_route.h" 34219182Sglebius#include <sys/param.h> 35219182Sglebius#include <sys/kernel.h> 36219182Sglebius#include <sys/limits.h> 37219182Sglebius#include <sys/mbuf.h> 38219182Sglebius#include <sys/syslog.h> 39219182Sglebius#include <sys/systm.h> 40219182Sglebius#include <sys/socket.h> 41219182Sglebius#include <sys/endian.h> 42219182Sglebius 43219182Sglebius#include <machine/atomic.h> 44219182Sglebius#include <machine/stdarg.h> 45219182Sglebius 46219182Sglebius#include <net/if.h> 47219182Sglebius#include <net/route.h> 48219182Sglebius#include <net/ethernet.h> 49219182Sglebius#include <netinet/in.h> 50219182Sglebius#include <netinet/in_systm.h> 51219182Sglebius#include <netinet/ip.h> 52219182Sglebius#include <netinet/ip6.h> 53219182Sglebius#include <netinet/tcp.h> 54219182Sglebius#include <netinet/udp.h> 55219182Sglebius 56219182Sglebius#include <netgraph/ng_message.h> 57219182Sglebius#include <netgraph/netgraph.h> 58219182Sglebius 59219182Sglebius#include <netgraph/netflow/netflow.h> 60219182Sglebius#include <netgraph/netflow/ng_netflow.h> 61219182Sglebius#include <netgraph/netflow/netflow_v9.h> 62219182Sglebius 63219182SglebiusMALLOC_DECLARE(M_NETFLOW_GENERAL); 64219182SglebiusMALLOC_DEFINE(M_NETFLOW_GENERAL, "netflow_general", "plog, V9 templates data"); 65219182Sglebius 66219182Sglebius/* 67219182Sglebius * Base V9 templates for L4+ IPv4/IPv6 protocols 68219182Sglebius */ 69219182Sglebiusstruct netflow_v9_template _netflow_v9_record_ipv4_tcp[] = 70219182Sglebius{ 71219182Sglebius { NETFLOW_V9_FIELD_IPV4_SRC_ADDR, 4}, 72219182Sglebius { NETFLOW_V9_FIELD_IPV4_DST_ADDR, 4}, 73219182Sglebius { NETFLOW_V9_FIELD_IPV4_NEXT_HOP, 4}, 74219182Sglebius { NETFLOW_V9_FIELD_INPUT_SNMP, 2}, 75219182Sglebius { NETFLOW_V9_FIELD_OUTPUT_SNMP, 2}, 76219182Sglebius { NETFLOW_V9_FIELD_IN_PKTS, sizeof(CNTR)}, 77219182Sglebius { NETFLOW_V9_FIELD_IN_BYTES, sizeof(CNTR)}, 78219182Sglebius { NETFLOW_V9_FIELD_OUT_PKTS, sizeof(CNTR)}, 79219182Sglebius { NETFLOW_V9_FIELD_OUT_BYTES, sizeof(CNTR)}, 80219182Sglebius { NETFLOW_V9_FIELD_FIRST_SWITCHED, 4}, 81219182Sglebius { NETFLOW_V9_FIELD_LAST_SWITCHED, 4}, 82219182Sglebius { NETFLOW_V9_FIELD_L4_SRC_PORT, 2}, 83219182Sglebius { NETFLOW_V9_FIELD_L4_DST_PORT, 2}, 84219182Sglebius { NETFLOW_V9_FIELD_TCP_FLAGS, 1}, 85219182Sglebius { NETFLOW_V9_FIELD_PROTOCOL, 1}, 86219182Sglebius { NETFLOW_V9_FIELD_TOS, 1}, 87219182Sglebius { NETFLOW_V9_FIELD_SRC_AS, 4}, 88219182Sglebius { NETFLOW_V9_FIELD_DST_AS, 4}, 89219182Sglebius { NETFLOW_V9_FIELD_SRC_MASK, 1}, 90219182Sglebius { NETFLOW_V9_FIELD_DST_MASK, 1}, 91219182Sglebius {0, 0} 92219182Sglebius}; 93219182Sglebius 94219182Sglebiusstruct netflow_v9_template _netflow_v9_record_ipv6_tcp[] = 95219182Sglebius{ 96219182Sglebius { NETFLOW_V9_FIELD_IPV6_SRC_ADDR, 16}, 97219182Sglebius { NETFLOW_V9_FIELD_IPV6_DST_ADDR, 16}, 98219182Sglebius { NETFLOW_V9_FIELD_IPV6_NEXT_HOP, 16}, 99219182Sglebius { NETFLOW_V9_FIELD_INPUT_SNMP, 2}, 100219182Sglebius { NETFLOW_V9_FIELD_OUTPUT_SNMP, 2}, 101219182Sglebius { NETFLOW_V9_FIELD_IN_PKTS, sizeof(CNTR)}, 102219182Sglebius { NETFLOW_V9_FIELD_IN_BYTES, sizeof(CNTR)}, 103219182Sglebius { NETFLOW_V9_FIELD_OUT_PKTS, sizeof(CNTR)}, 104219182Sglebius { NETFLOW_V9_FIELD_OUT_BYTES, sizeof(CNTR)}, 105219182Sglebius { NETFLOW_V9_FIELD_FIRST_SWITCHED, 4}, 106219182Sglebius { NETFLOW_V9_FIELD_LAST_SWITCHED, 4}, 107219182Sglebius { NETFLOW_V9_FIELD_L4_SRC_PORT, 2}, 108219182Sglebius { NETFLOW_V9_FIELD_L4_DST_PORT, 2}, 109219182Sglebius { NETFLOW_V9_FIELD_TCP_FLAGS, 1}, 110219182Sglebius { NETFLOW_V9_FIELD_PROTOCOL, 1}, 111219182Sglebius { NETFLOW_V9_FIELD_TOS, 1}, 112219182Sglebius { NETFLOW_V9_FIELD_SRC_AS, 4}, 113219182Sglebius { NETFLOW_V9_FIELD_DST_AS, 4}, 114219182Sglebius { NETFLOW_V9_FIELD_SRC_MASK, 1}, 115219182Sglebius { NETFLOW_V9_FIELD_DST_MASK, 1}, 116219182Sglebius {0, 0} 117219182Sglebius}; 118219182Sglebius 119219182Sglebius/* 120219182Sglebius * Pre-compiles flow exporter for all possible FlowSets 121219182Sglebius * so we can add flowset to packet via simple memcpy() 122219182Sglebius */ 123219182Sglebiusstatic void 124219182Sglebiusgenerate_v9_templates(priv_p priv) 125219182Sglebius{ 126219182Sglebius uint16_t *p, *template_fields_cnt; 127219182Sglebius int cnt; 128219182Sglebius 129219182Sglebius int flowset_size = sizeof(struct netflow_v9_flowset_header) + 130219182Sglebius _NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv4_tcp) + /* netflow_v9_record_ipv4_tcp */ 131219182Sglebius _NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv6_tcp); /* netflow_v9_record_ipv6_tcp */ 132219182Sglebius 133219182Sglebius priv->v9_flowsets[0] = malloc(flowset_size, M_NETFLOW_GENERAL, M_WAITOK | M_ZERO); 134219182Sglebius 135219182Sglebius if (flowset_size % 4) 136219182Sglebius flowset_size += 4 - (flowset_size % 4); /* Padding to 4-byte boundary */ 137219182Sglebius 138219182Sglebius priv->flowsets_count = 1; 139219182Sglebius p = (uint16_t *)priv->v9_flowsets[0]; 140219182Sglebius *p++ = 0; /* Flowset ID, 0 is reserved for Template FlowSets */ 141219182Sglebius *p++ = htons(flowset_size); /* Total FlowSet length */ 142219182Sglebius 143219182Sglebius /* 144219182Sglebius * Most common TCP/UDP IPv4 template, ID = 256 145219182Sglebius */ 146219182Sglebius *p++ = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + NETFLOW_V9_FLOW_V4_L4); 147219182Sglebius template_fields_cnt = p++; 148219182Sglebius for (cnt = 0; _netflow_v9_record_ipv4_tcp[cnt].field_id != 0; cnt++) { 149219182Sglebius *p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_id); 150219182Sglebius *p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_length); 151219182Sglebius } 152219182Sglebius *template_fields_cnt = htons(cnt); 153219182Sglebius 154219182Sglebius /* 155219182Sglebius * TCP/UDP IPv6 template, ID = 257 156219182Sglebius */ 157219182Sglebius *p++ = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + NETFLOW_V9_FLOW_V6_L4); 158219182Sglebius template_fields_cnt = p++; 159219182Sglebius for (cnt = 0; _netflow_v9_record_ipv6_tcp[cnt].field_id != 0; cnt++) { 160219182Sglebius *p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_id); 161219182Sglebius *p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_length); 162219182Sglebius } 163219182Sglebius *template_fields_cnt = htons(cnt); 164219182Sglebius 165219182Sglebius priv->flowset_records[0] = 2; 166219182Sglebius} 167219182Sglebius 168219182Sglebius/* Closes current data flowset */ 169219182Sglebiusstatic void inline 170219182Sglebiusclose_flowset(struct mbuf *m, struct netflow_v9_packet_opt *t) 171219182Sglebius{ 172219182Sglebius struct mbuf *m_old; 173219182Sglebius uint32_t zero = 0; 174219182Sglebius int offset = 0; 175219182Sglebius uint16_t *flowset_length, len; 176219182Sglebius 177219182Sglebius /* Hack to ensure we are not crossing mbuf boundary, length is uint16_t */ 178219182Sglebius m_old = m_getptr(m, t->flow_header + offsetof(struct netflow_v9_flowset_header, length), &offset); 179219182Sglebius flowset_length = (uint16_t *)(mtod(m_old, char *) + offset); 180219182Sglebius 181219182Sglebius len = (uint16_t)(m_pktlen(m) - t->flow_header); 182219182Sglebius /* Align on 4-byte boundary (RFC 3954, Clause 5.3) */ 183219182Sglebius if (len % 4) { 184219182Sglebius if (m_append(m, 4 - (len % 4), (void *)&zero) != 1) 185219182Sglebius panic("ng_netflow: m_append() failed!"); 186219182Sglebius 187219182Sglebius len += 4 - (len % 4); 188219182Sglebius } 189219182Sglebius 190219182Sglebius *flowset_length = htons(len); 191219182Sglebius} 192219182Sglebius 193219182Sglebius/* 194219182Sglebius * Non-static functions called from ng_netflow.c 195219182Sglebius */ 196219182Sglebius 197219182Sglebius/* We have full datagram in fib data. Send it to export hook. */ 198219182Sglebiusint 199219182Sglebiusexport9_send(priv_p priv, fib_export_p fe, item_p item, struct netflow_v9_packet_opt *t, int flags) 200219182Sglebius{ 201219182Sglebius struct mbuf *m = NGI_M(item); 202219182Sglebius struct netflow_v9_export_dgram *dgram = mtod(m, 203219182Sglebius struct netflow_v9_export_dgram *); 204219182Sglebius struct netflow_v9_header *header = &dgram->header; 205219182Sglebius struct timespec ts; 206219182Sglebius int error = 0; 207219182Sglebius 208219182Sglebius if (t == NULL) { 209219182Sglebius CTR0(KTR_NET, "export9_send(): V9 export packet without tag"); 210219182Sglebius NG_FREE_ITEM(item); 211219182Sglebius return (0); 212219182Sglebius } 213219182Sglebius 214219182Sglebius /* Close flowset if not closed already */ 215219182Sglebius if (m_pktlen(m) != t->flow_header) 216219182Sglebius close_flowset(m, t); 217219182Sglebius 218219182Sglebius /* Fill export header. */ 219219182Sglebius header->count = t->count; 220219182Sglebius header->sys_uptime = htonl(MILLIUPTIME(time_uptime)); 221219182Sglebius getnanotime(&ts); 222219182Sglebius header->unix_secs = htonl(ts.tv_sec); 223219182Sglebius header->seq_num = htonl(atomic_fetchadd_32(&fe->flow9_seq, 1)); 224219182Sglebius header->count = htons(t->count); 225238295Smelifaro header->source_id = htonl(fe->domain_id); 226219182Sglebius 227219182Sglebius if (priv->export9 != NULL) 228219182Sglebius NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export9, flags); 229219182Sglebius else 230219182Sglebius NG_FREE_ITEM(item); 231219182Sglebius 232219182Sglebius free(t, M_NETFLOW_GENERAL); 233219182Sglebius 234219182Sglebius return (error); 235219182Sglebius} 236219182Sglebius 237219182Sglebius 238219182Sglebius 239219182Sglebius/* Add V9 record to dgram. */ 240219182Sglebiusint 241219182Sglebiusexport9_add(item_p item, struct netflow_v9_packet_opt *t, struct flow_entry *fle) 242219182Sglebius{ 243219182Sglebius size_t len = 0; 244219182Sglebius struct netflow_v9_flowset_header fsh; 245219182Sglebius struct netflow_v9_record_general rg; 246219182Sglebius struct mbuf *m = NGI_M(item); 247219182Sglebius uint16_t flow_type; 248219182Sglebius struct flow_entry_data *fed; 249219182Sglebius#ifdef INET6 250219182Sglebius struct flow6_entry_data *fed6; 251219182Sglebius#endif 252219182Sglebius if (t == NULL) { 253219182Sglebius CTR0(KTR_NET, "ng_netflow: V9 export packet without tag!"); 254219182Sglebius return (0); 255219182Sglebius } 256219182Sglebius 257219182Sglebius /* Prepare flow record */ 258219182Sglebius fed = (struct flow_entry_data *)&fle->f; 259219229Sbz#ifdef INET6 260219182Sglebius fed6 = (struct flow6_entry_data *)&fle->f; 261219229Sbz#endif 262219182Sglebius /* We can use flow_type field since fle6 offset is equal to fle */ 263219182Sglebius flow_type = fed->r.flow_type; 264219182Sglebius 265219182Sglebius switch (flow_type) { 266219182Sglebius case NETFLOW_V9_FLOW_V4_L4: 267219182Sglebius { 268219182Sglebius /* IPv4 TCP/UDP/[SCTP] */ 269219182Sglebius struct netflow_v9_record_ipv4_tcp *rec = &rg.rec.v4_tcp; 270219182Sglebius 271219182Sglebius rec->src_addr = fed->r.r_src.s_addr; 272219182Sglebius rec->dst_addr = fed->r.r_dst.s_addr; 273219182Sglebius rec->next_hop = fed->next_hop.s_addr; 274219182Sglebius rec->i_ifx = htons(fed->fle_i_ifx); 275219182Sglebius rec->o_ifx = htons(fed->fle_o_ifx); 276219182Sglebius rec->i_packets = htonl(fed->packets); 277219182Sglebius rec->i_octets = htonl(fed->bytes); 278219182Sglebius rec->o_packets = htonl(0); 279219182Sglebius rec->o_octets = htonl(0); 280219182Sglebius rec->first = htonl(MILLIUPTIME(fed->first)); 281219182Sglebius rec->last = htonl(MILLIUPTIME(fed->last)); 282219182Sglebius rec->s_port = fed->r.r_sport; 283219182Sglebius rec->d_port = fed->r.r_dport; 284219182Sglebius rec->flags = fed->tcp_flags; 285219182Sglebius rec->prot = fed->r.r_ip_p; 286219182Sglebius rec->tos = fed->r.r_tos; 287219182Sglebius rec->dst_mask = fed->dst_mask; 288219182Sglebius rec->src_mask = fed->src_mask; 289219182Sglebius 290219182Sglebius /* Not supported fields. */ 291219182Sglebius rec->src_as = rec->dst_as = 0; 292219182Sglebius 293219182Sglebius len = sizeof(struct netflow_v9_record_ipv4_tcp); 294219182Sglebius break; 295219182Sglebius } 296219182Sglebius#ifdef INET6 297219182Sglebius case NETFLOW_V9_FLOW_V6_L4: 298219182Sglebius { 299219182Sglebius /* IPv6 TCP/UDP/[SCTP] */ 300219182Sglebius struct netflow_v9_record_ipv6_tcp *rec = &rg.rec.v6_tcp; 301219182Sglebius 302219182Sglebius rec->src_addr = fed6->r.src.r_src6; 303219182Sglebius rec->dst_addr = fed6->r.dst.r_dst6; 304219182Sglebius rec->next_hop = fed6->n.next_hop6; 305219182Sglebius rec->i_ifx = htons(fed6->fle_i_ifx); 306219182Sglebius rec->o_ifx = htons(fed6->fle_o_ifx); 307219182Sglebius rec->i_packets = htonl(fed6->packets); 308219182Sglebius rec->i_octets = htonl(fed6->bytes); 309219182Sglebius rec->o_packets = htonl(0); 310219182Sglebius rec->o_octets = htonl(0); 311219182Sglebius rec->first = htonl(MILLIUPTIME(fed6->first)); 312219182Sglebius rec->last = htonl(MILLIUPTIME(fed6->last)); 313219182Sglebius rec->s_port = fed6->r.r_sport; 314219182Sglebius rec->d_port = fed6->r.r_dport; 315219182Sglebius rec->flags = fed6->tcp_flags; 316219182Sglebius rec->prot = fed6->r.r_ip_p; 317219182Sglebius rec->tos = fed6->r.r_tos; 318219182Sglebius rec->dst_mask = fed6->dst_mask; 319219182Sglebius rec->src_mask = fed6->src_mask; 320219182Sglebius 321219182Sglebius /* Not supported fields. */ 322219182Sglebius rec->src_as = rec->dst_as = 0; 323219182Sglebius 324219182Sglebius len = sizeof(struct netflow_v9_record_ipv6_tcp); 325219182Sglebius break; 326219182Sglebius } 327219182Sglebius#endif 328219182Sglebius default: 329219182Sglebius { 330219182Sglebius CTR1(KTR_NET, "export9_add(): Don't know what to do with %d flow type!", flow_type); 331219182Sglebius return (0); 332219182Sglebius } 333219182Sglebius } 334219182Sglebius 335219182Sglebius /* Check if new records has the same template */ 336219182Sglebius if (flow_type != t->flow_type) { 337219182Sglebius /* close old flowset */ 338219182Sglebius if (t->flow_type != 0) 339219182Sglebius close_flowset(m, t); 340219182Sglebius 341219182Sglebius t->flow_type = flow_type; 342219182Sglebius t->flow_header = m_pktlen(m); 343219182Sglebius 344219182Sglebius /* Generate data flowset ID */ 345219182Sglebius fsh.id = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + flow_type); 346219182Sglebius fsh.length = 0; 347219182Sglebius 348219182Sglebius /* m_append should not fail since all data is already allocated */ 349219182Sglebius if (m_append(m, sizeof(fsh), (void *)&fsh) != 1) 350219182Sglebius panic("ng_netflow: m_append() failed"); 351219182Sglebius 352219182Sglebius } 353219182Sglebius 354219182Sglebius if (m_append(m, len, (void *)&rg.rec) != 1) 355219182Sglebius panic("ng_netflow: m_append() failed"); 356219182Sglebius 357219182Sglebius t->count++; 358219182Sglebius 359219182Sglebius if (m_pktlen(m) + sizeof(struct netflow_v9_record_general) + sizeof(struct netflow_v9_flowset_header) >= _NETFLOW_V9_MAX_SIZE(t->mtu)) 360219182Sglebius return (1); /* end of datagram */ 361219182Sglebius return (0); 362219182Sglebius} 363219182Sglebius 364219182Sglebius/* 365219182Sglebius * Detach export datagram from fib instance, if there is any. 366219182Sglebius * If there is no, allocate a new one. 367219182Sglebius */ 368219182Sglebiusitem_p 369219182Sglebiusget_export9_dgram(priv_p priv, fib_export_p fe, struct netflow_v9_packet_opt **tt) 370219182Sglebius{ 371219182Sglebius item_p item = NULL; 372219182Sglebius struct netflow_v9_packet_opt *t = NULL; 373219182Sglebius 374219182Sglebius mtx_lock(&fe->export9_mtx); 375219182Sglebius if (fe->exp.item9 != NULL) { 376219182Sglebius item = fe->exp.item9; 377219182Sglebius fe->exp.item9 = NULL; 378219182Sglebius t = fe->exp.item9_opt; 379219182Sglebius fe->exp.item9_opt = NULL; 380219182Sglebius } 381219182Sglebius mtx_unlock(&fe->export9_mtx); 382219182Sglebius 383219182Sglebius if (item == NULL) { 384219182Sglebius struct netflow_v9_export_dgram *dgram; 385219182Sglebius struct mbuf *m; 386219182Sglebius uint16_t mtu = priv->mtu; 387219182Sglebius 388219182Sglebius /* Allocate entire packet at once, allowing easy m_append() calls */ 389219182Sglebius m = m_getm(NULL, mtu, M_DONTWAIT, MT_DATA); 390219182Sglebius if (m == NULL) 391219182Sglebius return (NULL); 392219182Sglebius 393219182Sglebius t = malloc(sizeof(struct netflow_v9_packet_opt), M_NETFLOW_GENERAL, M_NOWAIT | M_ZERO); 394219182Sglebius if (t == NULL) { 395219182Sglebius m_free(m); 396219182Sglebius return (NULL); 397219182Sglebius } 398219182Sglebius 399219182Sglebius item = ng_package_data(m, NG_NOFLAGS); 400219182Sglebius if (item == NULL) { 401219182Sglebius free(t, M_NETFLOW_GENERAL); 402219182Sglebius return (NULL); 403219182Sglebius } 404219182Sglebius 405219182Sglebius dgram = mtod(m, struct netflow_v9_export_dgram *); 406219182Sglebius dgram->header.count = 0; 407219182Sglebius dgram->header.version = htons(NETFLOW_V9); 408219182Sglebius /* Set mbuf current data length */ 409219182Sglebius m->m_len = m->m_pkthdr.len = sizeof(struct netflow_v9_header); 410219182Sglebius 411219182Sglebius t->count = 0; 412219182Sglebius t->mtu = mtu; 413219182Sglebius t->flow_header = m->m_len; 414219182Sglebius 415219182Sglebius /* 416219182Sglebius * Check if we need to insert templates into packet 417219182Sglebius */ 418219182Sglebius 419219182Sglebius struct netflow_v9_flowset_header *fl; 420219182Sglebius 421238295Smelifaro if ((time_uptime >= priv->templ_time + fe->templ_last_ts) || 422219182Sglebius (fe->sent_packets >= priv->templ_packets + fe->templ_last_pkt)) { 423219182Sglebius 424238295Smelifaro fe->templ_last_ts = time_uptime; 425238295Smelifaro fe->templ_last_pkt = fe->sent_packets; 426238295Smelifaro 427219182Sglebius fl = priv->v9_flowsets[0]; 428219182Sglebius m_append(m, ntohs(fl->length), (void *)fl); 429219182Sglebius t->flow_header = m->m_len; 430219182Sglebius t->count += priv->flowset_records[0]; 431219182Sglebius } 432219182Sglebius 433219182Sglebius } 434219182Sglebius 435219182Sglebius *tt = t; 436219182Sglebius return (item); 437219182Sglebius} 438219182Sglebius 439219182Sglebius/* 440219182Sglebius * Re-attach incomplete datagram back to fib instance. 441219182Sglebius * If there is already another one, then send incomplete. 442219182Sglebius */ 443219182Sglebiusvoid 444219182Sglebiusreturn_export9_dgram(priv_p priv, fib_export_p fe, item_p item, struct netflow_v9_packet_opt *t, int flags) 445219182Sglebius{ 446219182Sglebius /* 447219182Sglebius * It may happen on SMP, that some thread has already 448219182Sglebius * put its item there, in this case we bail out and 449219182Sglebius * send what we have to collector. 450219182Sglebius */ 451219182Sglebius mtx_lock(&fe->export9_mtx); 452219182Sglebius if (fe->exp.item9 == NULL) { 453219182Sglebius fe->exp.item9 = item; 454219182Sglebius fe->exp.item9_opt = t; 455219182Sglebius mtx_unlock(&fe->export9_mtx); 456219182Sglebius } else { 457219182Sglebius mtx_unlock(&fe->export9_mtx); 458219182Sglebius export9_send(priv, fe, item, t, flags); 459219182Sglebius } 460219182Sglebius} 461219182Sglebius 462219182Sglebius/* Allocate memory and set up flow cache */ 463219182Sglebiusvoid 464219182Sglebiusng_netflow_v9_cache_init(priv_p priv) 465219182Sglebius{ 466219182Sglebius generate_v9_templates(priv); 467219182Sglebius 468219182Sglebius priv->templ_time = NETFLOW_V9_MAX_TIME_TEMPL; 469219182Sglebius priv->templ_packets = NETFLOW_V9_MAX_PACKETS_TEMPL; 470219182Sglebius priv->mtu = BASE_MTU; 471219182Sglebius} 472219182Sglebius 473219182Sglebius/* Free all flow cache memory. Called from ng_netflow_cache_flush() */ 474219182Sglebiusvoid 475219182Sglebiusng_netflow_v9_cache_flush(priv_p priv) 476219182Sglebius{ 477219182Sglebius int i; 478219182Sglebius 479219182Sglebius /* Free flowsets*/ 480219182Sglebius for (i = 0; i < priv->flowsets_count; i++) 481219182Sglebius free(priv->v9_flowsets[i], M_NETFLOW_GENERAL); 482219182Sglebius} 483243983Smelifaro 484243983Smelifaro/* Get a snapshot of NetFlow v9 settings */ 485243983Smelifarovoid 486243983Smelifarong_netflow_copyv9info(priv_p priv, struct ng_netflow_v9info *i) 487243983Smelifaro{ 488243983Smelifaro 489243983Smelifaro i->templ_time = priv->templ_time; 490243983Smelifaro i->templ_packets = priv->templ_packets; 491243983Smelifaro i->mtu = priv->mtu; 492243983Smelifaro} 493243983Smelifaro 494