1 2 /* 3 * ng_one2many.c 4 * 5 * Copyright (c) 2000 Whistle Communications, Inc. 6 * All rights reserved. 7 * 8 * Subject to the following obligations and disclaimer of warranty, use and 9 * redistribution of this software, in source or object code forms, with or 10 * without modifications are expressly permitted by Whistle Communications; 11 * provided, however, that: 12 * 1. Any and all reproductions of the source or object code must include the 13 * copyright notice above and the following disclaimer of warranties; and 14 * 2. No rights are granted, in any manner or form, to use Whistle 15 * Communications, Inc. trademarks, including the mark "WHISTLE 16 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 17 * such appears in the above copyright notice or in the software. 18 * 19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 35 * OF SUCH DAMAGE. 36 * 37 * Author: Archie Cobbs <archie@FreeBSD.org> 38 * 39 * $FreeBSD: src/sys/netgraph/ng_one2many.c,v 1.1.2.2 2002/07/02 23:44:02 archie Exp $ 40 * $DragonFly: src/sys/netgraph/one2many/ng_one2many.c,v 1.3 2003/08/07 21:17:32 dillon Exp $ 41 */ 42 43 /* 44 * ng_one2many(4) netgraph node type 45 * 46 * Packets received on the "one" hook are sent out each of the 47 * "many" hooks in round-robin fashion. Packets received on any 48 * "many" hook are always delivered to the "one" hook. 49 */ 50 51 #include <sys/param.h> 52 #include <sys/systm.h> 53 #include <sys/kernel.h> 54 #include <sys/malloc.h> 55 #include <sys/ctype.h> 56 #include <sys/mbuf.h> 57 #include <sys/errno.h> 58 59 #include <netgraph/ng_message.h> 60 #include <netgraph/netgraph.h> 61 #include <netgraph/ng_parse.h> 62 #include "ng_one2many.h" 63 64 /* Per-link private data */ 65 struct ng_one2many_link { 66 hook_p hook; /* netgraph hook */ 67 struct ng_one2many_link_stats stats; /* link stats */ 68 }; 69 70 /* Per-node private data */ 71 struct ng_one2many_private { 72 struct ng_one2many_config conf; /* node configuration */ 73 struct ng_one2many_link one; /* "one" hook */ 74 struct ng_one2many_link many[NG_ONE2MANY_MAX_LINKS]; 75 u_int16_t nextMany; /* next round-robin */ 76 u_int16_t numActiveMany; /* # active "many" */ 77 u_int16_t activeMany[NG_ONE2MANY_MAX_LINKS]; 78 }; 79 typedef struct ng_one2many_private *priv_p; 80 81 /* Netgraph node methods */ 82 static ng_constructor_t ng_one2many_constructor; 83 static ng_rcvmsg_t ng_one2many_rcvmsg; 84 static ng_shutdown_t ng_one2many_rmnode; 85 static ng_newhook_t ng_one2many_newhook; 86 static ng_rcvdata_t ng_one2many_rcvdata; 87 static ng_disconnect_t ng_one2many_disconnect; 88 89 /* Other functions */ 90 static void ng_one2many_update_many(priv_p priv); 91 92 /* Store each hook's link number in the private field */ 93 #define LINK_NUM(hook) (*(int16_t *)(&(hook)->private)) 94 95 /****************************************************************** 96 NETGRAPH PARSE TYPES 97 ******************************************************************/ 98 99 /* Parse type for struct ng_one2many_config */ 100 static const struct ng_parse_fixedarray_info 101 ng_one2many_enableLinks_array_type_info = { 102 &ng_parse_uint8_type, 103 NG_ONE2MANY_MAX_LINKS 104 }; 105 static const struct ng_parse_type ng_one2many_enableLinks_array_type = { 106 &ng_parse_fixedarray_type, 107 &ng_one2many_enableLinks_array_type_info, 108 }; 109 static const struct ng_parse_struct_field ng_one2many_config_type_fields[] 110 = NG_ONE2MANY_CONFIG_TYPE_INFO(&ng_one2many_enableLinks_array_type); 111 static const struct ng_parse_type ng_one2many_config_type = { 112 &ng_parse_struct_type, 113 &ng_one2many_config_type_fields 114 }; 115 116 /* Parse type for struct ng_one2many_link_stats */ 117 static const struct ng_parse_struct_field ng_one2many_link_stats_type_fields[] 118 = NG_ONE2MANY_LINK_STATS_TYPE_INFO; 119 static const struct ng_parse_type ng_one2many_link_stats_type = { 120 &ng_parse_struct_type, 121 &ng_one2many_link_stats_type_fields 122 }; 123 124 /* List of commands and how to convert arguments to/from ASCII */ 125 static const struct ng_cmdlist ng_one2many_cmdlist[] = { 126 { 127 NGM_ONE2MANY_COOKIE, 128 NGM_ONE2MANY_SET_CONFIG, 129 "setconfig", 130 &ng_one2many_config_type, 131 NULL 132 }, 133 { 134 NGM_ONE2MANY_COOKIE, 135 NGM_ONE2MANY_GET_CONFIG, 136 "getconfig", 137 NULL, 138 &ng_one2many_config_type 139 }, 140 { 141 NGM_ONE2MANY_COOKIE, 142 NGM_ONE2MANY_GET_STATS, 143 "getstats", 144 &ng_parse_int32_type, 145 &ng_one2many_link_stats_type 146 }, 147 { 148 NGM_ONE2MANY_COOKIE, 149 NGM_ONE2MANY_CLR_STATS, 150 "clrstats", 151 &ng_parse_int32_type, 152 NULL, 153 }, 154 { 155 NGM_ONE2MANY_COOKIE, 156 NGM_ONE2MANY_GETCLR_STATS, 157 "getclrstats", 158 &ng_parse_int32_type, 159 &ng_one2many_link_stats_type 160 }, 161 { 0 } 162 }; 163 164 /* Node type descriptor */ 165 static struct ng_type ng_one2many_typestruct = { 166 NG_VERSION, 167 NG_ONE2MANY_NODE_TYPE, 168 NULL, 169 ng_one2many_constructor, 170 ng_one2many_rcvmsg, 171 ng_one2many_rmnode, 172 ng_one2many_newhook, 173 NULL, 174 NULL, 175 ng_one2many_rcvdata, 176 ng_one2many_rcvdata, 177 ng_one2many_disconnect, 178 ng_one2many_cmdlist, 179 }; 180 NETGRAPH_INIT(one2many, &ng_one2many_typestruct); 181 182 /****************************************************************** 183 NETGRAPH NODE METHODS 184 ******************************************************************/ 185 186 /* 187 * Node constructor 188 */ 189 static int 190 ng_one2many_constructor(node_p *nodep) 191 { 192 priv_p priv; 193 int error; 194 195 /* Allocate and initialize private info */ 196 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 197 if (priv == NULL) 198 return (ENOMEM); 199 priv->conf.xmitAlg = NG_ONE2MANY_XMIT_ROUNDROBIN; 200 priv->conf.failAlg = NG_ONE2MANY_FAIL_MANUAL; 201 202 /* Call superclass constructor */ 203 if ((error = ng_make_node_common(&ng_one2many_typestruct, nodep))) { 204 FREE(priv, M_NETGRAPH); 205 return (error); 206 } 207 (*nodep)->private = priv; 208 209 /* Done */ 210 return (0); 211 } 212 213 /* 214 * Method for attaching a new hook 215 */ 216 static int 217 ng_one2many_newhook(node_p node, hook_p hook, const char *name) 218 { 219 const priv_p priv = node->private; 220 struct ng_one2many_link *link; 221 int linkNum; 222 u_long i; 223 224 /* Which hook? */ 225 if (strncmp(name, NG_ONE2MANY_HOOK_MANY_PREFIX, 226 strlen(NG_ONE2MANY_HOOK_MANY_PREFIX)) == 0) { 227 const char *cp; 228 char *eptr; 229 230 cp = name + strlen(NG_ONE2MANY_HOOK_MANY_PREFIX); 231 if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) 232 return (EINVAL); 233 i = strtoul(cp, &eptr, 10); 234 if (*eptr != '\0' || i < 0 || i >= NG_ONE2MANY_MAX_LINKS) 235 return (EINVAL); 236 linkNum = (int)i; 237 link = &priv->many[linkNum]; 238 } else if (strcmp(name, NG_ONE2MANY_HOOK_ONE) == 0) { 239 linkNum = NG_ONE2MANY_ONE_LINKNUM; 240 link = &priv->one; 241 } else 242 return (EINVAL); 243 244 /* Is hook already connected? (should never happen) */ 245 if (link->hook != NULL) 246 return (EISCONN); 247 248 /* Setup private info for this link */ 249 LINK_NUM(hook) = linkNum; 250 link->hook = hook; 251 bzero(&link->stats, sizeof(link->stats)); 252 if (linkNum != NG_ONE2MANY_ONE_LINKNUM) { 253 priv->conf.enabledLinks[linkNum] = 1; /* auto-enable link */ 254 ng_one2many_update_many(priv); 255 } 256 257 /* Done */ 258 return (0); 259 } 260 261 /* 262 * Receive a control message 263 */ 264 static int 265 ng_one2many_rcvmsg(node_p node, struct ng_mesg *msg, 266 const char *retaddr, struct ng_mesg **rptr) 267 { 268 const priv_p priv = node->private; 269 struct ng_mesg *resp = NULL; 270 int error = 0; 271 272 switch (msg->header.typecookie) { 273 case NGM_ONE2MANY_COOKIE: 274 switch (msg->header.cmd) { 275 case NGM_ONE2MANY_SET_CONFIG: 276 { 277 struct ng_one2many_config *conf; 278 int i; 279 280 /* Check that new configuration is valid */ 281 if (msg->header.arglen != sizeof(*conf)) { 282 error = EINVAL; 283 break; 284 } 285 conf = (struct ng_one2many_config *)msg->data; 286 switch (conf->xmitAlg) { 287 case NG_ONE2MANY_XMIT_ROUNDROBIN: 288 break; 289 default: 290 error = EINVAL; 291 break; 292 } 293 switch (conf->failAlg) { 294 case NG_ONE2MANY_FAIL_MANUAL: 295 break; 296 default: 297 error = EINVAL; 298 break; 299 } 300 if (error != 0) 301 break; 302 303 /* Normalized many link enabled bits */ 304 for (i = 0; i < NG_ONE2MANY_MAX_LINKS; i++) 305 conf->enabledLinks[i] = !!conf->enabledLinks[i]; 306 307 /* Copy config and reset */ 308 bcopy(conf, &priv->conf, sizeof(*conf)); 309 ng_one2many_update_many(priv); 310 break; 311 } 312 case NGM_ONE2MANY_GET_CONFIG: 313 { 314 struct ng_one2many_config *conf; 315 316 NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT); 317 if (resp == NULL) { 318 error = ENOMEM; 319 break; 320 } 321 conf = (struct ng_one2many_config *)resp->data; 322 bcopy(&priv->conf, conf, sizeof(priv->conf)); 323 break; 324 } 325 case NGM_ONE2MANY_GET_STATS: 326 case NGM_ONE2MANY_CLR_STATS: 327 case NGM_ONE2MANY_GETCLR_STATS: 328 { 329 struct ng_one2many_link *link; 330 int linkNum; 331 332 /* Get link */ 333 if (msg->header.arglen != sizeof(int32_t)) { 334 error = EINVAL; 335 break; 336 } 337 linkNum = *((int32_t *)msg->data); 338 if (linkNum == NG_ONE2MANY_ONE_LINKNUM) 339 link = &priv->one; 340 else if (linkNum == 0 341 && linkNum < NG_ONE2MANY_MAX_LINKS) { 342 link = &priv->many[linkNum]; 343 } else { 344 error = EINVAL; 345 break; 346 } 347 348 /* Get/clear stats */ 349 if (msg->header.cmd != NGM_ONE2MANY_CLR_STATS) { 350 NG_MKRESPONSE(resp, msg, 351 sizeof(link->stats), M_NOWAIT); 352 if (resp == NULL) { 353 error = ENOMEM; 354 break; 355 } 356 bcopy(&link->stats, 357 resp->data, sizeof(link->stats)); 358 } 359 if (msg->header.cmd != NGM_ONE2MANY_GET_STATS) 360 bzero(&link->stats, sizeof(link->stats)); 361 break; 362 } 363 default: 364 error = EINVAL; 365 break; 366 } 367 break; 368 default: 369 error = EINVAL; 370 break; 371 } 372 373 /* Done */ 374 if (rptr) 375 *rptr = resp; 376 else if (resp != NULL) 377 FREE(resp, M_NETGRAPH); 378 FREE(msg, M_NETGRAPH); 379 return (error); 380 } 381 382 /* 383 * Receive data on a hook 384 */ 385 static int 386 ng_one2many_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) 387 { 388 const node_p node = hook->node; 389 const priv_p priv = node->private; 390 struct ng_one2many_link *src; 391 struct ng_one2many_link *dst; 392 int error = 0; 393 int linkNum; 394 395 /* Get link number */ 396 linkNum = LINK_NUM(hook); 397 KASSERT(linkNum == NG_ONE2MANY_ONE_LINKNUM 398 || (linkNum >= 0 && linkNum < NG_ONE2MANY_MAX_LINKS), 399 ("%s: linkNum=%d", __FUNCTION__, linkNum)); 400 401 /* Figure out source link */ 402 src = (linkNum == NG_ONE2MANY_ONE_LINKNUM) ? 403 &priv->one : &priv->many[linkNum]; 404 KASSERT(src->hook != NULL, ("%s: no src%d", __FUNCTION__, linkNum)); 405 406 /* Update receive stats */ 407 src->stats.recvPackets++; 408 src->stats.recvOctets += m->m_pkthdr.len; 409 410 /* Figure out destination link */ 411 if (linkNum == NG_ONE2MANY_ONE_LINKNUM) { 412 if (priv->numActiveMany == 0) { 413 NG_FREE_DATA(m, meta); 414 return (ENOTCONN); 415 } 416 dst = &priv->many[priv->activeMany[priv->nextMany]]; 417 priv->nextMany = (priv->nextMany + 1) % priv->numActiveMany; 418 } else 419 dst = &priv->one; 420 421 /* Update transmit stats */ 422 dst->stats.xmitPackets++; 423 dst->stats.xmitOctets += m->m_pkthdr.len; 424 425 /* Deliver packet */ 426 NG_SEND_DATA(error, dst->hook, m, meta); 427 return (error); 428 } 429 430 /* 431 * Shutdown node 432 */ 433 static int 434 ng_one2many_rmnode(node_p node) 435 { 436 const priv_p priv = node->private; 437 438 ng_unname(node); 439 ng_cutlinks(node); 440 KASSERT(priv->numActiveMany == 0, 441 ("%s: numActiveMany=%d", __FUNCTION__, priv->numActiveMany)); 442 FREE(priv, M_NETGRAPH); 443 node->private = NULL; 444 ng_unref(node); 445 return (0); 446 } 447 448 /* 449 * Hook disconnection. 450 */ 451 static int 452 ng_one2many_disconnect(hook_p hook) 453 { 454 const priv_p priv = hook->node->private; 455 int linkNum; 456 457 /* Get link number */ 458 linkNum = LINK_NUM(hook); 459 KASSERT(linkNum == NG_ONE2MANY_ONE_LINKNUM 460 || (linkNum >= 0 && linkNum < NG_ONE2MANY_MAX_LINKS), 461 ("%s: linkNum=%d", __FUNCTION__, linkNum)); 462 463 /* Nuke the link */ 464 if (linkNum == NG_ONE2MANY_ONE_LINKNUM) 465 priv->one.hook = NULL; 466 else { 467 priv->many[linkNum].hook = NULL; 468 priv->conf.enabledLinks[linkNum] = 0; 469 ng_one2many_update_many(priv); 470 } 471 472 /* If no hooks left, go away */ 473 if (hook->node->numhooks == 0) 474 ng_rmnode(hook->node); 475 return (0); 476 } 477 478 /****************************************************************** 479 OTHER FUNCTIONS 480 ******************************************************************/ 481 482 /* 483 * Update internal state after the addition or removal of a "many" link 484 */ 485 static void 486 ng_one2many_update_many(priv_p priv) 487 { 488 int linkNum; 489 490 /* Update list of which "many" links are up */ 491 priv->numActiveMany = 0; 492 for (linkNum = 0; linkNum < NG_ONE2MANY_MAX_LINKS; linkNum++) { 493 switch (priv->conf.failAlg) { 494 case NG_ONE2MANY_FAIL_MANUAL: 495 if (priv->many[linkNum].hook != NULL 496 && priv->conf.enabledLinks[linkNum]) { 497 priv->activeMany[priv->numActiveMany] = linkNum; 498 priv->numActiveMany++; 499 } 500 break; 501 #ifdef INVARIANTS 502 default: 503 panic("%s: invalid failAlg", __FUNCTION__); 504 #endif 505 } 506 } 507 508 /* Update transmit algorithm state */ 509 switch (priv->conf.xmitAlg) { 510 case NG_ONE2MANY_XMIT_ROUNDROBIN: 511 if (priv->numActiveMany > 0) 512 priv->nextMany %= priv->numActiveMany; 513 break; 514 #ifdef INVARIANTS 515 default: 516 panic("%s: invalid xmitAlg", __FUNCTION__); 517 #endif 518 } 519 } 520 521 522