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