Path: blob/main/sys/netgraph/netflow/netflow_v9.c
103381 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2010 Alexander V. Chernikov <[email protected]>4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/cdefs.h>29#include "opt_inet6.h"30#include "opt_route.h"31#include <sys/param.h>32#include <sys/systm.h>33#include <sys/counter.h>34#include <sys/kernel.h>35#include <sys/ktr.h>36#include <sys/limits.h>37#include <sys/malloc.h>38#include <sys/mbuf.h>39#include <sys/syslog.h>40#include <sys/socket.h>41#include <vm/uma.h>4243#include <net/if.h>44#include <net/route.h>45#include <net/ethernet.h>46#include <netinet/in.h>47#include <netinet/in_systm.h>48#include <netinet/ip.h>49#include <netinet/ip6.h>50#include <netinet/tcp.h>51#include <netinet/udp.h>5253#include <netgraph/ng_message.h>54#include <netgraph/netgraph.h>5556#include <netgraph/netflow/netflow.h>57#include <netgraph/netflow/ng_netflow.h>58#include <netgraph/netflow/netflow_v9.h>5960MALLOC_DECLARE(M_NETFLOW_GENERAL);61MALLOC_DEFINE(M_NETFLOW_GENERAL, "netflow_general", "plog, V9 templates data");6263/*64* Base V9 templates for L4+ IPv4/IPv6 protocols65*/66struct netflow_v9_template _netflow_v9_record_ipv4_tcp[] =67{68{ NETFLOW_V9_FIELD_IPV4_SRC_ADDR, 4},69{ NETFLOW_V9_FIELD_IPV4_DST_ADDR, 4},70{ NETFLOW_V9_FIELD_IPV4_NEXT_HOP, 4},71{ NETFLOW_V9_FIELD_INPUT_SNMP, 2},72{ NETFLOW_V9_FIELD_OUTPUT_SNMP, 2},73{ NETFLOW_V9_FIELD_IN_PKTS, sizeof(CNTR)},74{ NETFLOW_V9_FIELD_IN_BYTES, sizeof(CNTR)},75{ NETFLOW_V9_FIELD_OUT_PKTS, sizeof(CNTR)},76{ NETFLOW_V9_FIELD_OUT_BYTES, sizeof(CNTR)},77{ NETFLOW_V9_FIELD_FIRST_SWITCHED, 4},78{ NETFLOW_V9_FIELD_LAST_SWITCHED, 4},79{ NETFLOW_V9_FIELD_L4_SRC_PORT, 2},80{ NETFLOW_V9_FIELD_L4_DST_PORT, 2},81{ NETFLOW_V9_FIELD_TCP_FLAGS, 1},82{ NETFLOW_V9_FIELD_PROTOCOL, 1},83{ NETFLOW_V9_FIELD_TOS, 1},84{ NETFLOW_V9_FIELD_SRC_AS, 4},85{ NETFLOW_V9_FIELD_DST_AS, 4},86{ NETFLOW_V9_FIELD_SRC_MASK, 1},87{ NETFLOW_V9_FIELD_DST_MASK, 1},88{0, 0}89};9091struct netflow_v9_template _netflow_v9_record_ipv6_tcp[] =92{93{ NETFLOW_V9_FIELD_IPV6_SRC_ADDR, 16},94{ NETFLOW_V9_FIELD_IPV6_DST_ADDR, 16},95{ NETFLOW_V9_FIELD_IPV6_NEXT_HOP, 16},96{ NETFLOW_V9_FIELD_INPUT_SNMP, 2},97{ NETFLOW_V9_FIELD_OUTPUT_SNMP, 2},98{ NETFLOW_V9_FIELD_IN_PKTS, sizeof(CNTR)},99{ NETFLOW_V9_FIELD_IN_BYTES, sizeof(CNTR)},100{ NETFLOW_V9_FIELD_OUT_PKTS, sizeof(CNTR)},101{ NETFLOW_V9_FIELD_OUT_BYTES, sizeof(CNTR)},102{ NETFLOW_V9_FIELD_FIRST_SWITCHED, 4},103{ NETFLOW_V9_FIELD_LAST_SWITCHED, 4},104{ NETFLOW_V9_FIELD_L4_SRC_PORT, 2},105{ NETFLOW_V9_FIELD_L4_DST_PORT, 2},106{ NETFLOW_V9_FIELD_TCP_FLAGS, 1},107{ NETFLOW_V9_FIELD_PROTOCOL, 1},108{ NETFLOW_V9_FIELD_TOS, 1},109{ NETFLOW_V9_FIELD_SRC_AS, 4},110{ NETFLOW_V9_FIELD_DST_AS, 4},111{ NETFLOW_V9_FIELD_SRC_MASK, 1},112{ NETFLOW_V9_FIELD_DST_MASK, 1},113{0, 0}114};115116/*117* Pre-compiles flow exporter for all possible FlowSets118* so we can add flowset to packet via simple memcpy()119*/120static void121generate_v9_templates(priv_p priv)122{123uint16_t *p, *template_fields_cnt;124int cnt;125126int flowset_size = sizeof(struct netflow_v9_flowset_header) +127_NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv4_tcp) + /* netflow_v9_record_ipv4_tcp */128_NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv6_tcp); /* netflow_v9_record_ipv6_tcp */129130priv->v9_flowsets[0] = malloc(flowset_size, M_NETFLOW_GENERAL, M_WAITOK | M_ZERO);131132if (flowset_size % 4)133flowset_size += 4 - (flowset_size % 4); /* Padding to 4-byte boundary */134135priv->flowsets_count = 1;136p = (uint16_t *)priv->v9_flowsets[0];137*p++ = 0; /* Flowset ID, 0 is reserved for Template FlowSets */138*p++ = htons(flowset_size); /* Total FlowSet length */139140/*141* Most common TCP/UDP IPv4 template, ID = 256142*/143*p++ = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + NETFLOW_V9_FLOW_V4_L4);144template_fields_cnt = p++;145for (cnt = 0; _netflow_v9_record_ipv4_tcp[cnt].field_id != 0; cnt++) {146*p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_id);147*p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_length);148}149*template_fields_cnt = htons(cnt);150151/*152* TCP/UDP IPv6 template, ID = 257153*/154*p++ = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + NETFLOW_V9_FLOW_V6_L4);155template_fields_cnt = p++;156for (cnt = 0; _netflow_v9_record_ipv6_tcp[cnt].field_id != 0; cnt++) {157*p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_id);158*p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_length);159}160*template_fields_cnt = htons(cnt);161162priv->flowset_records[0] = 2;163}164165/* Closes current data flowset */166static void inline167close_flowset(struct mbuf *m, struct netflow_v9_packet_opt *t)168{169struct mbuf *m_old;170uint32_t zero = 0;171int offset = 0;172uint16_t *flowset_length, len;173174/* Hack to ensure we are not crossing mbuf boundary, length is uint16_t */175m_old = m_getptr(m, t->flow_header + offsetof(struct netflow_v9_flowset_header, length), &offset);176flowset_length = (uint16_t *)(mtod(m_old, char *) + offset);177178len = (uint16_t)(m_pktlen(m) - t->flow_header);179/* Align on 4-byte boundary (RFC 3954, Clause 5.3) */180if (len % 4) {181if (m_append(m, 4 - (len % 4), (void *)&zero) != 1)182panic("ng_netflow: m_append() failed!");183184len += 4 - (len % 4);185}186187*flowset_length = htons(len);188}189190/*191* Non-static functions called from ng_netflow.c192*/193194/* We have full datagram in fib data. Send it to export hook. */195int196export9_send(priv_p priv, fib_export_p fe, item_p item, struct netflow_v9_packet_opt *t, int flags)197{198struct mbuf *m = NGI_M(item);199struct netflow_v9_export_dgram *dgram = mtod(m,200struct netflow_v9_export_dgram *);201struct netflow_v9_header *header = &dgram->header;202struct timespec ts;203int error = 0;204205if (t == NULL) {206CTR0(KTR_NET, "export9_send(): V9 export packet without tag");207NG_FREE_ITEM(item);208return (0);209}210211/* Close flowset if not closed already */212if (m_pktlen(m) != t->flow_header)213close_flowset(m, t);214215/* Fill export header. */216header->count = t->count;217header->sys_uptime = htonl(MILLIUPTIME(time_uptime));218getnanotime(&ts);219header->unix_secs = htonl(ts.tv_sec);220header->seq_num = htonl(atomic_fetchadd_32(&fe->flow9_seq, 1));221header->count = htons(t->count);222header->source_id = htonl(fe->domain_id);223224if (priv->export9 != NULL)225NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export9, flags);226else227NG_FREE_ITEM(item);228229fe->sent_packets++;230free(t, M_NETFLOW_GENERAL);231232return (error);233}234235/* Add V9 record to dgram. */236int237export9_add(item_p item, struct netflow_v9_packet_opt *t, struct flow_entry *fle)238{239size_t len = 0;240struct netflow_v9_flowset_header fsh;241struct netflow_v9_record_general rg;242struct mbuf *m = NGI_M(item);243uint16_t flow_type;244struct flow_entry_data *fed;245#ifdef INET6246struct flow6_entry_data *fed6;247#endif248if (t == NULL) {249CTR0(KTR_NET, "ng_netflow: V9 export packet without tag!");250return (0);251}252253/* Prepare flow record */254fed = (struct flow_entry_data *)&fle->f;255#ifdef INET6256fed6 = (struct flow6_entry_data *)&fle->f;257#endif258/* We can use flow_type field since fle6 offset is equal to fle */259flow_type = fed->r.flow_type;260261switch (flow_type) {262case NETFLOW_V9_FLOW_V4_L4:263{264/* IPv4 TCP/UDP/[SCTP] */265struct netflow_v9_record_ipv4_tcp *rec = &rg.rec.v4_tcp;266267rec->src_addr = fed->r.r_src.s_addr;268rec->dst_addr = fed->r.r_dst.s_addr;269rec->next_hop = fed->next_hop.s_addr;270rec->i_ifx = htons(fed->fle_i_ifx);271rec->o_ifx = htons(fed->fle_o_ifx);272rec->i_packets = htonl(fed->packets);273rec->i_octets = htonl(fed->bytes);274rec->o_packets = htonl(0);275rec->o_octets = htonl(0);276rec->first = htonl(MILLIUPTIME(fed->first));277rec->last = htonl(MILLIUPTIME(fed->last));278rec->s_port = fed->r.r_sport;279rec->d_port = fed->r.r_dport;280rec->flags = fed->tcp_flags;281rec->prot = fed->r.r_ip_p;282rec->tos = fed->r.r_tos;283rec->dst_mask = fed->dst_mask;284rec->src_mask = fed->src_mask;285286/* Not supported fields. */287rec->src_as = rec->dst_as = 0;288289len = sizeof(struct netflow_v9_record_ipv4_tcp);290break;291}292#ifdef INET6293case NETFLOW_V9_FLOW_V6_L4:294{295/* IPv6 TCP/UDP/[SCTP] */296struct netflow_v9_record_ipv6_tcp *rec = &rg.rec.v6_tcp;297298rec->src_addr = fed6->r.src.r_src6;299rec->dst_addr = fed6->r.dst.r_dst6;300rec->next_hop = fed6->n.next_hop6;301rec->i_ifx = htons(fed6->fle_i_ifx);302rec->o_ifx = htons(fed6->fle_o_ifx);303rec->i_packets = htonl(fed6->packets);304rec->i_octets = htonl(fed6->bytes);305rec->o_packets = htonl(0);306rec->o_octets = htonl(0);307rec->first = htonl(MILLIUPTIME(fed6->first));308rec->last = htonl(MILLIUPTIME(fed6->last));309rec->s_port = fed6->r.r_sport;310rec->d_port = fed6->r.r_dport;311rec->flags = fed6->tcp_flags;312rec->prot = fed6->r.r_ip_p;313rec->tos = fed6->r.r_tos;314rec->dst_mask = fed6->dst_mask;315rec->src_mask = fed6->src_mask;316317/* Not supported fields. */318rec->src_as = rec->dst_as = 0;319320len = sizeof(struct netflow_v9_record_ipv6_tcp);321break;322}323#endif324default:325{326CTR1(KTR_NET, "export9_add(): Don't know what to do with %d flow type!", flow_type);327return (0);328}329}330331/* Check if new records has the same template */332if (flow_type != t->flow_type) {333/* close old flowset */334if (t->flow_type != 0)335close_flowset(m, t);336337t->flow_type = flow_type;338t->flow_header = m_pktlen(m);339340/* Generate data flowset ID */341fsh.id = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + flow_type);342fsh.length = 0;343344/* m_append should not fail since all data is already allocated */345if (m_append(m, sizeof(fsh), (void *)&fsh) != 1)346panic("ng_netflow: m_append() failed");347348}349350if (m_append(m, len, (void *)&rg.rec) != 1)351panic("ng_netflow: m_append() failed");352353t->count++;354355if (m_pktlen(m) + sizeof(struct netflow_v9_record_general) + sizeof(struct netflow_v9_flowset_header) >= _NETFLOW_V9_MAX_SIZE(t->mtu))356return (1); /* end of datagram */357return (0);358}359360/*361* Detach export datagram from fib instance, if there is any.362* If there is no, allocate a new one.363*/364item_p365get_export9_dgram(priv_p priv, fib_export_p fe, struct netflow_v9_packet_opt **tt)366{367item_p item = NULL;368struct netflow_v9_packet_opt *t = NULL;369370mtx_lock(&fe->export9_mtx);371if (fe->exp.item9 != NULL) {372item = fe->exp.item9;373fe->exp.item9 = NULL;374t = fe->exp.item9_opt;375fe->exp.item9_opt = NULL;376}377mtx_unlock(&fe->export9_mtx);378379if (item == NULL) {380struct netflow_v9_export_dgram *dgram;381struct mbuf *m;382uint16_t mtu = priv->mtu;383384/* Allocate entire packet at once, allowing easy m_append() calls */385m = m_getm(NULL, mtu, M_NOWAIT, MT_DATA);386if (m == NULL)387return (NULL);388389t = malloc(sizeof(struct netflow_v9_packet_opt), M_NETFLOW_GENERAL, M_NOWAIT | M_ZERO);390if (t == NULL) {391m_free(m);392return (NULL);393}394395item = ng_package_data(m, NG_NOFLAGS);396if (item == NULL) {397free(t, M_NETFLOW_GENERAL);398return (NULL);399}400401dgram = mtod(m, struct netflow_v9_export_dgram *);402dgram->header.count = 0;403dgram->header.version = htons(NETFLOW_V9);404/* Set mbuf current data length */405m->m_len = m->m_pkthdr.len = sizeof(struct netflow_v9_header);406407t->count = 0;408t->mtu = mtu;409t->flow_header = m->m_len;410411/*412* Check if we need to insert templates into packet413*/414415struct netflow_v9_flowset_header *fl;416417if ((time_uptime >= priv->templ_time + fe->templ_last_ts) ||418(fe->sent_packets >= priv->templ_packets + fe->templ_last_pkt)) {419fe->templ_last_ts = time_uptime;420fe->templ_last_pkt = fe->sent_packets;421422fl = priv->v9_flowsets[0];423m_append(m, ntohs(fl->length), (void *)fl);424t->flow_header = m->m_len;425t->count += priv->flowset_records[0];426}427}428429*tt = t;430return (item);431}432433/*434* Re-attach incomplete datagram back to fib instance.435* If there is already another one, then send incomplete.436*/437void438return_export9_dgram(priv_p priv, fib_export_p fe, item_p item, struct netflow_v9_packet_opt *t, int flags)439{440/*441* It may happen on SMP, that some thread has already442* put its item there, in this case we bail out and443* send what we have to collector.444*/445mtx_lock(&fe->export9_mtx);446if (fe->exp.item9 == NULL) {447fe->exp.item9 = item;448fe->exp.item9_opt = t;449mtx_unlock(&fe->export9_mtx);450} else {451mtx_unlock(&fe->export9_mtx);452export9_send(priv, fe, item, t, flags);453}454}455456/* Allocate memory and set up flow cache */457void458ng_netflow_v9_cache_init(priv_p priv)459{460generate_v9_templates(priv);461462priv->templ_time = NETFLOW_V9_MAX_TIME_TEMPL;463priv->templ_packets = NETFLOW_V9_MAX_PACKETS_TEMPL;464priv->mtu = BASE_MTU;465}466467/* Free all flow cache memory. Called from ng_netflow_cache_flush() */468void469ng_netflow_v9_cache_flush(priv_p priv)470{471int i;472473/* Free flowsets*/474for (i = 0; i < priv->flowsets_count; i++)475free(priv->v9_flowsets[i], M_NETFLOW_GENERAL);476}477478/* Get a snapshot of NetFlow v9 settings */479void480ng_netflow_copyv9info(priv_p priv, struct ng_netflow_v9info *i)481{482483i->templ_time = priv->templ_time;484i->templ_packets = priv->templ_packets;485i->mtu = priv->mtu;486}487488489