flowacct.c revision 89ce534e7c3d320328321dbabb60ddda2a6a0e20
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <netinet/in_systm.h>
/*
* flowacct - IPQoS accounting module. The module maintains an array
* of 256 hash buckets. When the action routine is invoked for a flow,
* if the flow (identified by the 5-tuple: saddr, daddr, sport, dport, proto)
* is already present in the flow table (indexed by the hash function FLOW_HASH)
* then a check is made to see if an item for this flow with the same
* dsfield, projid & user id is present. If it is, then the number of packets
* and the bytes are incremented for that item. If the item does
* not exist a new item is added for the flow. If the flow is not present
* an entry is made for the flow.
*
* A timer runs thru the table and writes all the flow items that have
* timed out to the accounting file (via exacct PSARC/1999/119), if present
* Configuration commands to change the timing interval is provided. The
* flow timeout value can also be configured. While the timeout is in nsec,
* the flow timer interval is in usec.
* Information for an active flow can be obtained by using kstats.
*/
/* Used in computing the hash index */
#define FLOWACCT_ADDR_HASH(addr) \
#define FLOWACCT_FLOW_HASH(f) \
(((FLOWACCT_ADDR_HASH(f->saddr)) + \
(FLOWACCT_ADDR_HASH(f->daddr)) + \
/*
* Compute difference between a and b in nsec and store in delta.
* delta should be a hrtime_t. Taken from ip_mroute.c.
*/
#define FLOWACCT_DELTA(a, b, delta) { \
int xxs; \
\
switch (xxs) { \
case 2: \
/*FALLTHRU*/ \
case 1: \
break; \
default: \
} \
} \
}
/* Debug level */
int flowacct_debug = 0;
/* Collect timed out flows to be written to the accounting file */
typedef struct flow_records_s {
struct flow_records_s *next;
/* Get port information from the packet. Ignore fragments. */
static void
{
if (u1 != 0) {
return;
}
} else {
switch (*nexthdrp) {
case IPPROTO_HOPOPTS:
return;
break;
case IPPROTO_DSTOPTS:
return;
break;
case IPPROTO_ROUTING:
return;
break;
case IPPROTO_FRAGMENT:
return;
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_SCTP:
/*
* Verify we have at least ICMP_MIN_TP_HDR_LEN
* bytes of the ULP's header to get the port
* info.
*/
ICMP_MIN_TP_HDR_LEN) > endptr) {
return;
}
/* Get the protocol & ports */
return;
case IPPROTO_ICMPV6:
case IPPROTO_ENCAP:
case IPPROTO_IPV6:
case IPPROTO_ESP:
case IPPROTO_AH:
return;
case IPPROTO_NONE:
default:
return;
}
}
}
}
/*
* flowacct_find_ids(mp, header)
*
* attempt to discern the uid and projid of the originator of a packet by
* looking at the dblks making up the packet - yeuch!
*
* We do it by skipping any fragments with a credp of NULL (originated in
* kernel), taking the first value that isn't NULL to be the cred_t for the
* whole packet.
*/
static void
{
} else {
}
}
/*
* Extract header information in a header_t structure so that we don't have
* have to parse the packet everytime.
*/
static int
{
/* 0 means no port extracted. */
}
} else {
/*
* Need to pullup everything.
*/
flowacct0dbg(("flowacct_extract_header: "\
"pullup error"));
return (-1);
}
}
}
return (0);
}
/* Check if the flow (identified by the 5-tuple) exists in the hash table */
static flow_t *
{
return (flow);
}
}
}
/*
* Add an object to the list at insert_point. This could be a flow item or
* a flow itself.
*/
static list_hdr_t *
{
return ((list_hdr_t *)NULL);
}
flowacct0dbg(("flowacct_add_obj: error allocating mem"));
return ((list_hdr_t *)NULL);
}
if (insert_point == NULL) {
return (new_hdr);
}
return (new_hdr);
}
return (new_hdr);
}
return (new_hdr);
}
/* Delete an obj from the list. This could be a flow item or the flow itself */
static void
{
return;
}
}
}
}
}
if (mode == FLOWACCT_DEL_OBJ) {
switch (type) {
case FLOWACCT_FLOW:
break;
case FLOWACCT_ITEM:
break;
}
}
}
/*
* Checks if the given item (identified by dsfield, project id and uid)
* is already present for the flow.
*/
static flow_item_t *
{
continue;
}
return (item);
}
return ((flow_item_t *)NULL);
}
/*
* Add the flow to the table, if not already present. If the flow is
* present in the table, add the item. Also, update the flow stats.
* Additionally, re-adjust the timout list as well.
*/
static int
{
int index;
/* The timeout list */
flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
"error"));
return (-1);
}
flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
"error"));
return (-1);
}
added_flow = B_TRUE;
} else {
/*
* We need to make sure that this 'flow' is not deleted
* either by a scheduled timeout or an explict call
* to flowacct_timer() below.
*/
}
/*
* For all practical purposes, we limit the no. of entries in
* the flow table - i.e. the max_limt that a user specifies is
* the maximum no. of flow items in the table.
*/
/* Try timing out once */
if (just_once) {
/*
* Need to release the lock, as this entry
* could contain a flow that can be timed
* out.
*/
/* Lets check again */
goto try_again;
} else {
/* Need to remove the flow, if one was added */
if (added_flow) {
}
flowacct1dbg(("flowacct_update_flows_tbl: "\
"maximum active flows exceeded\n"));
return (-1);
}
}
/* Need to remove the flow, if one was added */
if (added_flow) {
}
flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
"error"));
return (-1);
}
/* Need to remove the flow, if one was added */
if (added_flow) {
}
flowacct0dbg(("flowacct_update_flows_tbl: mem alloc "\
"error\n"));
return (-1);
}
/* If a flow was added, add it too */
if (added_flow) {
}
} else {
}
gethrestime(&now);
/*
* Re-adjust the timeout list. The timer takes the thead lock
* follwed by fhead lock(s), so we release fhead, take thead
* and re-take fhead.
*/
/* If the flow was added, append it to the tail of the timeout list */
if (added_flow) {
} else {
}
/*
* Else, move this flow to the tail of the timeout list, if it is not
* already.
* flow->hdr in the timeout list :-
* timeout_next = NULL, timeout_prev != NULL, at the tail end.
* timeout_next != NULL, timeout_prev = NULL, at the head.
* timeout_next != NULL, timeout_prev != NULL, in the middle.
* timeout_next = NULL, timeout_prev = NULL, not in the timeout list,
* ignore such flow.
*/
} else {
}
}
}
/*
* Unset this variable, now it is fine even if this
* flow gets deleted (i.e. after timing out its
* flow items) since we are done using it.
*/
return (0);
}
void
flowacct_timeout_flows(void *args)
{
}
/* Delete the item from the flow in the flow table */
static void
{
*item_hdr = next_it_hdr;
}
/* Create a flow record for this timed out item */
static flow_records_t *
{
int count;
/* Record to be written into the accounting file */
flowacct0dbg(("flowacct_create_record: mem alloc error.\n"));
return (NULL);
}
flowacct0dbg(("flowacct_create_record: mem alloc error\n"));
return (NULL);
}
/* Copy the IP address */
}
/*
* Ports, protocol, version, dsfield, project id, uid, nbytes, npackets
* creation time and last seen.
*/
return (tmp_frec);
}
/*
* Scan thru the timeout list and write the records to the accounting file, if
* possible. Basically step thru the timeout list maintained in the last
* hash bucket, FLOW_COUNT_TBL + 1, and timeout flows. This could be called
* from the timer, FLOWACCT_TIMER - delete only timed out flows or when this
* instance is deleted, FLOWACCT_PURGE_FLOW - delete all the flows from the
* table or as FLOWACCT_JUST_ONE - delete the first timed out flow. Since the
* flows are cronologically arranged in the timeout list, when called as
* FLOWACCT_TIMER and FLOWACCT_JUST_ONE, we can stop when we come across
* the first flow that has not timed out (which means none of the following
* flows would have timed out).
*/
void
{
/* 2s-complement for subtraction */
/* Get the current time */
gethrestime(&now);
/*
* For each flow in the table, scan thru all the items and delete
* those that have exceeded the timeout. If all the items in a
* flow have timed out, delete the flow entry as well. Finally,
* write all the delted items to the accounting file.
*/
uint32_t items_deleted = 0;
/*LINTED*/
/*
* If type is FLOW_TIMER, then check if the item has timed out.
* If type is FLOW_PURGE delete the entry anyways.
*/
if ((type != FLOWACCT_PURGE_FLOW) &&
goto write_records;
}
/*
* Fill in the flow record to be
* written to the accounting file.
*/
/*
* If we don't have memory for records,
* we will come back in case this is
* called as FLOW_TIMER, else we will
* go ahead and delete the item from
* the table (when asked to PURGE the
* table), so there could be some
* entries not written to the file
* when this action instance is
* deleted.
*/
} else {
}
} else if (type != FLOWACCT_PURGE_FLOW) {
(~items_deleted + 1));
goto write_records;
}
/* Update stats */
1));
/* Delete the item */
}
/*
* Don't delete this flow if we are making place for
* a new item for this flow.
*/
} else {
}
} else {
}
}
if (type == FLOWACCT_JUST_ONE) {
goto write_records;
}
}
/* Write all the timed out flows to the accounting file */
}
}
/*
* Get the IP header contents from the packet, update the flow table with
* this item and return.
*/
int
{
/* If we don't find an M_DATA, return error */
} else {
flowacct0dbg(("flowacct_process: no data\n"));
return (EINVAL);
}
}
flowacct0dbg(("flowacct_process: error allocing mem"));
return (ENOMEM);
}
/* Get all the required information into header. */
return (EINVAL);
}
/* Updated the flow table with this entry */
return (ENOMEM);
}
/* Update global stats */
if (flowacct_data->flow_tid == 0) {
}
return (0);
}