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