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