From drleute at familydentistportwashington.com Fri Sep 4 11:19:11 2009 From: drleute at familydentistportwashington.com (Dr. Josh Leute) Date: Fri Sep 4 11:19:24 2009 Subject: Crisis Treatment Message-ID: <1cf48b0346e4432fb4ee161d9094708d@1stnewsletters.com> Greetings from Dr. Josh Leute! Crisis Treatment Many people don?t see a dentist on a regular basis. They go only when they feel they have a problem. We call this "crisis treatment" as opposed to "preventive treatment." While these patients may feel they are saving money, it usually ends up costing much more in both dollars and time. The reason for this is that most dental problems don?t have any symptoms until they reach the advanced stages. An example is tooth decay. We hear all the time, "Nothing hurts...I don?t have any problems." But tooth decay doesn?t hurt! Until, that is, it gets close to the nerve of the tooth. Then a root canal and crown are usually necessary, instead of the small filling, which could have been placed several years ago when the cavity was small. We can usually detect a cavity 3-4 years before it may develop any symptoms. It is not uncommon to see a patient with a tremendous cavity and they have never felt a thing! If you have questions regarding dental emergencies, please call our office at (262)284-5884 or email us at drleute@familydentistportwashington.com today. Best Regards, Dr. Josh Leute P.S. If you have any friends or family members who you feel could use our services, please don't hesitate to have them call us. We'll be sure to take good care of them. From drz at dentistsherwood.com Fri Sep 4 12:22:25 2009 From: drz at dentistsherwood.com (Dr. Julian H. Zhitnitsky) Date: Fri Sep 4 12:22:33 2009 Subject: Crisis Treatment Message-ID: Greetings from Dr. Julian H. Zhitnitsky! Crisis Treatment Many people don?t see a dentist on a regular basis. They go only when they feel they have a problem. We call this "crisis treatment" as opposed to "preventive treatment." While these patients may feel they are saving money, it usually ends up costing much more in both dollars and time. The reason for this is that most dental problems don?t have any symptoms until they reach the advanced stages. An example is tooth decay. We hear all the time, "Nothing hurts...I don?t have any problems." But tooth decay doesn?t hurt! Until, that is, it gets close to the nerve of the tooth. Then a root canal and crown are usually necessary, instead of the small filling, which could have been placed several years ago when the cavity was small. We can usually detect a cavity 3-4 years before it may develop any symptoms. It is not uncommon to see a patient with a tremendous cavity and they have never felt a thing! If you have questions regarding dental emergencies, please call our office at (818)785-8388 or email us at drz@dentistsherwood.com today. Best Regards, Dr. Julian H. Zhitnitsky P.S. If you have any friends or family members who you feel could use our services, please don't hesitate to have them call us. We'll be sure to take good care of them. From melifaro at ipfw.ru Sun Sep 6 20:06:25 2009 From: melifaro at ipfw.ru (Alexander V. Chernikov) Date: Sun Sep 6 20:07:35 2009 Subject: Call for testers: ng_netflow with v9 and IPv6 support Message-ID: <4AA41192.6080708@ipfw.ru> Hi! There are patches for 7.2R and . to make ng_netflow export IPv4/IPv6 flows via V9 protocol (RFC3954). Your feedback is welcome. Alternative links: http://static.ipfw.ru/patches/netflow_v9_20090906_72.diff http://static.ipfw.ru/patches/netflow_v9_20090906_curr.diff node runtime control messages: Set export version: setversion { version=X }. X=[59], defaults to 9 Set export interface mtu setmtu { mtu=X }. X has to be <= MHLEN at the moment, defaults to 1500 Set v9 template announce conditions settemplateperiodic { time=X packets=Y }. Send templates every X seconds or Y packets, X=600, Y=500 by default -------------- next part -------------- diff -urN sys/netgraph/_netflow.orig/netflow.c sys/netgraph/netflow/netflow.c --- sys/netgraph/_netflow.orig/netflow.c 2009-09-01 02:37:44.000000000 +0400 +++ sys/netgraph/netflow/netflow.c 2009-09-06 17:04:28.000000000 +0400 @@ -30,6 +30,8 @@ static const char rcs_id[] = "@(#) $FreeBSD: src/sys/netgraph/netflow/netflow.c,v 1.33 2008/12/15 06:10:57 qingli Exp $"; +#include "opt_inet6.h" + #include #include #include @@ -37,14 +39,18 @@ #include #include #include +#include #include +#include #include #include +#include #include #include #include +#include #include #include @@ -94,12 +100,99 @@ ((t) << 6) + /* 64 */ \ ((t) << 5) + /* 32 */ \ ((t) << 3)) /* 8 */ +/* + * Defines for different neflow version dispatchers + * + */ +static int export_add(item_p, struct flow_entry *); +static int export_add_v9(item_p, struct flow_entry *); +static int export_send(priv_p, item_p, int flags); +static int export_send_v9(priv_p, item_p, int flags); + +typedef int (*record_add_ptr)(item_p, struct flow_entry *); +typedef int (*record_send_ptr)(priv_p, item_p, int); +struct _netflow_dispatchers { + record_add_ptr record_add; + record_send_ptr record_send; +}; + +static struct _netflow_dispatchers netflow_dispatcher[] = +{ + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { export_add, export_send }, /* Version 5 */ + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { export_add_v9, export_send_v9 }, /* Version 9 */ + { NULL, NULL } +}; + +record_add_ptr record_add = NULL; +record_send_ptr record_send = NULL; MALLOC_DECLARE(M_NETFLOW_HASH); MALLOC_DEFINE(M_NETFLOW_HASH, "netflow_hash", "NetFlow hash"); -static int export_add(item_p, struct flow_entry *); -static int export_send(priv_p, item_p, int flags); + +static struct netflow_v9_template _netflow_v9_record_ipv4_tcp[] = +{ + { NETFLOW_V9_FIELD_IPV4_SRC_ADDR, 4}, + { NETFLOW_V9_FIELD_IPV4_DST_ADDR, 4}, + { NETFLOW_V9_FIELD_IPV4_NEXT_HOP, 4}, + { NETFLOW_V9_FIELD_INPUT_SNMP, 2}, + { NETFLOW_V9_FIELD_OUTPUT_SNMP, 2}, + { NETFLOW_V9_FIELD_IN_PKTS, sizeof(cntr)}, + { NETFLOW_V9_FIELD_IN_BYTES, sizeof(cntr)}, + { NETFLOW_V9_FIELD_OUT_PKTS, sizeof(cntr)}, + { NETFLOW_V9_FIELD_OUT_BYTES, sizeof(cntr)}, + { NETFLOW_V9_FIELD_FIRST_SWITCHED, 4}, + { NETFLOW_V9_FIELD_LAST_SWITCHED, 4}, + { NETFLOW_V9_FIELD_L4_SRC_PORT, 2}, + { NETFLOW_V9_FIELD_L4_DST_PORT, 2}, + { NETFLOW_V9_FIELD_TCP_FLAGS, 1}, + { NETFLOW_V9_FIELD_PROTOCOL, 1}, + { NETFLOW_V9_FIELD_TOS, 1}, + { NETFLOW_V9_FIELD_SRC_AS, 4}, + { NETFLOW_V9_FIELD_DST_AS, 4}, + { NETFLOW_V9_FIELD_SRC_MASK, 1}, + { NETFLOW_V9_FIELD_DST_MASK, 1}, + {0, 0} +}; + +static struct netflow_v9_template _netflow_v9_record_ipv6_tcp[] = +{ + { NETFLOW_V9_FIELD_IPV6_SRC_ADDR, 16}, + { NETFLOW_V9_FIELD_IPV6_DST_ADDR, 16}, + { NETFLOW_V9_FIELD_IPV6_NEXT_HOP, 16}, + { NETFLOW_V9_FIELD_INPUT_SNMP, 2}, + { NETFLOW_V9_FIELD_OUTPUT_SNMP, 2}, + { NETFLOW_V9_FIELD_IN_PKTS, sizeof(cntr)}, + { NETFLOW_V9_FIELD_IN_BYTES, sizeof(cntr)}, + { NETFLOW_V9_FIELD_OUT_PKTS, sizeof(cntr)}, + { NETFLOW_V9_FIELD_OUT_BYTES, sizeof(cntr)}, + { NETFLOW_V9_FIELD_FIRST_SWITCHED, 4}, + { NETFLOW_V9_FIELD_LAST_SWITCHED, 4}, + { NETFLOW_V9_FIELD_L4_SRC_PORT, 2}, + { NETFLOW_V9_FIELD_L4_DST_PORT, 2}, + { NETFLOW_V9_FIELD_TCP_FLAGS, 1}, + { NETFLOW_V9_FIELD_PROTOCOL, 1}, + { NETFLOW_V9_FIELD_TOS, 1}, + { NETFLOW_V9_FIELD_SRC_AS, 4}, + { NETFLOW_V9_FIELD_DST_AS, 4}, + { NETFLOW_V9_FIELD_SRC_MASK, 1}, + { NETFLOW_V9_FIELD_DST_MASK, 1}, + {0, 0} +}; + + +static int generate_v9_templates(priv_p priv); + +MALLOC_DECLARE(M_NETFLOW_GENERAL); +MALLOC_DEFINE(M_NETFLOW_GENERAL, "netflog_general", "plog, templates data"); /* Generate hash for a given flow record. */ static __inline uint32_t @@ -108,10 +201,24 @@ switch (r->r_ip_p) { case IPPROTO_TCP: case IPPROTO_UDP: - return FULL_HASH(r->r_src.s_addr, r->r_dst.s_addr, + return FULL_HASH(r->src.r_src.s_addr, r->dst.r_dst.s_addr, r->r_sport, r->r_dport); default: - return ADDR_HASH(r->r_src.s_addr, r->r_dst.s_addr); + return ADDR_HASH(r->src.r_src.s_addr, r->dst.r_dst.s_addr); + } +} + +/* Generate hash for a given flow record. Use lower 4 octets from v6 addresses */ +static __inline uint32_t +ip6_hash(struct flow_rec *r) +{ + switch (r->r_ip_p) { + case IPPROTO_TCP: + case IPPROTO_UDP: + return FULL_HASH(r->src.r_src6.__u6_addr.__u6_addr32[3], r->dst.r_dst6.__u6_addr.__u6_addr32[3], + r->r_sport, r->r_dport); + default: + return ADDR_HASH(r->src.r_src6.__u6_addr.__u6_addr32[3], r->dst.r_dst6.__u6_addr.__u6_addr32[3]); } } @@ -126,6 +233,7 @@ atomic_add_32(&priv->info.nfinfo_used, 1); + return (0); } @@ -141,6 +249,7 @@ /* * Detach export datagram from priv, if there is any. * If there is no, allocate a new one. + * -- V9/IPv6 ready */ static item_p get_export_dgram(priv_p priv) @@ -166,14 +275,163 @@ return (NULL); dgram = mtod(m, struct netflow_v5_export_dgram *); dgram->header.count = 0; - dgram->header.version = htons(NETFLOW_V5); + dgram->header.version = htons(priv->version); + + if (priv->version == NETFLOW_V9) { + atomic_fetchadd_32(&priv->sent_packets, 1); + + /* + * Let's insert mbuf tag to store some info + */ + struct netflow_v9_mbuf_tag *t; + struct m_tag *mt = m_tag_alloc(MTAG_NETFLOW, MTAG_NETFLOW_V9, sizeof(struct netflow_v9_mbuf_tag), M_NOWAIT); + if (mt == NULL) { + m_freem(m); + return (NULL); + } + + m_tag_init(m); + m_tag_prepend(m, mt); + t = (struct netflow_v9_mbuf_tag *)(mt + 1); + t->length = sizeof(struct netflow_v9_header); + t->count = 0; + t->mtu = priv->mtu; + t->flow_header = t->length; + + /* + * Check if we need to insert templates into packet + */ + + struct timespec ts; + struct netflow_v9_flowset_header *fl; + + getnanotime(&ts); + if ((ts.tv_sec >= priv->templ_time + priv->templ_last_ts) || (priv->sent_packets >= priv->templ_packets + priv->templ_last_pkt)) { + //vlog("INSERTING TEMPLATE: ts: %lu last: %u packets: %u last: %u delay: %u", ts.tv_sec, priv->templ_last_ts, priv->sent_packets, priv->templ_last_pkt, priv->templ_time); + atomic_store_rel_32(&priv->templ_last_ts, ts.tv_sec); + atomic_store_rel_32(&priv->templ_last_pkt, priv->sent_packets); + //vlog("ts: %lu last_t: %u last_p: %u delay: %u", ts.tv_sec, priv->templ_last_ts, priv->templ_last_pkt, priv->templ_time); + + fl = priv->v9_flowsets[0]; + bcopy(fl, (char *)dgram + t->length, ntohs(fl->length)); + + t->length += ntohs(fl->length); + t->flow_header = t->length; + t->count += priv->flowset_records[0]; + } + + } } return (item); } /* + * Pre-compiles flow exporter for all possible FlowSets + * so we can add flowset to packet via simple memcpy() + */ +#define __push(x) *p++ = htons((x)) +static int +generate_v9_templates(priv_p priv) +{ + uint16_t *p, *template_fields_cnt; + int cnt; + + int flowset_size = sizeof(struct netflow_v9_flowset_header) + + _NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv4_tcp) + /* netflow_v9_record_ipv4_tcp */ + _NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv6_tcp); /* netflow_v9_record_ipv6_tcp */ + + priv->v9_flowsets[0] = malloc(flowset_size, M_NETFLOW_GENERAL, M_WAITOK | M_ZERO); + if (priv->v9_flowsets[0] == NULL) + return (ENOMEM); + + + if (flowset_size % 4) + flowset_size += 4 - (flowset_size % 4); /* Padding to 4-byte boundary */ + + priv->flowsets_count = 1; + p = (uint16_t *)priv->v9_flowsets[0]; + *p++ = 0; /* Flowset ID, 0 is reserved for Template FlowSets */ + *p++ = htons(flowset_size); /* Total FlowSet length */ + + /* + * Most common TCP/UDP IPv4 template, ID = 256 + */ + *p++ = htons(0x100 + NETFLOW_V9_FLOW_V4_L4); + template_fields_cnt = p++; + for (cnt = 0; _netflow_v9_record_ipv4_tcp[cnt].field_id != 0; cnt++) { + *p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_id); + *p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_length); + } + *template_fields_cnt = htons(cnt); + + /* + * TCP/UDP IPv6 template, ID = 257 + */ + *p++ = htons(0x100 + NETFLOW_V9_FLOW_V6_L4); + template_fields_cnt = p++; + for (cnt = 0; _netflow_v9_record_ipv6_tcp[cnt].field_id != 0; cnt++) { + *p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_id); + *p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_length); + } + *template_fields_cnt = htons(cnt); + + + priv->flowset_records[0] = 2; + return (0); +} + +/* + * Switches version used for netflow export + * + */ +void +ng_netflow_switch_version(priv_p priv, int ver, int boot) +{ + item_p item = NULL; + + if ((ver != NETFLOW_V9) && (ver != NETFLOW_V5)) + return; + + if ((ver == priv->version) && (boot == 0)) + return; + + /* + * All new threads acquiring export datagram will wait for lock + * so we can change pointers. + * Existing threads with export datagram already held will call + * wrong export function which will do version check at the beginning + */ + + mtx_lock(&priv->export_mtx); + /* XXX: Need to be machine-independent here */ + record_add = netflow_dispatcher[ver].record_add; + record_send = netflow_dispatcher[ver].record_send; + //atomic_store_rel_ptr((unsigned long *)record_add, (unsigned long)netflow_dispatcher[ver].record_add); + //atomic_store_rel_ptr((unsigned long *)record_send, (unsigned long)netflow_dispatcher[ver].record_send); + item = priv->export_item; + priv->export_item = NULL; + mtx_unlock(&priv->export_mtx); + + if (ver == NETFLOW_V9) { + priv->templ_last_pkt = 0; + priv->templ_last_ts = 0; + } + + //vlog("NEW: add=%p send=%p", record_add, record_send); + + if (item != NULL) + (*netflow_dispatcher[priv->version].record_send)(priv, item, NG_NOFLAGS); + + if (boot) + log(LOG_DEBUG, "ng_netflow: v%d export started\n", ver); + else + log(LOG_DEBUG, "ng_netflow: export switched: v%d -> v%d\n", priv->version, ver); + priv->version = ver; +} + +/* * Re-attach incomplete datagram back to priv. * If there is already another one, then send incomplete. */ static void @@ -190,13 +448,14 @@ mtx_unlock(&priv->export_mtx); } else { mtx_unlock(&priv->export_mtx); - export_send(priv, item, flags); + (*record_send)(priv, item, flags); } } /* * The flow is over. Call export_add() and free it. If datagram is * full, then call export_send(). + * -- v9/IPv6 nearly ready */ static __inline void expire_flow(priv_p priv, item_p *item, struct flow_entry *fle, int flags) @@ -208,8 +467,8 @@ uma_zfree_arg(priv->zone, fle, priv); return; } - if (export_add(*item, fle) > 0) { - export_send(priv, *item, flags); + if ((*record_add)(*item, fle) > 0) { + (*record_send)(priv, *item, flags); *item = NULL; } uma_zfree_arg(priv->zone, fle, priv); @@ -234,11 +493,14 @@ * to be sure. */ static __inline int -hash_insert(priv_p priv, struct flow_hash_entry *hsh, struct flow_rec *r, +hash_insert(priv_p priv, struct flow_hash_entry *hsh, struct flow_rec *r, uint16_t eproto, int plen, uint8_t tcp_flags) { struct flow_entry *fle; struct sockaddr_in sin; +#ifdef INET6 + struct sockaddr_in6 sin6; +#endif struct rtentry *rt; mtx_assert(&hsh->mtx, MA_OWNED); @@ -257,6 +519,7 @@ bcopy(r, &fle->f.r, sizeof(struct flow_rec)); fle->f.bytes = plen; fle->f.packets = 1; + fle->f.proto = eproto; fle->f.tcp_flags = tcp_flags; fle->f.first = fle->f.last = time_uptime; @@ -265,46 +528,94 @@ * 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; - /* XXX MRT 0 as a default.. need the m here to get fib */ - rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, 0); - 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_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); - } - - /* 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; - /* XXX MRT 0 as a default revisit. need the mbuf for fib*/ - rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, 0); - 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 (eproto == ETHERTYPE_IP) { + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(struct sockaddr_in); + sin.sin_family = AF_INET; + sin.sin_addr = fle->f.r.dst.r_dst; + /* XXX MRT 0 as a default.. need the m here to get fib */ + rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, 0); + 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.n.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; + + 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.src.r_src; + /* XXX MRT 0 as a default revisit. need the mbuf for fib*/ + rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, 0); + 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); + } + } else { +#ifdef INET6 + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = fle->f.r.dst.r_dst6; + /* XXX fib works for AF_INET only */ + rt = rtalloc1_fib((struct sockaddr *)&sin6, 0, 0, 0); + 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_INET6) + fle->f.n.next_hop6 = + ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr; + if (rt_mask(rt)) +/* + fle->f.dst_mask = bitcount32(((struct sockaddr_in6 *) + 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 = 128; + + RTFREE_LOCKED(rt); + } + + /* Do route lookup on source address, to fill in src_mask. */ + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = fle->f.r.src.r_src6; + /* XXX MRT 0 as a default revisit. need the mbuf for fib*/ + rt = rtalloc1_fib((struct sockaddr *)&sin6, 0, 0, 0); + if (rt != NULL) { +/* + if (rt_mask(rt)) + fle->f.src_mask = bitcount32(((struct sockaddr_in6 *) + 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 = 128; + + RTFREE_LOCKED(rt); + } - RTFREE_LOCKED(rt); +#endif } /* Push new flow at the and of hash. */ @@ -347,6 +658,10 @@ mtx_init(&priv->export_mtx, "export dgram lock", NULL, MTX_DEF); + generate_v9_templates(priv); + + ng_netflow_switch_version(priv, priv->version, 1); + return (0); } @@ -359,6 +674,7 @@ item_p item = NULL; int i; + /* * We are going to free probably billable data. * Expire everything before freeing it. @@ -371,7 +687,7 @@ } if (item != NULL) - export_send(priv, item, NG_QUEUE); + (*record_send)(priv, item, NG_QUEUE); uma_zdestroy(priv->zone); @@ -383,46 +699,34 @@ if (priv->hash) free(priv->hash, M_NETFLOW_HASH); + /* FreeFlow Tables */ + for (i = 0; i < priv->flowsets_count; i++) + free(priv->v9_flowsets[i], M_NETFLOW_GENERAL); + mtx_destroy(&priv->export_mtx); } -/* Insert packet from into flow cache. */ +/* + * Insert packet from into flow cache. Assume size/version check passed + */ int -ng_netflow_flow_add(priv_p priv, struct ip *ip, unsigned int src_if_index) +ng_netflow_flow_add(priv_p priv, caddr_t ip_ptr, caddr_t upper_ptr, uint8_t upper_proto, uint8_t is_frag, unsigned int src_if_index) { register struct flow_entry *fle, *fle1; struct flow_hash_entry *hsh; struct flow_rec r; + struct ip *ip = NULL; +#ifdef INET6 + struct ip6_hdr *ip6 = NULL; +#endif item_p item = NULL; - int hlen, plen; + int plen; int error = 0; uint8_t tcp_flags = 0; - - /* Try to fill flow_rec r */ - bzero(&r, sizeof(r)); - /* check version */ - if (ip->ip_v != IPVERSION) - return (EINVAL); - - /* verify min header length */ - hlen = ip->ip_hl << 2; - - if (hlen < sizeof(struct ip)) - return (EINVAL); - - r.r_src = ip->ip_src; - r.r_dst = ip->ip_dst; - - /* save packet length */ - plen = ntohs(ip->ip_len); - - r.r_ip_p = ip->ip_p; - r.r_tos = ip->ip_tos; - - r.r_i_ifx = src_if_index; + uint16_t eproto; /* - * XXX NOTE: only first fragment of fragmented TCP, UDP and + * XXX Fragmentation NOTE: only first fragment of fragmented TCP, UDP and * ICMP packet will be recorded with proper s_port and d_port. * Following fragments will be recorded simply as IP packet with * ip_proto = ip->ip_p and s_port, d_port set to zero. @@ -430,22 +734,91 @@ * ip packet assebmling here. Anyway, (in)famous trafd works this way - * and nobody complains yet :) */ - if ((ip->ip_off & htons(IP_OFFMASK)) == 0) - switch(r.r_ip_p) { - case IPPROTO_TCP: - { - register struct tcphdr *tcp; - - tcp = (struct tcphdr *)((caddr_t )ip + hlen); - r.r_sport = tcp->th_sport; - r.r_dport = tcp->th_dport; - tcp_flags = tcp->th_flags; - break; + + /* Try to fill flow_rec r */ + bzero(&r, sizeof(r)); + ip = (struct ip *)ip_ptr; + if (ip->ip_v == IPVERSION) { + eproto = ETHERTYPE_IP; + r.src.r_src = ip->ip_src; + r.dst.r_dst = ip->ip_dst; + + /* Assume L$ template by default */ + r.flow_type = NETFLOW_V9_FLOW_V4_L4; + + /* save packet length */ + plen = ntohs(ip->ip_len); + + r.r_tos = ip->ip_tos; + + if (is_frag == 0) { + switch(upper_proto) { + case IPPROTO_TCP: + { + register struct tcphdr *tcp; + + tcp = (struct tcphdr *)upper_ptr; + r.r_ports = *(uint32_t *)upper_ptr; + tcp_flags = tcp->th_flags; + /* r.flow_type = NETFLOW_V9_FLOW_V4_L4; */ + break; + } + case IPPROTO_UDP: + case IPPROTO_SCTP: + { + r.r_ports = *(uint32_t *)upper_ptr; + /* r.flow_type = NETFLOW_V9_FLOW_V4_L4; */ + break; + } + + } } + + } else { +#ifdef INET6 + /* IPv6 traffic */ + ip = NULL; + ip6 = (struct ip6_hdr *)ip_ptr; + eproto = ETHERTYPE_IPV6; + + r.src.r_src6 = ip6->ip6_src; + r.dst.r_dst6 = ip6->ip6_dst; + + /* Assume L4 template by default */ + r.flow_type = NETFLOW_V9_FLOW_V6_L4; + + /* save packet length */ + plen = ntohs(ip6->ip6_plen) + sizeof(struct ip6_hdr); + + //r.r_tos = ip->ip_tos; + + if (is_frag == 0) { + switch(upper_proto) { + case IPPROTO_TCP: + { + register struct tcphdr *tcp; + + tcp = (struct tcphdr *)upper_ptr; + r.r_ports = *(uint32_t *)upper_ptr; + tcp_flags = tcp->th_flags; + /* r.flow_type = NETFLOW_V9_FLOW_V6_L4; */ + break; + } case IPPROTO_UDP: - r.r_ports = *(uint32_t *)((caddr_t )ip + hlen); - break; + case IPPROTO_SCTP: + { + r.r_ports = *(uint32_t *)upper_ptr; + /* r.flow_type = NETFLOW_V9_FLOW_V6_L4; */ + break; + } + + } } +#endif + } + + r.r_ip_p = upper_proto; + r.r_i_ifx = src_if_index; /* Update node statistics. XXX: race... */ priv->info.nfinfo_packets ++; @@ -486,7 +859,7 @@ * - it is going to overflow counter */ if (tcp_flags & TH_FIN || tcp_flags & TH_RST || AGED(fle) || - (fle->f.bytes >= (UINT_MAX - IF_MAXMTU)) ) { + (fle->f.bytes >= (cntr_max - IF_MAXMTU)) ) { TAILQ_REMOVE(&hsh->head, fle, fle_hash); expire_flow(priv, &item, fle, NG_QUEUE); atomic_add_32(&priv->info.nfinfo_act_exp, 1); @@ -502,7 +875,7 @@ } } } else /* A new flow entry. */ - error = hash_insert(priv, hsh, &r, plen, tcp_flags); + error = hash_insert(priv, hsh, &r, eproto, plen, tcp_flags); mtx_unlock(&hsh->mtx); @@ -611,6 +984,65 @@ return (error); } +/* We have full datagram in privdata. Send it to export hook. */ +static int +export_send_v9(priv_p priv, item_p item, int flags) +{ + struct mbuf *m = NGI_M(item); + struct netflow_v9_export_dgram *dgram = mtod(m, + struct netflow_v9_export_dgram *); + struct netflow_v9_header *header = &dgram->header; + struct timespec ts; + int error = 0; + uint16_t len = 0; + + struct netflow_v9_mbuf_tag *t; + struct netflow_v9_flowset_header *fs = NULL; + struct m_tag *mt = m_tag_locate(m, MTAG_NETFLOW, MTAG_NETFLOW_V9, NULL); + + if (mt == NULL) { + /* */ + log(LOG_DEBUG, "ng_netflow: V9 export packet without tag!\n"); + return (0); + } + + t = (struct netflow_v9_mbuf_tag *)(mt + 1); + + /* Close FlowSet if not closed */ + if (t->length != t->flow_header) { + fs = (struct netflow_v9_flowset_header *)(mtod(m, char *) + t->flow_header); + len = (uint16_t)(t->length - t->flow_header); + if (len % 4) { + t->length += 4 - (len % 4); + len += 4 - (len % 4); + } + fs->length = htons(len); + } + + + + /* Fill mbuf header. */ + m->m_len = m->m_pkthdr.len = t->length; + + /* Fill export header. */ + header->count = t->count; + header->sys_uptime = htonl(MILLIUPTIME(time_uptime)); + getnanotime(&ts); + header->unix_secs = htonl(ts.tv_sec); + header->seq_num = htonl(atomic_fetchadd_32(&priv->flow_seq, 1)); + header->count = htons(t->count); + header->source_id = htonl(NG_NODE_ID(priv->node)); + + /* remove tag */ + m_tag_delete_chain(m, NULL); + + if (priv->export != NULL) + NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export, flags); + else + NG_FREE_ITEM(item); + + return (error); +} /* Add export record to dgram. */ static int @@ -628,9 +1060,9 @@ ("ng_netflow: export too big")); /* Fill in export record. */ - rec->src_addr = fle->f.r.r_src.s_addr; - rec->dst_addr = fle->f.r.r_dst.s_addr; - rec->next_hop = fle->f.next_hop.s_addr; + rec->src_addr = fle->f.r.src.r_src.s_addr; + rec->dst_addr = fle->f.r.dst.r_dst.s_addr; + rec->next_hop = fle->f.n.next_hop.s_addr; rec->i_ifx = htons(fle->f.fle_i_ifx); rec->o_ifx = htons(fle->f.fle_o_ifx); rec->packets = htonl(fle->f.packets); @@ -654,6 +1086,135 @@ return (0); } +/* Add V9 record to dgram. */ +static int +export_add_v9(item_p item, struct flow_entry *fle) +{ + size_t len = 0; + uint16_t new_flow = 0; + void *offset_ptr; + struct netflow_v9_mbuf_tag *t; + struct netflow_v9_flowset_header *fs = NULL; + struct mbuf *m = NGI_M(item); + struct m_tag *mt = m_tag_locate(m, MTAG_NETFLOW, MTAG_NETFLOW_V9, NULL); + + if (mt == NULL) { + /* */ + log(LOG_DEBUG, "ng_netflow: V9 export packet without tag!\n"); + return (0); + } + + t = (struct netflow_v9_mbuf_tag *)(mt + 1); + offset_ptr = (mtod(m, char *) + t->length); + + /* Check if new records has the same template */ + if (fle->f.r.flow_type != t->flow_type) { + new_flow = 1; + + /* 'Close' old FlowSet */ + fs = (struct netflow_v9_flowset_header *)(mtod(m, char *) + t->flow_header); + len = (uint16_t)(t->length - t->flow_header); + if (len % 4) { + t->length += 4 - (len % 4); + len += 4 - (len % 4); + } + fs->length = htons(len); + + /* Prepare 'new', but do not modify any counters here because switch can fail */ + t->flow_type = NETFLOW_V9_FLOW_FAKE; + t->flow_header = t->length; + fs = (struct netflow_v9_flowset_header *)(mtod(m, char *) + t->length); + offset_ptr = (fs + 1); + } + + + switch (fle->f.r.flow_type) { + case NETFLOW_V9_FLOW_V4_L4: + { + /* IPv4 TCP/UDP/[SCTP] */ + struct netflow_v9_record_ipv4_tcp *rec = (struct netflow_v9_record_ipv4_tcp *)offset_ptr; + + + rec->src_addr = fle->f.r.src.r_src.s_addr; + rec->dst_addr = fle->f.r.dst.r_dst.s_addr; + rec->next_hop = fle->f.n.next_hop.s_addr; + rec->i_ifx = htons(fle->f.fle_i_ifx); + rec->o_ifx = htons(fle->f.fle_o_ifx); + rec->i_packets = htonl(fle->f.packets); + rec->i_octets = htonl(fle->f.bytes); + rec->o_packets = htonl(0); + rec->o_octets = htonl(0); + rec->first = htonl(MILLIUPTIME(fle->f.first)); + rec->last = htonl(MILLIUPTIME(fle->f.last)); + rec->s_port = fle->f.r.r_sport; + rec->d_port = fle->f.r.r_dport; + rec->flags = fle->f.tcp_flags; + rec->prot = fle->f.r.r_ip_p; + rec->tos = fle->f.r.r_tos; + rec->dst_mask = fle->f.dst_mask; + rec->src_mask = fle->f.src_mask; + + /* Not supported fields. */ + rec->src_as = rec->dst_as = 0; + + len = sizeof(struct netflow_v9_record_ipv4_tcp); + break; + } +#ifdef INET6 + case NETFLOW_V9_FLOW_V6_L4: + { + /* IPv6 TCP/UDP/[SCTP] */ + struct netflow_v9_record_ipv6_tcp *rec = (struct netflow_v9_record_ipv6_tcp *)offset_ptr; + + /* ACHTUNG! unchecked code! */ + rec->src_addr = fle->f.r.src.r_src6; + rec->dst_addr = fle->f.r.dst.r_dst6; + rec->next_hop = fle->f.n.next_hop6; + rec->i_ifx = htons(fle->f.fle_i_ifx); + rec->o_ifx = htons(fle->f.fle_o_ifx); + rec->i_packets = htonl(fle->f.packets); + rec->i_octets = htonl(fle->f.bytes); + rec->first = htonl(MILLIUPTIME(fle->f.first)); + rec->last = htonl(MILLIUPTIME(fle->f.last)); + rec->s_port = fle->f.r.r_sport; + rec->d_port = fle->f.r.r_dport; + rec->flags = fle->f.tcp_flags; + rec->prot = fle->f.r.r_ip_p; + rec->tos = fle->f.r.r_tos; + rec->dst_mask = fle->f.dst_mask; + rec->src_mask = fle->f.src_mask; + + /* Not supported fields. */ + rec->src_as = rec->dst_as = 0; + + len = sizeof(struct netflow_v9_record_ipv6_tcp); + break; + } +#endif + default: + { + log(LOG_DEBUG, "ng_netflow: Don't know what to do with %d flow type!\n", fle->f.r.flow_type); + return (0); + } + } + + if (new_flow) { + /* Generate data segment ID */ + fs->id = htons(0x100 + fle->f.r.flow_type); + t->flow_type = fle->f.r.flow_type; + t->length += sizeof(struct netflow_v9_flowset_header); + } + + t->length += len; + t->count++; + + if (t->length + NETFLOW_V9_MAX_RECORD_SIZE + 4 >= _NETFLOW_V9_MAX_SIZE(t->mtu)) + return (1); /* end of datagram */ + else + return (0); +} + + /* Periodic flow expiry run. */ void ng_netflow_expire(void *arg) diff -urN sys/netgraph/_netflow.orig/netflow.h sys/netgraph/netflow/netflow.h --- sys/netgraph/_netflow.orig/netflow.h 2009-09-01 02:37:44.000000000 +0400 +++ sys/netgraph/netflow/netflow.h 2009-09-06 12:41:29.000000000 +0400 @@ -46,6 +46,7 @@ #define NETFLOW_V1 1 #define NETFLOW_V5 5 +#define NETFLOW_V9 9 struct netflow_v1_header { @@ -69,6 +70,16 @@ uint16_t pad; /* Pad to word boundary */ } __attribute__((__packed__)); +struct netflow_v9_header +{ + uint16_t version; /* NetFlow version */ + uint16_t count; /* Total number of records in packet */ + uint32_t sys_uptime; /* System uptime */ + uint32_t unix_secs; /* Current seconds since 0000 UTC 1970 */ + uint32_t seq_num; /* Sequence number */ + uint32_t source_id; /* Observation Domain id */ +} __attribute__((__packed__)); + struct netflow_v1_record { uint32_t src_addr; /* Source IP address */ @@ -115,6 +126,142 @@ uint16_t pad2; /* Pad to word boundary */ } __attribute__((__packed__)); + + +/* RFC3954 field definitions */ +#define NETFLOW_V9_FIELD_IN_BYTES 1 /* Input bytes count for a flow. Default 4, can be 8 */ +#define NETFLOW_V9_FIELD_IN_PKTS 2 /* Incoming counter with number of packets associated with an IP Flow. Default 4 */ +#define NETFLOW_V9_FIELD_FLOWS 3 /* Number of Flows that were aggregated. Default 4 */ +#define NETFLOW_V9_FIELD_PROTOCOL 4 /* IP protocol byte. 1 */ +#define NETFLOW_V9_FIELD_TOS 5 /* Type of service byte setting when entering the incoming interface. 1 */ +#define NETFLOW_V9_FIELD_TCP_FLAGS 6 /* TCP flags; cumulative of all the TCP flags seen in this Flow. 1 */ +#define NETFLOW_V9_FIELD_L4_SRC_PORT 7 /* TCP/UDP source port number. 2 */ +#define NETFLOW_V9_FIELD_IPV4_SRC_ADDR 8 /* IPv4 source address. 4 */ +#define NETFLOW_V9_FIELD_SRC_MASK 9 /* The number of contiguous bits in the source subnet mask (i.e., the mask in slash notation). 1 */ +#define NETFLOW_V9_FIELD_INPUT_SNMP 10 /* Input interface index. Default 2 */ +#define NETFLOW_V9_FIELD_L4_DST_PORT 11 /* TCP/UDP destination port number. 2 */ +#define NETFLOW_V9_FIELD_IPV4_DST_ADDR 12 /* IPv4 destination address. 4 */ +#define NETFLOW_V9_FIELD_DST_MASK 13 /* The number of contiguous bits in the destination subnet mask (i.e., the mask in slash notation). 1 */ +#define NETFLOW_V9_FIELD_OUTPUT_SNMP 14 /* Output interface index. Default 2 */ +#define NETFLOW_V9_FIELD_IPV4_NEXT_HOP 15 /* IPv4 address of the next-hop router. 4 */ +#define NETFLOW_V9_FIELD_SRC_AS 16 /* Source BGP autonomous system number. Default 2, can be 4 */ +#define NETFLOW_V9_FIELD_DST_AS 17 /* Destination BGP autonomous system number. Default 2, can be 4 */ +#define NETFLOW_V9_FIELD_BGP_IPV4_NEXT_HOP 18 /* Next-hop router's IP address in the BGP domain. 4 */ +#define NETFLOW_V9_FIELD_MUL_DST_PKTS 19 /* IP multicast outgoing packet counter for packets associated with IP flow. Default 4 */ +#define NETFLOW_V9_FIELD_MUL_DST_BYTES 20 /* IP multicast outgoing Octet (byte) counter for the number of bytes associated with IP flow. Default 4 */ +#define NETFLOW_V9_FIELD_LAST_SWITCHED 21 /* sysUptime in msec at which the last packet of this Flow was switched. 4 */ +#define NETFLOW_V9_FIELD_FIRST_SWITCHED 22 /* sysUptime in msec at which the first packet of this Flow was switched. 4 */ +#define NETFLOW_V9_FIELD_OUT_BYTES 23 /* Outgoing counter for the number of bytes associated with an IP Flow. Default 4 */ +#define NETFLOW_V9_FIELD_OUT_PKTS 24 /* Outgoing counter for the number of packets associated with an IP Flow. Default 4 */ +#define NETFLOW_V9_FIELD_IPV6_SRC_ADDR 27 /* IPv6 source address. 16 */ +#define NETFLOW_V9_FIELD_IPV6_DST_ADDR 28 /* IPv6 destination address. 16 */ +#define NETFLOW_V9_FIELD_IPV6_SRC_MASK 29 /* Length of the IPv6 source mask in contiguous bits. 1 */ +#define NETFLOW_V9_FIELD_IPV6_DST_MASK 30 /* Length of the IPv6 destination mask in contiguous bits. 1 */ +#define NETFLOW_V9_FIELD_IPV6_FLOW_LABEL 31 /* IPv6 flow label as per RFC 2460 definition. 3 */ +#define NETFLOW_V9_FIELD_ICMP_TYPE 32 /* Internet Control Message Protocol (ICMP) packet type; reported as ICMP Type * 256 + ICMP code. 2 */ +#define NETFLOW_V9_FIELD_MUL_IGMP_TYPE 33 /* Internet Group Management Protocol (IGMP) packet type. 1 */ +#define NETFLOW_V9_FIELD_SAMPLING_INTERVAL 34 /* When using sampled NetFlow, the rate at which packets are sampled; for example, a value of 100 indicates that one of every hundred packets is sampled. 4 */ +#define NETFLOW_V9_FIELD_SAMPLING_ALGORITHM 35 /* For sampled NetFlow platform-wide: 0x01 deterministic sampling 0x02 random sampling. 1 */ +#define NETFLOW_V9_FIELD_FLOW_ACTIVE_TIMEOUT 36 /* Timeout value (in seconds) for active flow entries in the NetFlow cache. 2 */ +#define NETFLOW_V9_FIELD_FLOW_INACTIVE_TIMEOUT 37 /* Timeout value (in seconds) for inactive Flow entries in the NetFlow cache. 2 */ +#define NETFLOW_V9_FIELD_ENGINE_TYPE 38 /* Type of Flow switching engine (route processor, linecard, etc...). 1 */ +#define NETFLOW_V9_FIELD_ENGINE_ID 39 /* ID number of the Flow switching engine. 1 */ +#define NETFLOW_V9_FIELD_TOTAL_BYTES_EXP 40 /* Counter with for the number of bytes exported by the Observation Domain. Default 4 */ +#define NETFLOW_V9_FIELD_TOTAL_PKTS_EXP 41 /* Counter with for the number of packets exported by the Observation Domain. Default 4 */ +#define NETFLOW_V9_FIELD_TOTAL_FLOWS_EXP 42 /* Counter with for the number of flows exported by the Observation Domain. Default 4 */ +#define NETFLOW_V9_FIELD_MPLS_TOP_LABEL_TYPE 46 /* MPLS Top Label Type. 1 */ +#define NETFLOW_V9_FIELD_MPLS_TOP_LABEL_IP_ADDR 47 /* Forwarding Equivalent Class corresponding to the MPLS Top Label. 4 */ +#define NETFLOW_V9_FIELD_FLOW_SAMPLER_ID 48 /* Identifier shown in "show flow-sampler". 1 */ +#define NETFLOW_V9_FIELD_FLOW_SAMPLER_MODE 49 /* The type of algorithm used for sampling data. 2 */ +#define NETFLOW_V9_FIELD_FLOW_SAMPLER_RANDOM_INTERVAL 50 /* Packet interval at which to sample. 4. */ +#define NETFLOW_V9_FIELD_DST_TOS 55 /* Type of Service byte setting when exiting outgoing interface. 1. */ +#define NETFLOW_V9_FIELD_SRC_MAC 56 /* Source MAC Address. 6 */ +#define NETFLOW_V9_FIELD_DST_MAC 57 /* Destination MAC Address. 6 */ +#define NETFLOW_V9_FIELD_SRC_VLAN 58 /* Virtual LAN identifier associated with ingress interface. 2 */ +#define NETFLOW_V9_FIELD_DST_VLAN 59 /* irtual LAN identifier associated with egress interface. 2 */ +#define NETFLOW_V9_FIELD_IP_PROTOCOL_VERSION 60 /* Internet Protocol Version. Set to 4 for IPv4, set to 6 for IPv6. If not present in the template, then version 4 is assumed. 1. */ +#define NETFLOW_V9_FIELD_DIRECTION 61 /* Flow direction: 0 - ingress flow 1 - egress flow. 1 */ +#define NETFLOW_V9_FIELD_IPV6_NEXT_HOP 62 /* IPv6 address of the next-hop router. 16 */ +#define NETFLOW_V9_FIELD_BGP_IPV6_NEXT_HOP 63 /* Next-hop router in the BGP domain. 16 */ +#define NETFLOW_V9_FIELD_IPV6_OPTION_HEADERS 64 /* Bit-encoded field identifying IPv6 option headers found in the flow */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_1 70 /* MPLS label at position 1 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_2 71 /* MPLS label at position 2 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_3 72 /* MPLS label at position 3 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_4 73 /* MPLS label at position 4 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_5 74 /* MPLS label at position 5 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_6 75 /* MPLS label at position 6 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_7 76 /* MPLS label at position 7 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_8 77 /* MPLS label at position 8 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_9 78 /* MPLS label at position 9 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_10 79 /* MPLS label at position 10 in the stack. 3 */ + +#ifdef COUNTERS_64 +#define cntr uint64_t +#define cntr_max UINT64_MAX +#else +#define cntr uint32_t +#define cntr_max UINT_MAX +#endif + +struct netflow_v9_template +{ + int field_id; + int field_length; +}; + +/* Template ID for tcp/udp v4 streams ID:257 (0x100 + NETFLOW_V9_FLOW_V4_L4) */ +struct netflow_v9_record_ipv4_tcp +{ + uint32_t src_addr; /* Source IPv4 address (IPV4_SRC_ADDR) */ + uint32_t dst_addr; /* Destination IPv4 address (IPV4_DST_ADDR) */ + uint32_t next_hop; /* Next hop IPv4 address (IPV4_NEXT_HOP) */ + uint16_t i_ifx; /* Source interface index (INPUT_SNMP) */ + uint16_t o_ifx; /* Destination interface index (OUTPUT_SNMP) */ + cntr i_packets; /* Number of incoming packets in a flow (IN_PKTS) */ + cntr i_octets; /* Number of incoming octets in a flow (IN_BYTES) */ + cntr o_packets; /* Number of outgoing packets in a flow (OUT_PKTS) */ + cntr o_octets; /* Number of outgoing octets in a flow (OUT_BYTES) */ + uint32_t first; /* System uptime at start of a flow (FIRST_SWITCHED) */ + uint32_t last; /* System uptime at end of a flow (LAST_SWITCHED) */ + uint16_t s_port; /* Source port (L4_SRC_PORT) */ + uint16_t d_port; /* Destination port (L4_DST_PORT) */ + uint8_t flags; /* Cumulative OR of tcp flags (TCP_FLAGS) */ + uint8_t prot; /* IP protocol */ + uint8_t tos; /* IP type of service IN (or OUT) (TOS) */ + uint32_t src_as; /* Src peer/origin Autonomous System (SRC_AS) */ + uint32_t dst_as; /* Dst peer/origin Autonomous System (DST_AS) */ + uint8_t src_mask; /* Source route's mask bits (SRC_MASK) */ + uint8_t dst_mask; /* Destination route's mask bits (DST_MASK) */ +} __attribute__((__packed__)); + + +/* Template ID for tcp/udp v6 streams ID: 260 (0x100 + NETFLOW_V9_FLOW_V6_L4) */ +struct netflow_v9_record_ipv6_tcp +{ + struct in6_addr src_addr; /* Source IPv6 address (IPV6_SRC_ADDR) */ + struct in6_addr dst_addr; /* Destination IPv6 address (IPV6_DST_ADDR) */ + struct in6_addr next_hop; /* Next hop IPv6 address (IPV6_NEXT_HOP) */ + uint16_t i_ifx; /* Source interface index (INPUT_SNMP) */ + uint16_t o_ifx; /* Destination interface index (OUTPUT_SNMP) */ + cntr i_packets; /* Number of incoming packets in a flow (IN_PKTS) */ + cntr i_octets; /* Number of incoming octets in a flow (IN_BYTES) */ + cntr o_packets; /* Number of outgoing packets in a flow (OUT_PKTS) */ + cntr o_octets; /* Number of outgoing octets in a flow (OUT_BYTES) */ + uint32_t first; /* System uptime at start of a flow (FIRST_SWITCHED) */ + uint32_t last; /* System uptime at end of a flow (LAST_SWITCHED) */ + uint16_t s_port; /* Source port (L4_SRC_PORT) */ + uint16_t d_port; /* Destination port (L4_DST_PORT) */ + uint8_t flags; /* Cumulative OR of tcp flags (TCP_FLAGS) */ + uint8_t prot; /* IP protocol */ + uint8_t tos; /* IP type of service IN (or OUT) (TOS) */ + uint32_t src_as; /* Src peer/origin Autonomous System (SRC_AS) */ + uint32_t dst_as; /* Dst peer/origin Autonomous System (DST_AS) */ + uint8_t src_mask; /* Source route's mask bits (SRC_MASK) */ + uint8_t dst_mask; /* Destination route's mask bits (DST_MASK) */ +} __attribute__((__packed__)); + +#define vlog(x, ...) log(LOG_DEBUG, "%s:%d " x "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__) + #define NETFLOW_V1_MAX_RECORDS 24 #define NETFLOW_V5_MAX_RECORDS 30 @@ -123,7 +270,50 @@ #define NETFLOW_V5_MAX_SIZE (sizeof(netflow_v5_header)+ \ sizeof(netflow_v5_record)*NETFLOW_V5_MAX_RECORDS) +#define BASE_MTU 1500 +#define MIN_MTU sizeof(struct netflow_v5_header) +#define MAX_MTU 16384 +#define NETFLOW_V9_MAX_SIZE _NETFLOW_V9_MAX_SIZE(BASE_MTU) +#define _NETFLOW_V9_MAX_SIZE(x) (x) - sizeof(struct ip6_hdr) - sizeof(struct udphdr) +#define NETFLOW_V9_MAX_FLOWSETS 2 + +#define NETFLOW_V9_MAX_RECORD_SIZE sizeof(struct netflow_v9_record_ipv6_tcp) +#define NETFLOW_V9_MAX_PACKETS_TEMPL 500 /* Send data templates every ... packets */ +#define NETFLOW_V9_MAX_TIME_TEMPL 600 /* Send data templates every ... seconds */ +#define NETFLOW_V9_MAX_TEMPLATES 16 /* Not a real value */ +#define _NETFLOW_V9_TEMPLATE_SIZE(x) (sizeof(x) / sizeof(struct netflow_v9_template)) * 4 +//#define _NETFLOW_V9_TEMPLATE_SIZE(x) ((x) + 1) * 4 + +/* Flow Templates */ +#define NETFLOW_V9_FLOW_V4_L4 1 /* IPv4 TCP/UDP packet */ +#define NETFLOW_V9_FLOW_V4_ICMP 2 /* IPv4 ICMP packet, currently unused */ +#define NETFLOW_V9_FLOW_V4_L3 3 /* IPv4 IP packet */ +#define NETFLOW_V9_FLOW_V6_L4 4 /* IPv6 TCP/UDP packet */ +#define NETFLOW_V9_FLOW_V6_ICMP 5 /* IPv6 ICMP packet, currently unused */ +#define NETFLOW_V9_FLOW_V6_L3 6 /* IPv6 IP packet */ + +#define NETFLOW_V9_FLOW_FAKE 65535 /* Not uset used in real flowsets! */ + struct netflow_v5_export_dgram { struct netflow_v5_header header; struct netflow_v5_record r[NETFLOW_V5_MAX_RECORDS]; } __attribute__((__packed__)); + +struct netflow_v9_export_dgram { + struct netflow_v9_header header; + char data; /* MTU can change, record length is dynamic */ +}; + +struct netflow_v9_flowset_header { + uint16_t id; /* FlowSet id */ + uint16_t length; /* FlowSet length */ +} __attribute__((__packed__)); + +struct netflow_v9_mbuf_tag { + struct m_tag tag; /* pointer to mbuf tag */ + uint16_t length; /* Current packet length */ + uint16_t count; /* current records count */ + uint16_t mtu; /* max MTU shapshot */ + uint16_t flow_type; /* current flowset */ + uint16_t flow_header; /* offset pointing to current flow header */ +}; diff -urN sys/netgraph/_netflow.orig/ng_netflow.c sys/netgraph/netflow/ng_netflow.c --- sys/netgraph/_netflow.orig/ng_netflow.c 2009-09-01 02:37:44.000000000 +0400 +++ sys/netgraph/netflow/ng_netflow.c 2009-09-06 16:36:08.000000000 +0400 @@ -30,6 +30,8 @@ static const char rcs_id[] = "@(#) $FreeBSD: src/sys/netgraph/netflow/ng_netflow.c,v 1.20 2009/05/13 02:26:34 mav Exp $"; +#include "opt_inet6.h" + #include #include #include @@ -48,8 +50,11 @@ #include #include #include +#include +#include #include #include +#include #include #include @@ -114,6 +119,30 @@ &ng_netflow_setconfig_type_fields }; +/* Parse type for ng_netflow_setversion */ +static const struct ng_parse_struct_field ng_netflow_setversion_type_fields[] + = NG_NETFLOW_SETVERSION_TYPE; +static const struct ng_parse_type ng_netflow_setversion_type = { + &ng_parse_struct_type, + &ng_netflow_setversion_type_fields +}; + +/* Parse type for ng_netflow_settemplateperiodic */ +static const struct ng_parse_struct_field ng_netflow_settemplateperiodic_type_fields[] + = NG_NETFLOW_SETTEMPLATEPERIODIC_TYPE; +static const struct ng_parse_type ng_netflow_settemplateperiodic_type = { + &ng_parse_struct_type, + &ng_netflow_settemplateperiodic_type_fields +}; + +/* Parse type for ng_netflow_setmtu */ +static const struct ng_parse_struct_field ng_netflow_setmtu_type_fields[] + = NG_NETFLOW_SETMTU_TYPE; +static const struct ng_parse_type ng_netflow_setmtu_type = { + &ng_parse_struct_type, + &ng_netflow_setmtu_type_fields +}; + /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_netflow_cmds[] = { { @@ -158,6 +187,27 @@ &ng_netflow_setconfig_type, NULL }, + { + NGM_NETFLOW_COOKIE, + NGM_NETFLOW_SETVERSION, + "setversion", + &ng_netflow_setversion_type, + NULL + }, + { + NGM_NETFLOW_COOKIE, + NGM_NETFLOW_SETTEMPLATEPERIODIC, + "settemplateperiodic", + &ng_netflow_settemplateperiodic_type, + NULL + }, + { + NGM_NETFLOW_COOKIE, + NGM_NETFLOW_SETMTU, + "setmtu", + &ng_netflow_setmtu_type, + NULL + }, { 0 } }; @@ -202,6 +252,13 @@ for (i = 0; i < NG_NETFLOW_MAXIFACES; i++) priv->ifaces[i].info.conf = NG_NETFLOW_CONF_INGRESS; + /* Set v9 defaults */ + priv->version = NETFLOW_V9; + priv->templ_time = NETFLOW_V9_MAX_TIME_TEMPL; + priv->templ_packets = NETFLOW_V9_MAX_PACKETS_TEMPL; + priv->mtu = BASE_MTU; + priv->flow_seq = 0; + /* Initialize callout handle */ callout_init(&priv->exp_callout, CALLOUT_MPSAFE); @@ -434,6 +491,52 @@ break; } + case NGM_NETFLOW_SETVERSION: + { + struct ng_netflow_setversion *set; + + if (msg->header.arglen != sizeof(struct ng_netflow_setversion)) + ERROUT(EINVAL); + + set = (struct ng_netflow_setversion *)msg->data; + if ((set->version != NETFLOW_V9) && (set->version != NETFLOW_V5)) + ERROUT(EINVAL); + + ng_netflow_switch_version(priv, set->version, 0); + + break; + } + case NGM_NETFLOW_SETTEMPLATEPERIODIC: + { + struct ng_netflow_settemplateperiodic *set; + + if (msg->header.arglen != sizeof(struct ng_netflow_settemplateperiodic)) + ERROUT(EINVAL); + + set = (struct ng_netflow_settemplateperiodic *)msg->data; + + /* XXX: Set atomic here */ + priv->templ_packets = set->packets; + priv->templ_time = set->time; + + break; + } + case NGM_NETFLOW_SETMTU: + { + struct ng_netflow_setmtu *set; + + if (msg->header.arglen != sizeof(struct ng_netflow_setmtu)) + ERROUT(EINVAL); + + set = (struct ng_netflow_setmtu *)msg->data; + if ((set->mtu < MIN_MTU) || (set->mtu > MAX_MTU)) + ERROUT(EINVAL); + + /* XXX: Set atomic here */ + priv->mtu = set->mtu; + + break; + } case NGM_NETFLOW_SHOW: { uint32_t *last; @@ -481,12 +584,15 @@ const priv_p priv = NG_NODE_PRIVATE(node); const iface_p iface = NG_HOOK_PRIVATE(hook); hook_p out; - struct mbuf *m = NULL; - struct ip *ip; + struct mbuf *m, *m_old = NULL; + struct ip *ip = NULL; + struct ip6_hdr *ip6 = NULL; struct m_tag *mtag; - int pullup_len = 0; + int pullup_len = 0, off; + uint8_t upper_proto = 0, is_frag = 0; int error = 0, bypass = 0; unsigned int src_if_index; + caddr_t upper_ptr = NULL; if (hook == priv->export) { /* @@ -541,6 +647,7 @@ } NGI_GET_M(item, m); + m_old = m; /* Increase counters. */ iface->info.ifinfo_packets++; @@ -569,6 +676,22 @@ } \ } while (0) +/* +#define M_HCHECK(length) do { \ + pullup_len += length + sizeof(ip6_ext); \ + if ((m)->m_pkthdr.len < (pullup_len)) { \ + pullup_len -= length + sizeof(ip6_ext); + M_CHECK(length); + ` + } \ + if ((m)->m_len < (pullup_len) && \ + (((m) = m_pullup((m),(pullup_len))) == NULL)) { \ + error = ENOBUFS; \ + goto done; \ + } \ +} while (0) +*/ + switch (iface->info.ifinfo_dlt) { case DLT_EN10MB: /* Ethernet */ { @@ -586,6 +709,33 @@ eh = mtod(m, struct ether_header *); ip = (struct ip *)(eh + 1); break; +#ifdef INET6 + case ETHERTYPE_IPV6: + /* + * This is done not to pullup mbuf twice on every ext + * header + */ + pullup_len += sizeof(struct ip6_hdr) + sizeof(struct ip6_ext); + if (m->m_pkthdr.len >= pullup_len) { + if ((m = m_pullup(m, pullup_len)) == NULL) { + error = ENOBUFS; + goto done; + } + pullup_len -= sizeof(struct ip6_ext); + } else { + pullup_len -= sizeof(struct ip6_hdr) + sizeof(struct ip6_ext); + M_CHECK(sizeof(struct ip6_hdr)); + eh = mtod(m, struct ether_header *); + ip6 = (struct ip6_hdr *)(eh + 1); + if (ip6->ip6_nxt != IPPROTO_NONE) { + error = EINVAL; + goto bypass; + } + } + eh = mtod(m, struct ether_header *); + ip6 = (struct ip6_hdr *)(eh + 1); + break; +#endif case ETHERTYPE_VLAN: { struct ether_vlan_header *evh; @@ -597,6 +747,29 @@ M_CHECK(sizeof(struct ip)); ip = (struct ip *)(evh + 1); break; + } else if (ntohs(evh->evl_proto) == ETHERTYPE_IPV6) { +#ifdef INET6 + pullup_len += sizeof(struct ip6_hdr) + sizeof(struct ip6_ext); + if (m->m_pkthdr.len >= pullup_len) { + if ((m = m_pullup(m, pullup_len)) == NULL) { + error = ENOBUFS; + goto done; + } + pullup_len -= sizeof(struct ip6_ext); + } else { + pullup_len -= sizeof(struct ip6_hdr) + sizeof(struct ip6_ext); + M_CHECK(sizeof(struct ip6_hdr)); + eh = mtod(m, struct ether_header *); + ip6 = (struct ip6_hdr *)(eh + 1); + if (ip6->ip6_nxt != IPPROTO_NONE) { + error = EINVAL; + goto bypass; + } + } + eh = mtod(m, struct ether_header *); + ip6 = (struct ip6_hdr *)(eh + 1); + break; +#endif } } default: @@ -607,18 +780,32 @@ case DLT_RAW: /* IP packets */ M_CHECK(sizeof(struct ip)); ip = mtod(m, struct ip *); +#ifdef INET6 + if (ip->ip_v == 6) { + /* IPv6 packet */ + ip = NULL; + M_CHECK(sizeof(struct ip6_hdr)); + ip6 = mtod(m, struct ip6_hdr *); + } +#endif break; default: goto bypass; break; } - if ((ip->ip_off & htons(IP_OFFMASK)) == 0) { + if ((ip != NULL) && ((ip->ip_off & htons(IP_OFFMASK)) == 0)) { + if ((ip->ip_v != IPVERSION) || + ((ip->ip_hl << 2) < sizeof(struct ip))) + goto bypass; /* - * In case of IP header with options, we haven't pulled + * In case of IPv4 header with options, we haven't pulled * up enough, yet. */ pullup_len += (ip->ip_hl << 2) - sizeof(struct ip); + off = pullup_len; + //vlog("upper_offset = %d", off); + upper_proto = ip->ip_p; switch (ip->ip_p) { case IPPROTO_TCP: @@ -627,38 +814,161 @@ case IPPROTO_UDP: M_CHECK(sizeof(struct udphdr)); break; + case IPPROTO_SCTP: + M_CHECK(sizeof(struct sctphdr)); + break; } - } + } else if (ip != NULL) { + is_frag = 1; + upper_proto = ip->ip_p; + if ((ip->ip_v != IPVERSION) || + ((ip->ip_hl << 2) < sizeof(struct ip))) + goto bypass; + } else if (ip6 != NULL) { +#ifdef INET6 + /* Check if we can export */ + if (priv->version < NETFLOW_V9) + goto bypass; + + /* Loop thru IPv6 extended headers to get upper layer header / frag */ + int cur = ip6->ip6_nxt, hdr_off = 0; + struct ip6_ext *ip6e; + struct ip6_frag *ip6f; + + off = pullup_len; + upper_proto = cur; + + if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) + goto bypass; + + while (42) { + + /* + * At the moment we're 'standing' with pullup_len and + * off + */ + switch (cur) { + + /* Ensure pointer is contiguous */ + case IPPROTO_TCP: + M_CHECK(sizeof(struct tcphdr)); + goto loopend; + case IPPROTO_UDP: + M_CHECK(sizeof(struct udphdr)); + goto loopend; + case IPPROTO_SCTP: + M_CHECK(sizeof(struct sctphdr)); + goto loopend; + + /* Loop until 'real' upper layer headers */ + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: + ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); + upper_proto = ip6e->ip6e_nxt; + hdr_off = (ip6e->ip6e_len + 1) << 3; + break; - switch (iface->info.ifinfo_dlt) { - case DLT_EN10MB: - { - struct ether_header *eh; + /* RFC4302, can be before DSTOPTS */ + case IPPROTO_AH: + ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); + upper_proto = ip6e->ip6e_nxt; + hdr_off = (ip6e->ip6e_len + 2) << 2; + break; - eh = mtod(m, struct ether_header *); - switch (ntohs(eh->ether_type)) { - case ETHERTYPE_IP: - ip = (struct ip *)(eh + 1); - break; - case ETHERTYPE_VLAN: - { - struct ether_vlan_header *evh; + + case IPPROTO_FRAGMENT: + ip6f = (struct ip6_frag *)(mtod(m, caddr_t) + off); + upper_proto = ip6f->ip6f_nxt; + hdr_off = sizeof(struct ip6_frag); + off += hdr_off; + is_frag = 1; + goto loopend; + +/* + case IPPROTO_NONE: + goto loopend; +*/ + default: + goto loopend; + } - evh = mtod(m, struct ether_vlan_header *); - ip = (struct ip *)(evh + 1); + off += hdr_off + sizeof(struct ip6_ext); + cur = upper_proto; + if (m->m_pkthdr.len >= off) { + if ((m = m_pullup(m, off)) == NULL) { + error = ENOBUFS; + goto done; + } + off -= sizeof(struct ip6_ext); + pullup_len += hdr_off; + } else { + /* + * Packet ends somewhere here. + * if its next header is not IPPROTO_NONE it is + * possibly mailformed packet. Let's count as RAW IP + */ + upper_proto = IPPROTO_NONE; + off -= sizeof(struct ip6_ext); + goto loopend; + } + + } +#endif + } + +#ifdef INET6 +loopend: +#endif + if (m != m_old) { + switch (iface->info.ifinfo_dlt) { + case DLT_EN10MB: /* Ethernet */ + { + struct ether_header *eh; + + eh = mtod(m, struct ether_header *); + switch (ntohs(eh->ether_type)) { + case ETHERTYPE_IP: + ip = (struct ip *)(eh + 1); + break; + case ETHERTYPE_IPV6: + ip6 = (struct ip6_hdr *)(eh + 1); + break; + case ETHERTYPE_VLAN: + { + struct ether_vlan_header *evh; + + evh = mtod(m, struct ether_vlan_header *); + if (ntohs(evh->evl_proto) == ETHERTYPE_IP) { + ip = (struct ip *)(evh + 1); + break; + } else if (ntohs(evh->evl_proto) == ETHERTYPE_IPV6) { +#ifdef INET6 + ip6 = (struct ip6_hdr *)(evh + 1); + break; +#endif + } + } + default: + panic("ng_netflow entered deadcode"); + } + break; + } + case DLT_RAW: /* IP packets */ + ip = mtod(m, struct ip *); +#ifdef INET6 + if (ip->ip_v == 6) { + /* IPv6 packet */ + ip = NULL; + ip6 = mtod(m, struct ip6_hdr *); + } +#endif break; - } default: panic("ng_netflow entered deadcode"); } - break; - } - case DLT_RAW: - ip = mtod(m, struct ip *); - break; - default: - panic("ng_netflow entered deadcode"); } + upper_ptr = (caddr_t)(mtod(m, caddr_t) + off); #undef M_CHECK @@ -670,7 +980,7 @@ } else src_if_index = iface->info.ifinfo_index; - error = ng_netflow_flow_add(priv, ip, src_if_index); + error = ng_netflow_flow_add(priv, (ip != NULL) ? (caddr_t)ip : (caddr_t)ip6, upper_ptr, upper_proto, is_frag, src_if_index); bypass: if (out != NULL) { diff -urN sys/netgraph/_netflow.orig/ng_netflow.h sys/netgraph/netflow/ng_netflow.h --- sys/netgraph/_netflow.orig/ng_netflow.h 2009-09-01 02:37:44.000000000 +0400 +++ sys/netgraph/netflow/ng_netflow.h 2009-09-06 16:36:17.000000000 +0400 @@ -51,6 +51,9 @@ NGM_NETFLOW_SETIFINDEX = 5, /* set interface index */ NGM_NETFLOW_SETTIMEOUTS = 6, /* set active/inactive flow timeouts */ NGM_NETFLOW_SETCONFIG = 7, /* set flow generation options */ + NGM_NETFLOW_SETVERSION = 8, /* set flow export version */ + NGM_NETFLOW_SETTEMPLATEPERIODIC = 9, /* set v9 flow template periodic */ + NGM_NETFLOW_SETMTU = 10, /* set outgoing interface MTU */ }; /* This structure is returned by the NGM_NETFLOW_INFO message */ @@ -105,10 +108,34 @@ u_int32_t conf; /* new config */ }; +/* This structure is passed to NGM_NETFLOW_SETVERSION */ +struct ng_netflow_setversion { + u_int8_t version; /* netflow version */ +}; + +/* This structure is passed to NGM_NETFLOW_SETTEMPLATEPERIODIC */ +struct ng_netflow_settemplateperiodic { + u_int16_t time; /* max time between announce */ + u_int16_t packets; /* max packets between announce */ +}; + +/* This structure is passed to NGM_NETFLOW_SETMTU */ +struct ng_netflow_setmtu { + u_int16_t mtu; /* MTU for packet */ +}; + /* This is unique data, which identifies flow */ struct flow_rec { - struct in_addr r_src; - struct in_addr r_dst; + uint16_t flow_type; /* IPv4 L4/L3 Ipv6 L4/L3 flow, see NETFLOW_V9_FLOW* */ + uint16_t ether_proto; /* unused */ + union { + struct in_addr r_src; + struct in6_addr r_src6; + } src; + union { + struct in_addr r_dst; + struct in6_addr r_dst6; + } dst; union { struct { uint16_t s_port; /* source TCP/UDP port */ @@ -137,7 +164,10 @@ /* A flow entry which accumulates statistics */ struct flow_entry_data { struct flow_rec r; - struct in_addr next_hop; + union { + struct in_addr next_hop; + struct in6_addr next_hop6; + } n; uint16_t fle_o_ifx; /* output interface index */ #define fle_i_ifx r.misc.i.i_ifx uint8_t dst_mask; /* destination route mask bits */ @@ -146,6 +176,7 @@ u_long bytes; long first; /* uptime on first packet */ long last; /* uptime on last packet */ + uint16_t proto; /* IP/IPv6 */ u_char tcp_flags; /* cumulative OR */ }; @@ -227,6 +258,26 @@ { NULL } \ } +/* Parse the setversion structure */ +#define NG_NETFLOW_SETVERSION_TYPE { \ + { "version", &ng_parse_uint8_type }, \ + { NULL } \ +} + +/* Parse the settemplateperiodic structure */ +#define NG_NETFLOW_SETTEMPLATEPERIODIC_TYPE { \ + { "time", &ng_parse_uint16_type }, \ + { "packets", &ng_parse_uint16_type }, \ + { NULL } \ +} + +/* Parse the setmtu structure */ +#define NG_NETFLOW_SETMTU_TYPE { \ + { "mtu", &ng_parse_uint16_type }, \ + { NULL } \ +} + + /* Private hook data */ struct ng_netflow_iface { hook_p hook; /* NULL when disconnected */ @@ -270,6 +321,20 @@ item_p export_item; struct mtx export_mtx; uint32_t flow_seq; /* current flow sequence */ + /* + * RFC 3954 clause 7.3 + * "Both options MUST be configurable by the user on the Exporter." + */ + uint16_t templ_time; /* time between sending templates */ + uint16_t templ_packets; /* packets between sending templates */ + uint32_t templ_last_ts; /* unixtime of last template announce */ + uint32_t templ_last_pkt; /* packets count on last template announce */ + uint32_t sent_packets; /* packets sent by exporeter; can be reached another way ? */ + u_char version; /* Netflow export version */ + u_char flowsets_count; /* current flowsets used */ + u_char flowset_records[NETFLOW_V9_MAX_FLOWSETS - 1]; /* Count of records in each flowset */ + uint16_t mtu; /* export interface MTU */ + struct netflow_v9_flowset_header *v9_flowsets[NETFLOW_V9_MAX_FLOWSETS - 1]; /* Pointers to pre-compiled flowsets */ struct ng_netflow_iface ifaces[NG_NETFLOW_MAXIFACES]; }; @@ -286,14 +351,15 @@ #define MTAG_NETFLOW 1221656444 #define MTAG_NETFLOW_CALLED 0 +#define MTAG_NETFLOW_V9 1 /* Prototypes for netflow.c */ int ng_netflow_cache_init(priv_p); void ng_netflow_cache_flush(priv_p); void ng_netflow_copyinfo(priv_p, struct ng_netflow_info *); timeout_t ng_netflow_expire; -int ng_netflow_flow_add(priv_p, struct ip *, unsigned int src_if_index); +int ng_netflow_flow_add(priv_p, caddr_t ip_ptr, caddr_t upper_ptr, uint8_t upper_proto, uint8_t is_frag, unsigned int src_if_index); int ng_netflow_flow_show(priv_p, uint32_t last, struct ng_mesg *); - +void ng_netflow_switch_version(priv_p priv, int ver, int boot); #endif /* _KERNEL */ #endif /* _NG_NETFLOW_H_ */ diff -urN sys/modules/netgraph/netflow/Makefile.orig sys/modules/netgraph/netflow/Makefile --- sys/modules/netgraph/netflow/Makefile.orig 2009-09-06 16:31:18.000000000 +0400 +++ sys/modules/netgraph/netflow/Makefile 2009-09-06 16:33:32.000000000 +0400 @@ -3,9 +3,19 @@ # Author: Gleb Smirnoff # +.include + .PATH: ${.CURDIR}/../../../netgraph/netflow KMOD= ng_netflow -SRCS= ng_netflow.c netflow.c +SRCS= ng_netflow.c netflow.c opt_inet6.h + +.if !defined(KERNBUILDDIR) + +.if ${MK_INET6_SUPPORT} != "no" +opt_inet6.h: + echo "#define INET6 1" > ${.TARGET} +.endif +.endif .include -------------- next part -------------- diff -urN sys/netgraph/netflow/netflow.c.orig sys/netgraph/netflow/netflow.c --- sys/netgraph/netflow/netflow.c.orig 2009-01-09 23:55:26.000000000 +0300 +++ sys/netgraph/netflow/netflow.c 2009-09-06 18:51:53.000000000 +0400 @@ -30,6 +30,8 @@ static const char rcs_id[] = "@(#) $FreeBSD: src/sys/netgraph/netflow/netflow.c,v 1.25.2.3 2009/01/09 20:55:26 mav Exp $"; +#include "opt_inet6.h" + #include #include #include @@ -37,14 +39,18 @@ #include #include #include +#include #include +#include #include #include +#include #include #include #include +#include #include #include @@ -94,12 +100,99 @@ ((t) << 6) + /* 64 */ \ ((t) << 5) + /* 32 */ \ ((t) << 3)) /* 8 */ +/* + * Defines for different neflow version dispatchers + * + */ +static int export_add(item_p, struct flow_entry *); +static int export_add_v9(item_p, struct flow_entry *); +static int export_send(priv_p, item_p, int flags); +static int export_send_v9(priv_p, item_p, int flags); + +typedef int (*record_add_ptr)(item_p, struct flow_entry *); +typedef int (*record_send_ptr)(priv_p, item_p, int); +struct _netflow_dispatchers { + record_add_ptr record_add; + record_send_ptr record_send; +}; + +static struct _netflow_dispatchers netflow_dispatcher[] = +{ + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { export_add, export_send }, /* Version 5 */ + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { export_add_v9, export_send_v9 }, /* Version 9 */ + { NULL, NULL } +}; + +record_add_ptr record_add = NULL; +record_send_ptr record_send = NULL; MALLOC_DECLARE(M_NETFLOW_HASH); MALLOC_DEFINE(M_NETFLOW_HASH, "netflow_hash", "NetFlow hash"); -static int export_add(item_p, struct flow_entry *); -static int export_send(priv_p, item_p, int flags); + +static struct netflow_v9_template _netflow_v9_record_ipv4_tcp[] = +{ + { NETFLOW_V9_FIELD_IPV4_SRC_ADDR, 4}, + { NETFLOW_V9_FIELD_IPV4_DST_ADDR, 4}, + { NETFLOW_V9_FIELD_IPV4_NEXT_HOP, 4}, + { NETFLOW_V9_FIELD_INPUT_SNMP, 2}, + { NETFLOW_V9_FIELD_OUTPUT_SNMP, 2}, + { NETFLOW_V9_FIELD_IN_PKTS, sizeof(cntr)}, + { NETFLOW_V9_FIELD_IN_BYTES, sizeof(cntr)}, + { NETFLOW_V9_FIELD_OUT_PKTS, sizeof(cntr)}, + { NETFLOW_V9_FIELD_OUT_BYTES, sizeof(cntr)}, + { NETFLOW_V9_FIELD_FIRST_SWITCHED, 4}, + { NETFLOW_V9_FIELD_LAST_SWITCHED, 4}, + { NETFLOW_V9_FIELD_L4_SRC_PORT, 2}, + { NETFLOW_V9_FIELD_L4_DST_PORT, 2}, + { NETFLOW_V9_FIELD_TCP_FLAGS, 1}, + { NETFLOW_V9_FIELD_PROTOCOL, 1}, + { NETFLOW_V9_FIELD_TOS, 1}, + { NETFLOW_V9_FIELD_SRC_AS, 4}, + { NETFLOW_V9_FIELD_DST_AS, 4}, + { NETFLOW_V9_FIELD_SRC_MASK, 1}, + { NETFLOW_V9_FIELD_DST_MASK, 1}, + {0, 0} +}; + +static struct netflow_v9_template _netflow_v9_record_ipv6_tcp[] = +{ + { NETFLOW_V9_FIELD_IPV6_SRC_ADDR, 16}, + { NETFLOW_V9_FIELD_IPV6_DST_ADDR, 16}, + { NETFLOW_V9_FIELD_IPV6_NEXT_HOP, 16}, + { NETFLOW_V9_FIELD_INPUT_SNMP, 2}, + { NETFLOW_V9_FIELD_OUTPUT_SNMP, 2}, + { NETFLOW_V9_FIELD_IN_PKTS, sizeof(cntr)}, + { NETFLOW_V9_FIELD_IN_BYTES, sizeof(cntr)}, + { NETFLOW_V9_FIELD_OUT_PKTS, sizeof(cntr)}, + { NETFLOW_V9_FIELD_OUT_BYTES, sizeof(cntr)}, + { NETFLOW_V9_FIELD_FIRST_SWITCHED, 4}, + { NETFLOW_V9_FIELD_LAST_SWITCHED, 4}, + { NETFLOW_V9_FIELD_L4_SRC_PORT, 2}, + { NETFLOW_V9_FIELD_L4_DST_PORT, 2}, + { NETFLOW_V9_FIELD_TCP_FLAGS, 1}, + { NETFLOW_V9_FIELD_PROTOCOL, 1}, + { NETFLOW_V9_FIELD_TOS, 1}, + { NETFLOW_V9_FIELD_SRC_AS, 4}, + { NETFLOW_V9_FIELD_DST_AS, 4}, + { NETFLOW_V9_FIELD_SRC_MASK, 1}, + { NETFLOW_V9_FIELD_DST_MASK, 1}, + {0, 0} +}; + + +static int generate_v9_templates(priv_p priv); + +MALLOC_DECLARE(M_NETFLOW_GENERAL); +MALLOC_DEFINE(M_NETFLOW_GENERAL, "netflog_general", "plog, templates data"); /* Generate hash for a given flow record. */ static __inline uint32_t @@ -108,10 +201,24 @@ switch (r->r_ip_p) { case IPPROTO_TCP: case IPPROTO_UDP: - return FULL_HASH(r->r_src.s_addr, r->r_dst.s_addr, + return FULL_HASH(r->src.r_src.s_addr, r->dst.r_dst.s_addr, + r->r_sport, r->r_dport); + default: + return ADDR_HASH(r->src.r_src.s_addr, r->dst.r_dst.s_addr); + } +} + +/* Generate hash for a given flow record. Use lower 4 octets from v6 addresses */ +static __inline uint32_t +ip6_hash(struct flow_rec *r) +{ + switch (r->r_ip_p) { + case IPPROTO_TCP: + case IPPROTO_UDP: + return FULL_HASH(r->src.r_src6.__u6_addr.__u6_addr32[3], r->dst.r_dst6.__u6_addr.__u6_addr32[3], r->r_sport, r->r_dport); default: - return ADDR_HASH(r->r_src.s_addr, r->r_dst.s_addr); + return ADDR_HASH(r->src.r_src6.__u6_addr.__u6_addr32[3], r->dst.r_dst6.__u6_addr.__u6_addr32[3]); } } @@ -126,6 +233,7 @@ atomic_add_32(&priv->info.nfinfo_used, 1); + return (0); } @@ -141,6 +249,7 @@ /* * Detach export datagram from priv, if there is any. * If there is no, allocate a new one. + * -- V9/IPv6 ready */ static item_p get_export_dgram(priv_p priv) @@ -166,14 +275,163 @@ return (NULL); dgram = mtod(m, struct netflow_v5_export_dgram *); dgram->header.count = 0; - dgram->header.version = htons(NETFLOW_V5); + dgram->header.version = htons(priv->version); + + if (priv->version == NETFLOW_V9) { + atomic_fetchadd_32(&priv->sent_packets, 1); + + /* + * Let's insert mbuf tag to store some info + */ + struct netflow_v9_mbuf_tag *t; + struct m_tag *mt = m_tag_alloc(MTAG_NETFLOW, MTAG_NETFLOW_V9, sizeof(struct netflow_v9_mbuf_tag), M_NOWAIT); + if (mt == NULL) { + m_freem(m); + return (NULL); + } + + m_tag_init(m); + m_tag_prepend(m, mt); + t = (struct netflow_v9_mbuf_tag *)(mt + 1); + t->length = sizeof(struct netflow_v9_header); + t->count = 0; + t->mtu = priv->mtu; + t->flow_header = t->length; + + /* + * Check if we need to insert templates into packet + */ + + struct timespec ts; + struct netflow_v9_flowset_header *fl; + + getnanotime(&ts); + if ((ts.tv_sec >= priv->templ_time + priv->templ_last_ts) || (priv->sent_packets >= priv->templ_packets + priv->templ_last_pkt)) { + //vlog("INSERTING TEMPLATE: ts: %lu last: %u packets: %u last: %u delay: %u", ts.tv_sec, priv->templ_last_ts, priv->sent_packets, priv->templ_last_pkt, priv->templ_time); + atomic_store_rel_32(&priv->templ_last_ts, ts.tv_sec); + atomic_store_rel_32(&priv->templ_last_pkt, priv->sent_packets); + //vlog("ts: %lu last_t: %u last_p: %u delay: %u", ts.tv_sec, priv->templ_last_ts, priv->templ_last_pkt, priv->templ_time); + + fl = priv->v9_flowsets[0]; + bcopy(fl, (char *)dgram + t->length, ntohs(fl->length)); + + t->length += ntohs(fl->length); + t->flow_header = t->length; + t->count += priv->flowset_records[0]; + } + + } } return (item); } /* + * Pre-compiles flow exporter for all possible FlowSets + * so we can add flowset to packet via simple memcpy() + */ +#define __push(x) *p++ = htons((x)) +static int +generate_v9_templates(priv_p priv) +{ + uint16_t *p, *template_fields_cnt; + int cnt; + + int flowset_size = sizeof(struct netflow_v9_flowset_header) + + _NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv4_tcp) + /* netflow_v9_record_ipv4_tcp */ + _NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv6_tcp); /* netflow_v9_record_ipv6_tcp */ + + priv->v9_flowsets[0] = malloc(flowset_size, M_NETFLOW_GENERAL, M_WAITOK | M_ZERO); + if (priv->v9_flowsets[0] == NULL) + return (ENOMEM); + + + if (flowset_size % 4) + flowset_size += 4 - (flowset_size % 4); /* Padding to 4-byte boundary */ + + priv->flowsets_count = 1; + p = (uint16_t *)priv->v9_flowsets[0]; + *p++ = 0; /* Flowset ID, 0 is reserved for Template FlowSets */ + *p++ = htons(flowset_size); /* Total FlowSet length */ + + /* + * Most common TCP/UDP IPv4 template, ID = 256 + */ + *p++ = htons(0x100 + NETFLOW_V9_FLOW_V4_L4); + template_fields_cnt = p++; + for (cnt = 0; _netflow_v9_record_ipv4_tcp[cnt].field_id != 0; cnt++) { + *p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_id); + *p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_length); + } + *template_fields_cnt = htons(cnt); + + /* + * TCP/UDP IPv6 template, ID = 257 + */ + *p++ = htons(0x100 + NETFLOW_V9_FLOW_V6_L4); + template_fields_cnt = p++; + for (cnt = 0; _netflow_v9_record_ipv6_tcp[cnt].field_id != 0; cnt++) { + *p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_id); + *p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_length); + } + *template_fields_cnt = htons(cnt); + + + priv->flowset_records[0] = 2; + return (0); +} + +/* + * Switches version used for netflow export + * + */ +void +ng_netflow_switch_version(priv_p priv, int ver, int boot) +{ + item_p item = NULL; + + if ((ver != NETFLOW_V9) && (ver != NETFLOW_V5)) + return; + + if ((ver == priv->version) && (boot == 0)) + return; + + /* + * All new threads acquiring export datagram will wait for lock + * so we can change pointers. + * Existing threads with export datagram already held will call + * wrong export function which will do version check at the beginning + */ + + mtx_lock(&priv->export_mtx); + /* XXX: Need to be machine-independent here */ + record_add = netflow_dispatcher[ver].record_add; + record_send = netflow_dispatcher[ver].record_send; + //atomic_store_rel_ptr((unsigned long *)record_add, (unsigned long)netflow_dispatcher[ver].record_add); + //atomic_store_rel_ptr((unsigned long *)record_send, (unsigned long)netflow_dispatcher[ver].record_send); + item = priv->export_item; + priv->export_item = NULL; + mtx_unlock(&priv->export_mtx); + + if (ver == NETFLOW_V9) { + priv->templ_last_pkt = 0; + priv->templ_last_ts = 0; + } + + //vlog("NEW: add=%p send=%p", record_add, record_send); + + if (item != NULL) + (*netflow_dispatcher[priv->version].record_send)(priv, item, NG_NOFLAGS); + + if (boot) + log(LOG_DEBUG, "ng_netflow: v%d export started\n", ver); + else + log(LOG_DEBUG, "ng_netflow: export switched: v%d -> v%d\n", priv->version, ver); + priv->version = ver; +} + +/* * Re-attach incomplete datagram back to priv. * If there is already another one, then send incomplete. */ static void @@ -190,13 +448,14 @@ mtx_unlock(&priv->export_mtx); } else { mtx_unlock(&priv->export_mtx); - export_send(priv, item, flags); + (*record_send)(priv, item, flags); } } /* * The flow is over. Call export_add() and free it. If datagram is * full, then call export_send(). + * -- v9/IPv6 nearly ready */ static __inline void expire_flow(priv_p priv, item_p *item, struct flow_entry *fle, int flags) @@ -208,8 +467,8 @@ uma_zfree_arg(priv->zone, fle, priv); return; } - if (export_add(*item, fle) > 0) { - export_send(priv, *item, flags); + if ((*record_add)(*item, fle) > 0) { + (*record_send)(priv, *item, flags); *item = NULL; } uma_zfree_arg(priv->zone, fle, priv); @@ -234,12 +493,15 @@ * to be sure. */ static __inline int -hash_insert(priv_p priv, struct flow_hash_entry *hsh, struct flow_rec *r, +hash_insert(priv_p priv, struct flow_hash_entry *hsh, struct flow_rec *r, uint16_t eproto, int plen, uint8_t tcp_flags) { struct flow_entry *fle; - struct route ro; - struct sockaddr_in *sin; + struct sockaddr_in sin; +#ifdef INET6 + struct sockaddr_in6 sin6; +#endif + struct rtentry *rt; mtx_assert(&hsh->mtx, MA_OWNED); @@ -257,6 +519,7 @@ bcopy(r, &fle->f.r, sizeof(struct flow_rec)); fle->f.bytes = plen; fle->f.packets = 1; + fle->f.proto = eproto; fle->f.tcp_flags = tcp_flags; fle->f.first = fle->f.last = time_uptime; @@ -265,52 +528,93 @@ * 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((caddr_t)&ro, sizeof(ro)); - sin = (struct sockaddr_in *)&ro.ro_dst; - sin->sin_len = sizeof(*sin); - sin->sin_family = AF_INET; - sin->sin_addr = fle->f.r.r_dst; - /* XXX MRT 0 as a default.. need the m here to get fib */ - rtalloc_ign_fib(&ro, RTF_CLONING, 0); - if (ro.ro_rt != NULL) { - struct rtentry *rt = ro.ro_rt; - - 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_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(ro.ro_rt); - } - - /* Do route lookup on source address, to fill in src_mask. */ - - bzero((caddr_t)&ro, sizeof(ro)); - sin = (struct sockaddr_in *)&ro.ro_dst; - sin->sin_len = sizeof(*sin); - sin->sin_family = AF_INET; - sin->sin_addr = fle->f.r.r_src; - rtalloc_ign_fib(&ro, RTF_CLONING, 0); /* XXX MRT */ - if (ro.ro_rt != NULL) { - struct rtentry *rt = ro.ro_rt; - - 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(ro.ro_rt); + if (eproto == ETHERTYPE_IP) { + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(struct sockaddr_in); + sin.sin_family = AF_INET; + sin.sin_addr = fle->f.r.dst.r_dst; + /* XXX MRT 0 as a default.. need the m here to get fib */ + rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, 0); + 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.n.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; + + 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.src.r_src; + /* XXX MRT 0 as a default revisit. need the mbuf for fib*/ + rt = rtalloc1_fib((struct sockaddr *)&sin, 0, 0, 0); + 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); + } + } else { +#ifdef INET6 + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = fle->f.r.dst.r_dst6; + /* XXX fib works for AF_INET only */ + rt = rtalloc1_fib((struct sockaddr *)&sin6, 0, 0, 0); + 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_INET6) + fle->f.n.next_hop6 = + ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr; + if (rt_mask(rt)) +/* + fle->f.dst_mask = bitcount32(((struct sockaddr_in6 *) + 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 = 128; + + RTFREE_LOCKED(rt); + } + + /* Do route lookup on source address, to fill in src_mask. */ + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = fle->f.r.src.r_src6; + /* XXX MRT 0 as a default revisit. need the mbuf for fib*/ + rt = rtalloc1_fib((struct sockaddr *)&sin6, 0, 0, 0); + if (rt != NULL) { +/* + if (rt_mask(rt)) + fle->f.src_mask = bitcount32(((struct sockaddr_in6 *) + 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 = 128; + + RTFREE_LOCKED(rt); + } +#endif } /* Push new flow at the and of hash. */ @@ -354,6 +658,10 @@ mtx_init(&priv->export_mtx, "export dgram lock", NULL, MTX_DEF); + generate_v9_templates(priv); + + ng_netflow_switch_version(priv, priv->version, 1); + return (0); } @@ -366,6 +674,7 @@ item_p item = NULL; int i; + /* * We are going to free probably billable data. * Expire everything before freeing it. @@ -378,7 +687,7 @@ } if (item != NULL) - export_send(priv, item, NG_QUEUE); + (*record_send)(priv, item, NG_QUEUE); uma_zdestroy(priv->zone); @@ -390,46 +699,34 @@ if (priv->hash) FREE(priv->hash, M_NETFLOW_HASH); + /* FreeFlow Tables */ + for (i = 0; i < priv->flowsets_count; i++) + free(priv->v9_flowsets[i], M_NETFLOW_GENERAL); + mtx_destroy(&priv->export_mtx); } -/* Insert packet from into flow cache. */ +/* + * Insert packet from into flow cache. Assume size/version check passed + */ int -ng_netflow_flow_add(priv_p priv, struct ip *ip, unsigned int src_if_index) +ng_netflow_flow_add(priv_p priv, caddr_t ip_ptr, caddr_t upper_ptr, uint8_t upper_proto, uint8_t is_frag, unsigned int src_if_index) { register struct flow_entry *fle, *fle1; struct flow_hash_entry *hsh; struct flow_rec r; + struct ip *ip = NULL; +#ifdef INET6 + struct ip6_hdr *ip6 = NULL; +#endif item_p item = NULL; - int hlen, plen; + int plen; int error = 0; uint8_t tcp_flags = 0; - - /* Try to fill flow_rec r */ - bzero(&r, sizeof(r)); - /* check version */ - if (ip->ip_v != IPVERSION) - return (EINVAL); - - /* verify min header length */ - hlen = ip->ip_hl << 2; - - if (hlen < sizeof(struct ip)) - return (EINVAL); - - r.r_src = ip->ip_src; - r.r_dst = ip->ip_dst; - - /* save packet length */ - plen = ntohs(ip->ip_len); - - r.r_ip_p = ip->ip_p; - r.r_tos = ip->ip_tos; - - r.r_i_ifx = src_if_index; + uint16_t eproto; /* - * XXX NOTE: only first fragment of fragmented TCP, UDP and + * XXX Fragmentation NOTE: only first fragment of fragmented TCP, UDP and * ICMP packet will be recorded with proper s_port and d_port. * Following fragments will be recorded simply as IP packet with * ip_proto = ip->ip_p and s_port, d_port set to zero. @@ -437,22 +734,91 @@ * ip packet assebmling here. Anyway, (in)famous trafd works this way - * and nobody complains yet :) */ - if ((ip->ip_off & htons(IP_OFFMASK)) == 0) - switch(r.r_ip_p) { - case IPPROTO_TCP: - { - register struct tcphdr *tcp; - - tcp = (struct tcphdr *)((caddr_t )ip + hlen); - r.r_sport = tcp->th_sport; - r.r_dport = tcp->th_dport; - tcp_flags = tcp->th_flags; - break; + + /* Try to fill flow_rec r */ + bzero(&r, sizeof(r)); + ip = (struct ip *)ip_ptr; + if (ip->ip_v == IPVERSION) { + eproto = ETHERTYPE_IP; + r.src.r_src = ip->ip_src; + r.dst.r_dst = ip->ip_dst; + + /* Assume L$ template by default */ + r.flow_type = NETFLOW_V9_FLOW_V4_L4; + + /* save packet length */ + plen = ntohs(ip->ip_len); + + r.r_tos = ip->ip_tos; + + if (is_frag == 0) { + switch(upper_proto) { + case IPPROTO_TCP: + { + register struct tcphdr *tcp; + + tcp = (struct tcphdr *)upper_ptr; + r.r_ports = *(uint32_t *)upper_ptr; + tcp_flags = tcp->th_flags; + /* r.flow_type = NETFLOW_V9_FLOW_V4_L4; */ + break; + } + case IPPROTO_UDP: + case IPPROTO_SCTP: + { + r.r_ports = *(uint32_t *)upper_ptr; + /* r.flow_type = NETFLOW_V9_FLOW_V4_L4; */ + break; + } + + } } + + } else { +#ifdef INET6 + /* IPv6 traffic */ + ip = NULL; + ip6 = (struct ip6_hdr *)ip_ptr; + eproto = ETHERTYPE_IPV6; + + r.src.r_src6 = ip6->ip6_src; + r.dst.r_dst6 = ip6->ip6_dst; + + /* Assume L4 template by default */ + r.flow_type = NETFLOW_V9_FLOW_V6_L4; + + /* save packet length */ + plen = ntohs(ip6->ip6_plen) + sizeof(struct ip6_hdr); + + //r.r_tos = ip->ip_tos; + + if (is_frag == 0) { + switch(upper_proto) { + case IPPROTO_TCP: + { + register struct tcphdr *tcp; + + tcp = (struct tcphdr *)upper_ptr; + r.r_ports = *(uint32_t *)upper_ptr; + tcp_flags = tcp->th_flags; + /* r.flow_type = NETFLOW_V9_FLOW_V6_L4; */ + break; + } case IPPROTO_UDP: - r.r_ports = *(uint32_t *)((caddr_t )ip + hlen); - break; + case IPPROTO_SCTP: + { + r.r_ports = *(uint32_t *)upper_ptr; + /* r.flow_type = NETFLOW_V9_FLOW_V6_L4; */ + break; + } + + } } +#endif + } + + r.r_ip_p = upper_proto; + r.r_i_ifx = src_if_index; /* Update node statistics. XXX: race... */ priv->info.nfinfo_packets ++; @@ -493,7 +859,7 @@ * - it is going to overflow counter */ if (tcp_flags & TH_FIN || tcp_flags & TH_RST || AGED(fle) || - (fle->f.bytes >= (UINT_MAX - IF_MAXMTU)) ) { + (fle->f.bytes >= (cntr_max - IF_MAXMTU)) ) { TAILQ_REMOVE(&hsh->head, fle, fle_hash); expire_flow(priv, &item, fle, NG_QUEUE); atomic_add_32(&priv->info.nfinfo_act_exp, 1); @@ -509,7 +875,7 @@ } } } else /* A new flow entry. */ - error = hash_insert(priv, hsh, &r, plen, tcp_flags); + error = hash_insert(priv, hsh, &r, eproto, plen, tcp_flags); mtx_unlock(&hsh->mtx); @@ -618,6 +984,65 @@ return (error); } +/* We have full datagram in privdata. Send it to export hook. */ +static int +export_send_v9(priv_p priv, item_p item, int flags) +{ + struct mbuf *m = NGI_M(item); + struct netflow_v9_export_dgram *dgram = mtod(m, + struct netflow_v9_export_dgram *); + struct netflow_v9_header *header = &dgram->header; + struct timespec ts; + int error = 0; + uint16_t len = 0; + + struct netflow_v9_mbuf_tag *t; + struct netflow_v9_flowset_header *fs = NULL; + struct m_tag *mt = m_tag_locate(m, MTAG_NETFLOW, MTAG_NETFLOW_V9, NULL); + + if (mt == NULL) { + /* */ + log(LOG_DEBUG, "ng_netflow: V9 export packet without tag!\n"); + return (0); + } + + t = (struct netflow_v9_mbuf_tag *)(mt + 1); + + /* Close FlowSet if not closed */ + if (t->length != t->flow_header) { + fs = (struct netflow_v9_flowset_header *)(mtod(m, char *) + t->flow_header); + len = (uint16_t)(t->length - t->flow_header); + if (len % 4) { + t->length += 4 - (len % 4); + len += 4 - (len % 4); + } + fs->length = htons(len); + } + + + + /* Fill mbuf header. */ + m->m_len = m->m_pkthdr.len = t->length; + + /* Fill export header. */ + header->count = t->count; + header->sys_uptime = htonl(MILLIUPTIME(time_uptime)); + getnanotime(&ts); + header->unix_secs = htonl(ts.tv_sec); + header->seq_num = htonl(atomic_fetchadd_32(&priv->flow_seq, 1)); + header->count = htons(t->count); + header->source_id = htonl(NG_NODE_ID(priv->node)); + + /* remove tag */ + m_tag_delete_chain(m, NULL); + + if (priv->export != NULL) + NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export, flags); + else + NG_FREE_ITEM(item); + + return (error); +} /* Add export record to dgram. */ static int @@ -635,9 +1060,9 @@ ("ng_netflow: export too big")); /* Fill in export record. */ - rec->src_addr = fle->f.r.r_src.s_addr; - rec->dst_addr = fle->f.r.r_dst.s_addr; - rec->next_hop = fle->f.next_hop.s_addr; + rec->src_addr = fle->f.r.src.r_src.s_addr; + rec->dst_addr = fle->f.r.dst.r_dst.s_addr; + rec->next_hop = fle->f.n.next_hop.s_addr; rec->i_ifx = htons(fle->f.fle_i_ifx); rec->o_ifx = htons(fle->f.fle_o_ifx); rec->packets = htonl(fle->f.packets); @@ -661,6 +1086,135 @@ return (0); } +/* Add V9 record to dgram. */ +static int +export_add_v9(item_p item, struct flow_entry *fle) +{ + size_t len = 0; + uint16_t new_flow = 0; + void *offset_ptr; + struct netflow_v9_mbuf_tag *t; + struct netflow_v9_flowset_header *fs = NULL; + struct mbuf *m = NGI_M(item); + struct m_tag *mt = m_tag_locate(m, MTAG_NETFLOW, MTAG_NETFLOW_V9, NULL); + + if (mt == NULL) { + /* */ + log(LOG_DEBUG, "ng_netflow: V9 export packet without tag!\n"); + return (0); + } + + t = (struct netflow_v9_mbuf_tag *)(mt + 1); + offset_ptr = (mtod(m, char *) + t->length); + + /* Check if new records has the same template */ + if (fle->f.r.flow_type != t->flow_type) { + new_flow = 1; + + /* 'Close' old FlowSet */ + fs = (struct netflow_v9_flowset_header *)(mtod(m, char *) + t->flow_header); + len = (uint16_t)(t->length - t->flow_header); + if (len % 4) { + t->length += 4 - (len % 4); + len += 4 - (len % 4); + } + fs->length = htons(len); + + /* Prepare 'new', but do not modify any counters here because switch can fail */ + t->flow_type = NETFLOW_V9_FLOW_FAKE; + t->flow_header = t->length; + fs = (struct netflow_v9_flowset_header *)(mtod(m, char *) + t->length); + offset_ptr = (fs + 1); + } + + + switch (fle->f.r.flow_type) { + case NETFLOW_V9_FLOW_V4_L4: + { + /* IPv4 TCP/UDP/[SCTP] */ + struct netflow_v9_record_ipv4_tcp *rec = (struct netflow_v9_record_ipv4_tcp *)offset_ptr; + + + rec->src_addr = fle->f.r.src.r_src.s_addr; + rec->dst_addr = fle->f.r.dst.r_dst.s_addr; + rec->next_hop = fle->f.n.next_hop.s_addr; + rec->i_ifx = htons(fle->f.fle_i_ifx); + rec->o_ifx = htons(fle->f.fle_o_ifx); + rec->i_packets = htonl(fle->f.packets); + rec->i_octets = htonl(fle->f.bytes); + rec->o_packets = htonl(0); + rec->o_octets = htonl(0); + rec->first = htonl(MILLIUPTIME(fle->f.first)); + rec->last = htonl(MILLIUPTIME(fle->f.last)); + rec->s_port = fle->f.r.r_sport; + rec->d_port = fle->f.r.r_dport; + rec->flags = fle->f.tcp_flags; + rec->prot = fle->f.r.r_ip_p; + rec->tos = fle->f.r.r_tos; + rec->dst_mask = fle->f.dst_mask; + rec->src_mask = fle->f.src_mask; + + /* Not supported fields. */ + rec->src_as = rec->dst_as = 0; + + len = sizeof(struct netflow_v9_record_ipv4_tcp); + break; + } +#ifdef INET6 + case NETFLOW_V9_FLOW_V6_L4: + { + /* IPv6 TCP/UDP/[SCTP] */ + struct netflow_v9_record_ipv6_tcp *rec = (struct netflow_v9_record_ipv6_tcp *)offset_ptr; + + /* ACHTUNG! unchecked code! */ + rec->src_addr = fle->f.r.src.r_src6; + rec->dst_addr = fle->f.r.dst.r_dst6; + rec->next_hop = fle->f.n.next_hop6; + rec->i_ifx = htons(fle->f.fle_i_ifx); + rec->o_ifx = htons(fle->f.fle_o_ifx); + rec->i_packets = htonl(fle->f.packets); + rec->i_octets = htonl(fle->f.bytes); + rec->first = htonl(MILLIUPTIME(fle->f.first)); + rec->last = htonl(MILLIUPTIME(fle->f.last)); + rec->s_port = fle->f.r.r_sport; + rec->d_port = fle->f.r.r_dport; + rec->flags = fle->f.tcp_flags; + rec->prot = fle->f.r.r_ip_p; + rec->tos = fle->f.r.r_tos; + rec->dst_mask = fle->f.dst_mask; + rec->src_mask = fle->f.src_mask; + + /* Not supported fields. */ + rec->src_as = rec->dst_as = 0; + + len = sizeof(struct netflow_v9_record_ipv6_tcp); + break; + } +#endif + default: + { + log(LOG_DEBUG, "ng_netflow: Don't know what to do with %d flow type!\n", fle->f.r.flow_type); + return (0); + } + } + + if (new_flow) { + /* Generate data segment ID */ + fs->id = htons(0x100 + fle->f.r.flow_type); + t->flow_type = fle->f.r.flow_type; + t->length += sizeof(struct netflow_v9_flowset_header); + } + + t->length += len; + t->count++; + + if (t->length + NETFLOW_V9_MAX_RECORD_SIZE + 4 >= _NETFLOW_V9_MAX_SIZE(t->mtu)) + return (1); /* end of datagram */ + else + return (0); +} + + /* Periodic flow expiry run. */ void ng_netflow_expire(void *arg) diff -urN sys/netgraph/netflow/ng_netflow.c.orig sys/netgraph/netflow/ng_netflow.c --- sys/netgraph/netflow/ng_netflow.c.orig 2009-01-09 23:55:26.000000000 +0300 +++ sys/netgraph/netflow/ng_netflow.c 2009-09-06 18:38:40.000000000 +0400 @@ -30,6 +30,8 @@ static const char rcs_id[] = "@(#) $FreeBSD: src/sys/netgraph/netflow/ng_netflow.c,v 1.14.2.4 2009/01/09 20:55:26 mav Exp $"; +#include "opt_inet6.h" + #include #include #include @@ -48,8 +50,11 @@ #include #include #include +#include +#include #include #include +#include #include #include @@ -114,6 +119,30 @@ &ng_netflow_setconfig_type_fields }; +/* Parse type for ng_netflow_setversion */ +static const struct ng_parse_struct_field ng_netflow_setversion_type_fields[] + = NG_NETFLOW_SETVERSION_TYPE; +static const struct ng_parse_type ng_netflow_setversion_type = { + &ng_parse_struct_type, + &ng_netflow_setversion_type_fields +}; + +/* Parse type for ng_netflow_settemplateperiodic */ +static const struct ng_parse_struct_field ng_netflow_settemplateperiodic_type_fields[] + = NG_NETFLOW_SETTEMPLATEPERIODIC_TYPE; +static const struct ng_parse_type ng_netflow_settemplateperiodic_type = { + &ng_parse_struct_type, + &ng_netflow_settemplateperiodic_type_fields +}; + +/* Parse type for ng_netflow_setmtu */ +static const struct ng_parse_struct_field ng_netflow_setmtu_type_fields[] + = NG_NETFLOW_SETMTU_TYPE; +static const struct ng_parse_type ng_netflow_setmtu_type = { + &ng_parse_struct_type, + &ng_netflow_setmtu_type_fields +}; + /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_netflow_cmds[] = { { @@ -158,6 +187,27 @@ &ng_netflow_setconfig_type, NULL }, + { + NGM_NETFLOW_COOKIE, + NGM_NETFLOW_SETVERSION, + "setversion", + &ng_netflow_setversion_type, + NULL + }, + { + NGM_NETFLOW_COOKIE, + NGM_NETFLOW_SETTEMPLATEPERIODIC, + "settemplateperiodic", + &ng_netflow_settemplateperiodic_type, + NULL + }, + { + NGM_NETFLOW_COOKIE, + NGM_NETFLOW_SETMTU, + "setmtu", + &ng_netflow_setmtu_type, + NULL + }, { 0 } }; @@ -202,6 +252,13 @@ for (i = 0; i < NG_NETFLOW_MAXIFACES; i++) priv->ifaces[i].info.conf = NG_NETFLOW_CONF_INGRESS; + /* Set v9 defaults */ + priv->version = NETFLOW_V9; + priv->templ_time = NETFLOW_V9_MAX_TIME_TEMPL; + priv->templ_packets = NETFLOW_V9_MAX_PACKETS_TEMPL; + priv->mtu = BASE_MTU; + priv->flow_seq = 0; + /* Initialize callout handle */ callout_init(&priv->exp_callout, CALLOUT_MPSAFE); @@ -434,6 +491,52 @@ break; } + case NGM_NETFLOW_SETVERSION: + { + struct ng_netflow_setversion *set; + + if (msg->header.arglen != sizeof(struct ng_netflow_setversion)) + ERROUT(EINVAL); + + set = (struct ng_netflow_setversion *)msg->data; + if ((set->version != NETFLOW_V9) && (set->version != NETFLOW_V5)) + ERROUT(EINVAL); + + ng_netflow_switch_version(priv, set->version, 0); + + break; + } + case NGM_NETFLOW_SETTEMPLATEPERIODIC: + { + struct ng_netflow_settemplateperiodic *set; + + if (msg->header.arglen != sizeof(struct ng_netflow_settemplateperiodic)) + ERROUT(EINVAL); + + set = (struct ng_netflow_settemplateperiodic *)msg->data; + + /* XXX: Set atomic here */ + priv->templ_packets = set->packets; + priv->templ_time = set->time; + + break; + } + case NGM_NETFLOW_SETMTU: + { + struct ng_netflow_setmtu *set; + + if (msg->header.arglen != sizeof(struct ng_netflow_setmtu)) + ERROUT(EINVAL); + + set = (struct ng_netflow_setmtu *)msg->data; + if ((set->mtu < MIN_MTU) || (set->mtu > MAX_MTU)) + ERROUT(EINVAL); + + /* XXX: Set atomic here */ + priv->mtu = set->mtu; + + break; + } case NGM_NETFLOW_SHOW: { uint32_t *last; @@ -481,12 +584,15 @@ const priv_p priv = NG_NODE_PRIVATE(node); const iface_p iface = NG_HOOK_PRIVATE(hook); hook_p out; - struct mbuf *m = NULL; - struct ip *ip; + struct mbuf *m, *m_old = NULL; + struct ip *ip = NULL; + struct ip6_hdr *ip6 = NULL; struct m_tag *mtag; - int pullup_len = 0; + int pullup_len = 0, off = 0; + uint8_t upper_proto = 0, is_frag = 0; int error = 0, bypass = 0; unsigned int src_if_index; + caddr_t upper_ptr = NULL; if (hook == priv->export) { /* @@ -541,6 +647,7 @@ } NGI_GET_M(item, m); + m_old = m; /* Increase counters. */ iface->info.ifinfo_packets++; @@ -569,6 +676,22 @@ } \ } while (0) +/* +#define M_HCHECK(length) do { \ + pullup_len += length + sizeof(ip6_ext); \ + if ((m)->m_pkthdr.len < (pullup_len)) { \ + pullup_len -= length + sizeof(ip6_ext); + M_CHECK(length); + ` + } \ + if ((m)->m_len < (pullup_len) && \ + (((m) = m_pullup((m),(pullup_len))) == NULL)) { \ + error = ENOBUFS; \ + goto done; \ + } \ +} while (0) +*/ + switch (iface->info.ifinfo_dlt) { case DLT_EN10MB: /* Ethernet */ { @@ -586,6 +709,33 @@ eh = mtod(m, struct ether_header *); ip = (struct ip *)(eh + 1); break; +#ifdef INET6 + case ETHERTYPE_IPV6: + /* + * This is done not to pullup mbuf twice on every ext + * header + */ + pullup_len += sizeof(struct ip6_hdr) + sizeof(struct ip6_ext); + if (m->m_pkthdr.len >= pullup_len) { + if ((m = m_pullup(m, pullup_len)) == NULL) { + error = ENOBUFS; + goto done; + } + pullup_len -= sizeof(struct ip6_ext); + } else { + pullup_len -= sizeof(struct ip6_hdr) + sizeof(struct ip6_ext); + M_CHECK(sizeof(struct ip6_hdr)); + eh = mtod(m, struct ether_header *); + ip6 = (struct ip6_hdr *)(eh + 1); + if (ip6->ip6_nxt != IPPROTO_NONE) { + error = EINVAL; + goto bypass; + } + } + eh = mtod(m, struct ether_header *); + ip6 = (struct ip6_hdr *)(eh + 1); + break; +#endif case ETHERTYPE_VLAN: { struct ether_vlan_header *evh; @@ -597,6 +747,29 @@ M_CHECK(sizeof(struct ip)); ip = (struct ip *)(evh + 1); break; + } else if (ntohs(evh->evl_proto) == ETHERTYPE_IPV6) { +#ifdef INET6 + pullup_len += sizeof(struct ip6_hdr) + sizeof(struct ip6_ext); + if (m->m_pkthdr.len >= pullup_len) { + if ((m = m_pullup(m, pullup_len)) == NULL) { + error = ENOBUFS; + goto done; + } + pullup_len -= sizeof(struct ip6_ext); + } else { + pullup_len -= sizeof(struct ip6_hdr) + sizeof(struct ip6_ext); + M_CHECK(sizeof(struct ip6_hdr)); + eh = mtod(m, struct ether_header *); + ip6 = (struct ip6_hdr *)(eh + 1); + if (ip6->ip6_nxt != IPPROTO_NONE) { + error = EINVAL; + goto bypass; + } + } + eh = mtod(m, struct ether_header *); + ip6 = (struct ip6_hdr *)(eh + 1); + break; +#endif } } default: @@ -607,18 +780,32 @@ case DLT_RAW: /* IP packets */ M_CHECK(sizeof(struct ip)); ip = mtod(m, struct ip *); +#ifdef INET6 + if (ip->ip_v == 6) { + /* IPv6 packet */ + ip = NULL; + M_CHECK(sizeof(struct ip6_hdr)); + ip6 = mtod(m, struct ip6_hdr *); + } +#endif break; default: goto bypass; break; } - if ((ip->ip_off & htons(IP_OFFMASK)) == 0) { + if ((ip != NULL) && ((ip->ip_off & htons(IP_OFFMASK)) == 0)) { + if ((ip->ip_v != IPVERSION) || + ((ip->ip_hl << 2) < sizeof(struct ip))) + goto bypass; /* - * In case of IP header with options, we haven't pulled + * In case of IPv4 header with options, we haven't pulled * up enough, yet. */ pullup_len += (ip->ip_hl << 2) - sizeof(struct ip); + off = pullup_len; + //vlog("upper_offset = %d", off); + upper_proto = ip->ip_p; switch (ip->ip_p) { case IPPROTO_TCP: @@ -627,38 +814,161 @@ case IPPROTO_UDP: M_CHECK(sizeof(struct udphdr)); break; + case IPPROTO_SCTP: + M_CHECK(sizeof(struct sctphdr)); + break; } - } + } else if (ip != NULL) { + is_frag = 1; + upper_proto = ip->ip_p; + if ((ip->ip_v != IPVERSION) || + ((ip->ip_hl << 2) < sizeof(struct ip))) + goto bypass; + } else if (ip6 != NULL) { +#ifdef INET6 + /* Check if we can export */ + if (priv->version < NETFLOW_V9) + goto bypass; + + /* Loop thru IPv6 extended headers to get upper layer header / frag */ + int cur = ip6->ip6_nxt, hdr_off = 0; + struct ip6_ext *ip6e; + struct ip6_frag *ip6f; + + off = pullup_len; + upper_proto = cur; + + if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) + goto bypass; + + while (42) { + + /* + * At the moment we're 'standing' with pullup_len and + * off + */ + switch (cur) { + + /* Ensure pointer is contiguous */ + case IPPROTO_TCP: + M_CHECK(sizeof(struct tcphdr)); + goto loopend; + case IPPROTO_UDP: + M_CHECK(sizeof(struct udphdr)); + goto loopend; + case IPPROTO_SCTP: + M_CHECK(sizeof(struct sctphdr)); + goto loopend; + + /* Loop until 'real' upper layer headers */ + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: + ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); + upper_proto = ip6e->ip6e_nxt; + hdr_off = (ip6e->ip6e_len + 1) << 3; + break; - switch (iface->info.ifinfo_dlt) { - case DLT_EN10MB: - { - struct ether_header *eh; + /* RFC4302, can be before DSTOPTS */ + case IPPROTO_AH: + ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); + upper_proto = ip6e->ip6e_nxt; + hdr_off = (ip6e->ip6e_len + 2) << 2; + break; - eh = mtod(m, struct ether_header *); - switch (ntohs(eh->ether_type)) { - case ETHERTYPE_IP: - ip = (struct ip *)(eh + 1); - break; - case ETHERTYPE_VLAN: - { - struct ether_vlan_header *evh; + + case IPPROTO_FRAGMENT: + ip6f = (struct ip6_frag *)(mtod(m, caddr_t) + off); + upper_proto = ip6f->ip6f_nxt; + hdr_off = sizeof(struct ip6_frag); + off += hdr_off; + is_frag = 1; + goto loopend; + +/* + case IPPROTO_NONE: + goto loopend; +*/ + default: + goto loopend; + } - evh = mtod(m, struct ether_vlan_header *); - ip = (struct ip *)(evh + 1); + off += hdr_off + sizeof(struct ip6_ext); + cur = upper_proto; + if (m->m_pkthdr.len >= off) { + if ((m = m_pullup(m, off)) == NULL) { + error = ENOBUFS; + goto done; + } + off -= sizeof(struct ip6_ext); + pullup_len += hdr_off; + } else { + /* + * Packet ends somewhere here. + * if its next header is not IPPROTO_NONE it is + * possibly mailformed packet. Let's count as RAW IP + */ + upper_proto = IPPROTO_NONE; + off -= sizeof(struct ip6_ext); + goto loopend; + } + + } +#endif + } + +#ifdef INET6 +loopend: +#endif + if (m != m_old) { + switch (iface->info.ifinfo_dlt) { + case DLT_EN10MB: /* Ethernet */ + { + struct ether_header *eh; + + eh = mtod(m, struct ether_header *); + switch (ntohs(eh->ether_type)) { + case ETHERTYPE_IP: + ip = (struct ip *)(eh + 1); + break; + case ETHERTYPE_IPV6: + ip6 = (struct ip6_hdr *)(eh + 1); + break; + case ETHERTYPE_VLAN: + { + struct ether_vlan_header *evh; + + evh = mtod(m, struct ether_vlan_header *); + if (ntohs(evh->evl_proto) == ETHERTYPE_IP) { + ip = (struct ip *)(evh + 1); + break; + } else if (ntohs(evh->evl_proto) == ETHERTYPE_IPV6) { +#ifdef INET6 + ip6 = (struct ip6_hdr *)(evh + 1); + break; +#endif + } + } + default: + panic("ng_netflow entered deadcode"); + } + break; + } + case DLT_RAW: /* IP packets */ + ip = mtod(m, struct ip *); +#ifdef INET6 + if (ip->ip_v == 6) { + /* IPv6 packet */ + ip = NULL; + ip6 = mtod(m, struct ip6_hdr *); + } +#endif break; - } default: panic("ng_netflow entered deadcode"); } - break; - } - case DLT_RAW: - ip = mtod(m, struct ip *); - break; - default: - panic("ng_netflow entered deadcode"); } + upper_ptr = (caddr_t)(mtod(m, caddr_t) + off); #undef M_CHECK @@ -670,7 +980,7 @@ } else src_if_index = iface->info.ifinfo_index; - error = ng_netflow_flow_add(priv, ip, src_if_index); + error = ng_netflow_flow_add(priv, (ip != NULL) ? (caddr_t)ip : (caddr_t)ip6, upper_ptr, upper_proto, is_frag, src_if_index); bypass: if (out != NULL) { diff -urN sys/netgraph/_netflow.orig/netflow.h sys/netgraph/netflow/netflow.h --- sys/netgraph/_netflow.orig/netflow.h 2009-09-01 02:37:44.000000000 +0400 +++ sys/netgraph/netflow/netflow.h 2009-09-06 12:41:29.000000000 +0400 @@ -46,6 +46,7 @@ #define NETFLOW_V1 1 #define NETFLOW_V5 5 +#define NETFLOW_V9 9 struct netflow_v1_header { @@ -69,6 +70,16 @@ uint16_t pad; /* Pad to word boundary */ } __attribute__((__packed__)); +struct netflow_v9_header +{ + uint16_t version; /* NetFlow version */ + uint16_t count; /* Total number of records in packet */ + uint32_t sys_uptime; /* System uptime */ + uint32_t unix_secs; /* Current seconds since 0000 UTC 1970 */ + uint32_t seq_num; /* Sequence number */ + uint32_t source_id; /* Observation Domain id */ +} __attribute__((__packed__)); + struct netflow_v1_record { uint32_t src_addr; /* Source IP address */ @@ -115,6 +126,142 @@ uint16_t pad2; /* Pad to word boundary */ } __attribute__((__packed__)); + + +/* RFC3954 field definitions */ +#define NETFLOW_V9_FIELD_IN_BYTES 1 /* Input bytes count for a flow. Default 4, can be 8 */ +#define NETFLOW_V9_FIELD_IN_PKTS 2 /* Incoming counter with number of packets associated with an IP Flow. Default 4 */ +#define NETFLOW_V9_FIELD_FLOWS 3 /* Number of Flows that were aggregated. Default 4 */ +#define NETFLOW_V9_FIELD_PROTOCOL 4 /* IP protocol byte. 1 */ +#define NETFLOW_V9_FIELD_TOS 5 /* Type of service byte setting when entering the incoming interface. 1 */ +#define NETFLOW_V9_FIELD_TCP_FLAGS 6 /* TCP flags; cumulative of all the TCP flags seen in this Flow. 1 */ +#define NETFLOW_V9_FIELD_L4_SRC_PORT 7 /* TCP/UDP source port number. 2 */ +#define NETFLOW_V9_FIELD_IPV4_SRC_ADDR 8 /* IPv4 source address. 4 */ +#define NETFLOW_V9_FIELD_SRC_MASK 9 /* The number of contiguous bits in the source subnet mask (i.e., the mask in slash notation). 1 */ +#define NETFLOW_V9_FIELD_INPUT_SNMP 10 /* Input interface index. Default 2 */ +#define NETFLOW_V9_FIELD_L4_DST_PORT 11 /* TCP/UDP destination port number. 2 */ +#define NETFLOW_V9_FIELD_IPV4_DST_ADDR 12 /* IPv4 destination address. 4 */ +#define NETFLOW_V9_FIELD_DST_MASK 13 /* The number of contiguous bits in the destination subnet mask (i.e., the mask in slash notation). 1 */ +#define NETFLOW_V9_FIELD_OUTPUT_SNMP 14 /* Output interface index. Default 2 */ +#define NETFLOW_V9_FIELD_IPV4_NEXT_HOP 15 /* IPv4 address of the next-hop router. 4 */ +#define NETFLOW_V9_FIELD_SRC_AS 16 /* Source BGP autonomous system number. Default 2, can be 4 */ +#define NETFLOW_V9_FIELD_DST_AS 17 /* Destination BGP autonomous system number. Default 2, can be 4 */ +#define NETFLOW_V9_FIELD_BGP_IPV4_NEXT_HOP 18 /* Next-hop router's IP address in the BGP domain. 4 */ +#define NETFLOW_V9_FIELD_MUL_DST_PKTS 19 /* IP multicast outgoing packet counter for packets associated with IP flow. Default 4 */ +#define NETFLOW_V9_FIELD_MUL_DST_BYTES 20 /* IP multicast outgoing Octet (byte) counter for the number of bytes associated with IP flow. Default 4 */ +#define NETFLOW_V9_FIELD_LAST_SWITCHED 21 /* sysUptime in msec at which the last packet of this Flow was switched. 4 */ +#define NETFLOW_V9_FIELD_FIRST_SWITCHED 22 /* sysUptime in msec at which the first packet of this Flow was switched. 4 */ +#define NETFLOW_V9_FIELD_OUT_BYTES 23 /* Outgoing counter for the number of bytes associated with an IP Flow. Default 4 */ +#define NETFLOW_V9_FIELD_OUT_PKTS 24 /* Outgoing counter for the number of packets associated with an IP Flow. Default 4 */ +#define NETFLOW_V9_FIELD_IPV6_SRC_ADDR 27 /* IPv6 source address. 16 */ +#define NETFLOW_V9_FIELD_IPV6_DST_ADDR 28 /* IPv6 destination address. 16 */ +#define NETFLOW_V9_FIELD_IPV6_SRC_MASK 29 /* Length of the IPv6 source mask in contiguous bits. 1 */ +#define NETFLOW_V9_FIELD_IPV6_DST_MASK 30 /* Length of the IPv6 destination mask in contiguous bits. 1 */ +#define NETFLOW_V9_FIELD_IPV6_FLOW_LABEL 31 /* IPv6 flow label as per RFC 2460 definition. 3 */ +#define NETFLOW_V9_FIELD_ICMP_TYPE 32 /* Internet Control Message Protocol (ICMP) packet type; reported as ICMP Type * 256 + ICMP code. 2 */ +#define NETFLOW_V9_FIELD_MUL_IGMP_TYPE 33 /* Internet Group Management Protocol (IGMP) packet type. 1 */ +#define NETFLOW_V9_FIELD_SAMPLING_INTERVAL 34 /* When using sampled NetFlow, the rate at which packets are sampled; for example, a value of 100 indicates that one of every hundred packets is sampled. 4 */ +#define NETFLOW_V9_FIELD_SAMPLING_ALGORITHM 35 /* For sampled NetFlow platform-wide: 0x01 deterministic sampling 0x02 random sampling. 1 */ +#define NETFLOW_V9_FIELD_FLOW_ACTIVE_TIMEOUT 36 /* Timeout value (in seconds) for active flow entries in the NetFlow cache. 2 */ +#define NETFLOW_V9_FIELD_FLOW_INACTIVE_TIMEOUT 37 /* Timeout value (in seconds) for inactive Flow entries in the NetFlow cache. 2 */ +#define NETFLOW_V9_FIELD_ENGINE_TYPE 38 /* Type of Flow switching engine (route processor, linecard, etc...). 1 */ +#define NETFLOW_V9_FIELD_ENGINE_ID 39 /* ID number of the Flow switching engine. 1 */ +#define NETFLOW_V9_FIELD_TOTAL_BYTES_EXP 40 /* Counter with for the number of bytes exported by the Observation Domain. Default 4 */ +#define NETFLOW_V9_FIELD_TOTAL_PKTS_EXP 41 /* Counter with for the number of packets exported by the Observation Domain. Default 4 */ +#define NETFLOW_V9_FIELD_TOTAL_FLOWS_EXP 42 /* Counter with for the number of flows exported by the Observation Domain. Default 4 */ +#define NETFLOW_V9_FIELD_MPLS_TOP_LABEL_TYPE 46 /* MPLS Top Label Type. 1 */ +#define NETFLOW_V9_FIELD_MPLS_TOP_LABEL_IP_ADDR 47 /* Forwarding Equivalent Class corresponding to the MPLS Top Label. 4 */ +#define NETFLOW_V9_FIELD_FLOW_SAMPLER_ID 48 /* Identifier shown in "show flow-sampler". 1 */ +#define NETFLOW_V9_FIELD_FLOW_SAMPLER_MODE 49 /* The type of algorithm used for sampling data. 2 */ +#define NETFLOW_V9_FIELD_FLOW_SAMPLER_RANDOM_INTERVAL 50 /* Packet interval at which to sample. 4. */ +#define NETFLOW_V9_FIELD_DST_TOS 55 /* Type of Service byte setting when exiting outgoing interface. 1. */ +#define NETFLOW_V9_FIELD_SRC_MAC 56 /* Source MAC Address. 6 */ +#define NETFLOW_V9_FIELD_DST_MAC 57 /* Destination MAC Address. 6 */ +#define NETFLOW_V9_FIELD_SRC_VLAN 58 /* Virtual LAN identifier associated with ingress interface. 2 */ +#define NETFLOW_V9_FIELD_DST_VLAN 59 /* irtual LAN identifier associated with egress interface. 2 */ +#define NETFLOW_V9_FIELD_IP_PROTOCOL_VERSION 60 /* Internet Protocol Version. Set to 4 for IPv4, set to 6 for IPv6. If not present in the template, then version 4 is assumed. 1. */ +#define NETFLOW_V9_FIELD_DIRECTION 61 /* Flow direction: 0 - ingress flow 1 - egress flow. 1 */ +#define NETFLOW_V9_FIELD_IPV6_NEXT_HOP 62 /* IPv6 address of the next-hop router. 16 */ +#define NETFLOW_V9_FIELD_BGP_IPV6_NEXT_HOP 63 /* Next-hop router in the BGP domain. 16 */ +#define NETFLOW_V9_FIELD_IPV6_OPTION_HEADERS 64 /* Bit-encoded field identifying IPv6 option headers found in the flow */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_1 70 /* MPLS label at position 1 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_2 71 /* MPLS label at position 2 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_3 72 /* MPLS label at position 3 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_4 73 /* MPLS label at position 4 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_5 74 /* MPLS label at position 5 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_6 75 /* MPLS label at position 6 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_7 76 /* MPLS label at position 7 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_8 77 /* MPLS label at position 8 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_9 78 /* MPLS label at position 9 in the stack. 3 */ +#define NETFLOW_V9_FIELD_MPLS_LABEL_10 79 /* MPLS label at position 10 in the stack. 3 */ + +#ifdef COUNTERS_64 +#define cntr uint64_t +#define cntr_max UINT64_MAX +#else +#define cntr uint32_t +#define cntr_max UINT_MAX +#endif + +struct netflow_v9_template +{ + int field_id; + int field_length; +}; + +/* Template ID for tcp/udp v4 streams ID:257 (0x100 + NETFLOW_V9_FLOW_V4_L4) */ +struct netflow_v9_record_ipv4_tcp +{ + uint32_t src_addr; /* Source IPv4 address (IPV4_SRC_ADDR) */ + uint32_t dst_addr; /* Destination IPv4 address (IPV4_DST_ADDR) */ + uint32_t next_hop; /* Next hop IPv4 address (IPV4_NEXT_HOP) */ + uint16_t i_ifx; /* Source interface index (INPUT_SNMP) */ + uint16_t o_ifx; /* Destination interface index (OUTPUT_SNMP) */ + cntr i_packets; /* Number of incoming packets in a flow (IN_PKTS) */ + cntr i_octets; /* Number of incoming octets in a flow (IN_BYTES) */ + cntr o_packets; /* Number of outgoing packets in a flow (OUT_PKTS) */ + cntr o_octets; /* Number of outgoing octets in a flow (OUT_BYTES) */ + uint32_t first; /* System uptime at start of a flow (FIRST_SWITCHED) */ + uint32_t last; /* System uptime at end of a flow (LAST_SWITCHED) */ + uint16_t s_port; /* Source port (L4_SRC_PORT) */ + uint16_t d_port; /* Destination port (L4_DST_PORT) */ + uint8_t flags; /* Cumulative OR of tcp flags (TCP_FLAGS) */ + uint8_t prot; /* IP protocol */ + uint8_t tos; /* IP type of service IN (or OUT) (TOS) */ + uint32_t src_as; /* Src peer/origin Autonomous System (SRC_AS) */ + uint32_t dst_as; /* Dst peer/origin Autonomous System (DST_AS) */ + uint8_t src_mask; /* Source route's mask bits (SRC_MASK) */ + uint8_t dst_mask; /* Destination route's mask bits (DST_MASK) */ +} __attribute__((__packed__)); + + +/* Template ID for tcp/udp v6 streams ID: 260 (0x100 + NETFLOW_V9_FLOW_V6_L4) */ +struct netflow_v9_record_ipv6_tcp +{ + struct in6_addr src_addr; /* Source IPv6 address (IPV6_SRC_ADDR) */ + struct in6_addr dst_addr; /* Destination IPv6 address (IPV6_DST_ADDR) */ + struct in6_addr next_hop; /* Next hop IPv6 address (IPV6_NEXT_HOP) */ + uint16_t i_ifx; /* Source interface index (INPUT_SNMP) */ + uint16_t o_ifx; /* Destination interface index (OUTPUT_SNMP) */ + cntr i_packets; /* Number of incoming packets in a flow (IN_PKTS) */ + cntr i_octets; /* Number of incoming octets in a flow (IN_BYTES) */ + cntr o_packets; /* Number of outgoing packets in a flow (OUT_PKTS) */ + cntr o_octets; /* Number of outgoing octets in a flow (OUT_BYTES) */ + uint32_t first; /* System uptime at start of a flow (FIRST_SWITCHED) */ + uint32_t last; /* System uptime at end of a flow (LAST_SWITCHED) */ + uint16_t s_port; /* Source port (L4_SRC_PORT) */ + uint16_t d_port; /* Destination port (L4_DST_PORT) */ + uint8_t flags; /* Cumulative OR of tcp flags (TCP_FLAGS) */ + uint8_t prot; /* IP protocol */ + uint8_t tos; /* IP type of service IN (or OUT) (TOS) */ + uint32_t src_as; /* Src peer/origin Autonomous System (SRC_AS) */ + uint32_t dst_as; /* Dst peer/origin Autonomous System (DST_AS) */ + uint8_t src_mask; /* Source route's mask bits (SRC_MASK) */ + uint8_t dst_mask; /* Destination route's mask bits (DST_MASK) */ +} __attribute__((__packed__)); + +#define vlog(x, ...) log(LOG_DEBUG, "%s:%d " x "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__) + #define NETFLOW_V1_MAX_RECORDS 24 #define NETFLOW_V5_MAX_RECORDS 30 @@ -123,7 +270,50 @@ #define NETFLOW_V5_MAX_SIZE (sizeof(netflow_v5_header)+ \ sizeof(netflow_v5_record)*NETFLOW_V5_MAX_RECORDS) +#define BASE_MTU 1500 +#define MIN_MTU sizeof(struct netflow_v5_header) +#define MAX_MTU 16384 +#define NETFLOW_V9_MAX_SIZE _NETFLOW_V9_MAX_SIZE(BASE_MTU) +#define _NETFLOW_V9_MAX_SIZE(x) (x) - sizeof(struct ip6_hdr) - sizeof(struct udphdr) +#define NETFLOW_V9_MAX_FLOWSETS 2 + +#define NETFLOW_V9_MAX_RECORD_SIZE sizeof(struct netflow_v9_record_ipv6_tcp) +#define NETFLOW_V9_MAX_PACKETS_TEMPL 500 /* Send data templates every ... packets */ +#define NETFLOW_V9_MAX_TIME_TEMPL 600 /* Send data templates every ... seconds */ +#define NETFLOW_V9_MAX_TEMPLATES 16 /* Not a real value */ +#define _NETFLOW_V9_TEMPLATE_SIZE(x) (sizeof(x) / sizeof(struct netflow_v9_template)) * 4 +//#define _NETFLOW_V9_TEMPLATE_SIZE(x) ((x) + 1) * 4 + +/* Flow Templates */ +#define NETFLOW_V9_FLOW_V4_L4 1 /* IPv4 TCP/UDP packet */ +#define NETFLOW_V9_FLOW_V4_ICMP 2 /* IPv4 ICMP packet, currently unused */ +#define NETFLOW_V9_FLOW_V4_L3 3 /* IPv4 IP packet */ +#define NETFLOW_V9_FLOW_V6_L4 4 /* IPv6 TCP/UDP packet */ +#define NETFLOW_V9_FLOW_V6_ICMP 5 /* IPv6 ICMP packet, currently unused */ +#define NETFLOW_V9_FLOW_V6_L3 6 /* IPv6 IP packet */ + +#define NETFLOW_V9_FLOW_FAKE 65535 /* Not uset used in real flowsets! */ + struct netflow_v5_export_dgram { struct netflow_v5_header header; struct netflow_v5_record r[NETFLOW_V5_MAX_RECORDS]; } __attribute__((__packed__)); + +struct netflow_v9_export_dgram { + struct netflow_v9_header header; + char data; /* MTU can change, record length is dynamic */ +}; + +struct netflow_v9_flowset_header { + uint16_t id; /* FlowSet id */ + uint16_t length; /* FlowSet length */ +} __attribute__((__packed__)); + +struct netflow_v9_mbuf_tag { + struct m_tag tag; /* pointer to mbuf tag */ + uint16_t length; /* Current packet length */ + uint16_t count; /* current records count */ + uint16_t mtu; /* max MTU shapshot */ + uint16_t flow_type; /* current flowset */ + uint16_t flow_header; /* offset pointing to current flow header */ +}; diff -urN sys/netgraph/_netflow.orig/ng_netflow.h sys/netgraph/netflow/ng_netflow.h --- sys/netgraph/_netflow.orig/ng_netflow.h 2009-09-01 02:37:44.000000000 +0400 +++ sys/netgraph/netflow/ng_netflow.h 2009-09-06 16:36:17.000000000 +0400 @@ -51,6 +51,9 @@ NGM_NETFLOW_SETIFINDEX = 5, /* set interface index */ NGM_NETFLOW_SETTIMEOUTS = 6, /* set active/inactive flow timeouts */ NGM_NETFLOW_SETCONFIG = 7, /* set flow generation options */ + NGM_NETFLOW_SETVERSION = 8, /* set flow export version */ + NGM_NETFLOW_SETTEMPLATEPERIODIC = 9, /* set v9 flow template periodic */ + NGM_NETFLOW_SETMTU = 10, /* set outgoing interface MTU */ }; /* This structure is returned by the NGM_NETFLOW_INFO message */ @@ -105,10 +108,34 @@ u_int32_t conf; /* new config */ }; +/* This structure is passed to NGM_NETFLOW_SETVERSION */ +struct ng_netflow_setversion { + u_int8_t version; /* netflow version */ +}; + +/* This structure is passed to NGM_NETFLOW_SETTEMPLATEPERIODIC */ +struct ng_netflow_settemplateperiodic { + u_int16_t time; /* max time between announce */ + u_int16_t packets; /* max packets between announce */ +}; + +/* This structure is passed to NGM_NETFLOW_SETMTU */ +struct ng_netflow_setmtu { + u_int16_t mtu; /* MTU for packet */ +}; + /* This is unique data, which identifies flow */ struct flow_rec { - struct in_addr r_src; - struct in_addr r_dst; + uint16_t flow_type; /* IPv4 L4/L3 Ipv6 L4/L3 flow, see NETFLOW_V9_FLOW* */ + uint16_t ether_proto; /* unused */ + union { + struct in_addr r_src; + struct in6_addr r_src6; + } src; + union { + struct in_addr r_dst; + struct in6_addr r_dst6; + } dst; union { struct { uint16_t s_port; /* source TCP/UDP port */ @@ -137,7 +164,10 @@ /* A flow entry which accumulates statistics */ struct flow_entry_data { struct flow_rec r; - struct in_addr next_hop; + union { + struct in_addr next_hop; + struct in6_addr next_hop6; + } n; uint16_t fle_o_ifx; /* output interface index */ #define fle_i_ifx r.misc.i.i_ifx uint8_t dst_mask; /* destination route mask bits */ @@ -146,6 +176,7 @@ u_long bytes; long first; /* uptime on first packet */ long last; /* uptime on last packet */ + uint16_t proto; /* IP/IPv6 */ u_char tcp_flags; /* cumulative OR */ }; @@ -227,6 +258,26 @@ { NULL } \ } +/* Parse the setversion structure */ +#define NG_NETFLOW_SETVERSION_TYPE { \ + { "version", &ng_parse_uint8_type }, \ + { NULL } \ +} + +/* Parse the settemplateperiodic structure */ +#define NG_NETFLOW_SETTEMPLATEPERIODIC_TYPE { \ + { "time", &ng_parse_uint16_type }, \ + { "packets", &ng_parse_uint16_type }, \ + { NULL } \ +} + +/* Parse the setmtu structure */ +#define NG_NETFLOW_SETMTU_TYPE { \ + { "mtu", &ng_parse_uint16_type }, \ + { NULL } \ +} + + /* Private hook data */ struct ng_netflow_iface { hook_p hook; /* NULL when disconnected */ @@ -270,6 +321,20 @@ item_p export_item; struct mtx export_mtx; uint32_t flow_seq; /* current flow sequence */ + /* + * RFC 3954 clause 7.3 + * "Both options MUST be configurable by the user on the Exporter." + */ + uint16_t templ_time; /* time between sending templates */ + uint16_t templ_packets; /* packets between sending templates */ + uint32_t templ_last_ts; /* unixtime of last template announce */ + uint32_t templ_last_pkt; /* packets count on last template announce */ + uint32_t sent_packets; /* packets sent by exporeter; can be reached another way ? */ + u_char version; /* Netflow export version */ + u_char flowsets_count; /* current flowsets used */ + u_char flowset_records[NETFLOW_V9_MAX_FLOWSETS - 1]; /* Count of records in each flowset */ + uint16_t mtu; /* export interface MTU */ + struct netflow_v9_flowset_header *v9_flowsets[NETFLOW_V9_MAX_FLOWSETS - 1]; /* Pointers to pre-compiled flowsets */ struct ng_netflow_iface ifaces[NG_NETFLOW_MAXIFACES]; }; @@ -286,14 +351,15 @@ #define MTAG_NETFLOW 1221656444 #define MTAG_NETFLOW_CALLED 0 +#define MTAG_NETFLOW_V9 1 /* Prototypes for netflow.c */ int ng_netflow_cache_init(priv_p); void ng_netflow_cache_flush(priv_p); void ng_netflow_copyinfo(priv_p, struct ng_netflow_info *); timeout_t ng_netflow_expire; -int ng_netflow_flow_add(priv_p, struct ip *, unsigned int src_if_index); +int ng_netflow_flow_add(priv_p, caddr_t ip_ptr, caddr_t upper_ptr, uint8_t upper_proto, uint8_t is_frag, unsigned int src_if_index); int ng_netflow_flow_show(priv_p, uint32_t last, struct ng_mesg *); - +void ng_netflow_switch_version(priv_p priv, int ver, int boot); #endif /* _KERNEL */ #endif /* _NG_NETFLOW_H_ */ diff -urN sys/modules/netgraph/netflow/Makefile.orig sys/modules/netgraph/netflow/Makefile --- sys/modules/netgraph/netflow/Makefile.orig 2009-09-06 16:31:18.000000000 +0400 +++ sys/modules/netgraph/netflow/Makefile 2009-09-06 16:33:32.000000000 +0400 @@ -3,9 +3,19 @@ # Author: Gleb Smirnoff # +.include + .PATH: ${.CURDIR}/../../../netgraph/netflow KMOD= ng_netflow -SRCS= ng_netflow.c netflow.c +SRCS= ng_netflow.c netflow.c opt_inet6.h + +.if !defined(KERNBUILDDIR) + +.if ${MK_INET6_SUPPORT} != "no" +opt_inet6.h: + echo "#define INET6 1" > ${.TARGET} +.endif +.endif .include From grarpamp at gmail.com Sun Sep 6 22:31:53 2009 From: grarpamp at gmail.com (grarpamp) Date: Sun Sep 6 22:32:05 2009 Subject: Call for testers: ng_netflow with v9 and IPv6 support Message-ID: Wouldn't it be better to support the obvious formal emergent standards track protocol instead of the legacy informational one? Or to perform both via sysctl or other arguments/defines, with the standard IPFIX being the default mode? Have you reviewed the nProbe code for other various ideas? Thanks for working on netflow :) And any form of v6 is good to have finally. ========================================================================= Network Working Group B. Claise, Ed. Request for Comments: 5101 Cisco Systems, Inc. Category: Standards Track January 2008 Specification of the IP Flow Information Export (IPFIX) Protocol for the Exchange of IP Traffic Flow Information This document specifies an Internet standards track protocol for the Internet community Network Working Group J. Quittek Request for Comments: 5102 NEC Category: Standards Track S. Bryant B. Claise P. Aitken Cisco Systems, Inc. J. Meyer PayPal January 2008 Information Model for IP Flow Information Export This document specifies an Internet standards track protocol for the Internet community Network Working Group B. Trammell Request for Comments: 5103 CERT/NetSA Category: Standards Track E. Boschi Hitachi Europe January 2008 Bidirectional Flow Export Using IP Flow Information Export (IPFIX) This document specifies an Internet standards track protocol for the Internet community Network Working Group E. Boschi Request for Comments: 5153 Hitachi Europe Category: Informational L. Mark Fraunhofer FOKUS J. Quittek M. Stiemerling NEC P. Aitken Cisco Systems, Inc. April 2008 IP Flow Information Export (IPFIX) Implementation Guidelines This memo provides information for the Internet community. It does not specify an Internet standard of any kind. Network Working Group S. Leinen Request for Comments: 3955 SWITCH Category: Informational October 2004 Evaluation of Candidate Protocols for IP Flow Information Export (IPFIX) This memo provides information for the Internet community. It does not specify an Internet standard of any kind. Network Working Group J. Quittek Request for Comments: 3917 NEC Europe Ltd. Category: Informational T. Zseby Fraunhofer FOKUS B. Claise Cisco Systems S. Zander Swinburne University October 2004 Requirements for IP Flow Information Export (IPFIX) This memo provides information for the Internet community. It does not specify an Internet standard of any kind. And others... ========================================================================= Network Working Group B. Claise, Ed. Request for Comments: 3954 Cisco Systems Category: Informational October 2004 Cisco Systems NetFlow Services Export Version 9 This RFC documents the NetFlow services export protocol Version 9 as it was when submitted to the IETF as a basis for further work in the IPFIX WG. This RFC itself is not a candidate for any level of Internet Standard. The IETF disclaims any knowledge of the fitness of this RFC for any purpose... And others... ========================================================================= From julian at elischer.org Mon Sep 7 01:37:00 2009 From: julian at elischer.org (Julian Elischer) Date: Mon Sep 7 01:37:08 2009 Subject: Call for testers: ng_netflow with v9 and IPv6 support In-Reply-To: References: Message-ID: <4AA46103.9000108@elischer.org> grarpamp wrote: > Wouldn't it be better to support the obvious formal emergent standards > track protocol instead of the legacy informational one? Or to perform > both via sysctl or other arguments/defines, with the standard IPFIX > being the default mode? Have you reviewed the nProbe code for other > various ideas? Thanks for working on netflow :) And any form of v6 > is good to have finally. if you have time it sounds like you know enough about it to be useful.. get involved :-) > > ========================================================================= > > Network Working Group B. Claise, Ed. > Request for Comments: 5101 Cisco Systems, Inc. > Category: Standards Track January 2008 > Specification of the IP Flow Information Export (IPFIX) Protocol > for the Exchange of IP Traffic Flow Information > This document specifies an Internet standards track protocol for the > Internet community > > > Network Working Group J. Quittek > Request for Comments: 5102 NEC > Category: Standards Track S. Bryant > B. Claise > P. Aitken > Cisco Systems, Inc. > J. Meyer > PayPal > January 2008 > Information Model for IP Flow Information Export > This document specifies an Internet standards track protocol for the > Internet community > > > Network Working Group B. Trammell > Request for Comments: 5103 CERT/NetSA > Category: Standards Track E. Boschi > Hitachi Europe > January 2008 > Bidirectional Flow Export Using IP Flow Information Export (IPFIX) > This document specifies an Internet standards track protocol for the > Internet community > > > Network Working Group E. Boschi > Request for Comments: 5153 Hitachi Europe > Category: Informational L. Mark > Fraunhofer FOKUS > J. Quittek > M. Stiemerling > NEC > P. Aitken > Cisco Systems, Inc. > April 2008 > IP Flow Information Export (IPFIX) Implementation Guidelines > This memo provides information for the Internet community. It does > not specify an Internet standard of any kind. > > > Network Working Group S. Leinen > Request for Comments: 3955 SWITCH > Category: Informational October 2004 > Evaluation of Candidate Protocols for IP Flow Information Export (IPFIX) > This memo provides information for the Internet community. It does > not specify an Internet standard of any kind. > > > Network Working Group J. Quittek > Request for Comments: 3917 NEC Europe Ltd. > Category: Informational T. Zseby > Fraunhofer FOKUS > B. Claise > Cisco Systems > S. Zander > Swinburne University > October 2004 > Requirements for IP Flow Information Export (IPFIX) > This memo provides information for the Internet community. It does > not specify an Internet standard of any kind. > > > And others... > > ========================================================================= > > Network Working Group B. Claise, Ed. > Request for Comments: 3954 Cisco Systems > Category: Informational October 2004 > Cisco Systems NetFlow Services Export Version 9 > This RFC documents the NetFlow services export protocol Version 9 as > it was when submitted to the IETF as a basis for further work in the > IPFIX WG. > This RFC itself is not a candidate for any level of Internet > Standard. The IETF disclaims any knowledge of the fitness of this > RFC for any purpose... > > And others... > > ========================================================================= > _______________________________________________ > freebsd-net@freebsd.org mailing list > http://lists.freebsd.org/mailman/listinfo/freebsd-net > To unsubscribe, send any mail to "freebsd-net-unsubscribe@freebsd.org" From dyr at homelink.ru Mon Sep 7 07:55:07 2009 From: dyr at homelink.ru (Dennis Yusupoff) Date: Mon Sep 7 07:55:14 2009 Subject: Call for testers: ng_netflow with v9 and IPv6 support In-Reply-To: <4AA46103.9000108@elischer.org> References: <4AA46103.9000108@elischer.org> Message-ID: <1116202669.20090907115455@homelink.ru> By the way, what's about this one: --- The ng_netflow node type does not fill in AS numbers. This is due to the lack of necessary information in the kernel routing table. However, this information can be injected into the kernel from a routing daemon such as GNU Zebra. This functionality may become available in future releases. --- Is any chance to see it in, for example, 8.0? -- ? ?????????, ????????? ????????????? Ozerki.Net/Cifracom.Ru ?????? ????? mailto:dyr@homelink.ru From peterjeremy at acm.org Mon Sep 7 21:03:34 2009 From: peterjeremy at acm.org (Peter Jeremy) Date: Mon Sep 7 21:03:46 2009 Subject: Call for testers: ng_netflow with v9 and IPv6 support In-Reply-To: <1116202669.20090907115455@homelink.ru> References: <4AA46103.9000108@elischer.org> <1116202669.20090907115455@homelink.ru> Message-ID: <20090907195636.GB35275@server.vk2pj.dyndns.org> On 2009-Sep-07 11:54:55 +0400, Dennis Yusupoff wrote: >By the way, what's about this one: >--- > The ng_netflow node type does not fill in AS numbers. This is due to the ... >--- > >Is any chance to see it in, for example, 8.0? New feature cutoff for 8.0 was several months ago. Once the code exists, it may be a candidate for inclusion in a future 8.x release. -- Peter Jeremy -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 196 bytes Desc: not available Url : http://lists.freebsd.org/pipermail/freebsd-isp/attachments/20090907/88198fc6/attachment.pgp From vandj at securenet.net Tue Sep 8 16:15:15 2009 From: vandj at securenet.net (Jean M. Vandette) Date: Tue Sep 8 16:15:21 2009 Subject: Looking for News feed Message-ID: <20090908155910.8ABE3C2E5@mail.securenet.net> Greetings all Seems that both our upstream providers who were feeding us news had retired there news machines, as a result we have a news server which is not getting any feeds. Would there be anyone out there that can offer us full news feed Thanks in advance Regards Jean M. Vandette ************************************************************************************* *SecureNet Information Services Inc. 100 Alexis Nihon Blvd., Suite 283* *(514) 744-4242 Vox (514) 744-1552 Fax St. Laurent, Quebec H4M 2N7* ********** Providing Quality Public Internet access since 1994*************** From melifaro at ipfw.ru Tue Sep 8 18:39:37 2009 From: melifaro at ipfw.ru (Alexander V. Chernikov) Date: Tue Sep 8 18:39:46 2009 Subject: Call for testers: ng_netflow with v9 and IPv6 support In-Reply-To: References: Message-ID: <4AA6A4C8.4090200@ipfw.ru> grarpamp wrote: > Wouldn't it be better to support the obvious formal emergent standards > track protocol instead of the legacy informational one? Or to perform > both via sysctl or other arguments/defines, with the standard IPFIX > being the default mode? Have you reviewed the nProbe code for other > various ideas? Thanks for working on netflow :) And any form of v6 > is good to have finally. > Thanks for pointing out those RFCs. We're still using v5 for accounting, so my behavior was quite straightforward: can v5 count ipv6 ? No, what's next netflow version can ? v9? Ok, let's implement v9. However, IPFIX seems to be very much like v9 at a first glance. Moreover, ng_ksocket can handle udp/tcp/sctp exports. > ========================================================================= > > Network Working Group B. Claise, Ed. > Request for Comments: 5101 Cisco Systems, Inc. > Category: Standards Track January 2008 > Specification of the IP Flow Information Export (IPFIX) Protocol > for the Exchange of IP Traffic Flow Information > This document specifies an Internet standards track protocol for the > Internet community > > > Network Working Group J. Quittek > Request for Comments: 5102 NEC > Category: Standards Track S. Bryant > B. Claise > P. Aitken > Cisco Systems, Inc. > J. Meyer > PayPal > January 2008 > Information Model for IP Flow Information Export > This document specifies an Internet standards track protocol for the > Internet community > > > Network Working Group B. Trammell > Request for Comments: 5103 CERT/NetSA > Category: Standards Track E. Boschi > Hitachi Europe > January 2008 > Bidirectional Flow Export Using IP Flow Information Export (IPFIX) > This document specifies an Internet standards track protocol for the > Internet community > > > Network Working Group E. Boschi > Request for Comments: 5153 Hitachi Europe > Category: Informational L. Mark > Fraunhofer FOKUS > J. Quittek > M. Stiemerling > NEC > P. Aitken > Cisco Systems, Inc. > April 2008 > IP Flow Information Export (IPFIX) Implementation Guidelines > This memo provides information for the Internet community. It does > not specify an Internet standard of any kind. > > > Network Working Group S. Leinen > Request for Comments: 3955 SWITCH > Category: Informational October 2004 > Evaluation of Candidate Protocols for IP Flow Information Export (IPFIX) > This memo provides information for the Internet community. It does > not specify an Internet standard of any kind. > > > Network Working Group J. Quittek > Request for Comments: 3917 NEC Europe Ltd. > Category: Informational T. Zseby > Fraunhofer FOKUS > B. Claise > Cisco Systems > S. Zander > Swinburne University > October 2004 > Requirements for IP Flow Information Export (IPFIX) > This memo provides information for the Internet community. It does > not specify an Internet standard of any kind. > > > And others... > > ========================================================================= > > Network Working Group B. Claise, Ed. > Request for Comments: 3954 Cisco Systems > Category: Informational October 2004 > Cisco Systems NetFlow Services Export Version 9 > This RFC documents the NetFlow services export protocol Version 9 as > it was when submitted to the IETF as a basis for further work in the > IPFIX WG. > This RFC itself is not a candidate for any level of Internet > Standard. The IETF disclaims any knowledge of the fitness of this > RFC for any purpose... > > And others... > > ========================================================================= > _______________________________________________ > freebsd-net@freebsd.org mailing list > http://lists.freebsd.org/mailman/listinfo/freebsd-net > To unsubscribe, send any mail to "freebsd-net-unsubscribe@freebsd.org" > From grarpamp at gmail.com Wed Sep 9 02:05:15 2009 From: grarpamp at gmail.com (grarpamp) Date: Wed Sep 9 02:05:21 2009 Subject: Call for testers: ng_netflow with v9 and IPv6 support In-Reply-To: <4AA6A4C8.4090200@ipfw.ru> References: <4AA6A4C8.4090200@ipfw.ru> Message-ID: > Thanks for pointing out those RFCs. Sure. There are more I probably missed. Search rfc-editor or ietf for netflow or ipfix. > can v5 count ipv6 ? No, what's next netflow version can ? v9? Ok, let's > implement v9. Yep, ipv6 is becoming really important, definitely on backbones. nProbe has had it for a while. nProbe is a bpf flow sniffer and exporter. I don't know about any other open source ones that do, besides your patch :) > However, IPFIX seems to be very much like v9 at a first glance. v9 was the first that came out, mostly a defacto cisco proprietary thing as was v5. Netflow needed to be opened up / standardized. So ipfix group was started. cisco v9 was submitted to ietf ipfix working group as one of proposed ipfix methods. v9 was then modified in the group a bit and called IPFIX. Some of this is in rfc 3955. Ipfix is probably the way to go. Of course others on the lists could input whether they see more of v9 or ipfix now and in future. From riya.web14 at gmail.com Tue Sep 15 10:17:52 2009 From: riya.web14 at gmail.com (Nishant) Date: Tue Sep 15 10:23:34 2009 Subject: Affordable SEO, Link Building, Logo Designing, Web Designing & Web Development Services Message-ID: <046d01ca35ec$16df7e20$0301a8c0@officefcdbc080> Subject: Affordable SEO,Link Building,Logo Designing, Web Designing & Web Development Services. Hi, Greetings of the Day! We are a fast growing company based in India with primary focus on Website Designing,Web Development ( ASP.Net, Java ,Perl and PHP development) & SEO Services. We have a dedicated team of 35 professionals to serve you in building appropriate links and developing SEO. We can provide you with a fresh, professional image via a recognizable trademark or logo design. Our experts work to tight deadlines and are wholly dedicated to maintaining your online presence. We have our competency in CMS (Joomla, Modx, Mambo and other quality Content Management System) and e-commerce website. Most firms overseas have achieved a significant amount of savings by outsourcing either complete or part of their work to us in India. Other Affordable Search Engine Optomization Services Provided By Us: 1. Website Design & Development 2. SEO (Search Engine Optimization) 3. Link Building 4. Press Release 5. Article Submission/Article Writing 6. Blog Submissions/Writing 7. Directory Submission 8. Social Media Optimization. SEO SERVICES: We have launched our Christmas SEO packages on discounted prices. Avail the benefit of our discounted packages to boost up your business. Take advantage of our highly successful internet marketing service & software development work at very affordable price. If you have any query, we will be more than happy to provide you our quick assistance. Kind Regards, Name: Riya Alternate Email ID: Contact.seo1@yahoo.in Designation:Sales Executive. Country:India Note: We are not spammers and are against spamming of any kind. If you are not interested then you can reply with a simple \"NO\", We will never contact you again. From oleksandr at samoylyk.net Mon Sep 21 16:50:09 2009 From: oleksandr at samoylyk.net (Oleksandr Samoylyk) Date: Mon Sep 21 16:50:16 2009 Subject: Looking for News feed In-Reply-To: References: Message-ID: <4AB7AC07.1050401@samoylyk.net> On 9/8/2009 7:15 PM Jean M. Vandette wrote: > Greetings all > > Seems that both our upstream providers who were feeding us news had > retired there > news machines, as a result we have a news server which is not getting > any feeds. > Would there be anyone out there that can offer us full news feed > Hi Jean, Drop a line to : news.admin.peering You can also establish a nntp-peering with our text-only news server (details on http://news.telesweet.net/). Have a nice day! -- Oleksandr Samoylyk OVS-RIPE From bakul at bitblocks.com Mon Sep 28 19:10:08 2009 From: bakul at bitblocks.com (Bakul Shah) Date: Mon Sep 28 19:10:15 2009 Subject: file/backup server Message-ID: <20090928185157.7412A5B2C@mail.bitblocks.com> Not sure what is the right group for this kind of query but perhaps people on this group has had similar need. I'd like to give freebsd guys a first shot hence a freebsd group. I am looking for a vendor who will supply a small number of fileservers but mainly for backing up a few TB of data (exact number not yet known). These will be used in various sites. I am envisioning one or two zfs based system. One is on all the time and provides some shared storage and at least nightly backups. The other is used to sync with the first one and but not used otherwise -- used sort of like a tape drive (since they don't seem to scale to TB size backups without spending lots of money). zfs since it will allow me to grow storage seamlessly and raidz2 ought to be good enough. Other ideas are possible. If you know of a vendor or are one, please contact me privately. Please include me in the cc list in case you wish to reply to the group. Thanks! -- bakul From jhs at berklix.com Mon Sep 28 20:03:48 2009 From: jhs at berklix.com (Julian H. Stacey) Date: Mon Sep 28 20:04:00 2009 Subject: file/backup server In-Reply-To: Your message "Mon, 28 Sep 2009 11:51:57 PDT." <20090928185157.7412A5B2C@mail.bitblocks.com> Message-ID: <200909282005.n8SK5ccc073343@fire.js.berklix.net> Bakul Shah wrote: > Not sure what is the right group for this kind of query but > perhaps people on this group has had similar need. I'd like > to give freebsd guys a first shot hence a freebsd group. > > I am looking for a vendor who will supply a small > number of fileservers but mainly for backing up a few TB of > data (exact number not yet known). These will be used in > various sites. I am envisioning one or two zfs based system. > One is on all the time and provides some shared storage and > at least nightly backups. The other is used to sync with the > first one and but not used otherwise -- used sort of like a > tape drive (since they don't seem to scale to TB size backups > without spending lots of money). zfs since it will allow me > to grow storage seamlessly and raidz2 ought to be good > enough. Other ideas are possible. If you know of a vendor or > are one, please contact me privately. Please include me in > the cc list in case you wish to reply to the group. > > Thanks! Rod Grimes used to sell custom BSDboxes I recall. Various other people too. probably some are on hardware@ Also see http://www.freebsd.org/commercial/hardware.html For renting servers in Europe eg np@bsn.com &/or mwe@consol.de (bcc'd so they wont drown in follow up :-). &/or http://www.freebsd.org/commercial/isp.html Also http://www.freebsd.org/commercial/commercial.html Cheers, Julian -- Julian Stacey: BSD Unix Linux C Sys Eng Consultants Munich http://berklix.com Mail ASCII plain text not HTML & Base64. http://asciiribbon.org Virused Microsoft PCs cause spam. http://berklix.com/free/