xref: /illumos-gate/usr/src/uts/common/ipp/flowacct/flowacct.c (revision 89ce534e7c3d320328321dbabb60ddda2a6a0e20)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
56d730a61Svi117747  * Common Development and Distribution License (the "License").
66d730a61Svi117747  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
216d730a61Svi117747 
227c478bd9Sstevel@tonic-gate /*
236d730a61Svi117747  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
307c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
317c478bd9Sstevel@tonic-gate #include <sys/conf.h>
327c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
337c478bd9Sstevel@tonic-gate #include <netinet/in.h>
347c478bd9Sstevel@tonic-gate #include <netinet/in_systm.h>
357c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
367c478bd9Sstevel@tonic-gate #include <sys/socket.h>
377c478bd9Sstevel@tonic-gate #include <sys/acct.h>
387c478bd9Sstevel@tonic-gate #include <sys/exacct.h>
397c478bd9Sstevel@tonic-gate #include <inet/common.h>
407c478bd9Sstevel@tonic-gate #include <inet/ip.h>
417c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
427c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
437c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
447c478bd9Sstevel@tonic-gate #include <ipp/flowacct/flowacct_impl.h>
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate /*
477c478bd9Sstevel@tonic-gate  * flowacct - IPQoS accounting module. The module maintains an array
487c478bd9Sstevel@tonic-gate  * of 256 hash buckets. When the action routine is invoked for a flow,
497c478bd9Sstevel@tonic-gate  * if the flow (identified by the 5-tuple: saddr, daddr, sport, dport, proto)
507c478bd9Sstevel@tonic-gate  * is already present in the flow table (indexed by the hash function FLOW_HASH)
517c478bd9Sstevel@tonic-gate  * then a check is made to see if an item for this flow with the same
527c478bd9Sstevel@tonic-gate  * dsfield, projid & user id is present. If it is, then the number of packets
537c478bd9Sstevel@tonic-gate  * and the bytes are incremented for that item. If the item does
547c478bd9Sstevel@tonic-gate  * not exist a new item is added for the flow. If the flow is not present
557c478bd9Sstevel@tonic-gate  * an entry is made for the flow.
567c478bd9Sstevel@tonic-gate  *
577c478bd9Sstevel@tonic-gate  * A timer runs thru the table and writes all the flow items that have
587c478bd9Sstevel@tonic-gate  * timed out to the accounting file (via exacct PSARC/1999/119), if present
597c478bd9Sstevel@tonic-gate  * Configuration commands to change the timing interval is provided. The
607c478bd9Sstevel@tonic-gate  * flow timeout value can also be configured. While the timeout is in nsec,
617c478bd9Sstevel@tonic-gate  * the flow timer interval is in usec.
627c478bd9Sstevel@tonic-gate  * Information for an active flow can be obtained by using kstats.
637c478bd9Sstevel@tonic-gate  */
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate /* Used in computing the hash index */
667c478bd9Sstevel@tonic-gate #define	FLOWACCT_ADDR_HASH(addr) 			\
677c478bd9Sstevel@tonic-gate 	((addr).s6_addr8[8] ^ (addr).s6_addr8[9] ^ 	\
687c478bd9Sstevel@tonic-gate 	(addr).s6_addr8[10] ^ (addr).s6_addr8[13] ^ 	\
697c478bd9Sstevel@tonic-gate 	(addr).s6_addr8[14] ^ (addr).s6_addr8[15])
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate #define	FLOWACCT_FLOW_HASH(f)				\
727c478bd9Sstevel@tonic-gate 	(((FLOWACCT_ADDR_HASH(f->saddr)) + 		\
737c478bd9Sstevel@tonic-gate 	(FLOWACCT_ADDR_HASH(f->daddr)) + 		\
747c478bd9Sstevel@tonic-gate 	(f->proto) + (f->sport) + (f->dport)) 		\
757c478bd9Sstevel@tonic-gate 	% FLOW_TBL_COUNT)
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate /*
787c478bd9Sstevel@tonic-gate  * Compute difference between a and b in nsec and store in delta.
797c478bd9Sstevel@tonic-gate  * delta should be a hrtime_t. Taken from ip_mroute.c.
807c478bd9Sstevel@tonic-gate  */
817c478bd9Sstevel@tonic-gate #define	FLOWACCT_DELTA(a, b, delta) { \
827c478bd9Sstevel@tonic-gate 	int xxs; \
837c478bd9Sstevel@tonic-gate  \
847c478bd9Sstevel@tonic-gate 	delta = (a).tv_nsec - (b).tv_nsec; \
857c478bd9Sstevel@tonic-gate 	if ((xxs = (a).tv_sec - (b).tv_sec) != 0) { \
867c478bd9Sstevel@tonic-gate 		switch (xxs) { \
877c478bd9Sstevel@tonic-gate 		case 2: \
887c478bd9Sstevel@tonic-gate 		    delta += NANOSEC; \
897c478bd9Sstevel@tonic-gate 		    /*FALLTHRU*/ \
907c478bd9Sstevel@tonic-gate 		case 1: \
917c478bd9Sstevel@tonic-gate 		    delta += NANOSEC; \
927c478bd9Sstevel@tonic-gate 		    break; \
937c478bd9Sstevel@tonic-gate 		default: \
947c478bd9Sstevel@tonic-gate 		    delta += ((hrtime_t)NANOSEC * xxs); \
957c478bd9Sstevel@tonic-gate 		} \
967c478bd9Sstevel@tonic-gate 	} \
977c478bd9Sstevel@tonic-gate }
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate /* Debug level */
1007c478bd9Sstevel@tonic-gate int flowacct_debug = 0;
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate /* Collect timed out flows to be written to the accounting file */
1037c478bd9Sstevel@tonic-gate typedef struct flow_records_s {
1047c478bd9Sstevel@tonic-gate 	flow_usage_t *fl_use;
1057c478bd9Sstevel@tonic-gate 	struct flow_records_s *next;
1067c478bd9Sstevel@tonic-gate }flow_records_t;
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate /* Get port information from the packet. Ignore fragments. */
1097c478bd9Sstevel@tonic-gate static void
1107c478bd9Sstevel@tonic-gate flowacct_port_info(header_t *header, void *iph, int af, mblk_t *mp)
1117c478bd9Sstevel@tonic-gate {
1127c478bd9Sstevel@tonic-gate 	uint16_t *up;
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate 	if (af == AF_INET) {
1157c478bd9Sstevel@tonic-gate 		ipha_t *ipha = (ipha_t *)iph;
1167c478bd9Sstevel@tonic-gate 		uint32_t u2, u1;
1177c478bd9Sstevel@tonic-gate 		uint_t iplen;
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 		u2 = ntohs(ipha->ipha_fragment_offset_and_flags);
1207c478bd9Sstevel@tonic-gate 		u1 = u2 & (IPH_MF | IPH_OFFSET);
1217c478bd9Sstevel@tonic-gate 		if (u1 != 0) {
1227c478bd9Sstevel@tonic-gate 			return;
1237c478bd9Sstevel@tonic-gate 		}
1247c478bd9Sstevel@tonic-gate 		iplen = (ipha->ipha_version_and_hdr_length & 0xF) << 2;
1257c478bd9Sstevel@tonic-gate 		up = (uint16_t *)(mp->b_rptr + iplen);
1267c478bd9Sstevel@tonic-gate 		header->sport = (uint16_t)*up++;
1277c478bd9Sstevel@tonic-gate 		header->dport = (uint16_t)*up;
1287c478bd9Sstevel@tonic-gate 	} else {
1297c478bd9Sstevel@tonic-gate 		ip6_t *ip6h = (ip6_t *)iph;
1307c478bd9Sstevel@tonic-gate 		uint_t  length = IPV6_HDR_LEN;
1317c478bd9Sstevel@tonic-gate 		uint_t  ehdrlen;
1327c478bd9Sstevel@tonic-gate 		uint8_t *nexthdrp, *whereptr, *endptr;
1337c478bd9Sstevel@tonic-gate 		ip6_dest_t *desthdr;
1347c478bd9Sstevel@tonic-gate 		ip6_rthdr_t *rthdr;
1357c478bd9Sstevel@tonic-gate 		ip6_hbh_t *hbhhdr;
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 		whereptr = ((uint8_t *)&ip6h[1]);
1387c478bd9Sstevel@tonic-gate 		endptr = mp->b_wptr;
1397c478bd9Sstevel@tonic-gate 		nexthdrp = &ip6h->ip6_nxt;
1407c478bd9Sstevel@tonic-gate 		while (whereptr < endptr) {
1417c478bd9Sstevel@tonic-gate 			switch (*nexthdrp) {
1427c478bd9Sstevel@tonic-gate 			case IPPROTO_HOPOPTS:
1437c478bd9Sstevel@tonic-gate 				hbhhdr = (ip6_hbh_t *)whereptr;
1447c478bd9Sstevel@tonic-gate 				ehdrlen = 8 * (hbhhdr->ip6h_len + 1);
1457c478bd9Sstevel@tonic-gate 				if ((uchar_t *)hbhhdr +  ehdrlen > endptr)
1467c478bd9Sstevel@tonic-gate 					return;
1477c478bd9Sstevel@tonic-gate 				nexthdrp = &hbhhdr->ip6h_nxt;
1487c478bd9Sstevel@tonic-gate 				break;
1497c478bd9Sstevel@tonic-gate 			case IPPROTO_DSTOPTS:
1507c478bd9Sstevel@tonic-gate 				desthdr = (ip6_dest_t *)whereptr;
1517c478bd9Sstevel@tonic-gate 				ehdrlen = 8 * (desthdr->ip6d_len + 1);
1527c478bd9Sstevel@tonic-gate 				if ((uchar_t *)desthdr +  ehdrlen > endptr)
1537c478bd9Sstevel@tonic-gate 					return;
1547c478bd9Sstevel@tonic-gate 				nexthdrp = &desthdr->ip6d_nxt;
1557c478bd9Sstevel@tonic-gate 				break;
1567c478bd9Sstevel@tonic-gate 			case IPPROTO_ROUTING:
1577c478bd9Sstevel@tonic-gate 				rthdr = (ip6_rthdr_t *)whereptr;
1587c478bd9Sstevel@tonic-gate 				ehdrlen =  8 * (rthdr->ip6r_len + 1);
1597c478bd9Sstevel@tonic-gate 				if ((uchar_t *)rthdr +  ehdrlen > endptr)
1607c478bd9Sstevel@tonic-gate 					return;
1617c478bd9Sstevel@tonic-gate 				nexthdrp = &rthdr->ip6r_nxt;
1627c478bd9Sstevel@tonic-gate 				break;
1637c478bd9Sstevel@tonic-gate 			case IPPROTO_FRAGMENT:
1647c478bd9Sstevel@tonic-gate 				return;
1657c478bd9Sstevel@tonic-gate 			case IPPROTO_TCP:
1667c478bd9Sstevel@tonic-gate 			case IPPROTO_UDP:
1677c478bd9Sstevel@tonic-gate 			case IPPROTO_SCTP:
1687c478bd9Sstevel@tonic-gate 				/*
1697c478bd9Sstevel@tonic-gate 				 * Verify we have at least ICMP_MIN_TP_HDR_LEN
1707c478bd9Sstevel@tonic-gate 				 * bytes of the ULP's header to get the port
1717c478bd9Sstevel@tonic-gate 				 * info.
1727c478bd9Sstevel@tonic-gate 				 */
1737c478bd9Sstevel@tonic-gate 				if (((uchar_t *)ip6h + length +
1747c478bd9Sstevel@tonic-gate 				    ICMP_MIN_TP_HDR_LEN)  > endptr) {
1757c478bd9Sstevel@tonic-gate 					return;
1767c478bd9Sstevel@tonic-gate 				}
1777c478bd9Sstevel@tonic-gate 				/* Get the protocol & ports */
1787c478bd9Sstevel@tonic-gate 				header->proto = *nexthdrp;
1797c478bd9Sstevel@tonic-gate 				up = (uint16_t *)((uchar_t *)ip6h + length);
1807c478bd9Sstevel@tonic-gate 				header->sport = (uint16_t)*up++;
1817c478bd9Sstevel@tonic-gate 				header->dport = (uint16_t)*up;
1827c478bd9Sstevel@tonic-gate 				return;
1837c478bd9Sstevel@tonic-gate 			case IPPROTO_ICMPV6:
1847c478bd9Sstevel@tonic-gate 			case IPPROTO_ENCAP:
1857c478bd9Sstevel@tonic-gate 			case IPPROTO_IPV6:
1867c478bd9Sstevel@tonic-gate 			case IPPROTO_ESP:
1877c478bd9Sstevel@tonic-gate 			case IPPROTO_AH:
1887c478bd9Sstevel@tonic-gate 				header->proto = *nexthdrp;
1897c478bd9Sstevel@tonic-gate 				return;
1907c478bd9Sstevel@tonic-gate 			case IPPROTO_NONE:
1917c478bd9Sstevel@tonic-gate 			default:
1927c478bd9Sstevel@tonic-gate 				return;
1937c478bd9Sstevel@tonic-gate 			}
1947c478bd9Sstevel@tonic-gate 			length += ehdrlen;
1957c478bd9Sstevel@tonic-gate 			whereptr += ehdrlen;
1967c478bd9Sstevel@tonic-gate 		}
1977c478bd9Sstevel@tonic-gate 	}
1987c478bd9Sstevel@tonic-gate }
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate /*
2017c478bd9Sstevel@tonic-gate  * flowacct_find_ids(mp, header)
2027c478bd9Sstevel@tonic-gate  *
2037c478bd9Sstevel@tonic-gate  * attempt to discern the uid and projid of the originator of a packet by
2047c478bd9Sstevel@tonic-gate  * looking at the dblks making up the packet - yeuch!
2057c478bd9Sstevel@tonic-gate  *
2067c478bd9Sstevel@tonic-gate  * We do it by skipping any fragments with a credp of NULL (originated in
2077c478bd9Sstevel@tonic-gate  * kernel), taking the first value that isn't NULL to be the cred_t for the
2087c478bd9Sstevel@tonic-gate  * whole packet.
2097c478bd9Sstevel@tonic-gate  */
2107c478bd9Sstevel@tonic-gate static void
2117c478bd9Sstevel@tonic-gate flowacct_find_ids(mblk_t *mp, header_t *header)
2127c478bd9Sstevel@tonic-gate {
2137c478bd9Sstevel@tonic-gate 	cred_t *cr;
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate 	while (DB_CRED(mp) == NULL && mp->b_cont != NULL)
2167c478bd9Sstevel@tonic-gate 		mp = mp->b_cont;
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 	if ((cr = DB_CRED(mp)) != NULL) {
2197c478bd9Sstevel@tonic-gate 		header->uid = crgetuid(cr);
2207c478bd9Sstevel@tonic-gate 		header->projid = crgetprojid(cr);
2217c478bd9Sstevel@tonic-gate 	} else {
222f48205beScasper 		header->uid = (uid_t)-1;
2237c478bd9Sstevel@tonic-gate 		header->projid = -1;
2247c478bd9Sstevel@tonic-gate 	}
2257c478bd9Sstevel@tonic-gate }
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate /*
2287c478bd9Sstevel@tonic-gate  * Extract header information in a header_t structure so that we don't have
2297c478bd9Sstevel@tonic-gate  * have to parse the packet everytime.
2307c478bd9Sstevel@tonic-gate  */
2317c478bd9Sstevel@tonic-gate static int
2327c478bd9Sstevel@tonic-gate flowacct_extract_header(mblk_t *mp, header_t *header)
2337c478bd9Sstevel@tonic-gate {
2347c478bd9Sstevel@tonic-gate 	ipha_t *ipha;
2357c478bd9Sstevel@tonic-gate 	ip6_t *ip6h;
2367c478bd9Sstevel@tonic-gate #define	rptr	((uchar_t *)ipha)
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 	/* 0 means no port extracted. */
2397c478bd9Sstevel@tonic-gate 	header->sport = 0;
2407c478bd9Sstevel@tonic-gate 	header->dport = 0;
2417c478bd9Sstevel@tonic-gate 	flowacct_find_ids(mp, header);
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	V6_SET_ZERO(header->saddr);
2447c478bd9Sstevel@tonic-gate 	V6_SET_ZERO(header->daddr);
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 	ipha = (ipha_t *)mp->b_rptr;
2477c478bd9Sstevel@tonic-gate 	header->isv4 = IPH_HDR_VERSION(ipha) == IPV4_VERSION;
2487c478bd9Sstevel@tonic-gate 	if (header->isv4) {
2497c478bd9Sstevel@tonic-gate 		ipha = (ipha_t *)mp->b_rptr;
2507c478bd9Sstevel@tonic-gate 		V4_PART_OF_V6(header->saddr) = (int32_t)ipha->ipha_src;
2517c478bd9Sstevel@tonic-gate 		V4_PART_OF_V6(header->daddr) = (int32_t)ipha->ipha_dst;
2527c478bd9Sstevel@tonic-gate 		header->dsfield = ipha->ipha_type_of_service;
2537c478bd9Sstevel@tonic-gate 		header->proto = ipha->ipha_protocol;
2547c478bd9Sstevel@tonic-gate 		header->pktlen = ntohs(ipha->ipha_length);
2557c478bd9Sstevel@tonic-gate 		if ((header->proto == IPPROTO_TCP) ||
2567c478bd9Sstevel@tonic-gate 		    (header->proto == IPPROTO_UDP) ||
2577c478bd9Sstevel@tonic-gate 		    (header->proto == IPPROTO_SCTP)) {
2587c478bd9Sstevel@tonic-gate 			flowacct_port_info(header, ipha, AF_INET, mp);
2597c478bd9Sstevel@tonic-gate 		}
2607c478bd9Sstevel@tonic-gate 	} else {
2617c478bd9Sstevel@tonic-gate 		/*
2627c478bd9Sstevel@tonic-gate 		 * Need to pullup everything.
2637c478bd9Sstevel@tonic-gate 		 */
2647c478bd9Sstevel@tonic-gate 		if (mp->b_cont != NULL) {
2657c478bd9Sstevel@tonic-gate 			if (!pullupmsg(mp, -1)) {
2667c478bd9Sstevel@tonic-gate 				flowacct0dbg(("flowacct_extract_header: "\
2677c478bd9Sstevel@tonic-gate 				    "pullup error"));
2687c478bd9Sstevel@tonic-gate 				return (-1);
2697c478bd9Sstevel@tonic-gate 			}
2707c478bd9Sstevel@tonic-gate 		}
2717c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)mp->b_rptr;
2727c478bd9Sstevel@tonic-gate 		bcopy(ip6h->ip6_src.s6_addr32, header->saddr.s6_addr32,
2737c478bd9Sstevel@tonic-gate 		    sizeof (ip6h->ip6_src.s6_addr32));
2747c478bd9Sstevel@tonic-gate 		bcopy(ip6h->ip6_dst.s6_addr32, header->daddr.s6_addr32,
2757c478bd9Sstevel@tonic-gate 		    sizeof (ip6h->ip6_dst.s6_addr32));
2767c478bd9Sstevel@tonic-gate 		header->dsfield = __IPV6_TCLASS_FROM_FLOW(ip6h->ip6_vcf);
2777c478bd9Sstevel@tonic-gate 		header->proto = ip6h->ip6_nxt;
2787c478bd9Sstevel@tonic-gate 		header->pktlen = ntohs(ip6h->ip6_plen) +
2797c478bd9Sstevel@tonic-gate 		    ip_hdr_length_v6(mp, ip6h);
2807c478bd9Sstevel@tonic-gate 		flowacct_port_info(header, ip6h, AF_INET6, mp);
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	}
2837c478bd9Sstevel@tonic-gate #undef	rptr
2847c478bd9Sstevel@tonic-gate 	return (0);
2857c478bd9Sstevel@tonic-gate }
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate /* Check if the flow (identified by the 5-tuple) exists in the hash table */
2887c478bd9Sstevel@tonic-gate static flow_t *
2897c478bd9Sstevel@tonic-gate flowacct_flow_present(header_t *header, int index,
2907c478bd9Sstevel@tonic-gate     flowacct_data_t *flowacct_data)
2917c478bd9Sstevel@tonic-gate {
2927c478bd9Sstevel@tonic-gate 	list_hdr_t *hdr = flowacct_data->flows_tbl[index].head;
2937c478bd9Sstevel@tonic-gate 	flow_t *flow;
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate 	while (hdr != NULL) {
2967c478bd9Sstevel@tonic-gate 		flow = (flow_t *)hdr->objp;
2977c478bd9Sstevel@tonic-gate 		if ((flow != NULL) &&
2987c478bd9Sstevel@tonic-gate 		    (IN6_ARE_ADDR_EQUAL(&flow->saddr, &header->saddr)) &&
2997c478bd9Sstevel@tonic-gate 		    (IN6_ARE_ADDR_EQUAL(&flow->daddr, &header->daddr)) &&
3007c478bd9Sstevel@tonic-gate 		    (flow->proto == header->proto) &&
3017c478bd9Sstevel@tonic-gate 		    (flow->sport == header->sport) &&
3027c478bd9Sstevel@tonic-gate 		    (flow->dport == header->dport)) {
3037c478bd9Sstevel@tonic-gate 			return (flow);
3047c478bd9Sstevel@tonic-gate 		}
3057c478bd9Sstevel@tonic-gate 		hdr = hdr->next;
3067c478bd9Sstevel@tonic-gate 	}
3077c478bd9Sstevel@tonic-gate 	return ((flow_t *)NULL);
3087c478bd9Sstevel@tonic-gate }
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate /*
3117c478bd9Sstevel@tonic-gate  * Add an object to the list at insert_point. This could be a flow item or
3127c478bd9Sstevel@tonic-gate  * a flow itself.
3137c478bd9Sstevel@tonic-gate  */
3147c478bd9Sstevel@tonic-gate static list_hdr_t *
3157c478bd9Sstevel@tonic-gate flowacct_add_obj(list_head_t *tophdr, list_hdr_t *insert_point, void *obj)
3167c478bd9Sstevel@tonic-gate {
3177c478bd9Sstevel@tonic-gate 	list_hdr_t *new_hdr;
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 	if (tophdr == NULL) {
3207c478bd9Sstevel@tonic-gate 		return ((list_hdr_t *)NULL);
3217c478bd9Sstevel@tonic-gate 	}
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	new_hdr = (list_hdr_t *)kmem_zalloc(FLOWACCT_HDR_SZ, KM_NOSLEEP);
3247c478bd9Sstevel@tonic-gate 	if (new_hdr == NULL) {
3257c478bd9Sstevel@tonic-gate 		flowacct0dbg(("flowacct_add_obj: error allocating mem"));
3267c478bd9Sstevel@tonic-gate 		return ((list_hdr_t *)NULL);
3277c478bd9Sstevel@tonic-gate 	}
3287c478bd9Sstevel@tonic-gate 	gethrestime(&new_hdr->last_seen);
3297c478bd9Sstevel@tonic-gate 	new_hdr->objp = obj;
3307c478bd9Sstevel@tonic-gate 	tophdr->nbr_items++;
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	if (insert_point == NULL) {
3337c478bd9Sstevel@tonic-gate 		if (tophdr->head == NULL) {
3347c478bd9Sstevel@tonic-gate 			tophdr->head = new_hdr;
3357c478bd9Sstevel@tonic-gate 			tophdr->tail = new_hdr;
3367c478bd9Sstevel@tonic-gate 			return (new_hdr);
3377c478bd9Sstevel@tonic-gate 		}
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 		new_hdr->next = tophdr->head;
3407c478bd9Sstevel@tonic-gate 		tophdr->head->prev = new_hdr;
3417c478bd9Sstevel@tonic-gate 		tophdr->head = new_hdr;
3427c478bd9Sstevel@tonic-gate 		return (new_hdr);
3437c478bd9Sstevel@tonic-gate 	}
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	if (insert_point == tophdr->tail) {
3467c478bd9Sstevel@tonic-gate 		tophdr->tail->next = new_hdr;
3477c478bd9Sstevel@tonic-gate 		new_hdr->prev = tophdr->tail;
3487c478bd9Sstevel@tonic-gate 		tophdr->tail = new_hdr;
3497c478bd9Sstevel@tonic-gate 		return (new_hdr);
3507c478bd9Sstevel@tonic-gate 	}
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 	new_hdr->next = insert_point->next;
3537c478bd9Sstevel@tonic-gate 	new_hdr->prev = insert_point;
3547c478bd9Sstevel@tonic-gate 	insert_point->next->prev = new_hdr;
3557c478bd9Sstevel@tonic-gate 	insert_point->next = new_hdr;
3567c478bd9Sstevel@tonic-gate 	return (new_hdr);
3577c478bd9Sstevel@tonic-gate }
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate /* Delete an obj from the list. This could be a flow item or the flow itself */
3607c478bd9Sstevel@tonic-gate static void
3617c478bd9Sstevel@tonic-gate flowacct_del_obj(list_head_t *tophdr, list_hdr_t *hdr, uint_t mode)
3627c478bd9Sstevel@tonic-gate {
3637c478bd9Sstevel@tonic-gate 	size_t	length;
3647c478bd9Sstevel@tonic-gate 	uint_t	type;
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	if ((tophdr == NULL) || (hdr == NULL)) {
3677c478bd9Sstevel@tonic-gate 		return;
3687c478bd9Sstevel@tonic-gate 	}
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate 	type = ((flow_t *)hdr->objp)->type;
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate 	tophdr->nbr_items--;
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 	if (hdr->next != NULL) {
3757c478bd9Sstevel@tonic-gate 		hdr->next->prev = hdr->prev;
3767c478bd9Sstevel@tonic-gate 	}
3777c478bd9Sstevel@tonic-gate 	if (hdr->prev != NULL) {
3787c478bd9Sstevel@tonic-gate 		hdr->prev->next = hdr->next;
3797c478bd9Sstevel@tonic-gate 	}
3807c478bd9Sstevel@tonic-gate 	if (tophdr->head == hdr) {
3817c478bd9Sstevel@tonic-gate 		tophdr->head = hdr->next;
3827c478bd9Sstevel@tonic-gate 	}
3837c478bd9Sstevel@tonic-gate 	if (tophdr->tail == hdr) {
3847c478bd9Sstevel@tonic-gate 		tophdr->tail = hdr->prev;
3857c478bd9Sstevel@tonic-gate 	}
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	if (mode == FLOWACCT_DEL_OBJ) {
3887c478bd9Sstevel@tonic-gate 		switch (type) {
3897c478bd9Sstevel@tonic-gate 		case FLOWACCT_FLOW:
3907c478bd9Sstevel@tonic-gate 			length = FLOWACCT_FLOW_SZ;
3917c478bd9Sstevel@tonic-gate 			break;
3927c478bd9Sstevel@tonic-gate 		case FLOWACCT_ITEM:
3937c478bd9Sstevel@tonic-gate 			length = FLOWACCT_ITEM_SZ;
3947c478bd9Sstevel@tonic-gate 			break;
3957c478bd9Sstevel@tonic-gate 		}
3967c478bd9Sstevel@tonic-gate 		kmem_free(hdr->objp, length);
3976d730a61Svi117747 		hdr->objp = NULL;
3987c478bd9Sstevel@tonic-gate 	}
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate 	kmem_free((void *)hdr, FLOWACCT_HDR_SZ);
4017c478bd9Sstevel@tonic-gate }
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate /*
4047c478bd9Sstevel@tonic-gate  * Checks if the given item (identified by dsfield, project id and uid)
4057c478bd9Sstevel@tonic-gate  * is already present for the flow.
4067c478bd9Sstevel@tonic-gate  */
4077c478bd9Sstevel@tonic-gate static flow_item_t *
4087c478bd9Sstevel@tonic-gate flowacct_item_present(flow_t *flow, uint8_t dsfield, pid_t proj_id, uint_t uid)
4097c478bd9Sstevel@tonic-gate {
4107c478bd9Sstevel@tonic-gate 	list_hdr_t	*itemhdr;
4117c478bd9Sstevel@tonic-gate 	flow_item_t	*item;
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 	itemhdr = flow->items.head;
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate 	while (itemhdr != NULL) {
4167c478bd9Sstevel@tonic-gate 		item = (flow_item_t *)itemhdr->objp;
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 		if ((item->dsfield != dsfield) || (item->projid != proj_id) ||
4197c478bd9Sstevel@tonic-gate 		    (item->uid != uid)) {
4207c478bd9Sstevel@tonic-gate 			itemhdr = itemhdr->next;
4217c478bd9Sstevel@tonic-gate 			continue;
4227c478bd9Sstevel@tonic-gate 		}
4237c478bd9Sstevel@tonic-gate 		return (item);
4247c478bd9Sstevel@tonic-gate 	}
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 	return ((flow_item_t *)NULL);
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate /*
4307c478bd9Sstevel@tonic-gate  * Add the flow to the table, if not already present. If the flow is
4317c478bd9Sstevel@tonic-gate  * present in the table, add the item. Also, update the flow stats.
4327c478bd9Sstevel@tonic-gate  * Additionally, re-adjust the timout list as well.
4337c478bd9Sstevel@tonic-gate  */
4347c478bd9Sstevel@tonic-gate static int
4357c478bd9Sstevel@tonic-gate flowacct_update_flows_tbl(header_t *header, flowacct_data_t *flowacct_data)
4367c478bd9Sstevel@tonic-gate {
4377c478bd9Sstevel@tonic-gate 	int index;
4387c478bd9Sstevel@tonic-gate 	list_head_t *fhead;
4397c478bd9Sstevel@tonic-gate 	list_head_t *thead;
4407c478bd9Sstevel@tonic-gate 	list_head_t *ihead;
4417c478bd9Sstevel@tonic-gate 	boolean_t added_flow = B_FALSE;
4427c478bd9Sstevel@tonic-gate 	timespec_t  now;
4437c478bd9Sstevel@tonic-gate 	flow_item_t *item;
4447c478bd9Sstevel@tonic-gate 	flow_t *flow;
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 	index = FLOWACCT_FLOW_HASH(header);
4477c478bd9Sstevel@tonic-gate 	fhead = &flowacct_data->flows_tbl[index];
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	/* The timeout list */
4507c478bd9Sstevel@tonic-gate 	thead = &flowacct_data->flows_tbl[FLOW_TBL_COUNT];
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 	mutex_enter(&fhead->lock);
4537c478bd9Sstevel@tonic-gate 	flow = flowacct_flow_present(header, index, flowacct_data);
4547c478bd9Sstevel@tonic-gate 	if (flow == NULL) {
4557c478bd9Sstevel@tonic-gate 		flow = (flow_t *)kmem_zalloc(FLOWACCT_FLOW_SZ, KM_NOSLEEP);
4567c478bd9Sstevel@tonic-gate 		if (flow == NULL) {
457*89ce534eSudpa 			mutex_exit(&fhead->lock);
4587c478bd9Sstevel@tonic-gate 			flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
4597c478bd9Sstevel@tonic-gate 			    "error"));
4607c478bd9Sstevel@tonic-gate 			return (-1);
4617c478bd9Sstevel@tonic-gate 		}
4627c478bd9Sstevel@tonic-gate 		flow->hdr = flowacct_add_obj(fhead, fhead->tail, (void *)flow);
4637c478bd9Sstevel@tonic-gate 		if (flow->hdr == NULL) {
464*89ce534eSudpa 			mutex_exit(&fhead->lock);
465*89ce534eSudpa 			kmem_free(flow, FLOWACCT_FLOW_SZ);
4667c478bd9Sstevel@tonic-gate 			flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
4677c478bd9Sstevel@tonic-gate 			    "error"));
4687c478bd9Sstevel@tonic-gate 			return (-1);
4697c478bd9Sstevel@tonic-gate 		}
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate 		flow->type = FLOWACCT_FLOW;
4727c478bd9Sstevel@tonic-gate 		flow->isv4 = header->isv4;
4737c478bd9Sstevel@tonic-gate 		bcopy(header->saddr.s6_addr32, flow->saddr.s6_addr32,
4747c478bd9Sstevel@tonic-gate 		    sizeof (header->saddr.s6_addr32));
4757c478bd9Sstevel@tonic-gate 		bcopy(header->daddr.s6_addr32, flow->daddr.s6_addr32,
4767c478bd9Sstevel@tonic-gate 		    sizeof (header->daddr.s6_addr32));
4777c478bd9Sstevel@tonic-gate 		flow->proto = header->proto;
4787c478bd9Sstevel@tonic-gate 		flow->sport = header->sport;
4797c478bd9Sstevel@tonic-gate 		flow->dport = header->dport;
4807c478bd9Sstevel@tonic-gate 		flow->back_ptr = fhead;
4817c478bd9Sstevel@tonic-gate 		added_flow = B_TRUE;
4826d730a61Svi117747 	} else {
4836d730a61Svi117747 		/*
4846d730a61Svi117747 		 * We need to make sure that this 'flow' is not deleted
4856d730a61Svi117747 		 * either by a scheduled timeout or an explict call
4866d730a61Svi117747 		 * to flowacct_timer() below.
4876d730a61Svi117747 		 */
4886d730a61Svi117747 		flow->inuse = B_TRUE;
4897c478bd9Sstevel@tonic-gate 	}
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	ihead = &flow->items;
4927c478bd9Sstevel@tonic-gate 	item = flowacct_item_present(flow, header->dsfield, header->projid,
4937c478bd9Sstevel@tonic-gate 	    header->uid);
4947c478bd9Sstevel@tonic-gate 	if (item == NULL) {
4957c478bd9Sstevel@tonic-gate 		boolean_t just_once = B_TRUE;
4967c478bd9Sstevel@tonic-gate 		/*
4977c478bd9Sstevel@tonic-gate 		 * For all practical purposes, we limit the no. of entries in
4987c478bd9Sstevel@tonic-gate 		 * the flow table - i.e. the max_limt that a user specifies is
4997c478bd9Sstevel@tonic-gate 		 * the maximum no. of flow items in the table.
5007c478bd9Sstevel@tonic-gate 		 */
5017c478bd9Sstevel@tonic-gate 	try_again:
5027c478bd9Sstevel@tonic-gate 		atomic_add_32(&flowacct_data->nflows, 1);
5037c478bd9Sstevel@tonic-gate 		if (flowacct_data->nflows > flowacct_data->max_limit) {
5047c478bd9Sstevel@tonic-gate 			atomic_add_32(&flowacct_data->nflows, -1);
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate 			/* Try timing out once */
5077c478bd9Sstevel@tonic-gate 			if (just_once) {
5087c478bd9Sstevel@tonic-gate 				/*
5097c478bd9Sstevel@tonic-gate 				 * Need to release the lock, as this entry
5107c478bd9Sstevel@tonic-gate 				 * could contain a flow that can be timed
5117c478bd9Sstevel@tonic-gate 				 * out.
5127c478bd9Sstevel@tonic-gate 				 */
5137c478bd9Sstevel@tonic-gate 				mutex_exit(&fhead->lock);
5147c478bd9Sstevel@tonic-gate 				flowacct_timer(FLOWACCT_JUST_ONE,
5157c478bd9Sstevel@tonic-gate 				    flowacct_data);
5167c478bd9Sstevel@tonic-gate 				mutex_enter(&fhead->lock);
5177c478bd9Sstevel@tonic-gate 				/* Lets check again */
5187c478bd9Sstevel@tonic-gate 				just_once = B_FALSE;
5197c478bd9Sstevel@tonic-gate 				goto try_again;
5207c478bd9Sstevel@tonic-gate 			} else {
521cd09ed25Svi117747 				flow->inuse = B_FALSE;
522*89ce534eSudpa 				/* Need to remove the flow, if one was added */
5237c478bd9Sstevel@tonic-gate 				if (added_flow) {
5247c478bd9Sstevel@tonic-gate 					flowacct_del_obj(fhead, flow->hdr,
5257c478bd9Sstevel@tonic-gate 					    FLOWACCT_DEL_OBJ);
5267c478bd9Sstevel@tonic-gate 				}
527*89ce534eSudpa 				mutex_exit(&fhead->lock);
528*89ce534eSudpa 				flowacct1dbg(("flowacct_update_flows_tbl: "\
529*89ce534eSudpa 				    "maximum active flows exceeded\n"));
5307c478bd9Sstevel@tonic-gate 				return (-1);
5317c478bd9Sstevel@tonic-gate 			}
5327c478bd9Sstevel@tonic-gate 		}
5337c478bd9Sstevel@tonic-gate 		item = (flow_item_t *)kmem_zalloc(FLOWACCT_ITEM_SZ, KM_NOSLEEP);
5347c478bd9Sstevel@tonic-gate 		if (item == NULL) {
535*89ce534eSudpa 			flow->inuse = B_FALSE;
5367c478bd9Sstevel@tonic-gate 			/* Need to remove the flow, if one was added */
5377c478bd9Sstevel@tonic-gate 			if (added_flow) {
5387c478bd9Sstevel@tonic-gate 				flowacct_del_obj(fhead, flow->hdr,
5397c478bd9Sstevel@tonic-gate 				    FLOWACCT_DEL_OBJ);
5407c478bd9Sstevel@tonic-gate 			}
5417c478bd9Sstevel@tonic-gate 			mutex_exit(&fhead->lock);
542*89ce534eSudpa 			atomic_add_32(&flowacct_data->nflows, -1);
543*89ce534eSudpa 			flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
544*89ce534eSudpa 			    "error"));
5457c478bd9Sstevel@tonic-gate 			return (-1);
5467c478bd9Sstevel@tonic-gate 		}
5477c478bd9Sstevel@tonic-gate 		item->hdr = flowacct_add_obj(ihead, ihead->tail, (void *)item);
5487c478bd9Sstevel@tonic-gate 		if (item->hdr == NULL) {
549*89ce534eSudpa 			flow->inuse = B_FALSE;
5507c478bd9Sstevel@tonic-gate 			/* Need to remove the flow, if one was added */
5517c478bd9Sstevel@tonic-gate 			if (added_flow) {
5527c478bd9Sstevel@tonic-gate 				flowacct_del_obj(fhead, flow->hdr,
5537c478bd9Sstevel@tonic-gate 				    FLOWACCT_DEL_OBJ);
5547c478bd9Sstevel@tonic-gate 			}
5557c478bd9Sstevel@tonic-gate 			mutex_exit(&fhead->lock);
556*89ce534eSudpa 			atomic_add_32(&flowacct_data->nflows, -1);
557*89ce534eSudpa 			kmem_free(item, FLOWACCT_ITEM_SZ);
558*89ce534eSudpa 			flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
559*89ce534eSudpa 			    "error\n"));
5607c478bd9Sstevel@tonic-gate 			return (-1);
5617c478bd9Sstevel@tonic-gate 		}
5627c478bd9Sstevel@tonic-gate 		/* If a flow was added, add it too */
5637c478bd9Sstevel@tonic-gate 		if (added_flow) {
5647c478bd9Sstevel@tonic-gate 			atomic_add_64(&flowacct_data->usedmem,
5657c478bd9Sstevel@tonic-gate 			    FLOWACCT_FLOW_RECORD_SZ);
5667c478bd9Sstevel@tonic-gate 		}
5677c478bd9Sstevel@tonic-gate 		atomic_add_64(&flowacct_data->usedmem, FLOWACCT_ITEM_RECORD_SZ);
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 		item->type = FLOWACCT_ITEM;
5707c478bd9Sstevel@tonic-gate 		item->dsfield = header->dsfield;
5717c478bd9Sstevel@tonic-gate 		item->projid = header->projid;
5727c478bd9Sstevel@tonic-gate 		item->uid = header->uid;
5737c478bd9Sstevel@tonic-gate 		item->npackets = 1;
5747c478bd9Sstevel@tonic-gate 		item->nbytes = header->pktlen;
5757c478bd9Sstevel@tonic-gate 		item->creation_time = item->hdr->last_seen;
5767c478bd9Sstevel@tonic-gate 	} else {
5777c478bd9Sstevel@tonic-gate 		item->npackets++;
5787c478bd9Sstevel@tonic-gate 		item->nbytes += header->pktlen;
5797c478bd9Sstevel@tonic-gate 	}
5807c478bd9Sstevel@tonic-gate 	gethrestime(&now);
5817c478bd9Sstevel@tonic-gate 	flow->hdr->last_seen = item->hdr->last_seen = now;
5827c478bd9Sstevel@tonic-gate 	mutex_exit(&fhead->lock);
5837c478bd9Sstevel@tonic-gate 
5846d730a61Svi117747 	/*
5856d730a61Svi117747 	 * Re-adjust the timeout list. The timer takes the thead lock
5866d730a61Svi117747 	 * follwed by fhead lock(s), so we release fhead, take thead
5876d730a61Svi117747 	 * and re-take fhead.
5886d730a61Svi117747 	 */
5897c478bd9Sstevel@tonic-gate 	mutex_enter(&thead->lock);
5906d730a61Svi117747 	mutex_enter(&fhead->lock);
5917c478bd9Sstevel@tonic-gate 	/* If the flow was added, append it to the tail of the timeout list */
5927c478bd9Sstevel@tonic-gate 	if (added_flow) {
5937c478bd9Sstevel@tonic-gate 		if (thead->head == NULL) {
5947c478bd9Sstevel@tonic-gate 			thead->head = flow->hdr;
5957c478bd9Sstevel@tonic-gate 			thead->tail = flow->hdr;
5967c478bd9Sstevel@tonic-gate 		} else {
5977c478bd9Sstevel@tonic-gate 			thead->tail->timeout_next = flow->hdr;
5987c478bd9Sstevel@tonic-gate 			flow->hdr->timeout_prev = thead->tail;
5997c478bd9Sstevel@tonic-gate 			thead->tail = flow->hdr;
6007c478bd9Sstevel@tonic-gate 		}
6017c478bd9Sstevel@tonic-gate 	/*
6027c478bd9Sstevel@tonic-gate 	 * Else, move this flow to the tail of the timeout list, if it is not
6037c478bd9Sstevel@tonic-gate 	 * already.
604*89ce534eSudpa 	 * flow->hdr in the timeout list :-
605*89ce534eSudpa 	 * timeout_next = NULL, timeout_prev != NULL, at the tail end.
606*89ce534eSudpa 	 * timeout_next != NULL, timeout_prev = NULL, at the head.
607*89ce534eSudpa 	 * timeout_next != NULL, timeout_prev != NULL, in the middle.
608*89ce534eSudpa 	 * timeout_next = NULL, timeout_prev = NULL, not in the timeout list,
609*89ce534eSudpa 	 * ignore such flow.
6107c478bd9Sstevel@tonic-gate 	 */
611*89ce534eSudpa 	} else if ((flow->hdr->timeout_next != NULL) ||
612*89ce534eSudpa 	    (flow->hdr->timeout_prev != NULL)) {
613*89ce534eSudpa 		if (flow->hdr != thead->tail) {
6147c478bd9Sstevel@tonic-gate 			if (flow->hdr == thead->head) {
6157c478bd9Sstevel@tonic-gate 				thead->head->timeout_next->timeout_prev = NULL;
6167c478bd9Sstevel@tonic-gate 				thead->head = thead->head->timeout_next;
6177c478bd9Sstevel@tonic-gate 				flow->hdr->timeout_next = NULL;
6187c478bd9Sstevel@tonic-gate 				thead->tail->timeout_next = flow->hdr;
6197c478bd9Sstevel@tonic-gate 				flow->hdr->timeout_prev = thead->tail;
6207c478bd9Sstevel@tonic-gate 				thead->tail = flow->hdr;
6217c478bd9Sstevel@tonic-gate 			} else {
6227c478bd9Sstevel@tonic-gate 				flow->hdr->timeout_prev->timeout_next =
6237c478bd9Sstevel@tonic-gate 				    flow->hdr->timeout_next;
6247c478bd9Sstevel@tonic-gate 				flow->hdr->timeout_next->timeout_prev =
6257c478bd9Sstevel@tonic-gate 				    flow->hdr->timeout_prev;
6267c478bd9Sstevel@tonic-gate 				flow->hdr->timeout_next = NULL;
6277c478bd9Sstevel@tonic-gate 				thead->tail->timeout_next = flow->hdr;
6287c478bd9Sstevel@tonic-gate 				flow->hdr->timeout_prev = thead->tail;
6297c478bd9Sstevel@tonic-gate 				thead->tail = flow->hdr;
6307c478bd9Sstevel@tonic-gate 			}
631*89ce534eSudpa 		}
632*89ce534eSudpa 	}
6336d730a61Svi117747 	/*
6346d730a61Svi117747 	 * Unset this variable, now it is fine even if this
6356d730a61Svi117747 	 * flow gets deleted (i.e. after timing out its
6366d730a61Svi117747 	 * flow items) since we are done using it.
6376d730a61Svi117747 	 */
6386d730a61Svi117747 	flow->inuse = B_FALSE;
6396d730a61Svi117747 	mutex_exit(&fhead->lock);
6407c478bd9Sstevel@tonic-gate 	mutex_exit(&thead->lock);
6417c478bd9Sstevel@tonic-gate 	atomic_add_64(&flowacct_data->tbytes, header->pktlen);
6427c478bd9Sstevel@tonic-gate 	return (0);
6437c478bd9Sstevel@tonic-gate }
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate /* Timer for timing out flows/items from the flow table */
6467c478bd9Sstevel@tonic-gate void
6477c478bd9Sstevel@tonic-gate flowacct_timeout_flows(void *args)
6487c478bd9Sstevel@tonic-gate {
6497c478bd9Sstevel@tonic-gate 	flowacct_data_t *flowacct_data = (flowacct_data_t *)args;
6507c478bd9Sstevel@tonic-gate 	flowacct_timer(FLOWACCT_FLOW_TIMER, flowacct_data);
6517c478bd9Sstevel@tonic-gate 	flowacct_data->flow_tid = timeout(flowacct_timeout_flows, flowacct_data,
6527c478bd9Sstevel@tonic-gate 	    drv_usectohz(flowacct_data->timer));
6537c478bd9Sstevel@tonic-gate }
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate /* Delete the item from the flow in the flow table */
6577c478bd9Sstevel@tonic-gate static void
6587c478bd9Sstevel@tonic-gate flowacct_timeout_item(flow_t **flow, list_hdr_t **item_hdr)
6597c478bd9Sstevel@tonic-gate {
6607c478bd9Sstevel@tonic-gate 	list_hdr_t *next_it_hdr;
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	next_it_hdr = (*item_hdr)->next;
6637c478bd9Sstevel@tonic-gate 	flowacct_del_obj(&(*flow)->items, *item_hdr, FLOWACCT_DEL_OBJ);
6647c478bd9Sstevel@tonic-gate 	*item_hdr = next_it_hdr;
6657c478bd9Sstevel@tonic-gate }
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate /* Create a flow record for this timed out item */
6687c478bd9Sstevel@tonic-gate static flow_records_t *
6697c478bd9Sstevel@tonic-gate flowacct_create_record(flow_t *flow, list_hdr_t *ithdr)
6707c478bd9Sstevel@tonic-gate {
6717c478bd9Sstevel@tonic-gate 	int count;
6727c478bd9Sstevel@tonic-gate 	flow_item_t *item = (flow_item_t *)ithdr->objp;
6737c478bd9Sstevel@tonic-gate 	flow_records_t *tmp_frec = NULL;
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate 	/* Record to be written into the accounting file */
6767c478bd9Sstevel@tonic-gate 	tmp_frec = kmem_zalloc(sizeof (flow_records_t), KM_NOSLEEP);
6777c478bd9Sstevel@tonic-gate 	if (tmp_frec == NULL) {
6787c478bd9Sstevel@tonic-gate 		flowacct0dbg(("flowacct_create_record: mem alloc error.\n"));
6797c478bd9Sstevel@tonic-gate 		return (NULL);
6807c478bd9Sstevel@tonic-gate 	}
6817c478bd9Sstevel@tonic-gate 	tmp_frec->fl_use = kmem_zalloc(sizeof (flow_usage_t), KM_NOSLEEP);
6827c478bd9Sstevel@tonic-gate 	if (tmp_frec->fl_use == NULL) {
6837c478bd9Sstevel@tonic-gate 		flowacct0dbg(("flowacct_create_record: mem alloc error\n"));
6847c478bd9Sstevel@tonic-gate 		kmem_free(tmp_frec, sizeof (flow_records_t));
6857c478bd9Sstevel@tonic-gate 		return (NULL);
6867c478bd9Sstevel@tonic-gate 	}
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 	/* Copy the IP address */
6897c478bd9Sstevel@tonic-gate 	for (count = 0; count < 4; count++) {
6907c478bd9Sstevel@tonic-gate 		tmp_frec->fl_use->fu_saddr[count] =
6917c478bd9Sstevel@tonic-gate 		    htonl(flow->saddr.s6_addr32[count]);
6927c478bd9Sstevel@tonic-gate 		tmp_frec->fl_use->fu_daddr[count] =
6937c478bd9Sstevel@tonic-gate 		    htonl(flow->daddr.s6_addr32[count]);
6947c478bd9Sstevel@tonic-gate 	}
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate 	/*
6977c478bd9Sstevel@tonic-gate 	 * Ports, protocol, version, dsfield, project id, uid, nbytes, npackets
6987c478bd9Sstevel@tonic-gate 	 * creation time and last seen.
6997c478bd9Sstevel@tonic-gate 	 */
7007c478bd9Sstevel@tonic-gate 	tmp_frec->fl_use->fu_sport = htons(flow->sport);
7017c478bd9Sstevel@tonic-gate 	tmp_frec->fl_use->fu_dport = htons(flow->dport);
7027c478bd9Sstevel@tonic-gate 	tmp_frec->fl_use->fu_protocol = flow->proto;
7037c478bd9Sstevel@tonic-gate 	tmp_frec->fl_use->fu_isv4 = flow->isv4;
7047c478bd9Sstevel@tonic-gate 	tmp_frec->fl_use->fu_dsfield = item->dsfield;
7057c478bd9Sstevel@tonic-gate 	tmp_frec->fl_use->fu_projid = item->projid;
7067c478bd9Sstevel@tonic-gate 	tmp_frec->fl_use->fu_userid = item->uid;
7077c478bd9Sstevel@tonic-gate 	tmp_frec->fl_use->fu_nbytes = item->nbytes;
7087c478bd9Sstevel@tonic-gate 	tmp_frec->fl_use->fu_npackets = item->npackets;
7097c478bd9Sstevel@tonic-gate 	tmp_frec->fl_use->fu_lseen =
7107c478bd9Sstevel@tonic-gate 	    (uint64_t)(ulong_t)ithdr->last_seen.tv_sec;
7117c478bd9Sstevel@tonic-gate 	tmp_frec->fl_use->fu_ctime =
7127c478bd9Sstevel@tonic-gate 	    (uint64_t)(ulong_t)item->creation_time.tv_sec;
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 	return (tmp_frec);
7157c478bd9Sstevel@tonic-gate }
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate /*
7187c478bd9Sstevel@tonic-gate  * Scan thru the timeout list and write the records to the accounting file, if
7197c478bd9Sstevel@tonic-gate  * possible. Basically step thru the timeout list maintained in the last
7207c478bd9Sstevel@tonic-gate  * hash bucket, FLOW_COUNT_TBL + 1, and timeout flows. This could be called
7217c478bd9Sstevel@tonic-gate  * from the timer, FLOWACCT_TIMER - delete only timed out flows or when this
7227c478bd9Sstevel@tonic-gate  * instance is deleted, FLOWACCT_PURGE_FLOW - delete all the flows from the
7237c478bd9Sstevel@tonic-gate  * table or as FLOWACCT_JUST_ONE - delete the first timed out flow. Since the
7247c478bd9Sstevel@tonic-gate  * flows are cronologically arranged in the timeout list,  when called as
7257c478bd9Sstevel@tonic-gate  * FLOWACCT_TIMER and FLOWACCT_JUST_ONE, we can stop when we come across
7267c478bd9Sstevel@tonic-gate  * the first flow that has not timed out (which means none of the following
7277c478bd9Sstevel@tonic-gate  * flows would have timed out).
7287c478bd9Sstevel@tonic-gate  */
7297c478bd9Sstevel@tonic-gate void
7307c478bd9Sstevel@tonic-gate flowacct_timer(int type, flowacct_data_t *flowacct_data)
7317c478bd9Sstevel@tonic-gate {
7327c478bd9Sstevel@tonic-gate 	hrtime_t diff;
7337c478bd9Sstevel@tonic-gate 	timespec_t now;
7347c478bd9Sstevel@tonic-gate 	list_head_t *head, *thead;
7357c478bd9Sstevel@tonic-gate 	flow_t *flow;
7367c478bd9Sstevel@tonic-gate 	flow_item_t *item;
7377c478bd9Sstevel@tonic-gate 	list_hdr_t *fl_hdr, *next_fl_hdr;
7387c478bd9Sstevel@tonic-gate 	list_hdr_t *ithdr = (list_hdr_t *)NULL;
7397c478bd9Sstevel@tonic-gate 	flow_records_t *frec = NULL, *tmp_frec, *tail;
7407c478bd9Sstevel@tonic-gate 	uint64_t flow_size;
7417c478bd9Sstevel@tonic-gate 	uint64_t item_size;
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate 	ASSERT(flowacct_data != NULL);
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate 	/* 2s-complement for subtraction */
7467c478bd9Sstevel@tonic-gate 	flow_size = ~FLOWACCT_FLOW_RECORD_SZ + 1;
7477c478bd9Sstevel@tonic-gate 	item_size = ~FLOWACCT_ITEM_RECORD_SZ + 1;
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate 	/* Get the current time */
7507c478bd9Sstevel@tonic-gate 	gethrestime(&now);
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 	/*
7537c478bd9Sstevel@tonic-gate 	 * For each flow in the table, scan thru all the items and delete
7547c478bd9Sstevel@tonic-gate 	 * those that have exceeded the timeout. If all the items in a
7557c478bd9Sstevel@tonic-gate 	 * flow have timed out, delete the flow entry as well. Finally,
7567c478bd9Sstevel@tonic-gate 	 * write all the delted items to the accounting file.
7577c478bd9Sstevel@tonic-gate 	 */
7587c478bd9Sstevel@tonic-gate 	thead = &flowacct_data->flows_tbl[FLOW_TBL_COUNT];
7597c478bd9Sstevel@tonic-gate 
7607c478bd9Sstevel@tonic-gate 	mutex_enter(&thead->lock);
7617c478bd9Sstevel@tonic-gate 	fl_hdr = thead->head;
7627c478bd9Sstevel@tonic-gate 	while (fl_hdr != NULL) {
7637c478bd9Sstevel@tonic-gate 		uint32_t	items_deleted = 0;
7646d730a61Svi117747 
7657c478bd9Sstevel@tonic-gate 		next_fl_hdr = fl_hdr->timeout_next;
7667c478bd9Sstevel@tonic-gate 		flow = (flow_t *)fl_hdr->objp;
7677c478bd9Sstevel@tonic-gate 		head = flow->back_ptr;
7687c478bd9Sstevel@tonic-gate 		mutex_enter(&head->lock);
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 		/*LINTED*/
7717c478bd9Sstevel@tonic-gate 		FLOWACCT_DELTA(now, fl_hdr->last_seen, diff);
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 		/*
7747c478bd9Sstevel@tonic-gate 		 * If type is FLOW_TIMER, then check if the item has timed out.
7757c478bd9Sstevel@tonic-gate 		 * If type is FLOW_PURGE delete the entry anyways.
7767c478bd9Sstevel@tonic-gate 		 */
7777c478bd9Sstevel@tonic-gate 		if ((type != FLOWACCT_PURGE_FLOW) &&
7787c478bd9Sstevel@tonic-gate 		    (diff < flowacct_data->timeout)) {
7797c478bd9Sstevel@tonic-gate 			mutex_exit(&head->lock);
7807c478bd9Sstevel@tonic-gate 			mutex_exit(&thead->lock);
7817c478bd9Sstevel@tonic-gate 			goto write_records;
7827c478bd9Sstevel@tonic-gate 		}
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate 		ithdr = flow->items.head;
7857c478bd9Sstevel@tonic-gate 		while (ithdr != NULL) {
7867c478bd9Sstevel@tonic-gate 			item = (flow_item_t *)ithdr->objp;
7877c478bd9Sstevel@tonic-gate 			/*
7887c478bd9Sstevel@tonic-gate 			 * Fill in the flow record to be
7897c478bd9Sstevel@tonic-gate 			 * written to the accounting file.
7907c478bd9Sstevel@tonic-gate 			 */
7917c478bd9Sstevel@tonic-gate 			tmp_frec = flowacct_create_record(flow, ithdr);
7927c478bd9Sstevel@tonic-gate 			/*
7937c478bd9Sstevel@tonic-gate 			 * If we don't have memory for records,
7947c478bd9Sstevel@tonic-gate 			 * we will come back in case this is
7957c478bd9Sstevel@tonic-gate 			 * called as FLOW_TIMER, else we will
7967c478bd9Sstevel@tonic-gate 			 * go ahead and delete the item from
7977c478bd9Sstevel@tonic-gate 			 * the table (when asked to PURGE the
7987c478bd9Sstevel@tonic-gate 			 * table), so there could be some
7997c478bd9Sstevel@tonic-gate 			 * entries not written to the file
8007c478bd9Sstevel@tonic-gate 			 * when this action instance is
8017c478bd9Sstevel@tonic-gate 			 * deleted.
8027c478bd9Sstevel@tonic-gate 			 */
8037c478bd9Sstevel@tonic-gate 			if (tmp_frec != NULL) {
8047c478bd9Sstevel@tonic-gate 				tmp_frec->fl_use->fu_aname =
8057c478bd9Sstevel@tonic-gate 				    flowacct_data->act_name;
8067c478bd9Sstevel@tonic-gate 				if (frec == NULL) {
8077c478bd9Sstevel@tonic-gate 					frec = tmp_frec;
8087c478bd9Sstevel@tonic-gate 					tail = frec;
8097c478bd9Sstevel@tonic-gate 				} else {
8107c478bd9Sstevel@tonic-gate 					tail->next = tmp_frec;
8117c478bd9Sstevel@tonic-gate 					tail = tmp_frec;
8127c478bd9Sstevel@tonic-gate 				}
8137c478bd9Sstevel@tonic-gate 			} else if (type != FLOWACCT_PURGE_FLOW) {
8147c478bd9Sstevel@tonic-gate 				mutex_exit(&head->lock);
8157c478bd9Sstevel@tonic-gate 				mutex_exit(&thead->lock);
8167c478bd9Sstevel@tonic-gate 				atomic_add_32(&flowacct_data->nflows,
8177c478bd9Sstevel@tonic-gate 				    (~items_deleted + 1));
8187c478bd9Sstevel@tonic-gate 				goto write_records;
8197c478bd9Sstevel@tonic-gate 			}
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate 			/* Update stats */
8227c478bd9Sstevel@tonic-gate 			atomic_add_64(&flowacct_data->tbytes, (~item->nbytes +
8237c478bd9Sstevel@tonic-gate 			    1));
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate 			/* Delete the item */
8267c478bd9Sstevel@tonic-gate 			flowacct_timeout_item(&flow, &ithdr);
8277c478bd9Sstevel@tonic-gate 			items_deleted++;
8287c478bd9Sstevel@tonic-gate 			atomic_add_64(&flowacct_data->usedmem, item_size);
8297c478bd9Sstevel@tonic-gate 		}
8307c478bd9Sstevel@tonic-gate 		ASSERT(flow->items.nbr_items == 0);
8317c478bd9Sstevel@tonic-gate 		atomic_add_32(&flowacct_data->nflows, (~items_deleted + 1));
8327c478bd9Sstevel@tonic-gate 
8336d730a61Svi117747 		/*
8346d730a61Svi117747 		 * Don't delete this flow if we are making place for
8356d730a61Svi117747 		 * a new item for this flow.
8366d730a61Svi117747 		 */
8376d730a61Svi117747 		if (!flow->inuse) {
838cd09ed25Svi117747 			if (fl_hdr->timeout_prev != NULL) {
839cd09ed25Svi117747 				fl_hdr->timeout_prev->timeout_next =
840cd09ed25Svi117747 				    fl_hdr->timeout_next;
8417c478bd9Sstevel@tonic-gate 			} else {
8427c478bd9Sstevel@tonic-gate 				thead->head = fl_hdr->timeout_next;
8437c478bd9Sstevel@tonic-gate 			}
844cd09ed25Svi117747 			if (fl_hdr->timeout_next != NULL) {
845cd09ed25Svi117747 				fl_hdr->timeout_next->timeout_prev =
846cd09ed25Svi117747 				    fl_hdr->timeout_prev;
847cd09ed25Svi117747 			} else {
848cd09ed25Svi117747 				thead->tail = fl_hdr->timeout_prev;
849cd09ed25Svi117747 			}
850cd09ed25Svi117747 			fl_hdr->timeout_prev = NULL;
851cd09ed25Svi117747 			fl_hdr->timeout_next = NULL;
8527c478bd9Sstevel@tonic-gate 			flowacct_del_obj(head, fl_hdr, FLOWACCT_DEL_OBJ);
8537c478bd9Sstevel@tonic-gate 			atomic_add_64(&flowacct_data->usedmem, flow_size);
8546d730a61Svi117747 		}
8557c478bd9Sstevel@tonic-gate 		mutex_exit(&head->lock);
8567c478bd9Sstevel@tonic-gate 		if (type == FLOWACCT_JUST_ONE) {
8577c478bd9Sstevel@tonic-gate 			mutex_exit(&thead->lock);
8587c478bd9Sstevel@tonic-gate 			goto write_records;
8597c478bd9Sstevel@tonic-gate 		}
8607c478bd9Sstevel@tonic-gate 		fl_hdr = next_fl_hdr;
8617c478bd9Sstevel@tonic-gate 	}
8627c478bd9Sstevel@tonic-gate 	mutex_exit(&thead->lock);
8637c478bd9Sstevel@tonic-gate write_records:
8647c478bd9Sstevel@tonic-gate 	/* Write all the timed out flows to the accounting file */
8657c478bd9Sstevel@tonic-gate 	while (frec != NULL) {
8667c478bd9Sstevel@tonic-gate 		tmp_frec = frec->next;
8677c478bd9Sstevel@tonic-gate 		exacct_commit_flow(frec->fl_use);
8687c478bd9Sstevel@tonic-gate 		kmem_free(frec->fl_use, sizeof (flow_usage_t));
8697c478bd9Sstevel@tonic-gate 		kmem_free(frec, sizeof (flow_records_t));
8707c478bd9Sstevel@tonic-gate 		frec = tmp_frec;
8717c478bd9Sstevel@tonic-gate 	}
8727c478bd9Sstevel@tonic-gate }
8737c478bd9Sstevel@tonic-gate 
8747c478bd9Sstevel@tonic-gate /*
8757c478bd9Sstevel@tonic-gate  * Get the IP header contents from the packet, update the flow table with
8767c478bd9Sstevel@tonic-gate  * this item and return.
8777c478bd9Sstevel@tonic-gate  */
8787c478bd9Sstevel@tonic-gate int
8797c478bd9Sstevel@tonic-gate flowacct_process(mblk_t **mpp, flowacct_data_t *flowacct_data)
8807c478bd9Sstevel@tonic-gate {
8817c478bd9Sstevel@tonic-gate 	header_t *header;
8827c478bd9Sstevel@tonic-gate 	mblk_t *mp = *mpp;
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 	ASSERT(mp != NULL);
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 	/* If we don't find an M_DATA, return error */
8877c478bd9Sstevel@tonic-gate 	if (mp->b_datap->db_type != M_DATA) {
8887c478bd9Sstevel@tonic-gate 		if ((mp->b_cont != NULL) &&
8897c478bd9Sstevel@tonic-gate 		    (mp->b_cont->b_datap->db_type == M_DATA)) {
8907c478bd9Sstevel@tonic-gate 			mp = mp->b_cont;
8917c478bd9Sstevel@tonic-gate 		} else {
8927c478bd9Sstevel@tonic-gate 			flowacct0dbg(("flowacct_process: no data\n"));
8937c478bd9Sstevel@tonic-gate 			atomic_add_64(&flowacct_data->epackets, 1);
8947c478bd9Sstevel@tonic-gate 			return (EINVAL);
8957c478bd9Sstevel@tonic-gate 		}
8967c478bd9Sstevel@tonic-gate 	}
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 	header = kmem_zalloc(FLOWACCT_HEADER_SZ, KM_NOSLEEP);
8997c478bd9Sstevel@tonic-gate 	if (header == NULL) {
9007c478bd9Sstevel@tonic-gate 		flowacct0dbg(("flowacct_process: error allocing mem"));
9017c478bd9Sstevel@tonic-gate 		atomic_add_64(&flowacct_data->epackets, 1);
9027c478bd9Sstevel@tonic-gate 		return (ENOMEM);
9037c478bd9Sstevel@tonic-gate 	}
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 	/* Get all the required information into header. */
9067c478bd9Sstevel@tonic-gate 	if (flowacct_extract_header(mp, header) != 0) {
9077c478bd9Sstevel@tonic-gate 		kmem_free(header, FLOWACCT_HEADER_SZ);
9087c478bd9Sstevel@tonic-gate 		atomic_add_64(&flowacct_data->epackets, 1);
9097c478bd9Sstevel@tonic-gate 		return (EINVAL);
9107c478bd9Sstevel@tonic-gate 	}
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate 	/* Updated the flow table with this entry */
9137c478bd9Sstevel@tonic-gate 	if (flowacct_update_flows_tbl(header, flowacct_data) != 0) {
9147c478bd9Sstevel@tonic-gate 		kmem_free(header, FLOWACCT_HEADER_SZ);
9157c478bd9Sstevel@tonic-gate 		atomic_add_64(&flowacct_data->epackets, 1);
9167c478bd9Sstevel@tonic-gate 		return (ENOMEM);
9177c478bd9Sstevel@tonic-gate 	}
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 	/* Update global stats */
9207c478bd9Sstevel@tonic-gate 	atomic_add_64(&flowacct_data->npackets, 1);
9217c478bd9Sstevel@tonic-gate 	atomic_add_64(&flowacct_data->nbytes, header->pktlen);
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 	kmem_free(header, FLOWACCT_HEADER_SZ);
9247c478bd9Sstevel@tonic-gate 	if (flowacct_data->flow_tid == 0) {
9257c478bd9Sstevel@tonic-gate 		flowacct_data->flow_tid = timeout(flowacct_timeout_flows,
9267c478bd9Sstevel@tonic-gate 		    flowacct_data, drv_usectohz(flowacct_data->timer));
9277c478bd9Sstevel@tonic-gate 	}
9287c478bd9Sstevel@tonic-gate 	return (0);
9297c478bd9Sstevel@tonic-gate }
930