#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/sysmacros.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip6.h>
#include <inet/common.h>
#include <inet/ip.h>
#include <inet/ip6.h>
#include <ipp/meters/meter_impl.h>
int tokenmt_debug = 0;
static void tokenmt_update_tokens(tokenmt_data_t *, hrtime_t);
int
tokenmt_process(mblk_t **mpp, tokenmt_data_t *tokenmt_data,
ipp_action_id_t *next_action)
{
uint8_t dscp;
ipha_t *ipha;
ip6_t *ip6_hdr;
uint32_t pkt_len;
mblk_t *mp = *mpp;
hrtime_t now;
enum meter_colour colour;
tokenmt_cfg_t *cfg_parms = tokenmt_data->cfg_parms;
if (mp == NULL) {
tokenmt0dbg(("tokenmt_process: null mp!\n"));
atomic_inc_64(&tokenmt_data->epackets);
return (EINVAL);
}
if (mp->b_datap->db_type != M_DATA) {
if ((mp->b_cont != NULL) &&
(mp->b_cont->b_datap->db_type == M_DATA)) {
mp = mp->b_cont;
} else {
tokenmt0dbg(("tokenmt_process: no data\n"));
atomic_inc_64(&tokenmt_data->epackets);
return (EINVAL);
}
}
if ((mp->b_wptr - mp->b_rptr) < IP_SIMPLE_HDR_LENGTH) {
if (!pullupmsg(mp, IP_SIMPLE_HDR_LENGTH)) {
tokenmt0dbg(("tokenmt_process: pullup error\n"));
atomic_inc_64(&tokenmt_data->epackets);
return (EINVAL);
}
}
ipha = (ipha_t *)mp->b_rptr;
if (IPH_HDR_VERSION(ipha) == IPV4_VERSION) {
dscp = ipha->ipha_type_of_service;
pkt_len = ntohs(ipha->ipha_length);
} else {
ip6_hdr = (ip6_t *)mp->b_rptr;
dscp = __IPV6_TCLASS_FROM_FLOW(ip6_hdr->ip6_vcf);
pkt_len = ntohs(ip6_hdr->ip6_plen) +
ip_hdr_length_v6(mp, ip6_hdr);
}
pkt_len <<= 3;
now = gethrtime();
mutex_enter(&tokenmt_data->tokenmt_lock);
tokenmt_update_tokens(tokenmt_data, now);
if (cfg_parms->tokenmt_type == SRTCL_TOKENMT) {
if (!cfg_parms->colour_aware) {
if (pkt_len <= tokenmt_data->committed_tokens) {
tokenmt_data->committed_tokens -= pkt_len;
*next_action = cfg_parms->green_action;
} else if (pkt_len <= tokenmt_data->peak_tokens) {
ASSERT(cfg_parms->yellow_action !=
TOKENMT_NO_ACTION);
tokenmt_data->peak_tokens -= pkt_len;
*next_action = cfg_parms->yellow_action;
} else {
*next_action = cfg_parms->red_action;
}
} else {
colour = cfg_parms->dscp_to_colour[dscp >> 2];
if ((colour == TOKENMT_GREEN) &&
(pkt_len <= tokenmt_data->committed_tokens)) {
tokenmt_data->committed_tokens -= pkt_len;
*next_action = cfg_parms->green_action;
} else if (((colour == TOKENMT_GREEN) ||
(colour == TOKENMT_YELLOW)) &&
(pkt_len <= tokenmt_data->peak_tokens)) {
ASSERT(cfg_parms->yellow_action !=
TOKENMT_NO_ACTION);
tokenmt_data->peak_tokens -= pkt_len;
*next_action = cfg_parms->yellow_action;
} else {
*next_action = cfg_parms->red_action;
}
}
} else {
if (!cfg_parms->colour_aware) {
if (pkt_len > tokenmt_data->peak_tokens) {
*next_action = cfg_parms->red_action;
} else if (pkt_len > tokenmt_data->committed_tokens) {
ASSERT(cfg_parms->yellow_action !=
TOKENMT_NO_ACTION);
tokenmt_data->peak_tokens -= pkt_len;
*next_action = cfg_parms->yellow_action;
} else {
tokenmt_data->committed_tokens -= pkt_len;
tokenmt_data->peak_tokens -= pkt_len;
*next_action = cfg_parms->green_action;
}
} else {
colour = cfg_parms->dscp_to_colour[dscp >> 2];
if ((colour == TOKENMT_RED) ||
(pkt_len > tokenmt_data->peak_tokens)) {
*next_action = cfg_parms->red_action;
} else if ((colour == TOKENMT_YELLOW) ||
(pkt_len > tokenmt_data->committed_tokens)) {
ASSERT(cfg_parms->yellow_action !=
TOKENMT_NO_ACTION);
tokenmt_data->peak_tokens -= pkt_len;
*next_action = cfg_parms->yellow_action;
} else {
tokenmt_data->committed_tokens -= pkt_len;
tokenmt_data->peak_tokens -= pkt_len;
*next_action = cfg_parms->green_action;
}
}
}
mutex_exit(&tokenmt_data->tokenmt_lock);
if (*next_action == cfg_parms->green_action) {
atomic_inc_64(&tokenmt_data->green_packets);
atomic_add_64(&tokenmt_data->green_bits, pkt_len);
} else if (*next_action == cfg_parms->yellow_action) {
atomic_inc_64(&tokenmt_data->yellow_packets);
atomic_add_64(&tokenmt_data->yellow_bits, pkt_len);
} else {
ASSERT(*next_action == cfg_parms->red_action);
atomic_inc_64(&tokenmt_data->red_packets);
atomic_add_64(&tokenmt_data->red_bits, pkt_len);
}
return (0);
}
void
tokenmt_update_tokens(tokenmt_data_t *tokenmt_data, hrtime_t now)
{
tokenmt_cfg_t *cfg_parms = (tokenmt_cfg_t *)tokenmt_data->cfg_parms;
hrtime_t diff = now - tokenmt_data->last_seen;
uint64_t tokens;
switch (cfg_parms->tokenmt_type) {
case SRTCL_TOKENMT:
tokens = (cfg_parms->committed_rate * diff) /
METER_SEC_TO_NSEC;
if ((tokenmt_data->committed_tokens + tokens) >
cfg_parms->committed_burst) {
tokens = tokenmt_data->committed_tokens
+ tokens -
cfg_parms->committed_burst;
tokenmt_data->committed_tokens =
cfg_parms->committed_burst;
tokenmt_data->peak_tokens =
MIN(cfg_parms->peak_burst,
tokenmt_data->peak_tokens +
tokens);
} else {
tokenmt_data->committed_tokens +=
tokens;
}
break;
case TRTCL_TOKENMT:
tokens = (diff * cfg_parms->committed_rate) /
METER_SEC_TO_NSEC;
tokenmt_data->committed_tokens =
MIN(cfg_parms->committed_burst,
tokenmt_data->committed_tokens + tokens);
tokens = (diff * cfg_parms->peak_rate) /
METER_SEC_TO_NSEC;
tokenmt_data->peak_tokens =
MIN(cfg_parms->peak_burst,
tokenmt_data->peak_tokens + tokens);
break;
}
tokenmt_data->last_seen = now;
}