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
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.
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]
23 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/types.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>
37 #include <ipp/meters/meter_impl.h>
40 * Module : Single or Two Rate Metering module - tokenmt
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
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.
60 int tokenmt_debug
= 0;
63 static void tokenmt_update_tokens(tokenmt_data_t
*, hrtime_t
);
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
71 tokenmt_process(mblk_t
**mpp
, tokenmt_data_t
*tokenmt_data
,
72 ipp_action_id_t
*next_action
)
80 enum meter_colour colour
;
81 tokenmt_cfg_t
*cfg_parms
= tokenmt_data
->cfg_parms
;
84 tokenmt0dbg(("tokenmt_process: null mp!\n"));
85 atomic_inc_64(&tokenmt_data
->epackets
);
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
)) {
94 tokenmt0dbg(("tokenmt_process: no data\n"));
95 atomic_inc_64(&tokenmt_data
->epackets
);
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
);
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
);
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
);
121 /* Convert into bits */
126 mutex_enter(&tokenmt_data
->tokenmt_lock
);
127 /* Update the token counts */
128 tokenmt_update_tokens(tokenmt_data
, now
);
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.
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
) {
144 * Can't do this if yellow_action is not
147 ASSERT(cfg_parms
->yellow_action
!=
149 tokenmt_data
->peak_tokens
-= pkt_len
;
150 *next_action
= cfg_parms
->yellow_action
;
152 *next_action
= cfg_parms
->red_action
;
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
)) {
164 * Can't do this if yellow_action is not
167 ASSERT(cfg_parms
->yellow_action
!=
169 tokenmt_data
->peak_tokens
-= pkt_len
;
170 *next_action
= cfg_parms
->yellow_action
;
172 *next_action
= cfg_parms
->red_action
;
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
) {
181 * Can't do this if yellow_action is not
184 ASSERT(cfg_parms
->yellow_action
!=
186 tokenmt_data
->peak_tokens
-= pkt_len
;
187 *next_action
= cfg_parms
->yellow_action
;
189 tokenmt_data
->committed_tokens
-= pkt_len
;
190 tokenmt_data
->peak_tokens
-= pkt_len
;
191 *next_action
= cfg_parms
->green_action
;
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
)) {
201 * Can't do this if yellow_action is not
204 ASSERT(cfg_parms
->yellow_action
!=
206 tokenmt_data
->peak_tokens
-= pkt_len
;
207 *next_action
= cfg_parms
->yellow_action
;
209 tokenmt_data
->committed_tokens
-= pkt_len
;
210 tokenmt_data
->peak_tokens
-= pkt_len
;
211 *next_action
= cfg_parms
->green_action
;
215 mutex_exit(&tokenmt_data
->tokenmt_lock
);
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
);
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
);
234 tokenmt_update_tokens(tokenmt_data_t
*tokenmt_data
, hrtime_t now
)
236 tokenmt_cfg_t
*cfg_parms
= (tokenmt_cfg_t
*)tokenmt_data
->cfg_parms
;
237 hrtime_t diff
= now
- tokenmt_data
->last_seen
;
240 switch (cfg_parms
->tokenmt_type
) {
242 tokens
= (cfg_parms
->committed_rate
* diff
) /
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.
251 if ((tokenmt_data
->committed_tokens
+ tokens
) >
252 cfg_parms
->committed_burst
) {
253 tokens
= tokenmt_data
->committed_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
+
263 tokenmt_data
->committed_tokens
+=
268 /* Fill at the committed rate */
269 tokens
= (diff
* cfg_parms
->committed_rate
) /
271 tokenmt_data
->committed_tokens
=
272 MIN(cfg_parms
->committed_burst
,
273 tokenmt_data
->committed_tokens
+ tokens
);
275 /* Fill at the peak rate */
276 tokens
= (diff
* cfg_parms
->peak_rate
) /
278 tokenmt_data
->peak_tokens
=
279 MIN(cfg_parms
->peak_burst
,
280 tokenmt_data
->peak_tokens
+ tokens
);
283 tokenmt_data
->last_seen
= now
;