1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2002 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/kmem.h> 31 #include <sys/conf.h> 32 #include <sys/sysmacros.h> 33 #include <netinet/in.h> 34 #include <netinet/in_systm.h> 35 #include <netinet/ip6.h> 36 #include <inet/common.h> 37 #include <inet/ip.h> 38 #include <inet/ip6.h> 39 #include <ipp/meters/meter_impl.h> 40 41 /* 42 * Module : Single or Two Rate Metering module - tokenmt 43 * Description 44 * This module implements the metering part of RFC 2698 & 2697. It accepts the 45 * committed rate, peak rate (optional), committed burst and peak burst for a 46 * flow and determines if the flow is within the cfgd. rates and assigns 47 * next action appropriately.. 48 * If the peak rate is provided this acts as a two rate meter (RFC 2698), else 49 * a single rate meter (RFC 2697). If this is a two rate meter, then 50 * the outcome is either green, red or yellow. Else if this a single rate 51 * meter and the peak burst size is not provided, the outcome is either 52 * green or red. 53 * Internally, it maintains 2 token buckets, Tc & Tp, each filled with 54 * tokens equal to committed burst & peak burst respectively initially. 55 * When a packet arrives, tokens in Tc or Tp are updated at the committed 56 * or the peak rate up to a maximum of the committed or peak burst size. 57 * If there are enough tokens in Tc, the packet is Green, else if there are 58 * enough tokens in Tp, the packet is Yellow, else the packet is Red. In case 59 * of Green and Yellow packets, Tc and/or Tp is updated accordingly. 60 */ 61 62 int tokenmt_debug = 0; 63 64 /* Updating tokens */ 65 static void tokenmt_update_tokens(tokenmt_data_t *, hrtime_t); 66 67 /* 68 * Given a packet and the tokenmt_data it belongs to, this routine meters the 69 * ToS or DSCP for IPv4 and IPv6 resp. with the values configured for 70 * the tokenmt_data. 71 */ 72 int 73 tokenmt_process(mblk_t **mpp, tokenmt_data_t *tokenmt_data, 74 ipp_action_id_t *next_action) 75 { 76 uint8_t dscp; 77 ipha_t *ipha; 78 ip6_t *ip6_hdr; 79 uint32_t pkt_len; 80 mblk_t *mp = *mpp; 81 hrtime_t now; 82 enum meter_colour colour; 83 tokenmt_cfg_t *cfg_parms = tokenmt_data->cfg_parms; 84 85 if (mp == NULL) { 86 tokenmt0dbg(("tokenmt_process: null mp!\n")); 87 atomic_add_64(&tokenmt_data->epackets, 1); 88 return (EINVAL); 89 } 90 91 if (mp->b_datap->db_type != M_DATA) { 92 if ((mp->b_cont != NULL) && 93 (mp->b_cont->b_datap->db_type == M_DATA)) { 94 mp = mp->b_cont; 95 } else { 96 tokenmt0dbg(("tokenmt_process: no data\n")); 97 atomic_add_64(&tokenmt_data->epackets, 1); 98 return (EINVAL); 99 } 100 } 101 102 /* Figure out the ToS/Traffic Class and length from the message */ 103 if ((mp->b_wptr - mp->b_rptr) < IP_SIMPLE_HDR_LENGTH) { 104 if (!pullupmsg(mp, IP_SIMPLE_HDR_LENGTH)) { 105 tokenmt0dbg(("tokenmt_process: pullup error\n")); 106 atomic_add_64(&tokenmt_data->epackets, 1); 107 return (EINVAL); 108 } 109 } 110 ipha = (ipha_t *)mp->b_rptr; 111 if (IPH_HDR_VERSION(ipha) == IPV4_VERSION) { 112 /* discard last 2 unused bits */ 113 dscp = ipha->ipha_type_of_service; 114 pkt_len = ntohs(ipha->ipha_length); 115 } else { 116 ip6_hdr = (ip6_t *)mp->b_rptr; 117 /* discard ECN bits */ 118 dscp = __IPV6_TCLASS_FROM_FLOW(ip6_hdr->ip6_vcf); 119 pkt_len = ntohs(ip6_hdr->ip6_plen) + 120 ip_hdr_length_v6(mp, ip6_hdr); 121 } 122 123 /* Convert into bits */ 124 pkt_len <<= 3; 125 126 now = gethrtime(); 127 128 mutex_enter(&tokenmt_data->tokenmt_lock); 129 /* Update the token counts */ 130 tokenmt_update_tokens(tokenmt_data, now); 131 132 /* 133 * Figure out the drop preced. for the pkt. Need to be careful here 134 * because if the mode is set to COLOUR_AWARE, then the dscp value 135 * is used regardless of whether it was explicitly set or not. 136 * If the value is defaulted to 000 (drop precd.) then the pkt 137 * will always be coloured RED. 138 */ 139 if (cfg_parms->tokenmt_type == SRTCL_TOKENMT) { 140 if (!cfg_parms->colour_aware) { 141 if (pkt_len <= tokenmt_data->committed_tokens) { 142 tokenmt_data->committed_tokens -= pkt_len; 143 *next_action = cfg_parms->green_action; 144 } else if (pkt_len <= tokenmt_data->peak_tokens) { 145 /* 146 * Can't do this if yellow_action is not 147 * configured. 148 */ 149 ASSERT(cfg_parms->yellow_action != 150 TOKENMT_NO_ACTION); 151 tokenmt_data->peak_tokens -= pkt_len; 152 *next_action = cfg_parms->yellow_action; 153 } else { 154 *next_action = cfg_parms->red_action; 155 } 156 } else { 157 colour = cfg_parms->dscp_to_colour[dscp >> 2]; 158 if ((colour == TOKENMT_GREEN) && 159 (pkt_len <= tokenmt_data->committed_tokens)) { 160 tokenmt_data->committed_tokens -= pkt_len; 161 *next_action = cfg_parms->green_action; 162 } else if (((colour == TOKENMT_GREEN) || 163 (colour == TOKENMT_YELLOW)) && 164 (pkt_len <= tokenmt_data->peak_tokens)) { 165 /* 166 * Can't do this if yellow_action is not 167 * configured. 168 */ 169 ASSERT(cfg_parms->yellow_action != 170 TOKENMT_NO_ACTION); 171 tokenmt_data->peak_tokens -= pkt_len; 172 *next_action = cfg_parms->yellow_action; 173 } else { 174 *next_action = cfg_parms->red_action; 175 } 176 } 177 } else { 178 if (!cfg_parms->colour_aware) { 179 if (pkt_len > tokenmt_data->peak_tokens) { 180 *next_action = cfg_parms->red_action; 181 } else if (pkt_len > tokenmt_data->committed_tokens) { 182 /* 183 * Can't do this if yellow_action is not 184 * configured. 185 */ 186 ASSERT(cfg_parms->yellow_action != 187 TOKENMT_NO_ACTION); 188 tokenmt_data->peak_tokens -= pkt_len; 189 *next_action = cfg_parms->yellow_action; 190 } else { 191 tokenmt_data->committed_tokens -= pkt_len; 192 tokenmt_data->peak_tokens -= pkt_len; 193 *next_action = cfg_parms->green_action; 194 } 195 } else { 196 colour = cfg_parms->dscp_to_colour[dscp >> 2]; 197 if ((colour == TOKENMT_RED) || 198 (pkt_len > tokenmt_data->peak_tokens)) { 199 *next_action = cfg_parms->red_action; 200 } else if ((colour == TOKENMT_YELLOW) || 201 (pkt_len > tokenmt_data->committed_tokens)) { 202 /* 203 * Can't do this if yellow_action is not 204 * configured. 205 */ 206 ASSERT(cfg_parms->yellow_action != 207 TOKENMT_NO_ACTION); 208 tokenmt_data->peak_tokens -= pkt_len; 209 *next_action = cfg_parms->yellow_action; 210 } else { 211 tokenmt_data->committed_tokens -= pkt_len; 212 tokenmt_data->peak_tokens -= pkt_len; 213 *next_action = cfg_parms->green_action; 214 } 215 } 216 } 217 mutex_exit(&tokenmt_data->tokenmt_lock); 218 219 /* Update Stats */ 220 if (*next_action == cfg_parms->green_action) { 221 atomic_add_64(&tokenmt_data->green_packets, 1); 222 atomic_add_64(&tokenmt_data->green_bits, pkt_len); 223 } else if (*next_action == cfg_parms->yellow_action) { 224 atomic_add_64(&tokenmt_data->yellow_packets, 1); 225 atomic_add_64(&tokenmt_data->yellow_bits, pkt_len); 226 } else { 227 ASSERT(*next_action == cfg_parms->red_action); 228 atomic_add_64(&tokenmt_data->red_packets, 1); 229 atomic_add_64(&tokenmt_data->red_bits, pkt_len); 230 } 231 232 return (0); 233 } 234 235 void 236 tokenmt_update_tokens(tokenmt_data_t *tokenmt_data, hrtime_t now) 237 { 238 tokenmt_cfg_t *cfg_parms = (tokenmt_cfg_t *)tokenmt_data->cfg_parms; 239 hrtime_t diff = now - tokenmt_data->last_seen; 240 uint64_t tokens; 241 242 switch (cfg_parms->tokenmt_type) { 243 case SRTCL_TOKENMT: 244 tokens = (cfg_parms->committed_rate * diff) / 245 METER_SEC_TO_NSEC; 246 247 /* 248 * Add tokens at the committed rate to 249 * committed_tokens. If they are in excess of 250 * the committed burst, add the excess to 251 * peak_tokens, capped to peak_burst. 252 */ 253 if ((tokenmt_data->committed_tokens + tokens) > 254 cfg_parms->committed_burst) { 255 tokens = tokenmt_data->committed_tokens 256 + tokens - 257 cfg_parms->committed_burst; 258 tokenmt_data->committed_tokens = 259 cfg_parms->committed_burst; 260 tokenmt_data->peak_tokens = 261 MIN(cfg_parms->peak_burst, 262 tokenmt_data->peak_tokens + 263 tokens); 264 } else { 265 tokenmt_data->committed_tokens += 266 tokens; 267 } 268 break; 269 case TRTCL_TOKENMT: 270 /* Fill at the committed rate */ 271 tokens = (diff * cfg_parms->committed_rate) / 272 METER_SEC_TO_NSEC; 273 tokenmt_data->committed_tokens = 274 MIN(cfg_parms->committed_burst, 275 tokenmt_data->committed_tokens + tokens); 276 277 /* Fill at the peak rate */ 278 tokens = (diff * cfg_parms->peak_rate) / 279 METER_SEC_TO_NSEC; 280 tokenmt_data->peak_tokens = 281 MIN(cfg_parms->peak_burst, 282 tokenmt_data->peak_tokens + tokens); 283 break; 284 } 285 tokenmt_data->last_seen = now; 286 } 287