1 1.23 joe /* $NetBSD: altq_cdnr.c,v 1.23 2025/01/08 13:00:04 joe Exp $ */ 2 1.16 peter /* $KAME: altq_cdnr.c,v 1.15 2005/04/13 03:44:24 suz Exp $ */ 3 1.1 thorpej 4 1.1 thorpej /* 5 1.16 peter * Copyright (C) 1999-2002 6 1.1 thorpej * Sony Computer Science Laboratories Inc. All rights reserved. 7 1.1 thorpej * 8 1.1 thorpej * Redistribution and use in source and binary forms, with or without 9 1.1 thorpej * modification, are permitted provided that the following conditions 10 1.1 thorpej * are met: 11 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 12 1.1 thorpej * notice, this list of conditions and the following disclaimer. 13 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 15 1.1 thorpej * documentation and/or other materials provided with the distribution. 16 1.1 thorpej * 17 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND 18 1.1 thorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 1.1 thorpej * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 1.1 thorpej * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE 21 1.1 thorpej * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 1.1 thorpej * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 1.1 thorpej * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 1.1 thorpej * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 1.1 thorpej * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 1.1 thorpej * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 1.1 thorpej * SUCH DAMAGE. 28 1.1 thorpej */ 29 1.4 lukem 30 1.4 lukem #include <sys/cdefs.h> 31 1.23 joe __KERNEL_RCSID(0, "$NetBSD: altq_cdnr.c,v 1.23 2025/01/08 13:00:04 joe Exp $"); 32 1.1 thorpej 33 1.16 peter #ifdef _KERNEL_OPT 34 1.1 thorpej #include "opt_altq.h" 35 1.1 thorpej #include "opt_inet.h" 36 1.1 thorpej #endif 37 1.1 thorpej 38 1.1 thorpej #include <sys/param.h> 39 1.1 thorpej #include <sys/malloc.h> 40 1.1 thorpej #include <sys/mbuf.h> 41 1.1 thorpej #include <sys/socket.h> 42 1.1 thorpej #include <sys/sockio.h> 43 1.1 thorpej #include <sys/systm.h> 44 1.1 thorpej #include <sys/proc.h> 45 1.1 thorpej #include <sys/errno.h> 46 1.1 thorpej #include <sys/kernel.h> 47 1.1 thorpej #include <sys/queue.h> 48 1.13 christos #include <sys/kauth.h> 49 1.20 tls #include <sys/cprng.h> 50 1.1 thorpej 51 1.1 thorpej #include <net/if.h> 52 1.1 thorpej #include <net/if_types.h> 53 1.1 thorpej #include <netinet/in.h> 54 1.1 thorpej #include <netinet/in_systm.h> 55 1.1 thorpej #include <netinet/ip.h> 56 1.1 thorpej #ifdef INET6 57 1.1 thorpej #include <netinet/ip6.h> 58 1.1 thorpej #endif 59 1.1 thorpej 60 1.1 thorpej #include <altq/altq.h> 61 1.1 thorpej #include <altq/altq_conf.h> 62 1.1 thorpej #include <altq/altq_cdnr.h> 63 1.1 thorpej 64 1.16 peter #ifdef ALTQ3_COMPAT 65 1.1 thorpej /* 66 1.1 thorpej * diffserv traffic conditioning module 67 1.1 thorpej */ 68 1.1 thorpej 69 1.1 thorpej int altq_cdnr_enabled = 0; 70 1.1 thorpej 71 1.1 thorpej /* traffic conditioner is enabled by ALTQ_CDNR option in opt_altq.h */ 72 1.1 thorpej #ifdef ALTQ_CDNR 73 1.1 thorpej 74 1.1 thorpej /* cdnr_list keeps all cdnr's allocated. */ 75 1.1 thorpej static LIST_HEAD(, top_cdnr) tcb_list; 76 1.1 thorpej 77 1.16 peter static int altq_cdnr_input(struct mbuf *, int); 78 1.16 peter static struct top_cdnr *tcb_lookup(char *ifname); 79 1.16 peter static struct cdnr_block *cdnr_handle2cb(u_long); 80 1.16 peter static u_long cdnr_cb2handle(struct cdnr_block *); 81 1.16 peter static void *cdnr_cballoc(struct top_cdnr *, int, 82 1.16 peter struct tc_action *(*)(struct cdnr_block *, struct cdnr_pktinfo *)); 83 1.16 peter static void cdnr_cbdestroy(void *); 84 1.16 peter static int tca_verify_action(struct tc_action *); 85 1.16 peter static void tca_import_action(struct tc_action *, struct tc_action *); 86 1.16 peter static void tca_invalidate_action(struct tc_action *); 87 1.16 peter 88 1.16 peter static int generic_element_destroy(struct cdnr_block *); 89 1.16 peter static struct top_cdnr *top_create(struct ifaltq *); 90 1.16 peter static int top_destroy(struct top_cdnr *); 91 1.16 peter static struct cdnr_block *element_create(struct top_cdnr *, struct tc_action *); 92 1.16 peter static int element_destroy(struct cdnr_block *); 93 1.16 peter static void tb_import_profile(struct tbe *, struct tb_profile *); 94 1.16 peter static struct tbmeter *tbm_create(struct top_cdnr *, struct tb_profile *, 95 1.16 peter struct tc_action *, struct tc_action *); 96 1.16 peter static int tbm_destroy(struct tbmeter *); 97 1.16 peter static struct tc_action *tbm_input(struct cdnr_block *, struct cdnr_pktinfo *); 98 1.16 peter static struct trtcm *trtcm_create(struct top_cdnr *, 99 1.1 thorpej struct tb_profile *, struct tb_profile *, 100 1.1 thorpej struct tc_action *, struct tc_action *, struct tc_action *, 101 1.16 peter int); 102 1.16 peter static int trtcm_destroy(struct trtcm *); 103 1.16 peter static struct tc_action *trtcm_input(struct cdnr_block *, struct cdnr_pktinfo *); 104 1.16 peter static struct tswtcm *tswtcm_create(struct top_cdnr *, 105 1.1 thorpej u_int32_t, u_int32_t, u_int32_t, 106 1.16 peter struct tc_action *, struct tc_action *, struct tc_action *); 107 1.16 peter static int tswtcm_destroy(struct tswtcm *); 108 1.16 peter static struct tc_action *tswtcm_input(struct cdnr_block *, struct cdnr_pktinfo *); 109 1.16 peter 110 1.16 peter static int cdnrcmd_if_attach(char *); 111 1.16 peter static int cdnrcmd_if_detach(char *); 112 1.16 peter static int cdnrcmd_add_element(struct cdnr_add_element *); 113 1.16 peter static int cdnrcmd_delete_element(struct cdnr_delete_element *); 114 1.16 peter static int cdnrcmd_add_filter(struct cdnr_add_filter *); 115 1.16 peter static int cdnrcmd_delete_filter(struct cdnr_delete_filter *); 116 1.16 peter static int cdnrcmd_add_tbm(struct cdnr_add_tbmeter *); 117 1.16 peter static int cdnrcmd_modify_tbm(struct cdnr_modify_tbmeter *); 118 1.16 peter static int cdnrcmd_tbm_stats(struct cdnr_tbmeter_stats *); 119 1.16 peter static int cdnrcmd_add_trtcm(struct cdnr_add_trtcm *); 120 1.16 peter static int cdnrcmd_modify_trtcm(struct cdnr_modify_trtcm *); 121 1.16 peter static int cdnrcmd_tcm_stats(struct cdnr_tcm_stats *); 122 1.16 peter static int cdnrcmd_add_tswtcm(struct cdnr_add_tswtcm *); 123 1.16 peter static int cdnrcmd_modify_tswtcm(struct cdnr_modify_tswtcm *); 124 1.16 peter static int cdnrcmd_get_stats(struct cdnr_get_stats *); 125 1.16 peter 126 1.16 peter altqdev_decl(cdnr); 127 1.1 thorpej 128 1.1 thorpej /* 129 1.1 thorpej * top level input function called from ip_input. 130 1.1 thorpej * should be called before converting header fields to host-byte-order. 131 1.1 thorpej */ 132 1.1 thorpej int 133 1.16 peter altq_cdnr_input(struct mbuf *m, int af) 134 1.1 thorpej { 135 1.1 thorpej struct ifnet *ifp; 136 1.1 thorpej struct ip *ip; 137 1.1 thorpej struct top_cdnr *top; 138 1.1 thorpej struct tc_action *tca; 139 1.1 thorpej struct cdnr_block *cb; 140 1.1 thorpej struct cdnr_pktinfo pktinfo; 141 1.1 thorpej 142 1.21 ozaki ifp = m_get_rcvif_NOMPSAFE(m); 143 1.1 thorpej if (!ALTQ_IS_CNDTNING(&ifp->if_snd)) 144 1.1 thorpej /* traffic conditioner is not enabled on this interface */ 145 1.23 joe return 1; 146 1.1 thorpej 147 1.1 thorpej top = ifp->if_snd.altq_cdnr; 148 1.1 thorpej 149 1.1 thorpej ip = mtod(m, struct ip *); 150 1.1 thorpej #ifdef INET6 151 1.1 thorpej if (af == AF_INET6) { 152 1.1 thorpej u_int32_t flowlabel; 153 1.9 perry 154 1.1 thorpej flowlabel = ((struct ip6_hdr *)ip)->ip6_flow; 155 1.1 thorpej pktinfo.pkt_dscp = (ntohl(flowlabel) >> 20) & DSCP_MASK; 156 1.1 thorpej } else 157 1.1 thorpej #endif 158 1.1 thorpej pktinfo.pkt_dscp = ip->ip_tos & DSCP_MASK; 159 1.1 thorpej pktinfo.pkt_len = m_pktlen(m); 160 1.1 thorpej 161 1.1 thorpej tca = NULL; 162 1.1 thorpej 163 1.1 thorpej cb = acc_classify(&top->tc_classifier, m, af); 164 1.1 thorpej if (cb != NULL) 165 1.1 thorpej tca = &cb->cb_action; 166 1.1 thorpej 167 1.1 thorpej if (tca == NULL) 168 1.1 thorpej tca = &top->tc_block.cb_action; 169 1.1 thorpej 170 1.1 thorpej while (1) { 171 1.1 thorpej PKTCNTR_ADD(&top->tc_cnts[tca->tca_code], pktinfo.pkt_len); 172 1.9 perry 173 1.1 thorpej switch (tca->tca_code) { 174 1.1 thorpej case TCACODE_PASS: 175 1.23 joe return 1; 176 1.1 thorpej case TCACODE_DROP: 177 1.1 thorpej m_freem(m); 178 1.23 joe return 0; 179 1.1 thorpej case TCACODE_RETURN: 180 1.23 joe return 0; 181 1.1 thorpej case TCACODE_MARK: 182 1.1 thorpej #ifdef INET6 183 1.1 thorpej if (af == AF_INET6) { 184 1.1 thorpej struct ip6_hdr *ip6 = (struct ip6_hdr *)ip; 185 1.1 thorpej u_int32_t flowlabel; 186 1.1 thorpej 187 1.1 thorpej flowlabel = ntohl(ip6->ip6_flow); 188 1.1 thorpej flowlabel = (tca->tca_dscp << 20) | 189 1.1 thorpej (flowlabel & ~(DSCP_MASK << 20)); 190 1.1 thorpej ip6->ip6_flow = htonl(flowlabel); 191 1.1 thorpej } else 192 1.1 thorpej #endif 193 1.1 thorpej ip->ip_tos = tca->tca_dscp | 194 1.1 thorpej (ip->ip_tos & DSCP_CUMASK); 195 1.23 joe return 1; 196 1.1 thorpej case TCACODE_NEXT: 197 1.1 thorpej cb = tca->tca_next; 198 1.1 thorpej tca = (*cb->cb_input)(cb, &pktinfo); 199 1.1 thorpej break; 200 1.1 thorpej case TCACODE_NONE: 201 1.1 thorpej default: 202 1.23 joe return 1; 203 1.1 thorpej } 204 1.1 thorpej } 205 1.1 thorpej } 206 1.1 thorpej 207 1.1 thorpej static struct top_cdnr * 208 1.16 peter tcb_lookup(char *ifname) 209 1.1 thorpej { 210 1.1 thorpej struct top_cdnr *top; 211 1.1 thorpej struct ifnet *ifp; 212 1.1 thorpej 213 1.1 thorpej if ((ifp = ifunit(ifname)) != NULL) 214 1.1 thorpej LIST_FOREACH(top, &tcb_list, tc_next) 215 1.1 thorpej if (top->tc_ifq->altq_ifp == ifp) 216 1.23 joe return top; 217 1.23 joe return NULL; 218 1.1 thorpej } 219 1.1 thorpej 220 1.1 thorpej static struct cdnr_block * 221 1.16 peter cdnr_handle2cb(u_long handle) 222 1.1 thorpej { 223 1.1 thorpej struct cdnr_block *cb; 224 1.1 thorpej 225 1.1 thorpej cb = (struct cdnr_block *)handle; 226 1.1 thorpej if (handle != ALIGN(cb)) 227 1.23 joe return NULL; 228 1.9 perry 229 1.1 thorpej if (cb == NULL || cb->cb_handle != handle) 230 1.23 joe return NULL; 231 1.23 joe return cb; 232 1.1 thorpej } 233 1.1 thorpej 234 1.1 thorpej static u_long 235 1.16 peter cdnr_cb2handle(struct cdnr_block *cb) 236 1.1 thorpej { 237 1.1 thorpej return (cb->cb_handle); 238 1.1 thorpej } 239 1.1 thorpej 240 1.1 thorpej static void * 241 1.16 peter cdnr_cballoc(struct top_cdnr *top, int type, struct tc_action *(*input_func)( 242 1.16 peter struct cdnr_block *, struct cdnr_pktinfo *)) 243 1.1 thorpej { 244 1.1 thorpej struct cdnr_block *cb; 245 1.1 thorpej int size; 246 1.1 thorpej 247 1.1 thorpej switch (type) { 248 1.1 thorpej case TCETYPE_TOP: 249 1.1 thorpej size = sizeof(struct top_cdnr); 250 1.1 thorpej break; 251 1.1 thorpej case TCETYPE_ELEMENT: 252 1.1 thorpej size = sizeof(struct cdnr_block); 253 1.1 thorpej break; 254 1.1 thorpej case TCETYPE_TBMETER: 255 1.1 thorpej size = sizeof(struct tbmeter); 256 1.1 thorpej break; 257 1.1 thorpej case TCETYPE_TRTCM: 258 1.1 thorpej size = sizeof(struct trtcm); 259 1.1 thorpej break; 260 1.1 thorpej case TCETYPE_TSWTCM: 261 1.1 thorpej size = sizeof(struct tswtcm); 262 1.1 thorpej break; 263 1.1 thorpej default: 264 1.23 joe return NULL; 265 1.1 thorpej } 266 1.1 thorpej 267 1.11 christos cb = malloc(size, M_DEVBUF, M_WAITOK|M_ZERO); 268 1.1 thorpej if (cb == NULL) 269 1.23 joe return NULL; 270 1.9 perry 271 1.1 thorpej cb->cb_len = size; 272 1.1 thorpej cb->cb_type = type; 273 1.1 thorpej cb->cb_ref = 0; 274 1.1 thorpej cb->cb_handle = (u_long)cb; 275 1.1 thorpej if (top == NULL) 276 1.1 thorpej cb->cb_top = (struct top_cdnr *)cb; 277 1.1 thorpej else 278 1.1 thorpej cb->cb_top = top; 279 1.1 thorpej 280 1.1 thorpej if (input_func != NULL) { 281 1.1 thorpej /* 282 1.1 thorpej * if this cdnr has an action function, 283 1.1 thorpej * make tc_action to call itself. 284 1.1 thorpej */ 285 1.1 thorpej cb->cb_action.tca_code = TCACODE_NEXT; 286 1.1 thorpej cb->cb_action.tca_next = cb; 287 1.1 thorpej cb->cb_input = input_func; 288 1.1 thorpej } else 289 1.1 thorpej cb->cb_action.tca_code = TCACODE_NONE; 290 1.1 thorpej 291 1.1 thorpej /* if this isn't top, register the element to the top level cdnr */ 292 1.1 thorpej if (top != NULL) 293 1.1 thorpej LIST_INSERT_HEAD(&top->tc_elements, cb, cb_next); 294 1.1 thorpej 295 1.1 thorpej return ((void *)cb); 296 1.1 thorpej } 297 1.1 thorpej 298 1.1 thorpej static void 299 1.16 peter cdnr_cbdestroy(void *cblock) 300 1.1 thorpej { 301 1.1 thorpej struct cdnr_block *cb = cblock; 302 1.1 thorpej 303 1.1 thorpej /* delete filters belonging to this cdnr */ 304 1.1 thorpej acc_discard_filters(&cb->cb_top->tc_classifier, cb, 0); 305 1.1 thorpej 306 1.1 thorpej /* remove from the top level cdnr */ 307 1.1 thorpej if (cb->cb_top != cblock) 308 1.1 thorpej LIST_REMOVE(cb, cb_next); 309 1.1 thorpej 310 1.11 christos free(cb, M_DEVBUF); 311 1.1 thorpej } 312 1.1 thorpej 313 1.1 thorpej /* 314 1.1 thorpej * conditioner common destroy routine 315 1.1 thorpej */ 316 1.1 thorpej static int 317 1.16 peter generic_element_destroy(struct cdnr_block *cb) 318 1.1 thorpej { 319 1.1 thorpej int error = 0; 320 1.1 thorpej 321 1.1 thorpej switch (cb->cb_type) { 322 1.1 thorpej case TCETYPE_TOP: 323 1.1 thorpej error = top_destroy((struct top_cdnr *)cb); 324 1.1 thorpej break; 325 1.1 thorpej case TCETYPE_ELEMENT: 326 1.1 thorpej error = element_destroy(cb); 327 1.1 thorpej break; 328 1.1 thorpej case TCETYPE_TBMETER: 329 1.1 thorpej error = tbm_destroy((struct tbmeter *)cb); 330 1.1 thorpej break; 331 1.1 thorpej case TCETYPE_TRTCM: 332 1.1 thorpej error = trtcm_destroy((struct trtcm *)cb); 333 1.1 thorpej break; 334 1.1 thorpej case TCETYPE_TSWTCM: 335 1.1 thorpej error = tswtcm_destroy((struct tswtcm *)cb); 336 1.1 thorpej break; 337 1.1 thorpej default: 338 1.1 thorpej error = EINVAL; 339 1.1 thorpej } 340 1.23 joe return error; 341 1.1 thorpej } 342 1.1 thorpej 343 1.1 thorpej static int 344 1.16 peter tca_verify_action(struct tc_action *utca) 345 1.1 thorpej { 346 1.1 thorpej switch (utca->tca_code) { 347 1.1 thorpej case TCACODE_PASS: 348 1.1 thorpej case TCACODE_DROP: 349 1.1 thorpej case TCACODE_MARK: 350 1.1 thorpej /* these are ok */ 351 1.1 thorpej break; 352 1.1 thorpej 353 1.1 thorpej case TCACODE_HANDLE: 354 1.1 thorpej /* verify handle value */ 355 1.1 thorpej if (cdnr_handle2cb(utca->tca_handle) == NULL) 356 1.23 joe return -1; 357 1.1 thorpej break; 358 1.1 thorpej 359 1.1 thorpej case TCACODE_NONE: 360 1.1 thorpej case TCACODE_RETURN: 361 1.1 thorpej case TCACODE_NEXT: 362 1.1 thorpej default: 363 1.1 thorpej /* should not be passed from a user */ 364 1.23 joe return -1; 365 1.1 thorpej } 366 1.23 joe return 0; 367 1.1 thorpej } 368 1.1 thorpej 369 1.1 thorpej static void 370 1.16 peter tca_import_action(struct tc_action *ktca, struct tc_action *utca) 371 1.1 thorpej { 372 1.1 thorpej struct cdnr_block *cb; 373 1.1 thorpej 374 1.1 thorpej *ktca = *utca; 375 1.1 thorpej if (ktca->tca_code == TCACODE_HANDLE) { 376 1.1 thorpej cb = cdnr_handle2cb(ktca->tca_handle); 377 1.1 thorpej if (cb == NULL) { 378 1.1 thorpej ktca->tca_code = TCACODE_NONE; 379 1.1 thorpej return; 380 1.1 thorpej } 381 1.1 thorpej ktca->tca_code = TCACODE_NEXT; 382 1.1 thorpej ktca->tca_next = cb; 383 1.1 thorpej cb->cb_ref++; 384 1.1 thorpej } else if (ktca->tca_code == TCACODE_MARK) { 385 1.1 thorpej ktca->tca_dscp &= DSCP_MASK; 386 1.1 thorpej } 387 1.1 thorpej return; 388 1.1 thorpej } 389 1.1 thorpej 390 1.1 thorpej static void 391 1.16 peter tca_invalidate_action(struct tc_action *tca) 392 1.1 thorpej { 393 1.1 thorpej struct cdnr_block *cb; 394 1.1 thorpej 395 1.1 thorpej if (tca->tca_code == TCACODE_NEXT) { 396 1.1 thorpej cb = tca->tca_next; 397 1.1 thorpej if (cb == NULL) 398 1.1 thorpej return; 399 1.1 thorpej cb->cb_ref--; 400 1.1 thorpej } 401 1.1 thorpej tca->tca_code = TCACODE_NONE; 402 1.1 thorpej } 403 1.1 thorpej 404 1.1 thorpej /* 405 1.1 thorpej * top level traffic conditioner 406 1.1 thorpej */ 407 1.1 thorpej static struct top_cdnr * 408 1.16 peter top_create(struct ifaltq *ifq) 409 1.1 thorpej { 410 1.1 thorpej struct top_cdnr *top; 411 1.1 thorpej 412 1.1 thorpej if ((top = cdnr_cballoc(NULL, TCETYPE_TOP, NULL)) == NULL) 413 1.23 joe return NULL; 414 1.1 thorpej 415 1.1 thorpej top->tc_ifq = ifq; 416 1.1 thorpej /* set default action for the top level conditioner */ 417 1.1 thorpej top->tc_block.cb_action.tca_code = TCACODE_PASS; 418 1.1 thorpej 419 1.1 thorpej LIST_INSERT_HEAD(&tcb_list, top, tc_next); 420 1.1 thorpej 421 1.1 thorpej ifq->altq_cdnr = top; 422 1.1 thorpej 423 1.23 joe return top; 424 1.1 thorpej } 425 1.1 thorpej 426 1.1 thorpej static int 427 1.16 peter top_destroy(struct top_cdnr *top) 428 1.1 thorpej { 429 1.1 thorpej struct cdnr_block *cb; 430 1.1 thorpej 431 1.1 thorpej if (ALTQ_IS_CNDTNING(top->tc_ifq)) 432 1.1 thorpej ALTQ_CLEAR_CNDTNING(top->tc_ifq); 433 1.1 thorpej top->tc_ifq->altq_cdnr = NULL; 434 1.1 thorpej 435 1.1 thorpej /* 436 1.1 thorpej * destroy all the conditioner elements belonging to this interface 437 1.1 thorpej */ 438 1.1 thorpej while ((cb = LIST_FIRST(&top->tc_elements)) != NULL) { 439 1.1 thorpej while (cb != NULL && cb->cb_ref > 0) 440 1.1 thorpej cb = LIST_NEXT(cb, cb_next); 441 1.1 thorpej if (cb != NULL) 442 1.1 thorpej generic_element_destroy(cb); 443 1.1 thorpej } 444 1.9 perry 445 1.1 thorpej LIST_REMOVE(top, tc_next); 446 1.1 thorpej 447 1.1 thorpej cdnr_cbdestroy(top); 448 1.1 thorpej 449 1.1 thorpej /* if there is no active conditioner, remove the input hook */ 450 1.1 thorpej if (altq_input != NULL) { 451 1.1 thorpej LIST_FOREACH(top, &tcb_list, tc_next) 452 1.1 thorpej if (ALTQ_IS_CNDTNING(top->tc_ifq)) 453 1.1 thorpej break; 454 1.1 thorpej if (top == NULL) 455 1.1 thorpej altq_input = NULL; 456 1.1 thorpej } 457 1.1 thorpej 458 1.23 joe return 0; 459 1.1 thorpej } 460 1.1 thorpej 461 1.1 thorpej /* 462 1.1 thorpej * simple tc elements without input function (e.g., dropper and makers). 463 1.1 thorpej */ 464 1.1 thorpej static struct cdnr_block * 465 1.16 peter element_create(struct top_cdnr *top, struct tc_action *action) 466 1.1 thorpej { 467 1.1 thorpej struct cdnr_block *cb; 468 1.1 thorpej 469 1.1 thorpej if (tca_verify_action(action) < 0) 470 1.23 joe return NULL; 471 1.1 thorpej 472 1.1 thorpej if ((cb = cdnr_cballoc(top, TCETYPE_ELEMENT, NULL)) == NULL) 473 1.23 joe return NULL; 474 1.1 thorpej 475 1.1 thorpej tca_import_action(&cb->cb_action, action); 476 1.1 thorpej 477 1.23 joe return cb; 478 1.1 thorpej } 479 1.1 thorpej 480 1.1 thorpej static int 481 1.16 peter element_destroy(struct cdnr_block *cb) 482 1.1 thorpej { 483 1.1 thorpej if (cb->cb_ref > 0) 484 1.23 joe return EBUSY; 485 1.1 thorpej 486 1.1 thorpej tca_invalidate_action(&cb->cb_action); 487 1.1 thorpej 488 1.1 thorpej cdnr_cbdestroy(cb); 489 1.23 joe return 0; 490 1.1 thorpej } 491 1.1 thorpej 492 1.1 thorpej /* 493 1.1 thorpej * internal representation of token bucket parameters 494 1.1 thorpej * rate: byte_per_unittime << 32 495 1.1 thorpej * (((bits_per_sec) / 8) << 32) / machclk_freq 496 1.1 thorpej * depth: byte << 32 497 1.1 thorpej * 498 1.1 thorpej */ 499 1.1 thorpej #define TB_SHIFT 32 500 1.1 thorpej #define TB_SCALE(x) ((u_int64_t)(x) << TB_SHIFT) 501 1.1 thorpej #define TB_UNSCALE(x) ((x) >> TB_SHIFT) 502 1.1 thorpej 503 1.1 thorpej static void 504 1.16 peter tb_import_profile(struct tbe *tb, struct tb_profile *profile) 505 1.1 thorpej { 506 1.1 thorpej tb->rate = TB_SCALE(profile->rate / 8) / machclk_freq; 507 1.1 thorpej tb->depth = TB_SCALE(profile->depth); 508 1.1 thorpej if (tb->rate > 0) 509 1.1 thorpej tb->filluptime = tb->depth / tb->rate; 510 1.1 thorpej else 511 1.1 thorpej tb->filluptime = 0xffffffffffffffffLL; 512 1.1 thorpej tb->token = tb->depth; 513 1.1 thorpej tb->last = read_machclk(); 514 1.1 thorpej } 515 1.1 thorpej 516 1.1 thorpej /* 517 1.1 thorpej * simple token bucket meter 518 1.1 thorpej */ 519 1.1 thorpej static struct tbmeter * 520 1.16 peter tbm_create(struct top_cdnr *top, struct tb_profile *profile, 521 1.16 peter struct tc_action *in_action, struct tc_action *out_action) 522 1.1 thorpej { 523 1.1 thorpej struct tbmeter *tbm = NULL; 524 1.1 thorpej 525 1.1 thorpej if (tca_verify_action(in_action) < 0 526 1.1 thorpej || tca_verify_action(out_action) < 0) 527 1.23 joe return NULL; 528 1.1 thorpej 529 1.1 thorpej if ((tbm = cdnr_cballoc(top, TCETYPE_TBMETER, 530 1.1 thorpej tbm_input)) == NULL) 531 1.23 joe return NULL; 532 1.1 thorpej 533 1.1 thorpej tb_import_profile(&tbm->tb, profile); 534 1.1 thorpej 535 1.1 thorpej tca_import_action(&tbm->in_action, in_action); 536 1.1 thorpej tca_import_action(&tbm->out_action, out_action); 537 1.1 thorpej 538 1.23 joe return tbm; 539 1.1 thorpej } 540 1.1 thorpej 541 1.1 thorpej static int 542 1.16 peter tbm_destroy(struct tbmeter *tbm) 543 1.1 thorpej { 544 1.1 thorpej if (tbm->cdnrblk.cb_ref > 0) 545 1.23 joe return EBUSY; 546 1.1 thorpej 547 1.1 thorpej tca_invalidate_action(&tbm->in_action); 548 1.1 thorpej tca_invalidate_action(&tbm->out_action); 549 1.1 thorpej 550 1.1 thorpej cdnr_cbdestroy(tbm); 551 1.23 joe return 0; 552 1.1 thorpej } 553 1.9 perry 554 1.1 thorpej static struct tc_action * 555 1.16 peter tbm_input(struct cdnr_block *cb, struct cdnr_pktinfo *pktinfo) 556 1.1 thorpej { 557 1.1 thorpej struct tbmeter *tbm = (struct tbmeter *)cb; 558 1.1 thorpej u_int64_t len; 559 1.1 thorpej u_int64_t interval, now; 560 1.9 perry 561 1.1 thorpej len = TB_SCALE(pktinfo->pkt_len); 562 1.1 thorpej 563 1.1 thorpej if (tbm->tb.token < len) { 564 1.1 thorpej now = read_machclk(); 565 1.1 thorpej interval = now - tbm->tb.last; 566 1.1 thorpej if (interval >= tbm->tb.filluptime) 567 1.1 thorpej tbm->tb.token = tbm->tb.depth; 568 1.1 thorpej else { 569 1.1 thorpej tbm->tb.token += interval * tbm->tb.rate; 570 1.1 thorpej if (tbm->tb.token > tbm->tb.depth) 571 1.1 thorpej tbm->tb.token = tbm->tb.depth; 572 1.1 thorpej } 573 1.1 thorpej tbm->tb.last = now; 574 1.1 thorpej } 575 1.9 perry 576 1.1 thorpej if (tbm->tb.token < len) { 577 1.1 thorpej PKTCNTR_ADD(&tbm->out_cnt, pktinfo->pkt_len); 578 1.1 thorpej return (&tbm->out_action); 579 1.1 thorpej } 580 1.1 thorpej 581 1.1 thorpej tbm->tb.token -= len; 582 1.1 thorpej PKTCNTR_ADD(&tbm->in_cnt, pktinfo->pkt_len); 583 1.1 thorpej return (&tbm->in_action); 584 1.1 thorpej } 585 1.1 thorpej 586 1.1 thorpej /* 587 1.1 thorpej * two rate three color marker 588 1.1 thorpej * as described in draft-heinanen-diffserv-trtcm-01.txt 589 1.1 thorpej */ 590 1.1 thorpej static struct trtcm * 591 1.16 peter trtcm_create(struct top_cdnr *top, struct tb_profile *cmtd_profile, 592 1.16 peter struct tb_profile *peak_profile, struct tc_action *green_action, 593 1.16 peter struct tc_action *yellow_action, struct tc_action *red_action, 594 1.16 peter int coloraware) 595 1.1 thorpej { 596 1.1 thorpej struct trtcm *tcm = NULL; 597 1.1 thorpej 598 1.1 thorpej if (tca_verify_action(green_action) < 0 599 1.1 thorpej || tca_verify_action(yellow_action) < 0 600 1.1 thorpej || tca_verify_action(red_action) < 0) 601 1.23 joe return NULL; 602 1.1 thorpej 603 1.1 thorpej if ((tcm = cdnr_cballoc(top, TCETYPE_TRTCM, 604 1.1 thorpej trtcm_input)) == NULL) 605 1.23 joe return NULL; 606 1.1 thorpej 607 1.1 thorpej tb_import_profile(&tcm->cmtd_tb, cmtd_profile); 608 1.1 thorpej tb_import_profile(&tcm->peak_tb, peak_profile); 609 1.1 thorpej 610 1.1 thorpej tca_import_action(&tcm->green_action, green_action); 611 1.1 thorpej tca_import_action(&tcm->yellow_action, yellow_action); 612 1.1 thorpej tca_import_action(&tcm->red_action, red_action); 613 1.1 thorpej 614 1.1 thorpej /* set dscps to use */ 615 1.1 thorpej if (tcm->green_action.tca_code == TCACODE_MARK) 616 1.1 thorpej tcm->green_dscp = tcm->green_action.tca_dscp & DSCP_MASK; 617 1.1 thorpej else 618 1.1 thorpej tcm->green_dscp = DSCP_AF11; 619 1.1 thorpej if (tcm->yellow_action.tca_code == TCACODE_MARK) 620 1.1 thorpej tcm->yellow_dscp = tcm->yellow_action.tca_dscp & DSCP_MASK; 621 1.1 thorpej else 622 1.1 thorpej tcm->yellow_dscp = DSCP_AF12; 623 1.1 thorpej if (tcm->red_action.tca_code == TCACODE_MARK) 624 1.1 thorpej tcm->red_dscp = tcm->red_action.tca_dscp & DSCP_MASK; 625 1.1 thorpej else 626 1.1 thorpej tcm->red_dscp = DSCP_AF13; 627 1.1 thorpej 628 1.1 thorpej tcm->coloraware = coloraware; 629 1.1 thorpej 630 1.23 joe return tcm; 631 1.1 thorpej } 632 1.1 thorpej 633 1.1 thorpej static int 634 1.16 peter trtcm_destroy(struct trtcm *tcm) 635 1.1 thorpej { 636 1.1 thorpej if (tcm->cdnrblk.cb_ref > 0) 637 1.23 joe return EBUSY; 638 1.1 thorpej 639 1.1 thorpej tca_invalidate_action(&tcm->green_action); 640 1.1 thorpej tca_invalidate_action(&tcm->yellow_action); 641 1.1 thorpej tca_invalidate_action(&tcm->red_action); 642 1.1 thorpej 643 1.1 thorpej cdnr_cbdestroy(tcm); 644 1.23 joe return 0; 645 1.1 thorpej } 646 1.9 perry 647 1.1 thorpej static struct tc_action * 648 1.16 peter trtcm_input(struct cdnr_block *cb, struct cdnr_pktinfo *pktinfo) 649 1.1 thorpej { 650 1.1 thorpej struct trtcm *tcm = (struct trtcm *)cb; 651 1.1 thorpej u_int64_t len; 652 1.1 thorpej u_int64_t interval, now; 653 1.1 thorpej u_int8_t color; 654 1.1 thorpej 655 1.1 thorpej len = TB_SCALE(pktinfo->pkt_len); 656 1.1 thorpej if (tcm->coloraware) { 657 1.1 thorpej color = pktinfo->pkt_dscp; 658 1.1 thorpej if (color != tcm->yellow_dscp && color != tcm->red_dscp) 659 1.1 thorpej color = tcm->green_dscp; 660 1.1 thorpej } else { 661 1.1 thorpej /* if color-blind, precolor it as green */ 662 1.1 thorpej color = tcm->green_dscp; 663 1.1 thorpej } 664 1.1 thorpej 665 1.1 thorpej now = read_machclk(); 666 1.1 thorpej if (tcm->cmtd_tb.token < len) { 667 1.1 thorpej interval = now - tcm->cmtd_tb.last; 668 1.1 thorpej if (interval >= tcm->cmtd_tb.filluptime) 669 1.1 thorpej tcm->cmtd_tb.token = tcm->cmtd_tb.depth; 670 1.1 thorpej else { 671 1.1 thorpej tcm->cmtd_tb.token += interval * tcm->cmtd_tb.rate; 672 1.1 thorpej if (tcm->cmtd_tb.token > tcm->cmtd_tb.depth) 673 1.1 thorpej tcm->cmtd_tb.token = tcm->cmtd_tb.depth; 674 1.1 thorpej } 675 1.1 thorpej tcm->cmtd_tb.last = now; 676 1.1 thorpej } 677 1.1 thorpej if (tcm->peak_tb.token < len) { 678 1.1 thorpej interval = now - tcm->peak_tb.last; 679 1.1 thorpej if (interval >= tcm->peak_tb.filluptime) 680 1.1 thorpej tcm->peak_tb.token = tcm->peak_tb.depth; 681 1.1 thorpej else { 682 1.1 thorpej tcm->peak_tb.token += interval * tcm->peak_tb.rate; 683 1.1 thorpej if (tcm->peak_tb.token > tcm->peak_tb.depth) 684 1.1 thorpej tcm->peak_tb.token = tcm->peak_tb.depth; 685 1.1 thorpej } 686 1.1 thorpej tcm->peak_tb.last = now; 687 1.1 thorpej } 688 1.9 perry 689 1.1 thorpej if (color == tcm->red_dscp || tcm->peak_tb.token < len) { 690 1.1 thorpej pktinfo->pkt_dscp = tcm->red_dscp; 691 1.1 thorpej PKTCNTR_ADD(&tcm->red_cnt, pktinfo->pkt_len); 692 1.1 thorpej return (&tcm->red_action); 693 1.1 thorpej } 694 1.1 thorpej 695 1.1 thorpej if (color == tcm->yellow_dscp || tcm->cmtd_tb.token < len) { 696 1.1 thorpej pktinfo->pkt_dscp = tcm->yellow_dscp; 697 1.1 thorpej tcm->peak_tb.token -= len; 698 1.1 thorpej PKTCNTR_ADD(&tcm->yellow_cnt, pktinfo->pkt_len); 699 1.1 thorpej return (&tcm->yellow_action); 700 1.1 thorpej } 701 1.1 thorpej 702 1.1 thorpej pktinfo->pkt_dscp = tcm->green_dscp; 703 1.1 thorpej tcm->cmtd_tb.token -= len; 704 1.1 thorpej tcm->peak_tb.token -= len; 705 1.1 thorpej PKTCNTR_ADD(&tcm->green_cnt, pktinfo->pkt_len); 706 1.1 thorpej return (&tcm->green_action); 707 1.1 thorpej } 708 1.1 thorpej 709 1.1 thorpej /* 710 1.1 thorpej * time sliding window three color marker 711 1.1 thorpej * as described in draft-fang-diffserv-tc-tswtcm-00.txt 712 1.1 thorpej */ 713 1.1 thorpej static struct tswtcm * 714 1.16 peter tswtcm_create(struct top_cdnr *top, u_int32_t cmtd_rate, u_int32_t peak_rate, 715 1.16 peter u_int32_t avg_interval, struct tc_action *green_action, 716 1.16 peter struct tc_action *yellow_action, struct tc_action *red_action) 717 1.1 thorpej { 718 1.1 thorpej struct tswtcm *tsw; 719 1.1 thorpej 720 1.1 thorpej if (tca_verify_action(green_action) < 0 721 1.1 thorpej || tca_verify_action(yellow_action) < 0 722 1.1 thorpej || tca_verify_action(red_action) < 0) 723 1.23 joe return NULL; 724 1.1 thorpej 725 1.1 thorpej if ((tsw = cdnr_cballoc(top, TCETYPE_TSWTCM, 726 1.1 thorpej tswtcm_input)) == NULL) 727 1.23 joe return NULL; 728 1.1 thorpej 729 1.1 thorpej tca_import_action(&tsw->green_action, green_action); 730 1.1 thorpej tca_import_action(&tsw->yellow_action, yellow_action); 731 1.1 thorpej tca_import_action(&tsw->red_action, red_action); 732 1.1 thorpej 733 1.1 thorpej /* set dscps to use */ 734 1.1 thorpej if (tsw->green_action.tca_code == TCACODE_MARK) 735 1.1 thorpej tsw->green_dscp = tsw->green_action.tca_dscp & DSCP_MASK; 736 1.1 thorpej else 737 1.1 thorpej tsw->green_dscp = DSCP_AF11; 738 1.1 thorpej if (tsw->yellow_action.tca_code == TCACODE_MARK) 739 1.1 thorpej tsw->yellow_dscp = tsw->yellow_action.tca_dscp & DSCP_MASK; 740 1.1 thorpej else 741 1.1 thorpej tsw->yellow_dscp = DSCP_AF12; 742 1.1 thorpej if (tsw->red_action.tca_code == TCACODE_MARK) 743 1.1 thorpej tsw->red_dscp = tsw->red_action.tca_dscp & DSCP_MASK; 744 1.1 thorpej else 745 1.1 thorpej tsw->red_dscp = DSCP_AF13; 746 1.1 thorpej 747 1.1 thorpej /* convert rates from bits/sec to bytes/sec */ 748 1.1 thorpej tsw->cmtd_rate = cmtd_rate / 8; 749 1.1 thorpej tsw->peak_rate = peak_rate / 8; 750 1.1 thorpej tsw->avg_rate = 0; 751 1.1 thorpej 752 1.1 thorpej /* timewin is converted from msec to machine clock unit */ 753 1.1 thorpej tsw->timewin = (u_int64_t)machclk_freq * avg_interval / 1000; 754 1.1 thorpej 755 1.23 joe return tsw; 756 1.1 thorpej } 757 1.1 thorpej 758 1.1 thorpej static int 759 1.16 peter tswtcm_destroy(struct tswtcm *tsw) 760 1.1 thorpej { 761 1.1 thorpej if (tsw->cdnrblk.cb_ref > 0) 762 1.23 joe return EBUSY; 763 1.1 thorpej 764 1.1 thorpej tca_invalidate_action(&tsw->green_action); 765 1.1 thorpej tca_invalidate_action(&tsw->yellow_action); 766 1.1 thorpej tca_invalidate_action(&tsw->red_action); 767 1.1 thorpej 768 1.1 thorpej cdnr_cbdestroy(tsw); 769 1.23 joe return 0; 770 1.1 thorpej } 771 1.1 thorpej 772 1.1 thorpej static struct tc_action * 773 1.16 peter tswtcm_input(struct cdnr_block *cb, struct cdnr_pktinfo *pktinfo) 774 1.1 thorpej { 775 1.1 thorpej struct tswtcm *tsw = (struct tswtcm *)cb; 776 1.1 thorpej int len; 777 1.1 thorpej u_int32_t avg_rate; 778 1.1 thorpej u_int64_t interval, now, tmp; 779 1.1 thorpej 780 1.1 thorpej /* 781 1.1 thorpej * rate estimator 782 1.1 thorpej */ 783 1.1 thorpej len = pktinfo->pkt_len; 784 1.1 thorpej now = read_machclk(); 785 1.1 thorpej 786 1.1 thorpej interval = now - tsw->t_front; 787 1.1 thorpej /* 788 1.1 thorpej * calculate average rate: 789 1.1 thorpej * avg = (avg * timewin + pkt_len)/(timewin + interval) 790 1.1 thorpej * pkt_len needs to be multiplied by machclk_freq in order to 791 1.1 thorpej * get (bytes/sec). 792 1.1 thorpej * note: when avg_rate (bytes/sec) and timewin (machclk unit) are 793 1.1 thorpej * less than 32 bits, the following 64-bit operation has enough 794 1.1 thorpej * precision. 795 1.1 thorpej */ 796 1.1 thorpej tmp = ((u_int64_t)tsw->avg_rate * tsw->timewin 797 1.1 thorpej + (u_int64_t)len * machclk_freq) / (tsw->timewin + interval); 798 1.1 thorpej tsw->avg_rate = avg_rate = (u_int32_t)tmp; 799 1.1 thorpej tsw->t_front = now; 800 1.1 thorpej 801 1.1 thorpej /* 802 1.1 thorpej * marker 803 1.1 thorpej */ 804 1.1 thorpej if (avg_rate > tsw->cmtd_rate) { 805 1.20 tls u_int32_t randval = cprng_fast32() % avg_rate; 806 1.9 perry 807 1.1 thorpej if (avg_rate > tsw->peak_rate) { 808 1.1 thorpej if (randval < avg_rate - tsw->peak_rate) { 809 1.1 thorpej /* mark red */ 810 1.1 thorpej pktinfo->pkt_dscp = tsw->red_dscp; 811 1.1 thorpej PKTCNTR_ADD(&tsw->red_cnt, len); 812 1.1 thorpej return (&tsw->red_action); 813 1.1 thorpej } else if (randval < avg_rate - tsw->cmtd_rate) 814 1.1 thorpej goto mark_yellow; 815 1.1 thorpej } else { 816 1.1 thorpej /* peak_rate >= avg_rate > cmtd_rate */ 817 1.1 thorpej if (randval < avg_rate - tsw->cmtd_rate) { 818 1.1 thorpej mark_yellow: 819 1.1 thorpej pktinfo->pkt_dscp = tsw->yellow_dscp; 820 1.1 thorpej PKTCNTR_ADD(&tsw->yellow_cnt, len); 821 1.1 thorpej return (&tsw->yellow_action); 822 1.1 thorpej } 823 1.1 thorpej } 824 1.1 thorpej } 825 1.1 thorpej 826 1.1 thorpej /* mark green */ 827 1.1 thorpej pktinfo->pkt_dscp = tsw->green_dscp; 828 1.1 thorpej PKTCNTR_ADD(&tsw->green_cnt, len); 829 1.1 thorpej return (&tsw->green_action); 830 1.1 thorpej } 831 1.1 thorpej 832 1.1 thorpej /* 833 1.1 thorpej * ioctl requests 834 1.1 thorpej */ 835 1.1 thorpej static int 836 1.16 peter cdnrcmd_if_attach(char *ifname) 837 1.1 thorpej { 838 1.1 thorpej struct ifnet *ifp; 839 1.1 thorpej struct top_cdnr *top; 840 1.1 thorpej 841 1.1 thorpej if ((ifp = ifunit(ifname)) == NULL) 842 1.23 joe return EBADF; 843 1.1 thorpej 844 1.1 thorpej if (ifp->if_snd.altq_cdnr != NULL) 845 1.23 joe return EBUSY; 846 1.1 thorpej 847 1.1 thorpej if ((top = top_create(&ifp->if_snd)) == NULL) 848 1.23 joe return ENOMEM; 849 1.23 joe return 0; 850 1.1 thorpej } 851 1.1 thorpej 852 1.1 thorpej static int 853 1.16 peter cdnrcmd_if_detach(char *ifname) 854 1.1 thorpej { 855 1.1 thorpej struct top_cdnr *top; 856 1.1 thorpej 857 1.1 thorpej if ((top = tcb_lookup(ifname)) == NULL) 858 1.23 joe return EBADF; 859 1.1 thorpej 860 1.1 thorpej return top_destroy(top); 861 1.1 thorpej } 862 1.1 thorpej 863 1.1 thorpej static int 864 1.16 peter cdnrcmd_add_element(struct cdnr_add_element *ap) 865 1.1 thorpej { 866 1.1 thorpej struct top_cdnr *top; 867 1.1 thorpej struct cdnr_block *cb; 868 1.1 thorpej 869 1.1 thorpej if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL) 870 1.23 joe return EBADF; 871 1.1 thorpej 872 1.1 thorpej cb = element_create(top, &ap->action); 873 1.1 thorpej if (cb == NULL) 874 1.23 joe return EINVAL; 875 1.1 thorpej /* return a class handle to the user */ 876 1.1 thorpej ap->cdnr_handle = cdnr_cb2handle(cb); 877 1.23 joe return 0; 878 1.1 thorpej } 879 1.1 thorpej 880 1.1 thorpej static int 881 1.16 peter cdnrcmd_delete_element(struct cdnr_delete_element *ap) 882 1.1 thorpej { 883 1.1 thorpej struct top_cdnr *top; 884 1.1 thorpej struct cdnr_block *cb; 885 1.1 thorpej 886 1.1 thorpej if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL) 887 1.23 joe return EBADF; 888 1.1 thorpej 889 1.1 thorpej if ((cb = cdnr_handle2cb(ap->cdnr_handle)) == NULL) 890 1.23 joe return EINVAL; 891 1.1 thorpej 892 1.1 thorpej if (cb->cb_type != TCETYPE_ELEMENT) 893 1.1 thorpej return generic_element_destroy(cb); 894 1.1 thorpej 895 1.1 thorpej return element_destroy(cb); 896 1.1 thorpej } 897 1.1 thorpej 898 1.1 thorpej static int 899 1.16 peter cdnrcmd_add_filter(struct cdnr_add_filter *ap) 900 1.1 thorpej { 901 1.1 thorpej struct top_cdnr *top; 902 1.1 thorpej struct cdnr_block *cb; 903 1.1 thorpej 904 1.1 thorpej if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL) 905 1.23 joe return EBADF; 906 1.1 thorpej 907 1.1 thorpej if ((cb = cdnr_handle2cb(ap->cdnr_handle)) == NULL) 908 1.23 joe return EINVAL; 909 1.1 thorpej 910 1.1 thorpej return acc_add_filter(&top->tc_classifier, &ap->filter, 911 1.1 thorpej cb, &ap->filter_handle); 912 1.1 thorpej } 913 1.1 thorpej 914 1.1 thorpej static int 915 1.16 peter cdnrcmd_delete_filter(struct cdnr_delete_filter *ap) 916 1.1 thorpej { 917 1.1 thorpej struct top_cdnr *top; 918 1.1 thorpej 919 1.1 thorpej if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL) 920 1.23 joe return EBADF; 921 1.1 thorpej 922 1.1 thorpej return acc_delete_filter(&top->tc_classifier, ap->filter_handle); 923 1.1 thorpej } 924 1.1 thorpej 925 1.1 thorpej static int 926 1.16 peter cdnrcmd_add_tbm(struct cdnr_add_tbmeter *ap) 927 1.1 thorpej { 928 1.1 thorpej struct top_cdnr *top; 929 1.1 thorpej struct tbmeter *tbm; 930 1.1 thorpej 931 1.1 thorpej if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL) 932 1.23 joe return EBADF; 933 1.1 thorpej 934 1.1 thorpej tbm = tbm_create(top, &ap->profile, &ap->in_action, &ap->out_action); 935 1.1 thorpej if (tbm == NULL) 936 1.23 joe return EINVAL; 937 1.1 thorpej /* return a class handle to the user */ 938 1.1 thorpej ap->cdnr_handle = cdnr_cb2handle(&tbm->cdnrblk); 939 1.23 joe return 0; 940 1.1 thorpej } 941 1.1 thorpej 942 1.1 thorpej static int 943 1.16 peter cdnrcmd_modify_tbm(struct cdnr_modify_tbmeter *ap) 944 1.1 thorpej { 945 1.1 thorpej struct tbmeter *tbm; 946 1.1 thorpej 947 1.1 thorpej if ((tbm = (struct tbmeter *)cdnr_handle2cb(ap->cdnr_handle)) == NULL) 948 1.23 joe return EINVAL; 949 1.1 thorpej 950 1.1 thorpej tb_import_profile(&tbm->tb, &ap->profile); 951 1.1 thorpej 952 1.23 joe return 0; 953 1.1 thorpej } 954 1.1 thorpej 955 1.1 thorpej static int 956 1.16 peter cdnrcmd_tbm_stats(struct cdnr_tbmeter_stats *ap) 957 1.1 thorpej { 958 1.1 thorpej struct tbmeter *tbm; 959 1.1 thorpej 960 1.1 thorpej if ((tbm = (struct tbmeter *)cdnr_handle2cb(ap->cdnr_handle)) == NULL) 961 1.23 joe return EINVAL; 962 1.1 thorpej 963 1.1 thorpej ap->in_cnt = tbm->in_cnt; 964 1.1 thorpej ap->out_cnt = tbm->out_cnt; 965 1.1 thorpej 966 1.23 joe return 0; 967 1.1 thorpej } 968 1.1 thorpej 969 1.1 thorpej static int 970 1.16 peter cdnrcmd_add_trtcm(struct cdnr_add_trtcm *ap) 971 1.1 thorpej { 972 1.1 thorpej struct top_cdnr *top; 973 1.1 thorpej struct trtcm *tcm; 974 1.1 thorpej 975 1.1 thorpej if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL) 976 1.23 joe return EBADF; 977 1.1 thorpej 978 1.1 thorpej tcm = trtcm_create(top, &ap->cmtd_profile, &ap->peak_profile, 979 1.1 thorpej &ap->green_action, &ap->yellow_action, 980 1.1 thorpej &ap->red_action, ap->coloraware); 981 1.1 thorpej if (tcm == NULL) 982 1.23 joe return EINVAL; 983 1.1 thorpej 984 1.1 thorpej /* return a class handle to the user */ 985 1.1 thorpej ap->cdnr_handle = cdnr_cb2handle(&tcm->cdnrblk); 986 1.23 joe return 0; 987 1.1 thorpej } 988 1.1 thorpej 989 1.1 thorpej static int 990 1.16 peter cdnrcmd_modify_trtcm(struct cdnr_modify_trtcm *ap) 991 1.1 thorpej { 992 1.1 thorpej struct trtcm *tcm; 993 1.1 thorpej 994 1.1 thorpej if ((tcm = (struct trtcm *)cdnr_handle2cb(ap->cdnr_handle)) == NULL) 995 1.23 joe return EINVAL; 996 1.1 thorpej 997 1.1 thorpej tb_import_profile(&tcm->cmtd_tb, &ap->cmtd_profile); 998 1.1 thorpej tb_import_profile(&tcm->peak_tb, &ap->peak_profile); 999 1.1 thorpej 1000 1.23 joe return 0; 1001 1.1 thorpej } 1002 1.1 thorpej 1003 1.1 thorpej static int 1004 1.16 peter cdnrcmd_tcm_stats(struct cdnr_tcm_stats *ap) 1005 1.1 thorpej { 1006 1.1 thorpej struct cdnr_block *cb; 1007 1.1 thorpej 1008 1.1 thorpej if ((cb = cdnr_handle2cb(ap->cdnr_handle)) == NULL) 1009 1.23 joe return EINVAL; 1010 1.1 thorpej 1011 1.1 thorpej if (cb->cb_type == TCETYPE_TRTCM) { 1012 1.16 peter struct trtcm *tcm = (struct trtcm *)cb; 1013 1.9 perry 1014 1.16 peter ap->green_cnt = tcm->green_cnt; 1015 1.16 peter ap->yellow_cnt = tcm->yellow_cnt; 1016 1.16 peter ap->red_cnt = tcm->red_cnt; 1017 1.1 thorpej } else if (cb->cb_type == TCETYPE_TSWTCM) { 1018 1.16 peter struct tswtcm *tsw = (struct tswtcm *)cb; 1019 1.9 perry 1020 1.16 peter ap->green_cnt = tsw->green_cnt; 1021 1.16 peter ap->yellow_cnt = tsw->yellow_cnt; 1022 1.16 peter ap->red_cnt = tsw->red_cnt; 1023 1.1 thorpej } else 1024 1.23 joe return EINVAL; 1025 1.1 thorpej 1026 1.23 joe return 0; 1027 1.1 thorpej } 1028 1.1 thorpej 1029 1.1 thorpej static int 1030 1.16 peter cdnrcmd_add_tswtcm(struct cdnr_add_tswtcm *ap) 1031 1.1 thorpej { 1032 1.1 thorpej struct top_cdnr *top; 1033 1.1 thorpej struct tswtcm *tsw; 1034 1.1 thorpej 1035 1.1 thorpej if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL) 1036 1.23 joe return EBADF; 1037 1.1 thorpej 1038 1.1 thorpej if (ap->cmtd_rate > ap->peak_rate) 1039 1.23 joe return EINVAL; 1040 1.1 thorpej 1041 1.1 thorpej tsw = tswtcm_create(top, ap->cmtd_rate, ap->peak_rate, 1042 1.1 thorpej ap->avg_interval, &ap->green_action, 1043 1.1 thorpej &ap->yellow_action, &ap->red_action); 1044 1.1 thorpej if (tsw == NULL) 1045 1.23 joe return EINVAL; 1046 1.1 thorpej 1047 1.1 thorpej /* return a class handle to the user */ 1048 1.1 thorpej ap->cdnr_handle = cdnr_cb2handle(&tsw->cdnrblk); 1049 1.23 joe return 0; 1050 1.1 thorpej } 1051 1.1 thorpej 1052 1.1 thorpej static int 1053 1.16 peter cdnrcmd_modify_tswtcm(struct cdnr_modify_tswtcm *ap) 1054 1.1 thorpej { 1055 1.1 thorpej struct tswtcm *tsw; 1056 1.1 thorpej 1057 1.1 thorpej if ((tsw = (struct tswtcm *)cdnr_handle2cb(ap->cdnr_handle)) == NULL) 1058 1.23 joe return EINVAL; 1059 1.1 thorpej 1060 1.1 thorpej if (ap->cmtd_rate > ap->peak_rate) 1061 1.23 joe return EINVAL; 1062 1.1 thorpej 1063 1.1 thorpej /* convert rates from bits/sec to bytes/sec */ 1064 1.1 thorpej tsw->cmtd_rate = ap->cmtd_rate / 8; 1065 1.1 thorpej tsw->peak_rate = ap->peak_rate / 8; 1066 1.1 thorpej tsw->avg_rate = 0; 1067 1.1 thorpej 1068 1.1 thorpej /* timewin is converted from msec to machine clock unit */ 1069 1.1 thorpej tsw->timewin = (u_int64_t)machclk_freq * ap->avg_interval / 1000; 1070 1.1 thorpej 1071 1.23 joe return 0; 1072 1.1 thorpej } 1073 1.1 thorpej 1074 1.1 thorpej static int 1075 1.16 peter cdnrcmd_get_stats(struct cdnr_get_stats *ap) 1076 1.1 thorpej { 1077 1.1 thorpej struct top_cdnr *top; 1078 1.1 thorpej struct cdnr_block *cb; 1079 1.1 thorpej struct tbmeter *tbm; 1080 1.1 thorpej struct trtcm *tcm; 1081 1.1 thorpej struct tswtcm *tsw; 1082 1.1 thorpej struct tce_stats tce, *usp; 1083 1.1 thorpej int error, n, nskip, nelements; 1084 1.1 thorpej 1085 1.1 thorpej if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL) 1086 1.23 joe return EBADF; 1087 1.1 thorpej 1088 1.1 thorpej /* copy action stats */ 1089 1.7 christos (void)memcpy(ap->cnts, top->tc_cnts, sizeof(ap->cnts)); 1090 1.1 thorpej 1091 1.1 thorpej /* stats for each element */ 1092 1.1 thorpej nelements = ap->nelements; 1093 1.1 thorpej usp = ap->tce_stats; 1094 1.1 thorpej if (nelements <= 0 || usp == NULL) 1095 1.23 joe return 0; 1096 1.1 thorpej 1097 1.1 thorpej nskip = ap->nskip; 1098 1.1 thorpej n = 0; 1099 1.1 thorpej LIST_FOREACH(cb, &top->tc_elements, cb_next) { 1100 1.1 thorpej if (nskip > 0) { 1101 1.1 thorpej nskip--; 1102 1.1 thorpej continue; 1103 1.1 thorpej } 1104 1.1 thorpej 1105 1.7 christos (void)memset(&tce, 0, sizeof(tce)); 1106 1.1 thorpej tce.tce_handle = cb->cb_handle; 1107 1.1 thorpej tce.tce_type = cb->cb_type; 1108 1.1 thorpej switch (cb->cb_type) { 1109 1.1 thorpej case TCETYPE_TBMETER: 1110 1.1 thorpej tbm = (struct tbmeter *)cb; 1111 1.1 thorpej tce.tce_cnts[0] = tbm->in_cnt; 1112 1.1 thorpej tce.tce_cnts[1] = tbm->out_cnt; 1113 1.1 thorpej break; 1114 1.1 thorpej case TCETYPE_TRTCM: 1115 1.1 thorpej tcm = (struct trtcm *)cb; 1116 1.1 thorpej tce.tce_cnts[0] = tcm->green_cnt; 1117 1.1 thorpej tce.tce_cnts[1] = tcm->yellow_cnt; 1118 1.1 thorpej tce.tce_cnts[2] = tcm->red_cnt; 1119 1.1 thorpej break; 1120 1.1 thorpej case TCETYPE_TSWTCM: 1121 1.1 thorpej tsw = (struct tswtcm *)cb; 1122 1.1 thorpej tce.tce_cnts[0] = tsw->green_cnt; 1123 1.1 thorpej tce.tce_cnts[1] = tsw->yellow_cnt; 1124 1.1 thorpej tce.tce_cnts[2] = tsw->red_cnt; 1125 1.1 thorpej break; 1126 1.1 thorpej default: 1127 1.1 thorpej continue; 1128 1.1 thorpej } 1129 1.1 thorpej 1130 1.19 christos if ((error = copyout((void *)&tce, (void *)usp++, 1131 1.1 thorpej sizeof(tce))) != 0) 1132 1.23 joe return error; 1133 1.1 thorpej 1134 1.1 thorpej if (++n == nelements) 1135 1.1 thorpej break; 1136 1.1 thorpej } 1137 1.1 thorpej ap->nelements = n; 1138 1.1 thorpej 1139 1.23 joe return 0; 1140 1.1 thorpej } 1141 1.1 thorpej 1142 1.1 thorpej /* 1143 1.1 thorpej * conditioner device interface 1144 1.1 thorpej */ 1145 1.1 thorpej int 1146 1.18 christos cdnropen(dev_t dev, int flag, int fmt, 1147 1.18 christos struct lwp *l) 1148 1.1 thorpej { 1149 1.1 thorpej if (machclk_freq == 0) 1150 1.1 thorpej init_machclk(); 1151 1.1 thorpej 1152 1.1 thorpej if (machclk_freq == 0) { 1153 1.8 wiz printf("cdnr: no CPU clock available!\n"); 1154 1.23 joe return ENXIO; 1155 1.1 thorpej } 1156 1.1 thorpej 1157 1.1 thorpej /* everything will be done when the queueing scheme is attached. */ 1158 1.1 thorpej return 0; 1159 1.1 thorpej } 1160 1.1 thorpej 1161 1.1 thorpej int 1162 1.18 christos cdnrclose(dev_t dev, int flag, int fmt, 1163 1.18 christos struct lwp *l) 1164 1.1 thorpej { 1165 1.1 thorpej struct top_cdnr *top; 1166 1.1 thorpej int err, error = 0; 1167 1.1 thorpej 1168 1.1 thorpej while ((top = LIST_FIRST(&tcb_list)) != NULL) { 1169 1.1 thorpej /* destroy all */ 1170 1.1 thorpej err = top_destroy(top); 1171 1.1 thorpej if (err != 0 && error == 0) 1172 1.1 thorpej error = err; 1173 1.1 thorpej } 1174 1.1 thorpej altq_input = NULL; 1175 1.1 thorpej 1176 1.23 joe return error; 1177 1.1 thorpej } 1178 1.1 thorpej 1179 1.1 thorpej int 1180 1.19 christos cdnrioctl(dev_t dev, ioctlcmd_t cmd, void *addr, int flag, 1181 1.15 christos struct lwp *l) 1182 1.1 thorpej { 1183 1.1 thorpej struct top_cdnr *top; 1184 1.1 thorpej struct cdnr_interface *ifacep; 1185 1.1 thorpej int s, error = 0; 1186 1.1 thorpej 1187 1.1 thorpej /* check super-user privilege */ 1188 1.1 thorpej switch (cmd) { 1189 1.1 thorpej case CDNR_GETSTATS: 1190 1.1 thorpej break; 1191 1.1 thorpej default: 1192 1.17 elad if ((error = kauth_authorize_network(l->l_cred, 1193 1.17 elad KAUTH_NETWORK_ALTQ, KAUTH_REQ_NETWORK_ALTQ_CDNR, NULL, 1194 1.17 elad NULL, NULL)) != 0) 1195 1.1 thorpej return (error); 1196 1.1 thorpej break; 1197 1.1 thorpej } 1198 1.9 perry 1199 1.3 thorpej s = splnet(); 1200 1.1 thorpej switch (cmd) { 1201 1.9 perry 1202 1.1 thorpej case CDNR_IF_ATTACH: 1203 1.1 thorpej ifacep = (struct cdnr_interface *)addr; 1204 1.1 thorpej error = cdnrcmd_if_attach(ifacep->cdnr_ifname); 1205 1.1 thorpej break; 1206 1.1 thorpej 1207 1.1 thorpej case CDNR_IF_DETACH: 1208 1.1 thorpej ifacep = (struct cdnr_interface *)addr; 1209 1.1 thorpej error = cdnrcmd_if_detach(ifacep->cdnr_ifname); 1210 1.1 thorpej break; 1211 1.1 thorpej 1212 1.1 thorpej case CDNR_ENABLE: 1213 1.1 thorpej case CDNR_DISABLE: 1214 1.1 thorpej ifacep = (struct cdnr_interface *)addr; 1215 1.1 thorpej if ((top = tcb_lookup(ifacep->cdnr_ifname)) == NULL) { 1216 1.1 thorpej error = EBADF; 1217 1.1 thorpej break; 1218 1.1 thorpej } 1219 1.1 thorpej 1220 1.1 thorpej switch (cmd) { 1221 1.1 thorpej 1222 1.1 thorpej case CDNR_ENABLE: 1223 1.1 thorpej ALTQ_SET_CNDTNING(top->tc_ifq); 1224 1.1 thorpej if (altq_input == NULL) 1225 1.1 thorpej altq_input = altq_cdnr_input; 1226 1.1 thorpej break; 1227 1.1 thorpej 1228 1.1 thorpej case CDNR_DISABLE: 1229 1.1 thorpej ALTQ_CLEAR_CNDTNING(top->tc_ifq); 1230 1.1 thorpej LIST_FOREACH(top, &tcb_list, tc_next) 1231 1.1 thorpej if (ALTQ_IS_CNDTNING(top->tc_ifq)) 1232 1.1 thorpej break; 1233 1.1 thorpej if (top == NULL) 1234 1.1 thorpej altq_input = NULL; 1235 1.1 thorpej break; 1236 1.1 thorpej } 1237 1.1 thorpej break; 1238 1.1 thorpej 1239 1.1 thorpej case CDNR_ADD_ELEM: 1240 1.1 thorpej error = cdnrcmd_add_element((struct cdnr_add_element *)addr); 1241 1.1 thorpej break; 1242 1.1 thorpej 1243 1.1 thorpej case CDNR_DEL_ELEM: 1244 1.1 thorpej error = cdnrcmd_delete_element((struct cdnr_delete_element *)addr); 1245 1.1 thorpej break; 1246 1.1 thorpej 1247 1.1 thorpej case CDNR_ADD_TBM: 1248 1.1 thorpej error = cdnrcmd_add_tbm((struct cdnr_add_tbmeter *)addr); 1249 1.1 thorpej break; 1250 1.1 thorpej 1251 1.1 thorpej case CDNR_MOD_TBM: 1252 1.1 thorpej error = cdnrcmd_modify_tbm((struct cdnr_modify_tbmeter *)addr); 1253 1.1 thorpej break; 1254 1.1 thorpej 1255 1.1 thorpej case CDNR_TBM_STATS: 1256 1.1 thorpej error = cdnrcmd_tbm_stats((struct cdnr_tbmeter_stats *)addr); 1257 1.1 thorpej break; 1258 1.1 thorpej 1259 1.1 thorpej case CDNR_ADD_TCM: 1260 1.1 thorpej error = cdnrcmd_add_trtcm((struct cdnr_add_trtcm *)addr); 1261 1.1 thorpej break; 1262 1.1 thorpej 1263 1.1 thorpej case CDNR_MOD_TCM: 1264 1.1 thorpej error = cdnrcmd_modify_trtcm((struct cdnr_modify_trtcm *)addr); 1265 1.1 thorpej break; 1266 1.1 thorpej 1267 1.1 thorpej case CDNR_TCM_STATS: 1268 1.1 thorpej error = cdnrcmd_tcm_stats((struct cdnr_tcm_stats *)addr); 1269 1.1 thorpej break; 1270 1.1 thorpej 1271 1.1 thorpej case CDNR_ADD_FILTER: 1272 1.1 thorpej error = cdnrcmd_add_filter((struct cdnr_add_filter *)addr); 1273 1.1 thorpej break; 1274 1.1 thorpej 1275 1.1 thorpej case CDNR_DEL_FILTER: 1276 1.1 thorpej error = cdnrcmd_delete_filter((struct cdnr_delete_filter *)addr); 1277 1.1 thorpej break; 1278 1.1 thorpej 1279 1.1 thorpej case CDNR_GETSTATS: 1280 1.1 thorpej error = cdnrcmd_get_stats((struct cdnr_get_stats *)addr); 1281 1.1 thorpej break; 1282 1.1 thorpej 1283 1.1 thorpej case CDNR_ADD_TSW: 1284 1.1 thorpej error = cdnrcmd_add_tswtcm((struct cdnr_add_tswtcm *)addr); 1285 1.1 thorpej break; 1286 1.1 thorpej 1287 1.1 thorpej case CDNR_MOD_TSW: 1288 1.1 thorpej error = cdnrcmd_modify_tswtcm((struct cdnr_modify_tswtcm *)addr); 1289 1.1 thorpej break; 1290 1.1 thorpej 1291 1.1 thorpej default: 1292 1.1 thorpej error = EINVAL; 1293 1.1 thorpej break; 1294 1.1 thorpej } 1295 1.1 thorpej splx(s); 1296 1.1 thorpej 1297 1.1 thorpej return error; 1298 1.1 thorpej } 1299 1.1 thorpej 1300 1.1 thorpej #ifdef KLD_MODULE 1301 1.1 thorpej 1302 1.1 thorpej static struct altqsw cdnr_sw = 1303 1.1 thorpej {"cdnr", cdnropen, cdnrclose, cdnrioctl}; 1304 1.1 thorpej 1305 1.1 thorpej ALTQ_MODULE(altq_cdnr, ALTQT_CDNR, &cdnr_sw); 1306 1.1 thorpej 1307 1.1 thorpej #endif /* KLD_MODULE */ 1308 1.1 thorpej 1309 1.16 peter #endif /* ALTQ3_COMPAT */ 1310 1.1 thorpej #endif /* ALTQ_CDNR */ 1311