svn commit: r223787 - head/sys/netgraph/netflow

Gleb Smirnoff glebius at FreeBSD.org
Tue Jul 5 14:48:40 UTC 2011


Author: glebius
Date: Tue Jul  5 14:48:39 2011
New Revision: 223787
URL: http://svn.freebsd.org/changeset/base/223787

Log:
  o Eliminate flow6_hash_entry in favor of flow_hash_entry. We don't need
    a separate struct to start a slist of semi-opaque structs. This
    makes some code more compact.
  o Rewrite ng_netflow_flow_show() and its API/ABI:
    - Support for IPv6 is added.
    - Request and response now use same struct. Structure specifies
      version (6 or 4), index of last retrieved hash, and also index
      of last retrieved entry in the hash entry.

Modified:
  head/sys/netgraph/netflow/netflow.c
  head/sys/netgraph/netflow/ng_netflow.c
  head/sys/netgraph/netflow/ng_netflow.h

Modified: head/sys/netgraph/netflow/netflow.c
==============================================================================
--- head/sys/netgraph/netflow/netflow.c	Tue Jul  5 14:12:48 2011	(r223786)
+++ head/sys/netgraph/netflow/netflow.c	Tue Jul  5 14:48:39 2011	(r223787)
@@ -100,7 +100,7 @@ static int export_send(priv_p, fib_expor
 
 static int hash_insert(priv_p, struct flow_hash_entry *, struct flow_rec *, int, uint8_t);
 #ifdef INET6
-static int hash6_insert(priv_p, struct flow6_hash_entry *, struct flow6_rec *, int, uint8_t);
+static int hash6_insert(priv_p, struct flow_hash_entry *, struct flow6_rec *, int, uint8_t);
 #endif
 
 static __inline void expire_flow(priv_p, fib_export_p, struct flow_entry *, int);
@@ -412,7 +412,7 @@ hash_insert(priv_p priv, struct flow_has
 				bitcount32((x).__u6_addr.__u6_addr32[3])
 /* XXX: Do we need inline here ? */
 static __inline int
-hash6_insert(priv_p priv, struct flow6_hash_entry *hsh6, struct flow6_rec *r,
+hash6_insert(priv_p priv, struct flow_hash_entry *hsh6, struct flow6_rec *r,
 	int plen, uint8_t tcp_flags)
 {
 	struct flow6_entry *fle6;
@@ -491,7 +491,7 @@ hash6_insert(priv_p priv, struct flow6_h
 	}
 
 	/* Push new flow at the and of hash. */
-	TAILQ_INSERT_TAIL(&hsh6->head, fle6, fle6_hash);
+	TAILQ_INSERT_TAIL(&hsh6->head, (struct flow_entry *)fle6, fle_hash);
 
 	return (0);
 }
@@ -507,9 +507,6 @@ void
 ng_netflow_cache_init(priv_p priv)
 {
 	struct flow_hash_entry *hsh;
-#ifdef INET6	
-	struct flow6_hash_entry *hsh6;
-#endif
 	int i;
 
 	/* Initialize cache UMA zone. */
@@ -534,13 +531,13 @@ ng_netflow_cache_init(priv_p priv)
 
 #ifdef INET6
 	/* Allocate hash. */
-	priv->hash6 = malloc(NBUCKETS * sizeof(struct flow6_hash_entry),
+	priv->hash6 = malloc(NBUCKETS * sizeof(struct flow_hash_entry),
 	    M_NETFLOW_HASH, M_WAITOK | M_ZERO);
 
 	/* Initialize hash. */
-	for (i = 0, hsh6 = priv->hash6; i < NBUCKETS; i++, hsh6++) {
-		mtx_init(&hsh6->mtx, "hash mutex", NULL, MTX_DEF);
-		TAILQ_INIT(&hsh6->head);
+	for (i = 0, hsh = priv->hash6; i < NBUCKETS; i++, hsh++) {
+		mtx_init(&hsh->mtx, "hash mutex", NULL, MTX_DEF);
+		TAILQ_INIT(&hsh->head);
 	}
 #endif
 
@@ -588,10 +585,6 @@ ng_netflow_cache_flush(priv_p priv)
 {
 	struct flow_entry	*fle, *fle1;
 	struct flow_hash_entry	*hsh;
-#ifdef INET6
-	struct flow6_entry	*fle6, *fle61;
-	struct flow6_hash_entry	*hsh6;
-#endif
 	struct netflow_export_item exp;
 	fib_export_p fe;
 	int i;
@@ -610,11 +603,11 @@ ng_netflow_cache_flush(priv_p priv)
 			expire_flow(priv, fe, fle, NG_QUEUE);
 		}
 #ifdef INET6
-	for (hsh6 = priv->hash6, i = 0; i < NBUCKETS; hsh6++, i++)
-		TAILQ_FOREACH_SAFE(fle6, &hsh6->head, fle6_hash, fle61) {
-			TAILQ_REMOVE(&hsh6->head, fle6, fle6_hash);
-			fe = priv_to_fib(priv, fle6->f.r.fib);
-			expire_flow(priv, fe, (struct flow_entry *)fle6, NG_QUEUE);
+	for (hsh = priv->hash6, i = 0; i < NBUCKETS; hsh++, i++)
+		TAILQ_FOREACH_SAFE(fle, &hsh->head, fle_hash, fle1) {
+			TAILQ_REMOVE(&hsh->head, fle, fle_hash);
+			fe = priv_to_fib(priv, fle->f.r.fib);
+			expire_flow(priv, fe, fle, NG_QUEUE);
 		}
 #endif
 
@@ -629,8 +622,8 @@ ng_netflow_cache_flush(priv_p priv)
 #ifdef INET6
 	uma_zdestroy(priv->zone6);
 	/* Destroy hash mutexes. */
-	for (i = 0, hsh6 = priv->hash6; i < NBUCKETS; i++, hsh6++)
-		mtx_destroy(&hsh6->mtx);
+	for (i = 0, hsh = priv->hash6; i < NBUCKETS; i++, hsh++)
+		mtx_destroy(&hsh->mtx);
 
 	/* Free hash memory. */
 	if (priv->hash6 != NULL)
@@ -790,8 +783,9 @@ int
 ng_netflow_flow6_add(priv_p priv, fib_export_p fe, struct ip6_hdr *ip6, caddr_t upper_ptr, uint8_t upper_proto, 
 		uint8_t is_frag, unsigned int src_if_index)
 {
-	register struct flow6_entry	*fle6 = NULL, *fle61;
-	struct flow6_hash_entry		*hsh6;
+	register struct flow_entry	*fle = NULL, *fle1;
+	register struct flow6_entry	*fle6;
+	struct flow_hash_entry		*hsh;
 	struct flow6_rec		r;
 	int			plen;
 	int			error = 0;
@@ -846,9 +840,9 @@ ng_netflow_flow6_add(priv_p priv, fib_ex
 	priv->info.nfinfo_bytes6 += plen;
 
 	/* Find hash slot. */
-	hsh6 = &priv->hash6[ip6_hash(&r)];
+	hsh = &priv->hash6[ip6_hash(&r)];
 
-	mtx_lock(&hsh6->mtx);
+	mtx_lock(&hsh->mtx);
 
 	/*
 	 * Go through hash and find our entry. If we encounter an
@@ -856,19 +850,22 @@ ng_netflow_flow6_add(priv_p priv, fib_ex
 	 * search since most active entries are first, and most
 	 * searches are done on most active entries.
 	 */
-	TAILQ_FOREACH_REVERSE_SAFE(fle6, &hsh6->head, f6head, fle6_hash, fle61) {
-		if (fle6->f.version != IP6VERSION)
+	TAILQ_FOREACH_REVERSE_SAFE(fle, &hsh->head, fhead, fle_hash, fle1) {
+		if (fle->f.version != IP6VERSION)
 			continue;
+		fle6 = (struct flow6_entry *)fle;
 		if (bcmp(&r, &fle6->f.r, sizeof(struct flow6_rec)) == 0)
 			break;
 		if ((INACTIVE(fle6) && SMALL(fle6)) || AGED(fle6)) {
-			TAILQ_REMOVE(&hsh6->head, fle6, fle6_hash);
-			expire_flow(priv, priv_to_fib(priv, fle6->f.r.fib), (struct flow_entry *)fle6, NG_QUEUE);
+			TAILQ_REMOVE(&hsh->head, fle, fle_hash);
+			expire_flow(priv, priv_to_fib(priv, fle->f.r.fib), fle,
+			    NG_QUEUE);
 			atomic_add_32(&priv->info.nfinfo_act_exp, 1);
 		}
 	}
 
-	if (fle6 != NULL) {			/* An existent entry. */
+	if (fle != NULL) {			/* An existent entry. */
+		fle6 = (struct flow6_entry *)fle;
 
 		fle6->f.bytes += plen;
 		fle6->f.packets ++;
@@ -883,8 +880,9 @@ ng_netflow_flow6_add(priv_p priv, fib_ex
 		 */
 		if (tcp_flags & TH_FIN || tcp_flags & TH_RST || AGED(fle6) ||
 		    (fle6->f.bytes >= (CNTR_MAX - IF_MAXMTU)) ) {
-			TAILQ_REMOVE(&hsh6->head, fle6, fle6_hash);
-			expire_flow(priv, priv_to_fib(priv, fle6->f.r.fib), (struct flow_entry *)fle6, NG_QUEUE);
+			TAILQ_REMOVE(&hsh->head, fle, fle_hash);
+			expire_flow(priv, priv_to_fib(priv, fle->f.r.fib), fle,
+			    NG_QUEUE);
 			atomic_add_32(&priv->info.nfinfo_act_exp, 1);
 		} else {
 			/*
@@ -892,15 +890,15 @@ ng_netflow_flow6_add(priv_p priv, fib_ex
 			 * if it isn't there already. Next search will
 			 * locate it quicker.
 			 */
-			if (fle6 != TAILQ_LAST(&hsh6->head, f6head)) {
-				TAILQ_REMOVE(&hsh6->head, fle6, fle6_hash);
-				TAILQ_INSERT_TAIL(&hsh6->head, fle6, fle6_hash);
+			if (fle != TAILQ_LAST(&hsh->head, fhead)) {
+				TAILQ_REMOVE(&hsh->head, fle, fle_hash);
+				TAILQ_INSERT_TAIL(&hsh->head, fle, fle_hash);
 			}
 		}
 	} else				/* A new flow entry. */
-		error = hash6_insert(priv, hsh6, &r, plen, tcp_flags);
+		error = hash6_insert(priv, hsh, &r, plen, tcp_flags);
 
-	mtx_unlock(&hsh6->mtx);
+	mtx_unlock(&hsh->mtx);
 
 	return (error);
 }
@@ -910,64 +908,107 @@ ng_netflow_flow6_add(priv_p priv, fib_ex
  * Return records from cache to userland.
  *
  * TODO: matching particular IP should be done in kernel, here.
- * XXX: IPv6 flows will return random data
  */
 int
-ng_netflow_flow_show(priv_p priv, uint32_t last, struct ng_mesg *resp)
+ng_netflow_flow_show(priv_p priv, struct ngnf_show_header *req,
+struct ngnf_show_header *resp)
 {
 	struct flow_hash_entry	*hsh;
 	struct flow_entry	*fle;
-	struct ngnf_flows	*data;
-	int	i;
+	struct flow_entry_data	*data = (struct flow_entry_data *)(resp + 1);
+#ifdef INET6
+	struct flow6_entry_data	*data6 = (struct flow6_entry_data *)(resp + 1);
+#endif
+	int	i, max;
 
-	data = (struct ngnf_flows *)resp->data;
-	data->last = 0;
-	data->nentries = 0;
-
-	/* Check if this is a first run */
-	if (last == 0) {
-		hsh = priv->hash;
-		i = 0;
-	} else {
-		if (last > NBUCKETS-1)
-			return (EINVAL);
-		hsh = priv->hash + last;
-		i = last;
-	}
+	i = req->hash_id;
+	if (i > NBUCKETS-1)
+		return (EINVAL);
+
+#ifdef INET6
+	if (req->version == 6) {
+		resp->version = 6;
+		hsh = priv->hash6 + i;
+		max = NREC6_AT_ONCE;
+	} else
+#endif
+	if (req->version == 4) {
+		resp->version = 4;
+		hsh = priv->hash + i;
+		max = NREC_AT_ONCE;
+	} else
+		return (EINVAL);
 
 	/*
 	 * We will transfer not more than NREC_AT_ONCE. More data
 	 * will come in next message.
-	 * We send current hash index to userland, and userland should
-	 * return it back to us. Then, we will restart with new entry.
+	 * We send current hash index and current record number in list 
+	 * to userland, and userland should return it back to us. 
+	 * Then, we will restart with new entry.
 	 *
-	 * The resulting cache snapshot is inaccurate for the
-	 * following reasons:
-	 *  - we skip locked hash entries
-	 *  - we bail out, if someone wants our entry
-	 *  - we skip rest of entry, when hit NREC_AT_ONCE
+	 * The resulting cache snapshot can be inaccurate if flow expiration
+	 * is taking place on hash item between userland data requests for 
+	 * this hash item id.
 	 */
+	resp->nentries = 0;
 	for (; i < NBUCKETS; hsh++, i++) {
-		if (mtx_trylock(&hsh->mtx) == 0)
-			continue;
+		int list_id;
+
+		if (mtx_trylock(&hsh->mtx) == 0) {
+			/* 
+			 * Requested hash index is not available,
+			 * relay decision to skip or re-request data
+			 * to userland.
+			 */
+			resp->hash_id = i;
+			resp->list_id = 0;
+			return (0);
+		}
 
+		list_id = 0;
 		TAILQ_FOREACH(fle, &hsh->head, fle_hash) {
-			if (hsh->mtx.mtx_lock & MTX_CONTESTED)
-				break;
+			if (hsh->mtx.mtx_lock & MTX_CONTESTED) {
+				resp->hash_id = i;
+				resp->list_id = list_id;
+				return (0);
+			}
+
+			list_id++;
+			/* Search for particular record in list. */
+			if (req->list_id > 0) {
+				if (list_id < req->list_id)
+					continue;
 
-			bcopy(&fle->f, &(data->entries[data->nentries]),
-			    sizeof(fle->f));
-			data->nentries++;
-			if (data->nentries == NREC_AT_ONCE) {
-				mtx_unlock(&hsh->mtx);
-				if (++i < NBUCKETS)
-					data->last = i;
+				/* Requested list position found. */
+				req->list_id = 0;
+			}
+#ifdef INET6
+			if (req->version == 6) {
+				struct flow6_entry *fle6;
+
+				fle6 = (struct flow6_entry *)fle;
+				bcopy(&fle6->f, data6 + resp->nentries,
+				    sizeof(fle6->f));
+			} else
+#endif
+				bcopy(&fle->f, data + resp->nentries,
+				    sizeof(fle->f));
+			resp->nentries++;
+			if (resp->nentries == max) {
+				resp->hash_id = i;
+				/* 
+				 * If it was the last item in list
+				 * we simply skip to next hash_id.
+				 */
+				resp->list_id = list_id + 1;
 				return (0);
 			}
 		}
 		mtx_unlock(&hsh->mtx);
 	}
 
+	resp->hash_id = resp->list_id = 0;
+
 	return (0);
 }
 
@@ -1057,10 +1098,6 @@ ng_netflow_expire(void *arg)
 {
 	struct flow_entry	*fle, *fle1;
 	struct flow_hash_entry	*hsh;
-#ifdef INET6	
-	struct flow6_entry	*fle6, *fle61;
-	struct flow6_hash_entry	*hsh6;
-#endif	
 	priv_p			priv = (priv_p )arg;
 	uint32_t		used;
 	int			i;
@@ -1103,20 +1140,23 @@ ng_netflow_expire(void *arg)
 	}
 
 #ifdef INET6
-	for (hsh6 = priv->hash6, i = 0; i < NBUCKETS; hsh6++, i++) {
+	for (hsh = priv->hash6, i = 0; i < NBUCKETS; hsh++, i++) {
+		struct flow6_entry	*fle6;
+
 		/*
 		 * Skip entries, that are already being worked on.
 		 */
-		if (mtx_trylock(&hsh6->mtx) == 0)
+		if (mtx_trylock(&hsh->mtx) == 0)
 			continue;
 
 		used = atomic_load_acq_32(&priv->info.nfinfo_used6);
-		TAILQ_FOREACH_SAFE(fle6, &hsh6->head, fle6_hash, fle61) {
+		TAILQ_FOREACH_SAFE(fle, &hsh->head, fle_hash, fle1) {
+			fle6 = (struct flow6_entry *)fle;
 			/*
 			 * Interrupt thread wants this entry!
 			 * Quick! Quick! Bail out!
 			 */
-			if (hsh6->mtx.mtx_lock & MTX_CONTESTED)
+			if (hsh->mtx.mtx_lock & MTX_CONTESTED)
 				break;
 
 			/*
@@ -1128,13 +1168,14 @@ ng_netflow_expire(void *arg)
 
 			if ((INACTIVE(fle6) && (SMALL(fle6) ||
 			    (used > (NBUCKETS*2)))) || AGED(fle6)) {
-				TAILQ_REMOVE(&hsh6->head, fle6, fle6_hash);
-				expire_flow(priv, priv_to_fib(priv, fle6->f.r.fib), (struct flow_entry *)fle6, NG_NOFLAGS);
+				TAILQ_REMOVE(&hsh->head, fle, fle_hash);
+				expire_flow(priv, priv_to_fib(priv,
+				    fle->f.r.fib), fle, NG_NOFLAGS);
 				used--;
 				atomic_add_32(&priv->info.nfinfo_inact_exp, 1);
 			}
 		}
-		mtx_unlock(&hsh6->mtx);
+		mtx_unlock(&hsh->mtx);
 	}
 #endif
 

Modified: head/sys/netgraph/netflow/ng_netflow.c
==============================================================================
--- head/sys/netgraph/netflow/ng_netflow.c	Tue Jul  5 14:12:48 2011	(r223786)
+++ head/sys/netgraph/netflow/ng_netflow.c	Tue Jul  5 14:48:39 2011	(r223787)
@@ -504,19 +504,20 @@ ng_netflow_rcvmsg (node_p node, item_p i
 		}
 		case NGM_NETFLOW_SHOW:
 		{
-			uint32_t *last;
-
-			if (msg->header.arglen != sizeof(uint32_t))
+			if (msg->header.arglen != sizeof(struct ngnf_show_header))
 				ERROUT(EINVAL);
 
-			last = (uint32_t *)msg->data;
-
 			NG_MKRESPONSE(resp, msg, NGRESP_SIZE, M_NOWAIT);
 
 			if (!resp)
 				ERROUT(ENOMEM);
 
-			error = ng_netflow_flow_show(priv, *last, resp);
+			error = ng_netflow_flow_show(priv,
+			    (struct ngnf_show_header *)msg->data,
+			    (struct ngnf_show_header *)resp->data);
+
+			if (error)
+				NG_FREE_MSG(resp);
 
 			break;
 		}

Modified: head/sys/netgraph/netflow/ng_netflow.h
==============================================================================
--- head/sys/netgraph/netflow/ng_netflow.h	Tue Jul  5 14:12:48 2011	(r223786)
+++ head/sys/netgraph/netflow/ng_netflow.h	Tue Jul  5 14:48:39 2011	(r223787)
@@ -33,7 +33,7 @@
 #define	_NG_NETFLOW_H_
 
 #define NG_NETFLOW_NODE_TYPE	"netflow"
-#define NGM_NETFLOW_COOKIE	1299079728
+#define NGM_NETFLOW_COOKIE	1309868867
 
 #define	NG_NETFLOW_MAXIFACES	USHRT_MAX
 
@@ -133,6 +133,19 @@ struct ng_netflow_setmtu {
 	uint16_t mtu;		/* MTU for packet */
 };
 
+/* This structure is used in NGM_NETFLOW_SHOW request/responce */
+struct ngnf_show_header {
+	u_char		version;	/* IPv4 or IPv6 */
+	uint32_t	hash_id;	/* current hash index */
+	uint32_t	list_id;	/* current record number in given hash */
+	uint32_t	nentries;	/* number of records in response */
+};
+
+/* XXXGL
+ * Somewhere flow_rec6 is casted to flow_rec, and flow6_entry_data is
+ * casted to flow_entry_data. After casting, fle->r.fib is accessed.
+ * So beginning of these structs up to fib should be kept common.
+ */
 
 /* This is unique data, which identifies flow */
 struct flow_rec {
@@ -233,29 +246,24 @@ struct flow6_entry_data {
  * without overflowing socket receive buffer
  */
 #define NREC_AT_ONCE		1000
-#define NGRESP_SIZE		(sizeof(struct ngnf_flows) + (NREC_AT_ONCE * \
+#define NREC6_AT_ONCE		(NREC_AT_ONCE * sizeof(struct flow_entry_data) / \
+				sizeof(struct flow6_entry_data))
+#define NGRESP_SIZE		(sizeof(struct ngnf_show_header) + (NREC_AT_ONCE * \
 				sizeof(struct flow_entry_data)))
 #define SORCVBUF_SIZE		(NGRESP_SIZE + 2 * sizeof(struct ng_mesg))
 
-/* This struct is returned to userland, when "show cache ip flow" */
-struct ngnf_flows {
-	uint32_t		nentries;
-	uint32_t		last;
-	struct flow_entry_data	entries[0];
-};
-
 /* Everything below is for kernel */
 
 #ifdef _KERNEL
 
 struct flow_entry {
-	struct flow_entry_data	f;
 	TAILQ_ENTRY(flow_entry)	fle_hash;	/* entries in hash slot */
+	struct flow_entry_data	f;
 };
 
 struct flow6_entry {
+	TAILQ_ENTRY(flow_entry)	fle_hash;	/* entries in hash slot */
 	struct flow6_entry_data	f;
-	TAILQ_ENTRY(flow6_entry)	fle6_hash;	/* entries in hash slot */
 };
 /* Parsing declarations */
 
@@ -402,7 +410,7 @@ struct netflow {
 	/* IPv6 support */
 #ifdef INET6
 	uma_zone_t		zone6;
-	struct flow6_hash_entry	*hash6;
+	struct flow_hash_entry	*hash6;
 #endif
 	/* Multiple FIB support */
 	fib_export_p		fib_data[RT_NUMFIBS]; /* array of pointers to fib-specific data */
@@ -430,11 +438,6 @@ struct flow_hash_entry {
 	TAILQ_HEAD(fhead, flow_entry) head;
 };
 
-struct flow6_hash_entry {
-	struct mtx		mtx;
-	TAILQ_HEAD(f6head, flow6_entry) head;
-};
-
 #define	ERROUT(x)	{ error = (x); goto done; }
 
 #define MTAG_NETFLOW		1221656444
@@ -465,7 +468,7 @@ void	ng_netflow_copyinfo(priv_p, struct 
 timeout_t ng_netflow_expire;
 int 	ng_netflow_flow_add(priv_p, fib_export_p, struct ip *, caddr_t, uint8_t, uint8_t, unsigned int);
 int	ng_netflow_flow6_add(priv_p, fib_export_p, struct ip6_hdr *, caddr_t , uint8_t, uint8_t, unsigned int);
-int	ng_netflow_flow_show(priv_p, uint32_t last, struct ng_mesg *);
+int	ng_netflow_flow_show(priv_p, struct ngnf_show_header *req, struct ngnf_show_header *resp);
 
 void	ng_netflow_v9_cache_init(priv_p);
 void	ng_netflow_v9_cache_flush(priv_p);


More information about the svn-src-all mailing list