Make radix lookup on src and dst flow addresses optional

and configurable on per-interface basis.
Remove __inline__ for several functions being called once per
flow (e.g once per 10-20 packets on common traffic flows).
Update manual page to simplify search for BPF data link types.

Sponsored by Yandex LLC

Reviewed by:      glebius
Approved by:      ae(mentor)
MFC after:        2 weeks
This commit is contained in:
Alexander V. Chernikov 2012-06-18 13:56:36 +00:00
parent 0bd6bb6bb0
commit 36374fcf4b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=237227
4 changed files with 143 additions and 93 deletions

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd March 2, 2011
.Dd June 16, 2012
.Dt NG_NETFLOW 4
.Os
.Sh NAME
@ -112,7 +112,18 @@ The hook number is passed as an argument.
Sets data link type on the
.Va iface Ns Ar N
hook.
Currently, supported types are raw IP datagrams and Ethernet.
Currently, supported types are
.Cm DLT_RAW
(raw IP datagrams) and
.Cm DLT_EN10MB
(Ethernet).
DLT_ definitions can be found in
.In net/bpf.h
header.
Currently used values are 1 for
.Cm DLT_EN10MB
and 12 for
.Cm DLT_RAW .
This message type uses
.Vt "struct ng_netflow_setdlt"
as an argument:
@ -180,18 +191,36 @@ struct ng_netflow_setconfig {
#define NG_NETFLOW_CONF_EGRESS 2
#define NG_NETFLOW_CONF_ONCE 4
#define NG_NETFLOW_CONF_THISONCE 8
#define NG_NETFLOW_CONF_NOSRCLOOKUP 16
#define NG_NETFLOW_CONF_NODSTLOOKUP 32
};
.Ed
.Pp
Configuration is a bitmask of several options. Option NG_NETFLOW_CONF_INGRESS
enabled by default enables ingress NetFlow generation (for data coming from
ifaceX hook). Option NG_NETFLOW_CONF_EGRESS enables egress NetFlow (for data
coming from outX hook). Option NG_NETFLOW_CONF_ONCE defines that packet should
be accounted only once if it several times passes via netflow node. Option
NG_NETFLOW_CONF_THISONCE defines that packet should be accounted only once
if it several times passes via exactly this netflow node. Last two options are
important to avoid duplicate accounting when both ingress and egress NetFlow
are enabled.
ifaceX hook).
Option
.Va NG_NETFLOW_CONF_EGRESS
enables egress NetFlow (for data coming from outX hook).
Option
.Va NG_NETFLOW_CONF_ONCE
defines that packet should be accounted only once if it several times passes
via netflow node.
Option
.Va NG_NETFLOW_CONF_THISONCE
defines that packet should be accounted only once if it several times passes
via exactly this netflow node.
These two options are important to avoid duplicate accounting when both ingress
and egress NetFlow are enabled.
Option
.Va NG_NETFLOW_CONF_NOSRCLOOKUP
skips radix lookup on flow source address used to fill in network mask.
Option
.Va NG_NETFLOW_CONF_NODSTLOOKUP
skips radix lookup on destination (which fills egress interface id, destination
mask and gateway).
If one doesn't need data provided by lookups, he/she can disable them, to reduce
load on routers.
.It Dv NGM_NETFLOW_SETTEMPLATE
Sets various timeouts to announce data flow templates
(NetFlow v9-specific). This message requires

View File

@ -98,9 +98,9 @@ MALLOC_DEFINE(M_NETFLOW_HASH, "netflow_hash", "NetFlow hash");
static int export_add(item_p, struct flow_entry *);
static int export_send(priv_p, fib_export_p, item_p, int);
static int hash_insert(priv_p, struct flow_hash_entry *, struct flow_rec *, int, uint8_t);
static int hash_insert(priv_p, struct flow_hash_entry *, struct flow_rec *, int, uint8_t, uint8_t);
#ifdef INET6
static int hash6_insert(priv_p, struct flow_hash_entry *, struct flow6_rec *, int, uint8_t);
static int hash6_insert(priv_p, struct flow_hash_entry *, struct flow6_rec *, int, uint8_t, uint8_t);
#endif
static __inline void expire_flow(priv_p, fib_export_p, struct flow_entry *, int);
@ -325,9 +325,9 @@ ng_netflow_copyinfo(priv_p priv, struct ng_netflow_info *i)
* as this was done in previous version. Need to test & profile
* to be sure.
*/
static __inline int
static int
hash_insert(priv_p priv, struct flow_hash_entry *hsh, struct flow_rec *r,
int plen, uint8_t tcp_flags)
int plen, uint8_t flags, uint8_t tcp_flags)
{
struct flow_entry *fle;
struct sockaddr_in sin;
@ -358,44 +358,48 @@ hash_insert(priv_p priv, struct flow_hash_entry *hsh, struct flow_rec *r,
* First we do route table lookup on destination address. So we can
* fill in out_ifx, dst_mask, nexthop, and dst_as in future releases.
*/
bzero(&sin, sizeof(sin));
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_family = AF_INET;
sin.sin_addr = fle->f.r.r_dst;
rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, r->fib);
if (rt != NULL) {
fle->f.fle_o_ifx = rt->rt_ifp->if_index;
if ((flags & NG_NETFLOW_CONF_NODSTLOOKUP) == 0) {
bzero(&sin, sizeof(sin));
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_family = AF_INET;
sin.sin_addr = fle->f.r.r_dst;
rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, r->fib);
if (rt != NULL) {
fle->f.fle_o_ifx = rt->rt_ifp->if_index;
if (rt->rt_flags & RTF_GATEWAY &&
rt->rt_gateway->sa_family == AF_INET)
fle->f.next_hop =
((struct sockaddr_in *)(rt->rt_gateway))->sin_addr;
if (rt->rt_flags & RTF_GATEWAY &&
rt->rt_gateway->sa_family == AF_INET)
fle->f.next_hop =
((struct sockaddr_in *)(rt->rt_gateway))->sin_addr;
if (rt_mask(rt))
fle->f.dst_mask = bitcount32(((struct sockaddr_in *)
rt_mask(rt))->sin_addr.s_addr);
else if (rt->rt_flags & RTF_HOST)
/* Give up. We can't determine mask :( */
fle->f.dst_mask = 32;
if (rt_mask(rt))
fle->f.dst_mask = bitcount32(((struct sockaddr_in *)
rt_mask(rt))->sin_addr.s_addr);
else if (rt->rt_flags & RTF_HOST)
/* Give up. We can't determine mask :( */
fle->f.dst_mask = 32;
RTFREE_LOCKED(rt);
RTFREE_LOCKED(rt);
}
}
/* Do route lookup on source address, to fill in src_mask. */
bzero(&sin, sizeof(sin));
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_family = AF_INET;
sin.sin_addr = fle->f.r.r_src;
rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, r->fib);
if (rt != NULL) {
if (rt_mask(rt))
fle->f.src_mask = bitcount32(((struct sockaddr_in *)
rt_mask(rt))->sin_addr.s_addr);
else if (rt->rt_flags & RTF_HOST)
/* Give up. We can't determine mask :( */
fle->f.src_mask = 32;
if ((flags & NG_NETFLOW_CONF_NOSRCLOOKUP) == 0) {
bzero(&sin, sizeof(sin));
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_family = AF_INET;
sin.sin_addr = fle->f.r.r_src;
rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, r->fib);
if (rt != NULL) {
if (rt_mask(rt))
fle->f.src_mask = bitcount32(((struct sockaddr_in *)
rt_mask(rt))->sin_addr.s_addr);
else if (rt->rt_flags & RTF_HOST)
/* Give up. We can't determine mask :( */
fle->f.src_mask = 32;
RTFREE_LOCKED(rt);
RTFREE_LOCKED(rt);
}
}
/* Push new flow at the and of hash. */
@ -410,10 +414,10 @@ hash_insert(priv_p priv, struct flow_hash_entry *hsh, struct flow_rec *r,
bitcount32((x).__u6_addr.__u6_addr32[1]) + \
bitcount32((x).__u6_addr.__u6_addr32[2]) + \
bitcount32((x).__u6_addr.__u6_addr32[3])
/* XXX: Do we need inline here ? */
static __inline int
#define RT_MASK6(x) (ipv6_masklen(((struct sockaddr_in6 *)rt_mask(x))->sin6_addr))
static int
hash6_insert(priv_p priv, struct flow_hash_entry *hsh6, struct flow6_rec *r,
int plen, uint8_t tcp_flags)
int plen, uint8_t flags, uint8_t tcp_flags)
{
struct flow6_entry *fle6;
struct sockaddr_in6 *src, *dst;
@ -445,49 +449,55 @@ hash6_insert(priv_p priv, struct flow_hash_entry *hsh6, struct flow6_rec *r,
* First we do route table lookup on destination address. So we can
* fill in out_ifx, dst_mask, nexthop, and dst_as in future releases.
*/
bzero(&rin6, sizeof(struct route_in6));
dst = (struct sockaddr_in6 *)&rin6.ro_dst;
dst->sin6_len = sizeof(struct sockaddr_in6);
dst->sin6_family = AF_INET6;
dst->sin6_addr = r->dst.r_dst6;
if ((flags & NG_NETFLOW_CONF_NODSTLOOKUP) == 0)
{
bzero(&rin6, sizeof(struct route_in6));
dst = (struct sockaddr_in6 *)&rin6.ro_dst;
dst->sin6_len = sizeof(struct sockaddr_in6);
dst->sin6_family = AF_INET6;
dst->sin6_addr = r->dst.r_dst6;
rin6.ro_rt = rtalloc1_fib((struct sockaddr *)dst, 0, 0, r->fib);
rin6.ro_rt = rtalloc1_fib((struct sockaddr *)dst, 0, 0, r->fib);
if (rin6.ro_rt != NULL) {
rt = rin6.ro_rt;
fle6->f.fle_o_ifx = rt->rt_ifp->if_index;
if (rin6.ro_rt != NULL) {
rt = rin6.ro_rt;
fle6->f.fle_o_ifx = rt->rt_ifp->if_index;
if (rt->rt_flags & RTF_GATEWAY &&
rt->rt_gateway->sa_family == AF_INET6)
fle6->f.n.next_hop6 =
((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr;
if (rt->rt_flags & RTF_GATEWAY &&
rt->rt_gateway->sa_family == AF_INET6)
fle6->f.n.next_hop6 =
((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr;
if (rt_mask(rt))
fle6->f.dst_mask = ipv6_masklen(((struct sockaddr_in6 *)rt_mask(rt))->sin6_addr);
else
fle6->f.dst_mask = 128;
if (rt_mask(rt))
fle6->f.dst_mask = RT_MASK6(rt);
else
fle6->f.dst_mask = 128;
RTFREE_LOCKED(rt);
RTFREE_LOCKED(rt);
}
}
/* Do route lookup on source address, to fill in src_mask. */
bzero(&rin6, sizeof(struct route_in6));
src = (struct sockaddr_in6 *)&rin6.ro_dst;
src->sin6_len = sizeof(struct sockaddr_in6);
src->sin6_family = AF_INET6;
src->sin6_addr = r->src.r_src6;
if ((flags & NG_NETFLOW_CONF_NODSTLOOKUP) == 0)
{
/* Do route lookup on source address, to fill in src_mask. */
bzero(&rin6, sizeof(struct route_in6));
src = (struct sockaddr_in6 *)&rin6.ro_dst;
src->sin6_len = sizeof(struct sockaddr_in6);
src->sin6_family = AF_INET6;
src->sin6_addr = r->src.r_src6;
rin6.ro_rt = rtalloc1_fib((struct sockaddr *)src, 0, 0, r->fib);
rin6.ro_rt = rtalloc1_fib((struct sockaddr *)src, 0, 0, r->fib);
if (rin6.ro_rt != NULL) {
rt = rin6.ro_rt;
if (rin6.ro_rt != NULL) {
rt = rin6.ro_rt;
if (rt_mask(rt))
fle6->f.src_mask = ipv6_masklen(((struct sockaddr_in6 *)rt_mask(rt))->sin6_addr);
else
fle6->f.src_mask = 128;
if (rt_mask(rt))
fle6->f.src_mask = RT_MASK6(rt);
else
fle6->f.src_mask = 128;
RTFREE_LOCKED(rt);
RTFREE_LOCKED(rt);
}
}
/* Push new flow at the and of hash. */
@ -495,6 +505,8 @@ hash6_insert(priv_p priv, struct flow_hash_entry *hsh6, struct flow6_rec *r,
return (0);
}
#undef ipv6_masklen
#undef RT_MASK6
#endif
@ -651,7 +663,7 @@ ng_netflow_cache_flush(priv_p priv)
/* Insert packet from into flow cache. */
int
ng_netflow_flow_add(priv_p priv, fib_export_p fe, struct ip *ip, caddr_t upper_ptr, uint8_t upper_proto,
uint8_t is_frag, unsigned int src_if_index)
uint8_t flags, unsigned int src_if_index)
{
register struct flow_entry *fle, *fle1;
struct flow_hash_entry *hsh;
@ -770,7 +782,7 @@ ng_netflow_flow_add(priv_p priv, fib_export_p fe, struct ip *ip, caddr_t upper_p
}
}
} else /* A new flow entry. */
error = hash_insert(priv, hsh, &r, plen, tcp_flags);
error = hash_insert(priv, hsh, &r, plen, flags, tcp_flags);
mtx_unlock(&hsh->mtx);
@ -781,7 +793,7 @@ ng_netflow_flow_add(priv_p priv, fib_export_p fe, struct ip *ip, caddr_t upper_p
/* Insert IPv6 packet from into flow cache. */
int
ng_netflow_flow6_add(priv_p priv, fib_export_p fe, struct ip6_hdr *ip6, caddr_t upper_ptr, uint8_t upper_proto,
uint8_t is_frag, unsigned int src_if_index)
uint8_t flags, unsigned int src_if_index)
{
register struct flow_entry *fle = NULL, *fle1;
register struct flow6_entry *fle6;
@ -811,7 +823,7 @@ ng_netflow_flow6_add(priv_p priv, fib_export_p fe, struct ip6_hdr *ip6, caddr_t
#if 0
r.r_tos = ip->ip_tos;
#endif
if (is_frag == 0) {
if ((flags & NG_NETFLOW_IS_FRAG) == 0) {
switch(upper_proto) {
case IPPROTO_TCP:
{
@ -896,7 +908,7 @@ ng_netflow_flow6_add(priv_p priv, fib_export_p fe, struct ip6_hdr *ip6, caddr_t
}
}
} else /* A new flow entry. */
error = hash6_insert(priv, hsh, &r, plen, tcp_flags);
error = hash6_insert(priv, hsh, &r, plen, flags, tcp_flags);
mtx_unlock(&hsh->mtx);

View File

@ -560,7 +560,7 @@ ng_netflow_rcvdata (hook_p hook, item_p item)
struct ip6_hdr *ip6 = NULL;
struct m_tag *mtag;
int pullup_len = 0, off;
uint8_t acct = 0, bypass = 0, is_frag = 0, upper_proto = 0;
uint8_t acct = 0, bypass = 0, flags = 0, upper_proto = 0;
int error = 0, l3_off = 0;
unsigned int src_if_index;
caddr_t upper_ptr = NULL;
@ -619,6 +619,9 @@ ng_netflow_rcvdata (hook_p hook, item_p item)
}
}
/* Import configuration flags related to flow creation */
flags = iface->info.conf & NG_NETFLOW_FLOW_FLAGS;
NGI_GET_M(item, m);
m_old = m;
@ -759,7 +762,7 @@ ng_netflow_rcvdata (hook_p hook, item_p item)
}
} else if (ip != NULL) {
/* Nothing to save except upper layer proto, since this is packet fragment */
is_frag = 1;
flags |= NG_NETFLOW_IS_FRAG;
upper_proto = ip->ip_p;
if ((ip->ip_v != IPVERSION) ||
((ip->ip_hl << 2) < sizeof(struct ip)))
@ -821,7 +824,7 @@ ng_netflow_rcvdata (hook_p hook, item_p item)
upper_proto = ip6f->ip6f_nxt;
hdr_off = sizeof(struct ip6_frag);
off += hdr_off;
is_frag = 1;
flags |= NG_NETFLOW_IS_FRAG;
goto loopend;
#if 0
@ -886,10 +889,10 @@ ng_netflow_rcvdata (hook_p hook, item_p item)
}
if (ip != NULL)
error = ng_netflow_flow_add(priv, fe, ip, upper_ptr, upper_proto, is_frag, src_if_index);
error = ng_netflow_flow_add(priv, fe, ip, upper_ptr, upper_proto, flags, src_if_index);
#ifdef INET6
else if (ip6 != NULL)
error = ng_netflow_flow6_add(priv, fe, ip6, upper_ptr, upper_proto, is_frag, src_if_index);
error = ng_netflow_flow6_add(priv, fe, ip6, upper_ptr, upper_proto, flags, src_if_index);
#endif
else
goto bypass;

View File

@ -111,10 +111,16 @@ struct ng_netflow_settimeouts {
uint32_t active_timeout; /* flow active timeout */
};
#define NG_NETFLOW_CONF_INGRESS 1
#define NG_NETFLOW_CONF_EGRESS 2
#define NG_NETFLOW_CONF_ONCE 4
#define NG_NETFLOW_CONF_THISONCE 8
#define NG_NETFLOW_CONF_INGRESS 0x01 /* Account on ingress */
#define NG_NETFLOW_CONF_EGRESS 0x02 /* Account on egress */
#define NG_NETFLOW_CONF_ONCE 0x04 /* Add tag to account only once */
#define NG_NETFLOW_CONF_THISONCE 0x08 /* Account once in current node */
#define NG_NETFLOW_CONF_NOSRCLOOKUP 0x10 /* No radix lookup on src */
#define NG_NETFLOW_CONF_NODSTLOOKUP 0x20 /* No radix lookup on dst */
#define NG_NETFLOW_IS_FRAG 0x01
#define NG_NETFLOW_FLOW_FLAGS (NG_NETFLOW_CONF_NOSRCLOOKUP|\
NG_NETFLOW_CONF_NODSTLOOKUP)
/* This structure is passed to NGM_NETFLOW_SETCONFIG */
struct ng_netflow_setconfig {