4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/types.h>
30 #include <sys/atomic.h>
31 #include <netinet/in.h>
32 #include <netinet/in_systm.h>
33 #include <netinet/ip6.h>
34 #include <sys/socket.h>
36 #include <sys/exacct.h>
37 #include <inet/common.h>
41 #include <sys/strsun.h>
42 #include <sys/strsubr.h>
43 #include <ipp/flowacct/flowacct_impl.h>
46 * flowacct - IPQoS accounting module. The module maintains an array
47 * of 256 hash buckets. When the action routine is invoked for a flow,
48 * if the flow (identified by the 5-tuple: saddr, daddr, sport, dport, proto)
49 * is already present in the flow table (indexed by the hash function FLOW_HASH)
50 * then a check is made to see if an item for this flow with the same
51 * dsfield, projid & user id is present. If it is, then the number of packets
52 * and the bytes are incremented for that item. If the item does
53 * not exist a new item is added for the flow. If the flow is not present
54 * an entry is made for the flow.
56 * A timer runs thru the table and writes all the flow items that have
57 * timed out to the accounting file (via exacct PSARC/1999/119), if present
58 * Configuration commands to change the timing interval is provided. The
59 * flow timeout value can also be configured. While the timeout is in nsec,
60 * the flow timer interval is in usec.
61 * Information for an active flow can be obtained by using kstats.
64 /* Used in computing the hash index */
65 #define FLOWACCT_ADDR_HASH(addr) \
66 ((addr).s6_addr8[8] ^ (addr).s6_addr8[9] ^ \
67 (addr).s6_addr8[10] ^ (addr).s6_addr8[13] ^ \
68 (addr).s6_addr8[14] ^ (addr).s6_addr8[15])
70 #define FLOWACCT_FLOW_HASH(f) \
71 (((FLOWACCT_ADDR_HASH(f->saddr)) + \
72 (FLOWACCT_ADDR_HASH(f->daddr)) + \
73 (f->proto) + (f->sport) + (f->dport)) \
77 * Compute difference between a and b in nsec and store in delta.
78 * delta should be a hrtime_t. Taken from ip_mroute.c.
80 #define FLOWACCT_DELTA(a, b, delta) { \
83 delta = (a).tv_nsec - (b).tv_nsec; \
84 if ((xxs = (a).tv_sec - (b).tv_sec) != 0) { \
93 delta += ((hrtime_t)NANOSEC * xxs); \
99 int flowacct_debug
= 0;
101 /* Collect timed out flows to be written to the accounting file */
102 typedef struct flow_records_s
{
103 flow_usage_t
*fl_use
;
104 struct flow_records_s
*next
;
107 /* Get port information from the packet. Ignore fragments. */
109 flowacct_port_info(header_t
*header
, void *iph
, int af
, mblk_t
*mp
)
114 ipha_t
*ipha
= (ipha_t
*)iph
;
118 u2
= ntohs(ipha
->ipha_fragment_offset_and_flags
);
119 u1
= u2
& (IPH_MF
| IPH_OFFSET
);
123 iplen
= (ipha
->ipha_version_and_hdr_length
& 0xF) << 2;
124 up
= (uint16_t *)(mp
->b_rptr
+ iplen
);
125 header
->sport
= (uint16_t)*up
++;
126 header
->dport
= (uint16_t)*up
;
128 ip6_t
*ip6h
= (ip6_t
*)iph
;
129 uint_t length
= IPV6_HDR_LEN
;
131 uint8_t *nexthdrp
, *whereptr
, *endptr
;
136 whereptr
= ((uint8_t *)&ip6h
[1]);
138 nexthdrp
= &ip6h
->ip6_nxt
;
139 while (whereptr
< endptr
) {
141 case IPPROTO_HOPOPTS
:
142 hbhhdr
= (ip6_hbh_t
*)whereptr
;
143 ehdrlen
= 8 * (hbhhdr
->ip6h_len
+ 1);
144 if ((uchar_t
*)hbhhdr
+ ehdrlen
> endptr
)
146 nexthdrp
= &hbhhdr
->ip6h_nxt
;
148 case IPPROTO_DSTOPTS
:
149 desthdr
= (ip6_dest_t
*)whereptr
;
150 ehdrlen
= 8 * (desthdr
->ip6d_len
+ 1);
151 if ((uchar_t
*)desthdr
+ ehdrlen
> endptr
)
153 nexthdrp
= &desthdr
->ip6d_nxt
;
155 case IPPROTO_ROUTING
:
156 rthdr
= (ip6_rthdr_t
*)whereptr
;
157 ehdrlen
= 8 * (rthdr
->ip6r_len
+ 1);
158 if ((uchar_t
*)rthdr
+ ehdrlen
> endptr
)
160 nexthdrp
= &rthdr
->ip6r_nxt
;
162 case IPPROTO_FRAGMENT
:
168 * Verify we have at least ICMP_MIN_TP_HDR_LEN
169 * bytes of the ULP's header to get the port
172 if (((uchar_t
*)ip6h
+ length
+
173 ICMP_MIN_TP_HDR_LEN
) > endptr
) {
176 /* Get the protocol & ports */
177 header
->proto
= *nexthdrp
;
178 up
= (uint16_t *)((uchar_t
*)ip6h
+ length
);
179 header
->sport
= (uint16_t)*up
++;
180 header
->dport
= (uint16_t)*up
;
187 header
->proto
= *nexthdrp
;
200 * flowacct_find_ids(mp, header)
202 * attempt to discern the uid and projid of the originator of a packet by
203 * looking at the dblks making up the packet - yeuch!
205 * We do it by skipping any fragments with a credp of NULL (originated in
206 * kernel), taking the first value that isn't NULL to be the cred_t for the
210 flowacct_find_ids(mblk_t
*mp
, header_t
*header
)
214 cr
= msg_getcred(mp
, NULL
);
216 header
->uid
= crgetuid(cr
);
217 header
->projid
= crgetprojid(cr
);
219 header
->uid
= (uid_t
)-1;
225 * Extract header information in a header_t structure so that we don't have
226 * have to parse the packet everytime.
229 flowacct_extract_header(mblk_t
*mp
, header_t
*header
)
233 #define rptr ((uchar_t *)ipha)
235 /* 0 means no port extracted. */
238 flowacct_find_ids(mp
, header
);
240 V6_SET_ZERO(header
->saddr
);
241 V6_SET_ZERO(header
->daddr
);
243 ipha
= (ipha_t
*)mp
->b_rptr
;
244 header
->isv4
= IPH_HDR_VERSION(ipha
) == IPV4_VERSION
;
246 ipha
= (ipha_t
*)mp
->b_rptr
;
247 V4_PART_OF_V6(header
->saddr
) = (int32_t)ipha
->ipha_src
;
248 V4_PART_OF_V6(header
->daddr
) = (int32_t)ipha
->ipha_dst
;
249 header
->dsfield
= ipha
->ipha_type_of_service
;
250 header
->proto
= ipha
->ipha_protocol
;
251 header
->pktlen
= ntohs(ipha
->ipha_length
);
252 if ((header
->proto
== IPPROTO_TCP
) ||
253 (header
->proto
== IPPROTO_UDP
) ||
254 (header
->proto
== IPPROTO_SCTP
)) {
255 flowacct_port_info(header
, ipha
, AF_INET
, mp
);
259 * Need to pullup everything.
261 if (mp
->b_cont
!= NULL
) {
262 if (!pullupmsg(mp
, -1)) {
263 flowacct0dbg(("flowacct_extract_header: "\
268 ip6h
= (ip6_t
*)mp
->b_rptr
;
269 bcopy(ip6h
->ip6_src
.s6_addr32
, header
->saddr
.s6_addr32
,
270 sizeof (ip6h
->ip6_src
.s6_addr32
));
271 bcopy(ip6h
->ip6_dst
.s6_addr32
, header
->daddr
.s6_addr32
,
272 sizeof (ip6h
->ip6_dst
.s6_addr32
));
273 header
->dsfield
= __IPV6_TCLASS_FROM_FLOW(ip6h
->ip6_vcf
);
274 header
->proto
= ip6h
->ip6_nxt
;
275 header
->pktlen
= ntohs(ip6h
->ip6_plen
) +
276 ip_hdr_length_v6(mp
, ip6h
);
277 flowacct_port_info(header
, ip6h
, AF_INET6
, mp
);
284 /* Check if the flow (identified by the 5-tuple) exists in the hash table */
286 flowacct_flow_present(header_t
*header
, int index
,
287 flowacct_data_t
*flowacct_data
)
289 list_hdr_t
*hdr
= flowacct_data
->flows_tbl
[index
].head
;
292 while (hdr
!= NULL
) {
293 flow
= (flow_t
*)hdr
->objp
;
294 if ((flow
!= NULL
) &&
295 (IN6_ARE_ADDR_EQUAL(&flow
->saddr
, &header
->saddr
)) &&
296 (IN6_ARE_ADDR_EQUAL(&flow
->daddr
, &header
->daddr
)) &&
297 (flow
->proto
== header
->proto
) &&
298 (flow
->sport
== header
->sport
) &&
299 (flow
->dport
== header
->dport
)) {
304 return ((flow_t
*)NULL
);
308 * Add an object to the list at insert_point. This could be a flow item or
312 flowacct_add_obj(list_head_t
*tophdr
, list_hdr_t
*insert_point
, void *obj
)
316 if (tophdr
== NULL
) {
317 return ((list_hdr_t
*)NULL
);
320 new_hdr
= (list_hdr_t
*)kmem_zalloc(FLOWACCT_HDR_SZ
, KM_NOSLEEP
);
321 if (new_hdr
== NULL
) {
322 flowacct0dbg(("flowacct_add_obj: error allocating mem"));
323 return ((list_hdr_t
*)NULL
);
325 gethrestime(&new_hdr
->last_seen
);
329 if (insert_point
== NULL
) {
330 if (tophdr
->head
== NULL
) {
331 tophdr
->head
= new_hdr
;
332 tophdr
->tail
= new_hdr
;
336 new_hdr
->next
= tophdr
->head
;
337 tophdr
->head
->prev
= new_hdr
;
338 tophdr
->head
= new_hdr
;
342 if (insert_point
== tophdr
->tail
) {
343 tophdr
->tail
->next
= new_hdr
;
344 new_hdr
->prev
= tophdr
->tail
;
345 tophdr
->tail
= new_hdr
;
349 new_hdr
->next
= insert_point
->next
;
350 new_hdr
->prev
= insert_point
;
351 insert_point
->next
->prev
= new_hdr
;
352 insert_point
->next
= new_hdr
;
356 /* Delete an obj from the list. This could be a flow item or the flow itself */
358 flowacct_del_obj(list_head_t
*tophdr
, list_hdr_t
*hdr
, uint_t mode
)
363 if ((tophdr
== NULL
) || (hdr
== NULL
)) {
367 type
= ((flow_t
*)hdr
->objp
)->type
;
371 if (hdr
->next
!= NULL
) {
372 hdr
->next
->prev
= hdr
->prev
;
374 if (hdr
->prev
!= NULL
) {
375 hdr
->prev
->next
= hdr
->next
;
377 if (tophdr
->head
== hdr
) {
378 tophdr
->head
= hdr
->next
;
380 if (tophdr
->tail
== hdr
) {
381 tophdr
->tail
= hdr
->prev
;
384 if (mode
== FLOWACCT_DEL_OBJ
) {
387 length
= FLOWACCT_FLOW_SZ
;
390 length
= FLOWACCT_ITEM_SZ
;
393 kmem_free(hdr
->objp
, length
);
397 kmem_free((void *)hdr
, FLOWACCT_HDR_SZ
);
401 * Checks if the given item (identified by dsfield, project id and uid)
402 * is already present for the flow.
405 flowacct_item_present(flow_t
*flow
, uint8_t dsfield
, pid_t proj_id
, uint_t uid
)
410 itemhdr
= flow
->items
.head
;
412 while (itemhdr
!= NULL
) {
413 item
= (flow_item_t
*)itemhdr
->objp
;
415 if ((item
->dsfield
!= dsfield
) || (item
->projid
!= proj_id
) ||
416 (item
->uid
!= uid
)) {
417 itemhdr
= itemhdr
->next
;
423 return ((flow_item_t
*)NULL
);
427 * Add the flow to the table, if not already present. If the flow is
428 * present in the table, add the item. Also, update the flow stats.
429 * Additionally, re-adjust the timout list as well.
432 flowacct_update_flows_tbl(header_t
*header
, flowacct_data_t
*flowacct_data
)
438 boolean_t added_flow
= B_FALSE
;
443 index
= FLOWACCT_FLOW_HASH(header
);
444 fhead
= &flowacct_data
->flows_tbl
[index
];
446 /* The timeout list */
447 thead
= &flowacct_data
->flows_tbl
[FLOW_TBL_COUNT
];
449 mutex_enter(&fhead
->lock
);
450 flow
= flowacct_flow_present(header
, index
, flowacct_data
);
452 flow
= (flow_t
*)kmem_zalloc(FLOWACCT_FLOW_SZ
, KM_NOSLEEP
);
454 mutex_exit(&fhead
->lock
);
455 flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
459 flow
->hdr
= flowacct_add_obj(fhead
, fhead
->tail
, (void *)flow
);
460 if (flow
->hdr
== NULL
) {
461 mutex_exit(&fhead
->lock
);
462 kmem_free(flow
, FLOWACCT_FLOW_SZ
);
463 flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
468 flow
->type
= FLOWACCT_FLOW
;
469 flow
->isv4
= header
->isv4
;
470 bcopy(header
->saddr
.s6_addr32
, flow
->saddr
.s6_addr32
,
471 sizeof (header
->saddr
.s6_addr32
));
472 bcopy(header
->daddr
.s6_addr32
, flow
->daddr
.s6_addr32
,
473 sizeof (header
->daddr
.s6_addr32
));
474 flow
->proto
= header
->proto
;
475 flow
->sport
= header
->sport
;
476 flow
->dport
= header
->dport
;
477 flow
->back_ptr
= fhead
;
481 * We need to make sure that this 'flow' is not deleted
482 * either by a scheduled timeout or an explict call
483 * to flowacct_timer() below.
485 flow
->inuse
= B_TRUE
;
488 ihead
= &flow
->items
;
489 item
= flowacct_item_present(flow
, header
->dsfield
, header
->projid
,
492 boolean_t just_once
= B_TRUE
;
494 * For all practical purposes, we limit the no. of entries in
495 * the flow table - i.e. the max_limt that a user specifies is
496 * the maximum no. of flow items in the table.
499 atomic_inc_32(&flowacct_data
->nflows
);
500 if (flowacct_data
->nflows
> flowacct_data
->max_limit
) {
501 atomic_dec_32(&flowacct_data
->nflows
);
503 /* Try timing out once */
506 * Need to release the lock, as this entry
507 * could contain a flow that can be timed
510 mutex_exit(&fhead
->lock
);
511 flowacct_timer(FLOWACCT_JUST_ONE
,
513 mutex_enter(&fhead
->lock
);
514 /* Lets check again */
518 flow
->inuse
= B_FALSE
;
519 /* Need to remove the flow, if one was added */
521 flowacct_del_obj(fhead
, flow
->hdr
,
524 mutex_exit(&fhead
->lock
);
525 flowacct1dbg(("flowacct_update_flows_tbl: "\
526 "maximum active flows exceeded\n"));
530 item
= (flow_item_t
*)kmem_zalloc(FLOWACCT_ITEM_SZ
, KM_NOSLEEP
);
532 flow
->inuse
= B_FALSE
;
533 /* Need to remove the flow, if one was added */
535 flowacct_del_obj(fhead
, flow
->hdr
,
538 mutex_exit(&fhead
->lock
);
539 atomic_dec_32(&flowacct_data
->nflows
);
540 flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
544 item
->hdr
= flowacct_add_obj(ihead
, ihead
->tail
, (void *)item
);
545 if (item
->hdr
== NULL
) {
546 flow
->inuse
= B_FALSE
;
547 /* Need to remove the flow, if one was added */
549 flowacct_del_obj(fhead
, flow
->hdr
,
552 mutex_exit(&fhead
->lock
);
553 atomic_dec_32(&flowacct_data
->nflows
);
554 kmem_free(item
, FLOWACCT_ITEM_SZ
);
555 flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
559 /* If a flow was added, add it too */
561 atomic_add_64(&flowacct_data
->usedmem
,
562 FLOWACCT_FLOW_RECORD_SZ
);
564 atomic_add_64(&flowacct_data
->usedmem
, FLOWACCT_ITEM_RECORD_SZ
);
566 item
->type
= FLOWACCT_ITEM
;
567 item
->dsfield
= header
->dsfield
;
568 item
->projid
= header
->projid
;
569 item
->uid
= header
->uid
;
571 item
->nbytes
= header
->pktlen
;
572 item
->creation_time
= item
->hdr
->last_seen
;
575 item
->nbytes
+= header
->pktlen
;
578 flow
->hdr
->last_seen
= item
->hdr
->last_seen
= now
;
579 mutex_exit(&fhead
->lock
);
582 * Re-adjust the timeout list. The timer takes the thead lock
583 * follwed by fhead lock(s), so we release fhead, take thead
586 mutex_enter(&thead
->lock
);
587 mutex_enter(&fhead
->lock
);
588 /* If the flow was added, append it to the tail of the timeout list */
590 if (thead
->head
== NULL
) {
591 thead
->head
= flow
->hdr
;
592 thead
->tail
= flow
->hdr
;
594 thead
->tail
->timeout_next
= flow
->hdr
;
595 flow
->hdr
->timeout_prev
= thead
->tail
;
596 thead
->tail
= flow
->hdr
;
599 * Else, move this flow to the tail of the timeout list, if it is not
601 * flow->hdr in the timeout list :-
602 * timeout_next = NULL, timeout_prev != NULL, at the tail end.
603 * timeout_next != NULL, timeout_prev = NULL, at the head.
604 * timeout_next != NULL, timeout_prev != NULL, in the middle.
605 * timeout_next = NULL, timeout_prev = NULL, not in the timeout list,
608 } else if ((flow
->hdr
->timeout_next
!= NULL
) ||
609 (flow
->hdr
->timeout_prev
!= NULL
)) {
610 if (flow
->hdr
!= thead
->tail
) {
611 if (flow
->hdr
== thead
->head
) {
612 thead
->head
->timeout_next
->timeout_prev
= NULL
;
613 thead
->head
= thead
->head
->timeout_next
;
614 flow
->hdr
->timeout_next
= NULL
;
615 thead
->tail
->timeout_next
= flow
->hdr
;
616 flow
->hdr
->timeout_prev
= thead
->tail
;
617 thead
->tail
= flow
->hdr
;
619 flow
->hdr
->timeout_prev
->timeout_next
=
620 flow
->hdr
->timeout_next
;
621 flow
->hdr
->timeout_next
->timeout_prev
=
622 flow
->hdr
->timeout_prev
;
623 flow
->hdr
->timeout_next
= NULL
;
624 thead
->tail
->timeout_next
= flow
->hdr
;
625 flow
->hdr
->timeout_prev
= thead
->tail
;
626 thead
->tail
= flow
->hdr
;
631 * Unset this variable, now it is fine even if this
632 * flow gets deleted (i.e. after timing out its
633 * flow items) since we are done using it.
635 flow
->inuse
= B_FALSE
;
636 mutex_exit(&fhead
->lock
);
637 mutex_exit(&thead
->lock
);
638 atomic_add_64(&flowacct_data
->tbytes
, header
->pktlen
);
642 /* Timer for timing out flows/items from the flow table */
644 flowacct_timeout_flows(void *args
)
646 flowacct_data_t
*flowacct_data
= (flowacct_data_t
*)args
;
647 flowacct_timer(FLOWACCT_FLOW_TIMER
, flowacct_data
);
648 flowacct_data
->flow_tid
= timeout(flowacct_timeout_flows
, flowacct_data
,
649 drv_usectohz(flowacct_data
->timer
));
653 /* Delete the item from the flow in the flow table */
655 flowacct_timeout_item(flow_t
**flow
, list_hdr_t
**item_hdr
)
657 list_hdr_t
*next_it_hdr
;
659 next_it_hdr
= (*item_hdr
)->next
;
660 flowacct_del_obj(&(*flow
)->items
, *item_hdr
, FLOWACCT_DEL_OBJ
);
661 *item_hdr
= next_it_hdr
;
664 /* Create a flow record for this timed out item */
665 static flow_records_t
*
666 flowacct_create_record(flow_t
*flow
, list_hdr_t
*ithdr
)
669 flow_item_t
*item
= (flow_item_t
*)ithdr
->objp
;
670 flow_records_t
*tmp_frec
= NULL
;
672 /* Record to be written into the accounting file */
673 tmp_frec
= kmem_zalloc(sizeof (flow_records_t
), KM_NOSLEEP
);
674 if (tmp_frec
== NULL
) {
675 flowacct0dbg(("flowacct_create_record: mem alloc error.\n"));
678 tmp_frec
->fl_use
= kmem_zalloc(sizeof (flow_usage_t
), KM_NOSLEEP
);
679 if (tmp_frec
->fl_use
== NULL
) {
680 flowacct0dbg(("flowacct_create_record: mem alloc error\n"));
681 kmem_free(tmp_frec
, sizeof (flow_records_t
));
685 /* Copy the IP address */
686 for (count
= 0; count
< 4; count
++) {
687 tmp_frec
->fl_use
->fu_saddr
[count
] =
688 htonl(flow
->saddr
.s6_addr32
[count
]);
689 tmp_frec
->fl_use
->fu_daddr
[count
] =
690 htonl(flow
->daddr
.s6_addr32
[count
]);
694 * Ports, protocol, version, dsfield, project id, uid, nbytes, npackets
695 * creation time and last seen.
697 tmp_frec
->fl_use
->fu_sport
= htons(flow
->sport
);
698 tmp_frec
->fl_use
->fu_dport
= htons(flow
->dport
);
699 tmp_frec
->fl_use
->fu_protocol
= flow
->proto
;
700 tmp_frec
->fl_use
->fu_isv4
= flow
->isv4
;
701 tmp_frec
->fl_use
->fu_dsfield
= item
->dsfield
;
702 tmp_frec
->fl_use
->fu_projid
= item
->projid
;
703 tmp_frec
->fl_use
->fu_userid
= item
->uid
;
704 tmp_frec
->fl_use
->fu_nbytes
= item
->nbytes
;
705 tmp_frec
->fl_use
->fu_npackets
= item
->npackets
;
706 tmp_frec
->fl_use
->fu_lseen
=
707 (uint64_t)(ulong_t
)ithdr
->last_seen
.tv_sec
;
708 tmp_frec
->fl_use
->fu_ctime
=
709 (uint64_t)(ulong_t
)item
->creation_time
.tv_sec
;
715 * Scan thru the timeout list and write the records to the accounting file, if
716 * possible. Basically step thru the timeout list maintained in the last
717 * hash bucket, FLOW_COUNT_TBL + 1, and timeout flows. This could be called
718 * from the timer, FLOWACCT_TIMER - delete only timed out flows or when this
719 * instance is deleted, FLOWACCT_PURGE_FLOW - delete all the flows from the
720 * table or as FLOWACCT_JUST_ONE - delete the first timed out flow. Since the
721 * flows are cronologically arranged in the timeout list, when called as
722 * FLOWACCT_TIMER and FLOWACCT_JUST_ONE, we can stop when we come across
723 * the first flow that has not timed out (which means none of the following
724 * flows would have timed out).
727 flowacct_timer(int type
, flowacct_data_t
*flowacct_data
)
731 list_head_t
*head
, *thead
;
734 list_hdr_t
*fl_hdr
, *next_fl_hdr
;
735 list_hdr_t
*ithdr
= (list_hdr_t
*)NULL
;
736 flow_records_t
*frec
= NULL
, *tmp_frec
, *tail
;
740 ASSERT(flowacct_data
!= NULL
);
742 /* 2s-complement for subtraction */
743 flow_size
= ~FLOWACCT_FLOW_RECORD_SZ
+ 1;
744 item_size
= ~FLOWACCT_ITEM_RECORD_SZ
+ 1;
746 /* Get the current time */
750 * For each flow in the table, scan thru all the items and delete
751 * those that have exceeded the timeout. If all the items in a
752 * flow have timed out, delete the flow entry as well. Finally,
753 * write all the delted items to the accounting file.
755 thead
= &flowacct_data
->flows_tbl
[FLOW_TBL_COUNT
];
757 mutex_enter(&thead
->lock
);
758 fl_hdr
= thead
->head
;
759 while (fl_hdr
!= NULL
) {
760 uint32_t items_deleted
= 0;
762 next_fl_hdr
= fl_hdr
->timeout_next
;
763 flow
= (flow_t
*)fl_hdr
->objp
;
764 head
= flow
->back_ptr
;
765 mutex_enter(&head
->lock
);
768 FLOWACCT_DELTA(now
, fl_hdr
->last_seen
, diff
);
771 * If type is FLOW_TIMER, then check if the item has timed out.
772 * If type is FLOW_PURGE delete the entry anyways.
774 if ((type
!= FLOWACCT_PURGE_FLOW
) &&
775 (diff
< flowacct_data
->timeout
)) {
776 mutex_exit(&head
->lock
);
777 mutex_exit(&thead
->lock
);
781 ithdr
= flow
->items
.head
;
782 while (ithdr
!= NULL
) {
783 item
= (flow_item_t
*)ithdr
->objp
;
785 * Fill in the flow record to be
786 * written to the accounting file.
788 tmp_frec
= flowacct_create_record(flow
, ithdr
);
790 * If we don't have memory for records,
791 * we will come back in case this is
792 * called as FLOW_TIMER, else we will
793 * go ahead and delete the item from
794 * the table (when asked to PURGE the
795 * table), so there could be some
796 * entries not written to the file
797 * when this action instance is
800 if (tmp_frec
!= NULL
) {
801 tmp_frec
->fl_use
->fu_aname
=
802 flowacct_data
->act_name
;
807 tail
->next
= tmp_frec
;
810 } else if (type
!= FLOWACCT_PURGE_FLOW
) {
811 mutex_exit(&head
->lock
);
812 mutex_exit(&thead
->lock
);
813 atomic_add_32(&flowacct_data
->nflows
,
814 (~items_deleted
+ 1));
819 atomic_add_64(&flowacct_data
->tbytes
, (~item
->nbytes
+
822 /* Delete the item */
823 flowacct_timeout_item(&flow
, &ithdr
);
825 atomic_add_64(&flowacct_data
->usedmem
, item_size
);
827 ASSERT(flow
->items
.nbr_items
== 0);
828 atomic_add_32(&flowacct_data
->nflows
, (~items_deleted
+ 1));
831 * Don't delete this flow if we are making place for
832 * a new item for this flow.
835 if (fl_hdr
->timeout_prev
!= NULL
) {
836 fl_hdr
->timeout_prev
->timeout_next
=
837 fl_hdr
->timeout_next
;
839 thead
->head
= fl_hdr
->timeout_next
;
841 if (fl_hdr
->timeout_next
!= NULL
) {
842 fl_hdr
->timeout_next
->timeout_prev
=
843 fl_hdr
->timeout_prev
;
845 thead
->tail
= fl_hdr
->timeout_prev
;
847 fl_hdr
->timeout_prev
= NULL
;
848 fl_hdr
->timeout_next
= NULL
;
849 flowacct_del_obj(head
, fl_hdr
, FLOWACCT_DEL_OBJ
);
850 atomic_add_64(&flowacct_data
->usedmem
, flow_size
);
852 mutex_exit(&head
->lock
);
853 if (type
== FLOWACCT_JUST_ONE
) {
854 mutex_exit(&thead
->lock
);
857 fl_hdr
= next_fl_hdr
;
859 mutex_exit(&thead
->lock
);
861 /* Write all the timed out flows to the accounting file */
862 while (frec
!= NULL
) {
863 tmp_frec
= frec
->next
;
864 exacct_commit_flow(frec
->fl_use
);
865 kmem_free(frec
->fl_use
, sizeof (flow_usage_t
));
866 kmem_free(frec
, sizeof (flow_records_t
));
872 * Get the IP header contents from the packet, update the flow table with
873 * this item and return.
876 flowacct_process(mblk_t
**mpp
, flowacct_data_t
*flowacct_data
)
883 /* If we don't find an M_DATA, return error */
884 if (mp
->b_datap
->db_type
!= M_DATA
) {
885 if ((mp
->b_cont
!= NULL
) &&
886 (mp
->b_cont
->b_datap
->db_type
== M_DATA
)) {
889 flowacct0dbg(("flowacct_process: no data\n"));
890 atomic_inc_64(&flowacct_data
->epackets
);
895 header
= kmem_zalloc(FLOWACCT_HEADER_SZ
, KM_NOSLEEP
);
896 if (header
== NULL
) {
897 flowacct0dbg(("flowacct_process: error allocing mem"));
898 atomic_inc_64(&flowacct_data
->epackets
);
902 /* Get all the required information into header. */
903 if (flowacct_extract_header(mp
, header
) != 0) {
904 kmem_free(header
, FLOWACCT_HEADER_SZ
);
905 atomic_inc_64(&flowacct_data
->epackets
);
909 /* Updated the flow table with this entry */
910 if (flowacct_update_flows_tbl(header
, flowacct_data
) != 0) {
911 kmem_free(header
, FLOWACCT_HEADER_SZ
);
912 atomic_inc_64(&flowacct_data
->epackets
);
916 /* Update global stats */
917 atomic_inc_64(&flowacct_data
->npackets
);
918 atomic_add_64(&flowacct_data
->nbytes
, header
->pktlen
);
920 kmem_free(header
, FLOWACCT_HEADER_SZ
);
921 if (flowacct_data
->flow_tid
== 0) {
922 flowacct_data
->flow_tid
= timeout(flowacct_timeout_flows
,
923 flowacct_data
, drv_usectohz(flowacct_data
->timer
));