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