1 2 /* 3 * ng_ppp.c 4 * 5 * Copyright (c) 1996-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$ 40 * $Whistle: ng_ppp.c,v 1.24 1999/11/01 09:24:52 julian Exp $ 41 */ 42 43 /* 44 * PPP node type. 45 */ 46 47 #include <sys/param.h> 48 #include <sys/systm.h> 49 #include <sys/kernel.h> 50 #include <sys/limits.h> 51 #include <sys/time.h> 52 #include <sys/mbuf.h> 53 #include <sys/malloc.h> 54 #include <sys/errno.h> 55 #include <sys/ctype.h> 56 57 #include <netgraph/ng_message.h> 58 #include <netgraph/netgraph.h> 59 #include <netgraph/ng_parse.h> 60 #include <netgraph/ng_ppp.h> 61 #include <netgraph/ng_vjc.h> 62 63 #ifdef NG_SEPARATE_MALLOC 64 MALLOC_DEFINE(M_NETGRAPH_PPP, "netgraph_ppp", "netgraph ppp node"); 65 #else 66 #define M_NETGRAPH_PPP M_NETGRAPH 67 #endif 68 69 #define PROT_VALID(p) (((p) & 0x0101) == 0x0001) 70 #define PROT_COMPRESSABLE(p) (((p) & 0xff00) == 0x0000) 71 72 /* Some PPP protocol numbers we're interested in */ 73 #define PROT_APPLETALK 0x0029 74 #define PROT_COMPD 0x00fd 75 #define PROT_CRYPTD 0x0053 76 #define PROT_IP 0x0021 77 #define PROT_IPV6 0x0057 78 #define PROT_IPX 0x002b 79 #define PROT_LCP 0xc021 80 #define PROT_MP 0x003d 81 #define PROT_VJCOMP 0x002d 82 #define PROT_VJUNCOMP 0x002f 83 84 /* Multilink PPP definitions */ 85 #define MP_MIN_MRRU 1500 /* per RFC 1990 */ 86 #define MP_INITIAL_SEQ 0 /* per RFC 1990 */ 87 #define MP_MIN_LINK_MRU 32 88 89 #define MP_SHORT_SEQ_MASK 0x00000fff /* short seq # mask */ 90 #define MP_SHORT_SEQ_HIBIT 0x00000800 /* short seq # high bit */ 91 #define MP_SHORT_FIRST_FLAG 0x00008000 /* first fragment in frame */ 92 #define MP_SHORT_LAST_FLAG 0x00004000 /* last fragment in frame */ 93 94 #define MP_LONG_SEQ_MASK 0x00ffffff /* long seq # mask */ 95 #define MP_LONG_SEQ_HIBIT 0x00800000 /* long seq # high bit */ 96 #define MP_LONG_FIRST_FLAG 0x80000000 /* first fragment in frame */ 97 #define MP_LONG_LAST_FLAG 0x40000000 /* last fragment in frame */ 98 99 #define MP_NOSEQ 0x7fffffff /* impossible sequence number */ 100 101 /* Sign extension of MP sequence numbers */ 102 #define MP_SHORT_EXTEND(s) (((s) & MP_SHORT_SEQ_HIBIT) ? \ 103 ((s) | ~MP_SHORT_SEQ_MASK) \ 104 : ((s) & MP_SHORT_SEQ_MASK)) 105 #define MP_LONG_EXTEND(s) (((s) & MP_LONG_SEQ_HIBIT) ? \ 106 ((s) | ~MP_LONG_SEQ_MASK) \ 107 : ((s) & MP_LONG_SEQ_MASK)) 108 109 /* Comparision of MP sequence numbers. Note: all sequence numbers 110 except priv->xseq are stored with the sign bit extended. */ 111 #define MP_SHORT_SEQ_DIFF(x,y) MP_SHORT_EXTEND((x) - (y)) 112 #define MP_LONG_SEQ_DIFF(x,y) MP_LONG_EXTEND((x) - (y)) 113 114 #define MP_RECV_SEQ_DIFF(priv,x,y) \ 115 ((priv)->conf.recvShortSeq ? \ 116 MP_SHORT_SEQ_DIFF((x), (y)) : \ 117 MP_LONG_SEQ_DIFF((x), (y))) 118 119 /* Increment receive sequence number */ 120 #define MP_NEXT_RECV_SEQ(priv,seq) \ 121 ((priv)->conf.recvShortSeq ? \ 122 MP_SHORT_EXTEND((seq) + 1) : \ 123 MP_LONG_EXTEND((seq) + 1)) 124 125 /* Don't fragment transmitted packets smaller than this */ 126 #define MP_MIN_FRAG_LEN 6 127 128 /* Maximum fragment reasssembly queue length */ 129 #define MP_MAX_QUEUE_LEN 128 130 131 /* Fragment queue scanner period */ 132 #define MP_FRAGTIMER_INTERVAL (hz/2) 133 134 /* We store incoming fragments this way */ 135 struct ng_ppp_frag { 136 int seq; /* fragment seq# */ 137 u_char first; /* First in packet? */ 138 u_char last; /* Last in packet? */ 139 struct timeval timestamp; /* time of reception */ 140 struct mbuf *data; /* Fragment data */ 141 TAILQ_ENTRY(ng_ppp_frag) f_qent; /* Fragment queue */ 142 }; 143 144 /* We use integer indicies to refer to the non-link hooks */ 145 static const char *const ng_ppp_hook_names[] = { 146 NG_PPP_HOOK_ATALK, 147 #define HOOK_INDEX_ATALK 0 148 NG_PPP_HOOK_BYPASS, 149 #define HOOK_INDEX_BYPASS 1 150 NG_PPP_HOOK_COMPRESS, 151 #define HOOK_INDEX_COMPRESS 2 152 NG_PPP_HOOK_ENCRYPT, 153 #define HOOK_INDEX_ENCRYPT 3 154 NG_PPP_HOOK_DECOMPRESS, 155 #define HOOK_INDEX_DECOMPRESS 4 156 NG_PPP_HOOK_DECRYPT, 157 #define HOOK_INDEX_DECRYPT 5 158 NG_PPP_HOOK_INET, 159 #define HOOK_INDEX_INET 6 160 NG_PPP_HOOK_IPX, 161 #define HOOK_INDEX_IPX 7 162 NG_PPP_HOOK_VJC_COMP, 163 #define HOOK_INDEX_VJC_COMP 8 164 NG_PPP_HOOK_VJC_IP, 165 #define HOOK_INDEX_VJC_IP 9 166 NG_PPP_HOOK_VJC_UNCOMP, 167 #define HOOK_INDEX_VJC_UNCOMP 10 168 NG_PPP_HOOK_VJC_VJIP, 169 #define HOOK_INDEX_VJC_VJIP 11 170 NG_PPP_HOOK_IPV6, 171 #define HOOK_INDEX_IPV6 12 172 NULL 173 #define HOOK_INDEX_MAX 13 174 }; 175 176 /* We store index numbers in the hook private pointer. The HOOK_INDEX() 177 for a hook is either the index (above) for normal hooks, or the ones 178 complement of the link number for link hooks. 179 XXX Not any more.. (what a hack) 180 #define HOOK_INDEX(hook) (*((int16_t *) &(hook)->private)) 181 */ 182 183 /* Per-link private information */ 184 struct ng_ppp_link { 185 struct ng_ppp_link_conf conf; /* link configuration */ 186 hook_p hook; /* connection to link data */ 187 int32_t seq; /* highest rec'd seq# - MSEQ */ 188 u_int32_t latency; /* calculated link latency */ 189 struct timeval lastWrite; /* time of last write */ 190 int bytesInQueue; /* bytes in the output queue */ 191 struct ng_ppp_link_stat stats; /* Link stats */ 192 }; 193 194 /* Total per-node private information */ 195 struct ng_ppp_private { 196 struct ng_ppp_bund_conf conf; /* bundle config */ 197 struct ng_ppp_link_stat bundleStats; /* bundle stats */ 198 struct ng_ppp_link links[NG_PPP_MAX_LINKS];/* per-link info */ 199 int32_t xseq; /* next out MP seq # */ 200 int32_t mseq; /* min links[i].seq */ 201 u_char vjCompHooked; /* VJ comp hooked up? */ 202 u_char allLinksEqual; /* all xmit the same? */ 203 u_int numActiveLinks; /* how many links up */ 204 int activeLinks[NG_PPP_MAX_LINKS]; /* indicies */ 205 u_int lastLink; /* for round robin */ 206 hook_p hooks[HOOK_INDEX_MAX]; /* non-link hooks */ 207 TAILQ_HEAD(ng_ppp_fraglist, ng_ppp_frag) /* fragment queue */ 208 frags; 209 int qlen; /* fraq queue length */ 210 struct callout fragTimer; /* fraq queue check */ 211 }; 212 typedef struct ng_ppp_private *priv_p; 213 214 /* Netgraph node methods */ 215 static ng_constructor_t ng_ppp_constructor; 216 static ng_rcvmsg_t ng_ppp_rcvmsg; 217 static ng_shutdown_t ng_ppp_shutdown; 218 static ng_newhook_t ng_ppp_newhook; 219 static ng_rcvdata_t ng_ppp_rcvdata; 220 static ng_disconnect_t ng_ppp_disconnect; 221 222 /* Helper functions */ 223 static int ng_ppp_input(node_p node, int bypass, 224 int linkNum, item_p item); 225 static int ng_ppp_output(node_p node, int bypass, int proto, 226 int linkNum, item_p item); 227 static int ng_ppp_mp_input(node_p node, int linkNum, item_p item); 228 static int ng_ppp_check_packet(node_p node); 229 static void ng_ppp_get_packet(node_p node, struct mbuf **mp); 230 static int ng_ppp_frag_process(node_p node); 231 static int ng_ppp_frag_trim(node_p node); 232 static void ng_ppp_frag_timeout(node_p node, hook_p hook, void *arg1, 233 int arg2); 234 static void ng_ppp_frag_checkstale(node_p node); 235 static void ng_ppp_frag_reset(node_p node); 236 static int ng_ppp_mp_output(node_p node, struct mbuf *m); 237 static void ng_ppp_mp_strategy(node_p node, int len, int *distrib); 238 static int ng_ppp_intcmp(void *latency, const void *v1, const void *v2); 239 static struct mbuf *ng_ppp_addproto(struct mbuf *m, int proto, int compOK); 240 static struct mbuf *ng_ppp_prepend(struct mbuf *m, const void *buf, int len); 241 static int ng_ppp_config_valid(node_p node, 242 const struct ng_ppp_node_conf *newConf); 243 static void ng_ppp_update(node_p node, int newConf); 244 static void ng_ppp_start_frag_timer(node_p node); 245 static void ng_ppp_stop_frag_timer(node_p node); 246 247 /* Parse type for struct ng_ppp_mp_state_type */ 248 static const struct ng_parse_fixedarray_info ng_ppp_rseq_array_info = { 249 &ng_parse_hint32_type, 250 NG_PPP_MAX_LINKS 251 }; 252 static const struct ng_parse_type ng_ppp_rseq_array_type = { 253 &ng_parse_fixedarray_type, 254 &ng_ppp_rseq_array_info, 255 }; 256 static const struct ng_parse_struct_field ng_ppp_mp_state_type_fields[] 257 = NG_PPP_MP_STATE_TYPE_INFO(&ng_ppp_rseq_array_type); 258 static const struct ng_parse_type ng_ppp_mp_state_type = { 259 &ng_parse_struct_type, 260 &ng_ppp_mp_state_type_fields 261 }; 262 263 /* Parse type for struct ng_ppp_link_conf */ 264 static const struct ng_parse_struct_field ng_ppp_link_type_fields[] 265 = NG_PPP_LINK_TYPE_INFO; 266 static const struct ng_parse_type ng_ppp_link_type = { 267 &ng_parse_struct_type, 268 &ng_ppp_link_type_fields 269 }; 270 271 /* Parse type for struct ng_ppp_bund_conf */ 272 static const struct ng_parse_struct_field ng_ppp_bund_type_fields[] 273 = NG_PPP_BUND_TYPE_INFO; 274 static const struct ng_parse_type ng_ppp_bund_type = { 275 &ng_parse_struct_type, 276 &ng_ppp_bund_type_fields 277 }; 278 279 /* Parse type for struct ng_ppp_node_conf */ 280 static const struct ng_parse_fixedarray_info ng_ppp_array_info = { 281 &ng_ppp_link_type, 282 NG_PPP_MAX_LINKS 283 }; 284 static const struct ng_parse_type ng_ppp_link_array_type = { 285 &ng_parse_fixedarray_type, 286 &ng_ppp_array_info, 287 }; 288 static const struct ng_parse_struct_field ng_ppp_conf_type_fields[] 289 = NG_PPP_CONFIG_TYPE_INFO(&ng_ppp_bund_type, &ng_ppp_link_array_type); 290 static const struct ng_parse_type ng_ppp_conf_type = { 291 &ng_parse_struct_type, 292 &ng_ppp_conf_type_fields 293 }; 294 295 /* Parse type for struct ng_ppp_link_stat */ 296 static const struct ng_parse_struct_field ng_ppp_stats_type_fields[] 297 = NG_PPP_STATS_TYPE_INFO; 298 static const struct ng_parse_type ng_ppp_stats_type = { 299 &ng_parse_struct_type, 300 &ng_ppp_stats_type_fields 301 }; 302 303 /* List of commands and how to convert arguments to/from ASCII */ 304 static const struct ng_cmdlist ng_ppp_cmds[] = { 305 { 306 NGM_PPP_COOKIE, 307 NGM_PPP_SET_CONFIG, 308 "setconfig", 309 &ng_ppp_conf_type, 310 NULL 311 }, 312 { 313 NGM_PPP_COOKIE, 314 NGM_PPP_GET_CONFIG, 315 "getconfig", 316 NULL, 317 &ng_ppp_conf_type 318 }, 319 { 320 NGM_PPP_COOKIE, 321 NGM_PPP_GET_MP_STATE, 322 "getmpstate", 323 NULL, 324 &ng_ppp_mp_state_type 325 }, 326 { 327 NGM_PPP_COOKIE, 328 NGM_PPP_GET_LINK_STATS, 329 "getstats", 330 &ng_parse_int16_type, 331 &ng_ppp_stats_type 332 }, 333 { 334 NGM_PPP_COOKIE, 335 NGM_PPP_CLR_LINK_STATS, 336 "clrstats", 337 &ng_parse_int16_type, 338 NULL 339 }, 340 { 341 NGM_PPP_COOKIE, 342 NGM_PPP_GETCLR_LINK_STATS, 343 "getclrstats", 344 &ng_parse_int16_type, 345 &ng_ppp_stats_type 346 }, 347 { 0 } 348 }; 349 350 /* Node type descriptor */ 351 static struct ng_type ng_ppp_typestruct = { 352 .version = NG_ABI_VERSION, 353 .name = NG_PPP_NODE_TYPE, 354 .constructor = ng_ppp_constructor, 355 .rcvmsg = ng_ppp_rcvmsg, 356 .shutdown = ng_ppp_shutdown, 357 .newhook = ng_ppp_newhook, 358 .rcvdata = ng_ppp_rcvdata, 359 .disconnect = ng_ppp_disconnect, 360 .cmdlist = ng_ppp_cmds, 361 }; 362 NETGRAPH_INIT(ppp, &ng_ppp_typestruct); 363 364 /* Address and control field header */ 365 static const u_char ng_ppp_acf[2] = { 0xff, 0x03 }; 366 367 /* Maximum time we'll let a complete incoming packet sit in the queue */ 368 static const struct timeval ng_ppp_max_staleness = { 2, 0 }; /* 2 seconds */ 369 370 #define ERROUT(x) do { error = (x); goto done; } while (0) 371 372 /************************************************************************ 373 NETGRAPH NODE STUFF 374 ************************************************************************/ 375 376 /* 377 * Node type constructor 378 */ 379 static int 380 ng_ppp_constructor(node_p node) 381 { 382 priv_p priv; 383 int i; 384 385 /* Allocate private structure */ 386 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH_PPP, M_NOWAIT | M_ZERO); 387 if (priv == NULL) 388 return (ENOMEM); 389 390 NG_NODE_SET_PRIVATE(node, priv); 391 392 /* Initialize state */ 393 TAILQ_INIT(&priv->frags); 394 for (i = 0; i < NG_PPP_MAX_LINKS; i++) 395 priv->links[i].seq = MP_NOSEQ; 396 ng_callout_init(&priv->fragTimer); 397 398 /* Done */ 399 return (0); 400 } 401 402 /* 403 * Give our OK for a hook to be added 404 */ 405 static int 406 ng_ppp_newhook(node_p node, hook_p hook, const char *name) 407 { 408 const priv_p priv = NG_NODE_PRIVATE(node); 409 int linkNum = -1; 410 hook_p *hookPtr = NULL; 411 int hookIndex = -1; 412 413 /* Figure out which hook it is */ 414 if (strncmp(name, NG_PPP_HOOK_LINK_PREFIX, /* a link hook? */ 415 strlen(NG_PPP_HOOK_LINK_PREFIX)) == 0) { 416 const char *cp; 417 char *eptr; 418 419 cp = name + strlen(NG_PPP_HOOK_LINK_PREFIX); 420 if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) 421 return (EINVAL); 422 linkNum = (int)strtoul(cp, &eptr, 10); 423 if (*eptr != '\0' || linkNum < 0 || linkNum >= NG_PPP_MAX_LINKS) 424 return (EINVAL); 425 hookPtr = &priv->links[linkNum].hook; 426 hookIndex = ~linkNum; 427 } else { /* must be a non-link hook */ 428 int i; 429 430 for (i = 0; ng_ppp_hook_names[i] != NULL; i++) { 431 if (strcmp(name, ng_ppp_hook_names[i]) == 0) { 432 hookPtr = &priv->hooks[i]; 433 hookIndex = i; 434 break; 435 } 436 } 437 if (ng_ppp_hook_names[i] == NULL) 438 return (EINVAL); /* no such hook */ 439 } 440 441 /* See if hook is already connected */ 442 if (*hookPtr != NULL) 443 return (EISCONN); 444 445 /* Disallow more than one link unless multilink is enabled */ 446 if (linkNum != -1 && priv->links[linkNum].conf.enableLink 447 && !priv->conf.enableMultilink && priv->numActiveLinks >= 1) 448 return (ENODEV); 449 450 /* OK */ 451 *hookPtr = hook; 452 NG_HOOK_SET_PRIVATE(hook, (void *)(intptr_t)hookIndex); 453 ng_ppp_update(node, 0); 454 return (0); 455 } 456 457 /* 458 * Receive a control message 459 */ 460 static int 461 ng_ppp_rcvmsg(node_p node, item_p item, hook_p lasthook) 462 { 463 const priv_p priv = NG_NODE_PRIVATE(node); 464 struct ng_mesg *resp = NULL; 465 int error = 0; 466 struct ng_mesg *msg; 467 468 NGI_GET_MSG(item, msg); 469 switch (msg->header.typecookie) { 470 case NGM_PPP_COOKIE: 471 switch (msg->header.cmd) { 472 case NGM_PPP_SET_CONFIG: 473 { 474 struct ng_ppp_node_conf *const conf = 475 (struct ng_ppp_node_conf *)msg->data; 476 int i; 477 478 /* Check for invalid or illegal config */ 479 if (msg->header.arglen != sizeof(*conf)) 480 ERROUT(EINVAL); 481 if (!ng_ppp_config_valid(node, conf)) 482 ERROUT(EINVAL); 483 484 /* Copy config */ 485 priv->conf = conf->bund; 486 for (i = 0; i < NG_PPP_MAX_LINKS; i++) 487 priv->links[i].conf = conf->links[i]; 488 ng_ppp_update(node, 1); 489 break; 490 } 491 case NGM_PPP_GET_CONFIG: 492 { 493 struct ng_ppp_node_conf *conf; 494 int i; 495 496 NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT); 497 if (resp == NULL) 498 ERROUT(ENOMEM); 499 conf = (struct ng_ppp_node_conf *)resp->data; 500 conf->bund = priv->conf; 501 for (i = 0; i < NG_PPP_MAX_LINKS; i++) 502 conf->links[i] = priv->links[i].conf; 503 break; 504 } 505 case NGM_PPP_GET_MP_STATE: 506 { 507 struct ng_ppp_mp_state *info; 508 int i; 509 510 NG_MKRESPONSE(resp, msg, sizeof(*info), M_NOWAIT); 511 if (resp == NULL) 512 ERROUT(ENOMEM); 513 info = (struct ng_ppp_mp_state *)resp->data; 514 bzero(info, sizeof(*info)); 515 for (i = 0; i < NG_PPP_MAX_LINKS; i++) { 516 if (priv->links[i].seq != MP_NOSEQ) 517 info->rseq[i] = priv->links[i].seq; 518 } 519 info->mseq = priv->mseq; 520 info->xseq = priv->xseq; 521 break; 522 } 523 case NGM_PPP_GET_LINK_STATS: 524 case NGM_PPP_CLR_LINK_STATS: 525 case NGM_PPP_GETCLR_LINK_STATS: 526 { 527 struct ng_ppp_link_stat *stats; 528 u_int16_t linkNum; 529 530 if (msg->header.arglen != sizeof(u_int16_t)) 531 ERROUT(EINVAL); 532 linkNum = *((u_int16_t *) msg->data); 533 if (linkNum >= NG_PPP_MAX_LINKS 534 && linkNum != NG_PPP_BUNDLE_LINKNUM) 535 ERROUT(EINVAL); 536 stats = (linkNum == NG_PPP_BUNDLE_LINKNUM) ? 537 &priv->bundleStats : &priv->links[linkNum].stats; 538 if (msg->header.cmd != NGM_PPP_CLR_LINK_STATS) { 539 NG_MKRESPONSE(resp, msg, 540 sizeof(struct ng_ppp_link_stat), M_NOWAIT); 541 if (resp == NULL) 542 ERROUT(ENOMEM); 543 bcopy(stats, resp->data, sizeof(*stats)); 544 } 545 if (msg->header.cmd != NGM_PPP_GET_LINK_STATS) 546 bzero(stats, sizeof(*stats)); 547 break; 548 } 549 default: 550 error = EINVAL; 551 break; 552 } 553 break; 554 case NGM_VJC_COOKIE: 555 { 556 /* 557 * Forward it to the vjc node. leave the 558 * old return address alone. 559 * If we have no hook, let NG_RESPOND_MSG 560 * clean up any remaining resources. 561 * Because we have no resp, the item will be freed 562 * along with anything it references. Don't 563 * let msg be freed twice. 564 */ 565 NGI_MSG(item) = msg; /* put it back in the item */ 566 msg = NULL; 567 if ((lasthook = priv->links[HOOK_INDEX_VJC_IP].hook)) { 568 NG_FWD_ITEM_HOOK(error, item, lasthook); 569 } 570 return (error); 571 } 572 default: 573 error = EINVAL; 574 break; 575 } 576 done: 577 NG_RESPOND_MSG(error, node, item, resp); 578 NG_FREE_MSG(msg); 579 return (error); 580 } 581 582 /* 583 * Receive data on a hook 584 */ 585 static int 586 ng_ppp_rcvdata(hook_p hook, item_p item) 587 { 588 const node_p node = NG_HOOK_NODE(hook); 589 const priv_p priv = NG_NODE_PRIVATE(node); 590 const int index = (intptr_t)NG_HOOK_PRIVATE(hook); 591 u_int16_t linkNum = NG_PPP_BUNDLE_LINKNUM; 592 hook_p outHook = NULL; 593 int proto = 0, error; 594 struct mbuf *m; 595 596 NGI_GET_M(item, m); 597 /* Did it come from a link hook? */ 598 if (index < 0) { 599 struct ng_ppp_link *link; 600 601 /* Convert index into a link number */ 602 linkNum = (u_int16_t)~index; 603 KASSERT(linkNum < NG_PPP_MAX_LINKS, 604 ("%s: bogus index 0x%x", __func__, index)); 605 link = &priv->links[linkNum]; 606 607 /* Stats */ 608 link->stats.recvFrames++; 609 link->stats.recvOctets += m->m_pkthdr.len; 610 611 /* Strip address and control fields, if present */ 612 if (m->m_pkthdr.len >= 2) { 613 if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { 614 NG_FREE_ITEM(item); 615 return (ENOBUFS); 616 } 617 if (bcmp(mtod(m, u_char *), &ng_ppp_acf, 2) == 0) 618 m_adj(m, 2); 619 } 620 621 /* Dispatch incoming frame (if not enabled, to bypass) */ 622 NGI_M(item) = m; /* put changed m back in item */ 623 return ng_ppp_input(node, 624 !link->conf.enableLink, linkNum, item); 625 } 626 627 /* Get protocol & check if data allowed from this hook */ 628 NGI_M(item) = m; /* put possibly changed m back in item */ 629 switch (index) { 630 631 /* Outgoing data */ 632 case HOOK_INDEX_ATALK: 633 if (!priv->conf.enableAtalk) { 634 NG_FREE_ITEM(item); 635 return (ENXIO); 636 } 637 proto = PROT_APPLETALK; 638 break; 639 case HOOK_INDEX_IPX: 640 if (!priv->conf.enableIPX) { 641 NG_FREE_ITEM(item); 642 return (ENXIO); 643 } 644 proto = PROT_IPX; 645 break; 646 case HOOK_INDEX_IPV6: 647 if (!priv->conf.enableIPv6) { 648 NG_FREE_ITEM(item); 649 return (ENXIO); 650 } 651 proto = PROT_IPV6; 652 break; 653 case HOOK_INDEX_INET: 654 case HOOK_INDEX_VJC_VJIP: 655 if (!priv->conf.enableIP) { 656 NG_FREE_ITEM(item); 657 return (ENXIO); 658 } 659 proto = PROT_IP; 660 break; 661 case HOOK_INDEX_VJC_COMP: 662 if (!priv->conf.enableVJCompression) { 663 NG_FREE_ITEM(item); 664 return (ENXIO); 665 } 666 proto = PROT_VJCOMP; 667 break; 668 case HOOK_INDEX_VJC_UNCOMP: 669 if (!priv->conf.enableVJCompression) { 670 NG_FREE_ITEM(item); 671 return (ENXIO); 672 } 673 proto = PROT_VJUNCOMP; 674 break; 675 case HOOK_INDEX_COMPRESS: 676 if (!priv->conf.enableCompression) { 677 NG_FREE_ITEM(item); 678 return (ENXIO); 679 } 680 proto = PROT_COMPD; 681 break; 682 case HOOK_INDEX_ENCRYPT: 683 if (!priv->conf.enableEncryption) { 684 NG_FREE_ITEM(item); 685 return (ENXIO); 686 } 687 proto = PROT_CRYPTD; 688 break; 689 case HOOK_INDEX_BYPASS: 690 if (m->m_pkthdr.len < 4) { 691 NG_FREE_ITEM(item); 692 return (EINVAL); 693 } 694 if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { 695 NGI_M(item) = NULL; /* don't free twice */ 696 NG_FREE_ITEM(item); 697 return (ENOBUFS); 698 } 699 NGI_M(item) = m; /* m may have changed */ 700 linkNum = ntohs(mtod(m, u_int16_t *)[0]); 701 proto = ntohs(mtod(m, u_int16_t *)[1]); 702 m_adj(m, 4); 703 if (linkNum >= NG_PPP_MAX_LINKS 704 && linkNum != NG_PPP_BUNDLE_LINKNUM) { 705 NG_FREE_ITEM(item); 706 return (EINVAL); 707 } 708 break; 709 710 /* Incoming data */ 711 case HOOK_INDEX_VJC_IP: 712 if (!priv->conf.enableIP || !priv->conf.enableVJDecompression) { 713 NG_FREE_ITEM(item); 714 return (ENXIO); 715 } 716 break; 717 case HOOK_INDEX_DECOMPRESS: 718 if (!priv->conf.enableDecompression) { 719 NG_FREE_ITEM(item); 720 return (ENXIO); 721 } 722 break; 723 case HOOK_INDEX_DECRYPT: 724 if (!priv->conf.enableDecryption) { 725 NG_FREE_ITEM(item); 726 return (ENXIO); 727 } 728 break; 729 default: 730 panic("%s: bogus index 0x%x", __func__, index); 731 } 732 733 /* Now figure out what to do with the frame */ 734 switch (index) { 735 736 /* Outgoing data */ 737 case HOOK_INDEX_INET: 738 if (priv->conf.enableVJCompression && priv->vjCompHooked) { 739 outHook = priv->hooks[HOOK_INDEX_VJC_IP]; 740 break; 741 } 742 /* FALLTHROUGH */ 743 case HOOK_INDEX_ATALK: 744 case HOOK_INDEX_IPV6: 745 case HOOK_INDEX_IPX: 746 case HOOK_INDEX_VJC_COMP: 747 case HOOK_INDEX_VJC_UNCOMP: 748 case HOOK_INDEX_VJC_VJIP: 749 if (priv->conf.enableCompression 750 && priv->hooks[HOOK_INDEX_COMPRESS] != NULL) { 751 if ((m = ng_ppp_addproto(m, proto, 0)) == NULL) { 752 NGI_M(item) = NULL; 753 NG_FREE_ITEM(item); 754 return (ENOBUFS); 755 } 756 NGI_M(item) = m; /* m may have changed */ 757 outHook = priv->hooks[HOOK_INDEX_COMPRESS]; 758 break; 759 } 760 /* FALLTHROUGH */ 761 case HOOK_INDEX_COMPRESS: 762 if (priv->conf.enableEncryption 763 && priv->hooks[HOOK_INDEX_ENCRYPT] != NULL) { 764 if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) { 765 NGI_M(item) = NULL; 766 NG_FREE_ITEM(item); 767 return (ENOBUFS); 768 } 769 NGI_M(item) = m; /* m may have changed */ 770 outHook = priv->hooks[HOOK_INDEX_ENCRYPT]; 771 break; 772 } 773 /* FALLTHROUGH */ 774 case HOOK_INDEX_ENCRYPT: 775 return ng_ppp_output(node, 0, proto, NG_PPP_BUNDLE_LINKNUM, item); 776 777 case HOOK_INDEX_BYPASS: 778 return ng_ppp_output(node, 1, proto, linkNum, item); 779 780 /* Incoming data */ 781 case HOOK_INDEX_DECRYPT: 782 case HOOK_INDEX_DECOMPRESS: 783 return ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, item); 784 785 case HOOK_INDEX_VJC_IP: 786 outHook = priv->hooks[HOOK_INDEX_INET]; 787 break; 788 } 789 790 /* Send packet out hook */ 791 NG_FWD_ITEM_HOOK(error, item, outHook); 792 return (error); 793 } 794 795 /* 796 * Destroy node 797 */ 798 static int 799 ng_ppp_shutdown(node_p node) 800 { 801 const priv_p priv = NG_NODE_PRIVATE(node); 802 803 /* Stop fragment queue timer */ 804 ng_ppp_stop_frag_timer(node); 805 806 /* Take down netgraph node */ 807 ng_ppp_frag_reset(node); 808 bzero(priv, sizeof(*priv)); 809 FREE(priv, M_NETGRAPH_PPP); 810 NG_NODE_SET_PRIVATE(node, NULL); 811 NG_NODE_UNREF(node); /* let the node escape */ 812 return (0); 813 } 814 815 /* 816 * Hook disconnection 817 */ 818 static int 819 ng_ppp_disconnect(hook_p hook) 820 { 821 const node_p node = NG_HOOK_NODE(hook); 822 const priv_p priv = NG_NODE_PRIVATE(node); 823 const int index = (intptr_t)NG_HOOK_PRIVATE(hook); 824 825 /* Zero out hook pointer */ 826 if (index < 0) 827 priv->links[~index].hook = NULL; 828 else 829 priv->hooks[index] = NULL; 830 831 /* Update derived info (or go away if no hooks left) */ 832 if (NG_NODE_NUMHOOKS(node) > 0) { 833 ng_ppp_update(node, 0); 834 } else { 835 if (NG_NODE_IS_VALID(node)) { 836 ng_rmnode_self(node); 837 } 838 } 839 return (0); 840 } 841 842 /************************************************************************ 843 HELPER STUFF 844 ************************************************************************/ 845 846 /* 847 * Handle an incoming frame. Extract the PPP protocol number 848 * and dispatch accordingly. 849 */ 850 static int 851 ng_ppp_input(node_p node, int bypass, int linkNum, item_p item) 852 { 853 const priv_p priv = NG_NODE_PRIVATE(node); 854 hook_p outHook = NULL; 855 int proto, error; 856 struct mbuf *m; 857 858 859 NGI_GET_M(item, m); 860 /* Extract protocol number */ 861 for (proto = 0; !PROT_VALID(proto) && m->m_pkthdr.len > 0; ) { 862 if (m->m_len < 1 && (m = m_pullup(m, 1)) == NULL) { 863 NG_FREE_ITEM(item); 864 return (ENOBUFS); 865 } 866 proto = (proto << 8) + *mtod(m, u_char *); 867 m_adj(m, 1); 868 } 869 if (!PROT_VALID(proto)) { 870 if (linkNum == NG_PPP_BUNDLE_LINKNUM) 871 priv->bundleStats.badProtos++; 872 else 873 priv->links[linkNum].stats.badProtos++; 874 NG_FREE_ITEM(item); 875 NG_FREE_M(m); 876 return (EINVAL); 877 } 878 879 /* Bypass frame? */ 880 if (bypass) 881 goto bypass; 882 883 /* Check protocol */ 884 switch (proto) { 885 case PROT_COMPD: 886 if (priv->conf.enableDecompression) 887 outHook = priv->hooks[HOOK_INDEX_DECOMPRESS]; 888 break; 889 case PROT_CRYPTD: 890 if (priv->conf.enableDecryption) 891 outHook = priv->hooks[HOOK_INDEX_DECRYPT]; 892 break; 893 case PROT_VJCOMP: 894 if (priv->conf.enableVJDecompression && priv->vjCompHooked) 895 outHook = priv->hooks[HOOK_INDEX_VJC_COMP]; 896 break; 897 case PROT_VJUNCOMP: 898 if (priv->conf.enableVJDecompression && priv->vjCompHooked) 899 outHook = priv->hooks[HOOK_INDEX_VJC_UNCOMP]; 900 break; 901 case PROT_MP: 902 if (priv->conf.enableMultilink 903 && linkNum != NG_PPP_BUNDLE_LINKNUM) { 904 NGI_M(item) = m; 905 return ng_ppp_mp_input(node, linkNum, item); 906 } 907 break; 908 case PROT_APPLETALK: 909 if (priv->conf.enableAtalk) 910 outHook = priv->hooks[HOOK_INDEX_ATALK]; 911 break; 912 case PROT_IPX: 913 if (priv->conf.enableIPX) 914 outHook = priv->hooks[HOOK_INDEX_IPX]; 915 break; 916 case PROT_IP: 917 if (priv->conf.enableIP) 918 outHook = priv->hooks[HOOK_INDEX_INET]; 919 break; 920 case PROT_IPV6: 921 if (priv->conf.enableIPv6) 922 outHook = priv->hooks[HOOK_INDEX_IPV6]; 923 break; 924 } 925 926 bypass: 927 /* For unknown/inactive protocols, forward out the bypass hook */ 928 if (outHook == NULL) { 929 u_int16_t hdr[2]; 930 931 hdr[0] = htons(linkNum); 932 hdr[1] = htons((u_int16_t)proto); 933 if ((m = ng_ppp_prepend(m, &hdr, 4)) == NULL) { 934 NG_FREE_ITEM(item); 935 return (ENOBUFS); 936 } 937 outHook = priv->hooks[HOOK_INDEX_BYPASS]; 938 } 939 940 /* Forward frame */ 941 NG_FWD_NEW_DATA(error, item, outHook, m); 942 return (error); 943 } 944 945 /* 946 * Deliver a frame out a link, either a real one or NG_PPP_BUNDLE_LINKNUM. 947 * If the link is not enabled then ENXIO is returned, unless "bypass" is != 0. 948 * 949 * If the frame is too big for the particular link, return EMSGSIZE. 950 */ 951 static int 952 ng_ppp_output(node_p node, int bypass, 953 int proto, int linkNum, item_p item) 954 { 955 const priv_p priv = NG_NODE_PRIVATE(node); 956 struct ng_ppp_link *link; 957 int len, error; 958 struct mbuf *m; 959 u_int16_t mru; 960 961 /* Extract mbuf */ 962 NGI_GET_M(item, m); 963 964 /* If not doing MP, map bundle virtual link to (the only) link */ 965 if (linkNum == NG_PPP_BUNDLE_LINKNUM && !priv->conf.enableMultilink) 966 linkNum = priv->activeLinks[0]; 967 968 /* Get link pointer (optimization) */ 969 link = (linkNum != NG_PPP_BUNDLE_LINKNUM) ? 970 &priv->links[linkNum] : NULL; 971 972 /* Check link status (if real) */ 973 if (linkNum != NG_PPP_BUNDLE_LINKNUM) { 974 if (!bypass && !link->conf.enableLink) { 975 NG_FREE_M(m); 976 NG_FREE_ITEM(item); 977 return (ENXIO); 978 } 979 if (link->hook == NULL) { 980 NG_FREE_M(m); 981 NG_FREE_ITEM(item); 982 return (ENETDOWN); 983 } 984 } 985 986 /* Check peer's MRU for this link */ 987 mru = (link != NULL) ? link->conf.mru : priv->conf.mrru; 988 if (mru != 0 && m->m_pkthdr.len > mru) { 989 NG_FREE_M(m); 990 NG_FREE_ITEM(item); 991 return (EMSGSIZE); 992 } 993 994 /* Prepend protocol number, possibly compressed */ 995 if ((m = ng_ppp_addproto(m, proto, 996 linkNum == NG_PPP_BUNDLE_LINKNUM 997 || link->conf.enableProtoComp)) == NULL) { 998 NG_FREE_ITEM(item); 999 return (ENOBUFS); 1000 } 1001 1002 /* Special handling for the MP virtual link */ 1003 if (linkNum == NG_PPP_BUNDLE_LINKNUM) { 1004 /* discard the queue item */ 1005 NG_FREE_ITEM(item); 1006 return ng_ppp_mp_output(node, m); 1007 } 1008 1009 /* Prepend address and control field (unless compressed) */ 1010 if (proto == PROT_LCP || !link->conf.enableACFComp) { 1011 if ((m = ng_ppp_prepend(m, &ng_ppp_acf, 2)) == NULL) { 1012 NG_FREE_ITEM(item); 1013 return (ENOBUFS); 1014 } 1015 } 1016 1017 /* Deliver frame */ 1018 len = m->m_pkthdr.len; 1019 NG_FWD_NEW_DATA(error, item, link->hook, m); 1020 1021 /* Update stats and 'bytes in queue' counter */ 1022 if (error == 0) { 1023 link->stats.xmitFrames++; 1024 link->stats.xmitOctets += len; 1025 link->bytesInQueue += len; 1026 getmicrouptime(&link->lastWrite); 1027 } 1028 return error; 1029 } 1030 1031 /* 1032 * Handle an incoming multi-link fragment 1033 * 1034 * The fragment reassembly algorithm is somewhat complex. This is mainly 1035 * because we are required not to reorder the reconstructed packets, yet 1036 * fragments are only guaranteed to arrive in order on a per-link basis. 1037 * In other words, when we have a complete packet ready, but the previous 1038 * packet is still incomplete, we have to decide between delivering the 1039 * complete packet and throwing away the incomplete one, or waiting to 1040 * see if the remainder of the incomplete one arrives, at which time we 1041 * can deliver both packets, in order. 1042 * 1043 * This problem is exacerbated by "sequence number slew", which is when 1044 * the sequence numbers coming in from different links are far apart from 1045 * each other. In particular, certain unnamed equipment (*cough* Ascend) 1046 * has been seen to generate sequence number slew of up to 10 on an ISDN 1047 * 2B-channel MP link. There is nothing invalid about sequence number slew 1048 * but it makes the reasssembly process have to work harder. 1049 * 1050 * However, the peer is required to transmit fragments in order on each 1051 * link. That means if we define MSEQ as the minimum over all links of 1052 * the highest sequence number received on that link, then we can always 1053 * give up any hope of receiving a fragment with sequence number < MSEQ in 1054 * the future (all of this using 'wraparound' sequence number space). 1055 * Therefore we can always immediately throw away incomplete packets 1056 * missing fragments with sequence numbers < MSEQ. 1057 * 1058 * Here is an overview of our algorithm: 1059 * 1060 * o Received fragments are inserted into a queue, for which we 1061 * maintain these invariants between calls to this function: 1062 * 1063 * - Fragments are ordered in the queue by sequence number 1064 * - If a complete packet is at the head of the queue, then 1065 * the first fragment in the packet has seq# > MSEQ + 1 1066 * (otherwise, we could deliver it immediately) 1067 * - If any fragments have seq# < MSEQ, then they are necessarily 1068 * part of a packet whose missing seq#'s are all > MSEQ (otherwise, 1069 * we can throw them away because they'll never be completed) 1070 * - The queue contains at most MP_MAX_QUEUE_LEN fragments 1071 * 1072 * o We have a periodic timer that checks the queue for the first 1073 * complete packet that has been sitting in the queue "too long". 1074 * When one is detected, all previous (incomplete) fragments are 1075 * discarded, their missing fragments are declared lost and MSEQ 1076 * is increased. 1077 * 1078 * o If we recieve a fragment with seq# < MSEQ, we throw it away 1079 * because we've already delcared it lost. 1080 * 1081 * This assumes linkNum != NG_PPP_BUNDLE_LINKNUM. 1082 */ 1083 static int 1084 ng_ppp_mp_input(node_p node, int linkNum, item_p item) 1085 { 1086 const priv_p priv = NG_NODE_PRIVATE(node); 1087 struct ng_ppp_link *const link = &priv->links[linkNum]; 1088 struct ng_ppp_frag frag0, *frag = &frag0; 1089 struct ng_ppp_frag *qent; 1090 int i, diff, inserted; 1091 struct mbuf *m; 1092 1093 NGI_GET_M(item, m); 1094 NG_FREE_ITEM(item); 1095 /* Stats */ 1096 priv->bundleStats.recvFrames++; 1097 priv->bundleStats.recvOctets += m->m_pkthdr.len; 1098 1099 /* Extract fragment information from MP header */ 1100 if (priv->conf.recvShortSeq) { 1101 u_int16_t shdr; 1102 1103 if (m->m_pkthdr.len < 2) { 1104 link->stats.runts++; 1105 NG_FREE_M(m); 1106 return (EINVAL); 1107 } 1108 if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) 1109 return (ENOBUFS); 1110 1111 shdr = ntohs(*mtod(m, u_int16_t *)); 1112 frag->seq = MP_SHORT_EXTEND(shdr); 1113 frag->first = (shdr & MP_SHORT_FIRST_FLAG) != 0; 1114 frag->last = (shdr & MP_SHORT_LAST_FLAG) != 0; 1115 diff = MP_SHORT_SEQ_DIFF(frag->seq, priv->mseq); 1116 m_adj(m, 2); 1117 } else { 1118 u_int32_t lhdr; 1119 1120 if (m->m_pkthdr.len < 4) { 1121 link->stats.runts++; 1122 NG_FREE_M(m); 1123 return (EINVAL); 1124 } 1125 if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) 1126 return (ENOBUFS); 1127 1128 lhdr = ntohl(*mtod(m, u_int32_t *)); 1129 frag->seq = MP_LONG_EXTEND(lhdr); 1130 frag->first = (lhdr & MP_LONG_FIRST_FLAG) != 0; 1131 frag->last = (lhdr & MP_LONG_LAST_FLAG) != 0; 1132 diff = MP_LONG_SEQ_DIFF(frag->seq, priv->mseq); 1133 m_adj(m, 4); 1134 } 1135 frag->data = m; 1136 getmicrouptime(&frag->timestamp); 1137 1138 /* If sequence number is < MSEQ, we've already declared this 1139 fragment as lost, so we have no choice now but to drop it */ 1140 if (diff < 0) { 1141 link->stats.dropFragments++; 1142 NG_FREE_M(m); 1143 return (0); 1144 } 1145 1146 /* Update highest received sequence number on this link and MSEQ */ 1147 priv->mseq = link->seq = frag->seq; 1148 for (i = 0; i < priv->numActiveLinks; i++) { 1149 struct ng_ppp_link *const alink = 1150 &priv->links[priv->activeLinks[i]]; 1151 1152 if (MP_RECV_SEQ_DIFF(priv, alink->seq, priv->mseq) < 0) 1153 priv->mseq = alink->seq; 1154 } 1155 1156 /* Allocate a new frag struct for the queue */ 1157 MALLOC(frag, struct ng_ppp_frag *, sizeof(*frag), M_NETGRAPH_PPP, M_NOWAIT); 1158 if (frag == NULL) { 1159 NG_FREE_M(m); 1160 ng_ppp_frag_process(node); 1161 return (ENOMEM); 1162 } 1163 *frag = frag0; 1164 1165 /* Add fragment to queue, which is sorted by sequence number */ 1166 inserted = 0; 1167 TAILQ_FOREACH_REVERSE(qent, &priv->frags, ng_ppp_fraglist, f_qent) { 1168 diff = MP_RECV_SEQ_DIFF(priv, frag->seq, qent->seq); 1169 if (diff > 0) { 1170 TAILQ_INSERT_AFTER(&priv->frags, qent, frag, f_qent); 1171 inserted = 1; 1172 break; 1173 } else if (diff == 0) { /* should never happen! */ 1174 link->stats.dupFragments++; 1175 NG_FREE_M(frag->data); 1176 FREE(frag, M_NETGRAPH_PPP); 1177 return (EINVAL); 1178 } 1179 } 1180 if (!inserted) 1181 TAILQ_INSERT_HEAD(&priv->frags, frag, f_qent); 1182 priv->qlen++; 1183 1184 /* Process the queue */ 1185 return ng_ppp_frag_process(node); 1186 } 1187 1188 /* 1189 * Examine our list of fragments, and determine if there is a 1190 * complete and deliverable packet at the head of the list. 1191 * Return 1 if so, zero otherwise. 1192 */ 1193 static int 1194 ng_ppp_check_packet(node_p node) 1195 { 1196 const priv_p priv = NG_NODE_PRIVATE(node); 1197 struct ng_ppp_frag *qent, *qnext; 1198 1199 /* Check for empty queue */ 1200 if (TAILQ_EMPTY(&priv->frags)) 1201 return (0); 1202 1203 /* Check first fragment is the start of a deliverable packet */ 1204 qent = TAILQ_FIRST(&priv->frags); 1205 if (!qent->first || MP_RECV_SEQ_DIFF(priv, qent->seq, priv->mseq) > 1) 1206 return (0); 1207 1208 /* Check that all the fragments are there */ 1209 while (!qent->last) { 1210 qnext = TAILQ_NEXT(qent, f_qent); 1211 if (qnext == NULL) /* end of queue */ 1212 return (0); 1213 if (qnext->seq != MP_NEXT_RECV_SEQ(priv, qent->seq)) 1214 return (0); 1215 qent = qnext; 1216 } 1217 1218 /* Got one */ 1219 return (1); 1220 } 1221 1222 /* 1223 * Pull a completed packet off the head of the incoming fragment queue. 1224 * This assumes there is a completed packet there to pull off. 1225 */ 1226 static void 1227 ng_ppp_get_packet(node_p node, struct mbuf **mp) 1228 { 1229 const priv_p priv = NG_NODE_PRIVATE(node); 1230 struct ng_ppp_frag *qent, *qnext; 1231 struct mbuf *m = NULL, *tail; 1232 1233 qent = TAILQ_FIRST(&priv->frags); 1234 KASSERT(!TAILQ_EMPTY(&priv->frags) && qent->first, 1235 ("%s: no packet", __func__)); 1236 for (tail = NULL; qent != NULL; qent = qnext) { 1237 qnext = TAILQ_NEXT(qent, f_qent); 1238 KASSERT(!TAILQ_EMPTY(&priv->frags), 1239 ("%s: empty q", __func__)); 1240 TAILQ_REMOVE(&priv->frags, qent, f_qent); 1241 if (tail == NULL) 1242 tail = m = qent->data; 1243 else { 1244 m->m_pkthdr.len += qent->data->m_pkthdr.len; 1245 tail->m_next = qent->data; 1246 } 1247 while (tail->m_next != NULL) 1248 tail = tail->m_next; 1249 if (qent->last) 1250 qnext = NULL; 1251 FREE(qent, M_NETGRAPH_PPP); 1252 priv->qlen--; 1253 } 1254 *mp = m; 1255 } 1256 1257 /* 1258 * Trim fragments from the queue whose packets can never be completed. 1259 * This assumes a complete packet is NOT at the beginning of the queue. 1260 * Returns 1 if fragments were removed, zero otherwise. 1261 */ 1262 static int 1263 ng_ppp_frag_trim(node_p node) 1264 { 1265 const priv_p priv = NG_NODE_PRIVATE(node); 1266 struct ng_ppp_frag *qent, *qnext = NULL; 1267 int removed = 0; 1268 1269 /* Scan for "dead" fragments and remove them */ 1270 while (1) { 1271 int dead = 0; 1272 1273 /* If queue is empty, we're done */ 1274 if (TAILQ_EMPTY(&priv->frags)) 1275 break; 1276 1277 /* Determine whether first fragment can ever be completed */ 1278 TAILQ_FOREACH(qent, &priv->frags, f_qent) { 1279 if (MP_RECV_SEQ_DIFF(priv, qent->seq, priv->mseq) >= 0) 1280 break; 1281 qnext = TAILQ_NEXT(qent, f_qent); 1282 KASSERT(qnext != NULL, 1283 ("%s: last frag < MSEQ?", __func__)); 1284 if (qnext->seq != MP_NEXT_RECV_SEQ(priv, qent->seq) 1285 || qent->last || qnext->first) { 1286 dead = 1; 1287 break; 1288 } 1289 } 1290 if (!dead) 1291 break; 1292 1293 /* Remove fragment and all others in the same packet */ 1294 while ((qent = TAILQ_FIRST(&priv->frags)) != qnext) { 1295 KASSERT(!TAILQ_EMPTY(&priv->frags), 1296 ("%s: empty q", __func__)); 1297 priv->bundleStats.dropFragments++; 1298 TAILQ_REMOVE(&priv->frags, qent, f_qent); 1299 NG_FREE_M(qent->data); 1300 FREE(qent, M_NETGRAPH_PPP); 1301 priv->qlen--; 1302 removed = 1; 1303 } 1304 } 1305 return (removed); 1306 } 1307 1308 /* 1309 * Run the queue, restoring the queue invariants 1310 */ 1311 static int 1312 ng_ppp_frag_process(node_p node) 1313 { 1314 const priv_p priv = NG_NODE_PRIVATE(node); 1315 struct mbuf *m; 1316 item_p item; 1317 1318 /* Deliver any deliverable packets */ 1319 while (ng_ppp_check_packet(node)) { 1320 ng_ppp_get_packet(node, &m); 1321 item = ng_package_data(m, NULL); 1322 ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, item); 1323 } 1324 1325 /* Delete dead fragments and try again */ 1326 if (ng_ppp_frag_trim(node)) { 1327 while (ng_ppp_check_packet(node)) { 1328 ng_ppp_get_packet(node, &m); 1329 item = ng_package_data(m, NULL); 1330 ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, item); 1331 } 1332 } 1333 1334 /* Check for stale fragments while we're here */ 1335 ng_ppp_frag_checkstale(node); 1336 1337 /* Check queue length */ 1338 if (priv->qlen > MP_MAX_QUEUE_LEN) { 1339 struct ng_ppp_frag *qent; 1340 int i; 1341 1342 /* Get oldest fragment */ 1343 KASSERT(!TAILQ_EMPTY(&priv->frags), 1344 ("%s: empty q", __func__)); 1345 qent = TAILQ_FIRST(&priv->frags); 1346 1347 /* Bump MSEQ if necessary */ 1348 if (MP_RECV_SEQ_DIFF(priv, priv->mseq, qent->seq) < 0) { 1349 priv->mseq = qent->seq; 1350 for (i = 0; i < priv->numActiveLinks; i++) { 1351 struct ng_ppp_link *const alink = 1352 &priv->links[priv->activeLinks[i]]; 1353 1354 if (MP_RECV_SEQ_DIFF(priv, 1355 alink->seq, priv->mseq) < 0) 1356 alink->seq = priv->mseq; 1357 } 1358 } 1359 1360 /* Drop it */ 1361 priv->bundleStats.dropFragments++; 1362 TAILQ_REMOVE(&priv->frags, qent, f_qent); 1363 NG_FREE_M(qent->data); 1364 FREE(qent, M_NETGRAPH_PPP); 1365 priv->qlen--; 1366 1367 /* Process queue again */ 1368 return ng_ppp_frag_process(node); 1369 } 1370 1371 /* Done */ 1372 return (0); 1373 } 1374 1375 /* 1376 * Check for 'stale' completed packets that need to be delivered 1377 * 1378 * If a link goes down or has a temporary failure, MSEQ can get 1379 * "stuck", because no new incoming fragments appear on that link. 1380 * This can cause completed packets to never get delivered if 1381 * their sequence numbers are all > MSEQ + 1. 1382 * 1383 * This routine checks how long all of the completed packets have 1384 * been sitting in the queue, and if too long, removes fragments 1385 * from the queue and increments MSEQ to allow them to be delivered. 1386 */ 1387 static void 1388 ng_ppp_frag_checkstale(node_p node) 1389 { 1390 const priv_p priv = NG_NODE_PRIVATE(node); 1391 struct ng_ppp_frag *qent, *beg, *end; 1392 struct timeval now, age; 1393 struct mbuf *m; 1394 int i, seq; 1395 item_p item; 1396 int endseq; 1397 1398 now.tv_sec = 0; /* uninitialized state */ 1399 while (1) { 1400 1401 /* If queue is empty, we're done */ 1402 if (TAILQ_EMPTY(&priv->frags)) 1403 break; 1404 1405 /* Find the first complete packet in the queue */ 1406 beg = end = NULL; 1407 seq = TAILQ_FIRST(&priv->frags)->seq; 1408 TAILQ_FOREACH(qent, &priv->frags, f_qent) { 1409 if (qent->first) 1410 beg = qent; 1411 else if (qent->seq != seq) 1412 beg = NULL; 1413 if (beg != NULL && qent->last) { 1414 end = qent; 1415 break; 1416 } 1417 seq = MP_NEXT_RECV_SEQ(priv, seq); 1418 } 1419 1420 /* If none found, exit */ 1421 if (end == NULL) 1422 break; 1423 1424 /* Get current time (we assume we've been up for >= 1 second) */ 1425 if (now.tv_sec == 0) 1426 getmicrouptime(&now); 1427 1428 /* Check if packet has been queued too long */ 1429 age = now; 1430 timevalsub(&age, &beg->timestamp); 1431 if (timevalcmp(&age, &ng_ppp_max_staleness, < )) 1432 break; 1433 1434 /* Throw away junk fragments in front of the completed packet */ 1435 while ((qent = TAILQ_FIRST(&priv->frags)) != beg) { 1436 KASSERT(!TAILQ_EMPTY(&priv->frags), 1437 ("%s: empty q", __func__)); 1438 priv->bundleStats.dropFragments++; 1439 TAILQ_REMOVE(&priv->frags, qent, f_qent); 1440 NG_FREE_M(qent->data); 1441 FREE(qent, M_NETGRAPH_PPP); 1442 priv->qlen--; 1443 } 1444 1445 /* Extract completed packet */ 1446 endseq = end->seq; 1447 ng_ppp_get_packet(node, &m); 1448 1449 /* Bump MSEQ if necessary */ 1450 if (MP_RECV_SEQ_DIFF(priv, priv->mseq, endseq) < 0) { 1451 priv->mseq = endseq; 1452 for (i = 0; i < priv->numActiveLinks; i++) { 1453 struct ng_ppp_link *const alink = 1454 &priv->links[priv->activeLinks[i]]; 1455 1456 if (MP_RECV_SEQ_DIFF(priv, 1457 alink->seq, priv->mseq) < 0) 1458 alink->seq = priv->mseq; 1459 } 1460 } 1461 1462 /* Deliver packet */ 1463 item = ng_package_data(m, NULL); 1464 ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, item); 1465 } 1466 } 1467 1468 /* 1469 * Periodically call ng_ppp_frag_checkstale() 1470 */ 1471 static void 1472 ng_ppp_frag_timeout(node_p node, hook_p hook, void *arg1, int arg2) 1473 { 1474 /* XXX: is this needed? */ 1475 if (NG_NODE_NOT_VALID(node)) 1476 return; 1477 1478 /* Scan the fragment queue */ 1479 ng_ppp_frag_checkstale(node); 1480 1481 /* Start timer again */ 1482 ng_ppp_start_frag_timer(node); 1483 } 1484 1485 /* 1486 * Deliver a frame out on the bundle, i.e., figure out how to fragment 1487 * the frame across the individual PPP links and do so. 1488 */ 1489 static int 1490 ng_ppp_mp_output(node_p node, struct mbuf *m) 1491 { 1492 const priv_p priv = NG_NODE_PRIVATE(node); 1493 const int hdr_len = priv->conf.xmitShortSeq ? 2 : 4; 1494 int distrib[NG_PPP_MAX_LINKS]; 1495 int firstFragment; 1496 int activeLinkNum; 1497 item_p item; 1498 1499 /* At least one link must be active */ 1500 if (priv->numActiveLinks == 0) { 1501 NG_FREE_M(m); 1502 return (ENETDOWN); 1503 } 1504 1505 /* Round-robin strategy */ 1506 if (priv->conf.enableRoundRobin || m->m_pkthdr.len < MP_MIN_FRAG_LEN) { 1507 activeLinkNum = priv->lastLink++ % priv->numActiveLinks; 1508 bzero(&distrib, priv->numActiveLinks * sizeof(distrib[0])); 1509 distrib[activeLinkNum] = m->m_pkthdr.len; 1510 goto deliver; 1511 } 1512 1513 /* Strategy when all links are equivalent (optimize the common case) */ 1514 if (priv->allLinksEqual) { 1515 const int fraction = m->m_pkthdr.len / priv->numActiveLinks; 1516 int i, remain; 1517 1518 for (i = 0; i < priv->numActiveLinks; i++) 1519 distrib[priv->lastLink++ % priv->numActiveLinks] 1520 = fraction; 1521 remain = m->m_pkthdr.len - (fraction * priv->numActiveLinks); 1522 while (remain > 0) { 1523 distrib[priv->lastLink++ % priv->numActiveLinks]++; 1524 remain--; 1525 } 1526 goto deliver; 1527 } 1528 1529 /* Strategy when all links are not equivalent */ 1530 ng_ppp_mp_strategy(node, m->m_pkthdr.len, distrib); 1531 1532 deliver: 1533 /* Update stats */ 1534 priv->bundleStats.xmitFrames++; 1535 priv->bundleStats.xmitOctets += m->m_pkthdr.len; 1536 1537 /* Send alloted portions of frame out on the link(s) */ 1538 for (firstFragment = 1, activeLinkNum = priv->numActiveLinks - 1; 1539 activeLinkNum >= 0; activeLinkNum--) { 1540 const int linkNum = priv->activeLinks[activeLinkNum]; 1541 struct ng_ppp_link *const link = &priv->links[linkNum]; 1542 1543 /* Deliver fragment(s) out the next link */ 1544 for ( ; distrib[activeLinkNum] > 0; firstFragment = 0) { 1545 int len, lastFragment, error; 1546 struct mbuf *m2; 1547 1548 /* Calculate fragment length; don't exceed link MTU */ 1549 len = distrib[activeLinkNum]; 1550 if (len > link->conf.mru - hdr_len) 1551 len = link->conf.mru - hdr_len; 1552 distrib[activeLinkNum] -= len; 1553 lastFragment = (len == m->m_pkthdr.len); 1554 1555 /* Split off next fragment as "m2" */ 1556 m2 = m; 1557 if (!lastFragment) { 1558 struct mbuf *n = m_split(m, len, M_DONTWAIT); 1559 1560 if (n == NULL) { 1561 NG_FREE_M(m); 1562 return (ENOMEM); 1563 } 1564 m = n; 1565 } 1566 1567 /* Prepend MP header */ 1568 if (priv->conf.xmitShortSeq) { 1569 u_int16_t shdr; 1570 1571 shdr = priv->xseq; 1572 priv->xseq = 1573 (priv->xseq + 1) & MP_SHORT_SEQ_MASK; 1574 if (firstFragment) 1575 shdr |= MP_SHORT_FIRST_FLAG; 1576 if (lastFragment) 1577 shdr |= MP_SHORT_LAST_FLAG; 1578 shdr = htons(shdr); 1579 m2 = ng_ppp_prepend(m2, &shdr, 2); 1580 } else { 1581 u_int32_t lhdr; 1582 1583 lhdr = priv->xseq; 1584 priv->xseq = 1585 (priv->xseq + 1) & MP_LONG_SEQ_MASK; 1586 if (firstFragment) 1587 lhdr |= MP_LONG_FIRST_FLAG; 1588 if (lastFragment) 1589 lhdr |= MP_LONG_LAST_FLAG; 1590 lhdr = htonl(lhdr); 1591 m2 = ng_ppp_prepend(m2, &lhdr, 4); 1592 } 1593 if (m2 == NULL) { 1594 if (!lastFragment) 1595 m_freem(m); 1596 return (ENOBUFS); 1597 } 1598 1599 /* Send fragment */ 1600 item = ng_package_data(m2, NULL); 1601 error = ng_ppp_output(node, 0, PROT_MP, linkNum, item); 1602 if (error != 0) { 1603 if (!lastFragment) 1604 NG_FREE_M(m); 1605 return (error); 1606 } 1607 } 1608 } 1609 1610 /* Done */ 1611 return (0); 1612 } 1613 1614 /* 1615 * Computing the optimal fragmentation 1616 * ----------------------------------- 1617 * 1618 * This routine tries to compute the optimal fragmentation pattern based 1619 * on each link's latency, bandwidth, and calculated additional latency. 1620 * The latter quantity is the additional latency caused by previously 1621 * written data that has not been transmitted yet. 1622 * 1623 * This algorithm is only useful when not all of the links have the 1624 * same latency and bandwidth values. 1625 * 1626 * The essential idea is to make the last bit of each fragment of the 1627 * frame arrive at the opposite end at the exact same time. This greedy 1628 * algorithm is optimal, in that no other scheduling could result in any 1629 * packet arriving any sooner unless packets are delivered out of order. 1630 * 1631 * Suppose link i has bandwidth b_i (in tens of bytes per milisecond) and 1632 * latency l_i (in miliseconds). Consider the function function f_i(t) 1633 * which is equal to the number of bytes that will have arrived at 1634 * the peer after t miliseconds if we start writing continuously at 1635 * time t = 0. Then f_i(t) = b_i * (t - l_i) = ((b_i * t) - (l_i * b_i). 1636 * That is, f_i(t) is a line with slope b_i and y-intersect -(l_i * b_i). 1637 * Note that the y-intersect is always <= zero because latency can't be 1638 * negative. Note also that really the function is f_i(t) except when 1639 * f_i(t) is negative, in which case the function is zero. To take 1640 * care of this, let Q_i(t) = { if (f_i(t) > 0) return 1; else return 0; }. 1641 * So the actual number of bytes that will have arrived at the peer after 1642 * t miliseconds is f_i(t) * Q_i(t). 1643 * 1644 * At any given time, each link has some additional latency a_i >= 0 1645 * due to previously written fragment(s) which are still in the queue. 1646 * This value is easily computed from the time since last transmission, 1647 * the previous latency value, the number of bytes written, and the 1648 * link's bandwidth. 1649 * 1650 * Assume that l_i includes any a_i already, and that the links are 1651 * sorted by latency, so that l_i <= l_{i+1}. 1652 * 1653 * Let N be the total number of bytes in the current frame we are sending. 1654 * 1655 * Suppose we were to start writing bytes at time t = 0 on all links 1656 * simultaneously, which is the most we can possibly do. Then let 1657 * F(t) be equal to the total number of bytes received by the peer 1658 * after t miliseconds. Then F(t) = Sum_i (f_i(t) * Q_i(t)). 1659 * 1660 * Our goal is simply this: fragment the frame across the links such 1661 * that the peer is able to reconstruct the completed frame as soon as 1662 * possible, i.e., at the least possible value of t. Call this value t_0. 1663 * 1664 * Then it follows that F(t_0) = N. Our strategy is first to find the value 1665 * of t_0, and then deduce how many bytes to write to each link. 1666 * 1667 * Rewriting F(t_0): 1668 * 1669 * t_0 = ( N + Sum_i ( l_i * b_i * Q_i(t_0) ) ) / Sum_i ( b_i * Q_i(t_0) ) 1670 * 1671 * Now, we note that Q_i(t) is constant for l_i <= t <= l_{i+1}. t_0 will 1672 * lie in one of these ranges. To find it, we just need to find the i such 1673 * that F(l_i) <= N <= F(l_{i+1}). Then we compute all the constant values 1674 * for Q_i() in this range, plug in the remaining values, solving for t_0. 1675 * 1676 * Once t_0 is known, then the number of bytes to send on link i is 1677 * just f_i(t_0) * Q_i(t_0). 1678 * 1679 * In other words, we start allocating bytes to the links one at a time. 1680 * We keep adding links until the frame is completely sent. Some links 1681 * may not get any bytes because their latency is too high. 1682 * 1683 * Is all this work really worth the trouble? Depends on the situation. 1684 * The bigger the ratio of computer speed to link speed, and the more 1685 * important total bundle latency is (e.g., for interactive response time), 1686 * the more it's worth it. There is however the cost of calling this 1687 * function for every frame. The running time is O(n^2) where n is the 1688 * number of links that receive a non-zero number of bytes. 1689 * 1690 * Since latency is measured in miliseconds, the "resolution" of this 1691 * algorithm is one milisecond. 1692 * 1693 * To avoid this algorithm altogether, configure all links to have the 1694 * same latency and bandwidth. 1695 */ 1696 static void 1697 ng_ppp_mp_strategy(node_p node, int len, int *distrib) 1698 { 1699 const priv_p priv = NG_NODE_PRIVATE(node); 1700 int latency[NG_PPP_MAX_LINKS]; 1701 int sortByLatency[NG_PPP_MAX_LINKS]; 1702 int activeLinkNum; 1703 int t0, total, topSum, botSum; 1704 struct timeval now; 1705 int i, numFragments; 1706 1707 /* If only one link, this gets real easy */ 1708 if (priv->numActiveLinks == 1) { 1709 distrib[0] = len; 1710 return; 1711 } 1712 1713 /* Get current time */ 1714 getmicrouptime(&now); 1715 1716 /* Compute latencies for each link at this point in time */ 1717 for (activeLinkNum = 0; 1718 activeLinkNum < priv->numActiveLinks; activeLinkNum++) { 1719 struct ng_ppp_link *alink; 1720 struct timeval diff; 1721 int xmitBytes; 1722 1723 /* Start with base latency value */ 1724 alink = &priv->links[priv->activeLinks[activeLinkNum]]; 1725 latency[activeLinkNum] = alink->latency; 1726 sortByLatency[activeLinkNum] = activeLinkNum; /* see below */ 1727 1728 /* Any additional latency? */ 1729 if (alink->bytesInQueue == 0) 1730 continue; 1731 1732 /* Compute time delta since last write */ 1733 diff = now; 1734 timevalsub(&diff, &alink->lastWrite); 1735 if (now.tv_sec < 0 || diff.tv_sec >= 10) { /* sanity */ 1736 alink->bytesInQueue = 0; 1737 continue; 1738 } 1739 1740 /* How many bytes could have transmitted since last write? */ 1741 xmitBytes = (alink->conf.bandwidth * diff.tv_sec) 1742 + (alink->conf.bandwidth * (diff.tv_usec / 1000)) / 100; 1743 alink->bytesInQueue -= xmitBytes; 1744 if (alink->bytesInQueue < 0) 1745 alink->bytesInQueue = 0; 1746 else 1747 latency[activeLinkNum] += 1748 (100 * alink->bytesInQueue) / alink->conf.bandwidth; 1749 } 1750 1751 /* Sort active links by latency */ 1752 qsort_r(sortByLatency, 1753 priv->numActiveLinks, sizeof(*sortByLatency), latency, ng_ppp_intcmp); 1754 1755 /* Find the interval we need (add links in sortByLatency[] order) */ 1756 for (numFragments = 1; 1757 numFragments < priv->numActiveLinks; numFragments++) { 1758 for (total = i = 0; i < numFragments; i++) { 1759 int flowTime; 1760 1761 flowTime = latency[sortByLatency[numFragments]] 1762 - latency[sortByLatency[i]]; 1763 total += ((flowTime * priv->links[ 1764 priv->activeLinks[sortByLatency[i]]].conf.bandwidth) 1765 + 99) / 100; 1766 } 1767 if (total >= len) 1768 break; 1769 } 1770 1771 /* Solve for t_0 in that interval */ 1772 for (topSum = botSum = i = 0; i < numFragments; i++) { 1773 int bw = priv->links[ 1774 priv->activeLinks[sortByLatency[i]]].conf.bandwidth; 1775 1776 topSum += latency[sortByLatency[i]] * bw; /* / 100 */ 1777 botSum += bw; /* / 100 */ 1778 } 1779 t0 = ((len * 100) + topSum + botSum / 2) / botSum; 1780 1781 /* Compute f_i(t_0) all i */ 1782 bzero(distrib, priv->numActiveLinks * sizeof(*distrib)); 1783 for (total = i = 0; i < numFragments; i++) { 1784 int bw = priv->links[ 1785 priv->activeLinks[sortByLatency[i]]].conf.bandwidth; 1786 1787 distrib[sortByLatency[i]] = 1788 (bw * (t0 - latency[sortByLatency[i]]) + 50) / 100; 1789 total += distrib[sortByLatency[i]]; 1790 } 1791 1792 /* Deal with any rounding error */ 1793 if (total < len) { 1794 struct ng_ppp_link *fastLink = 1795 &priv->links[priv->activeLinks[sortByLatency[0]]]; 1796 int fast = 0; 1797 1798 /* Find the fastest link */ 1799 for (i = 1; i < numFragments; i++) { 1800 struct ng_ppp_link *const link = 1801 &priv->links[priv->activeLinks[sortByLatency[i]]]; 1802 1803 if (link->conf.bandwidth > fastLink->conf.bandwidth) { 1804 fast = i; 1805 fastLink = link; 1806 } 1807 } 1808 distrib[sortByLatency[fast]] += len - total; 1809 } else while (total > len) { 1810 struct ng_ppp_link *slowLink = 1811 &priv->links[priv->activeLinks[sortByLatency[0]]]; 1812 int delta, slow = 0; 1813 1814 /* Find the slowest link that still has bytes to remove */ 1815 for (i = 1; i < numFragments; i++) { 1816 struct ng_ppp_link *const link = 1817 &priv->links[priv->activeLinks[sortByLatency[i]]]; 1818 1819 if (distrib[sortByLatency[slow]] == 0 1820 || (distrib[sortByLatency[i]] > 0 1821 && link->conf.bandwidth < 1822 slowLink->conf.bandwidth)) { 1823 slow = i; 1824 slowLink = link; 1825 } 1826 } 1827 delta = total - len; 1828 if (delta > distrib[sortByLatency[slow]]) 1829 delta = distrib[sortByLatency[slow]]; 1830 distrib[sortByLatency[slow]] -= delta; 1831 total -= delta; 1832 } 1833 } 1834 1835 /* 1836 * Compare two integers 1837 */ 1838 static int 1839 ng_ppp_intcmp(void *latency, const void *v1, const void *v2) 1840 { 1841 const int index1 = *((const int *) v1); 1842 const int index2 = *((const int *) v2); 1843 1844 return ((int *)latency)[index1] - ((int *)latency)[index2]; 1845 } 1846 1847 /* 1848 * Prepend a possibly compressed PPP protocol number in front of a frame 1849 */ 1850 static struct mbuf * 1851 ng_ppp_addproto(struct mbuf *m, int proto, int compOK) 1852 { 1853 if (compOK && PROT_COMPRESSABLE(proto)) { 1854 u_char pbyte = (u_char)proto; 1855 1856 return ng_ppp_prepend(m, &pbyte, 1); 1857 } else { 1858 u_int16_t pword = htons((u_int16_t)proto); 1859 1860 return ng_ppp_prepend(m, &pword, 2); 1861 } 1862 } 1863 1864 /* 1865 * Prepend some bytes to an mbuf 1866 */ 1867 static struct mbuf * 1868 ng_ppp_prepend(struct mbuf *m, const void *buf, int len) 1869 { 1870 M_PREPEND(m, len, M_DONTWAIT); 1871 if (m == NULL || (m->m_len < len && (m = m_pullup(m, len)) == NULL)) 1872 return (NULL); 1873 bcopy(buf, mtod(m, u_char *), len); 1874 return (m); 1875 } 1876 1877 /* 1878 * Update private information that is derived from other private information 1879 */ 1880 static void 1881 ng_ppp_update(node_p node, int newConf) 1882 { 1883 const priv_p priv = NG_NODE_PRIVATE(node); 1884 int i; 1885 1886 /* Update active status for VJ Compression */ 1887 priv->vjCompHooked = priv->hooks[HOOK_INDEX_VJC_IP] != NULL 1888 && priv->hooks[HOOK_INDEX_VJC_COMP] != NULL 1889 && priv->hooks[HOOK_INDEX_VJC_UNCOMP] != NULL 1890 && priv->hooks[HOOK_INDEX_VJC_VJIP] != NULL; 1891 1892 /* Increase latency for each link an amount equal to one MP header */ 1893 if (newConf) { 1894 for (i = 0; i < NG_PPP_MAX_LINKS; i++) { 1895 int hdrBytes; 1896 1897 hdrBytes = (priv->links[i].conf.enableACFComp ? 0 : 2) 1898 + (priv->links[i].conf.enableProtoComp ? 1 : 2) 1899 + (priv->conf.xmitShortSeq ? 2 : 4); 1900 priv->links[i].latency = 1901 priv->links[i].conf.latency + 1902 ((hdrBytes * priv->links[i].conf.bandwidth) + 50) 1903 / 100; 1904 } 1905 } 1906 1907 /* Update list of active links */ 1908 bzero(&priv->activeLinks, sizeof(priv->activeLinks)); 1909 priv->numActiveLinks = 0; 1910 priv->allLinksEqual = 1; 1911 for (i = 0; i < NG_PPP_MAX_LINKS; i++) { 1912 struct ng_ppp_link *const link = &priv->links[i]; 1913 1914 /* Is link active? */ 1915 if (link->conf.enableLink && link->hook != NULL) { 1916 struct ng_ppp_link *link0; 1917 1918 /* Add link to list of active links */ 1919 priv->activeLinks[priv->numActiveLinks++] = i; 1920 link0 = &priv->links[priv->activeLinks[0]]; 1921 1922 /* Determine if all links are still equal */ 1923 if (link->latency != link0->latency 1924 || link->conf.bandwidth != link0->conf.bandwidth) 1925 priv->allLinksEqual = 0; 1926 1927 /* Initialize rec'd sequence number */ 1928 if (link->seq == MP_NOSEQ) { 1929 link->seq = (link == link0) ? 1930 MP_INITIAL_SEQ : link0->seq; 1931 } 1932 } else 1933 link->seq = MP_NOSEQ; 1934 } 1935 1936 /* Update MP state as multi-link is active or not */ 1937 if (priv->conf.enableMultilink && priv->numActiveLinks > 0) 1938 ng_ppp_start_frag_timer(node); 1939 else { 1940 ng_ppp_stop_frag_timer(node); 1941 ng_ppp_frag_reset(node); 1942 priv->xseq = MP_INITIAL_SEQ; 1943 priv->mseq = MP_INITIAL_SEQ; 1944 for (i = 0; i < NG_PPP_MAX_LINKS; i++) { 1945 struct ng_ppp_link *const link = &priv->links[i]; 1946 1947 bzero(&link->lastWrite, sizeof(link->lastWrite)); 1948 link->bytesInQueue = 0; 1949 link->seq = MP_NOSEQ; 1950 } 1951 } 1952 } 1953 1954 /* 1955 * Determine if a new configuration would represent a valid change 1956 * from the current configuration and link activity status. 1957 */ 1958 static int 1959 ng_ppp_config_valid(node_p node, const struct ng_ppp_node_conf *newConf) 1960 { 1961 const priv_p priv = NG_NODE_PRIVATE(node); 1962 int i, newNumLinksActive; 1963 1964 /* Check per-link config and count how many links would be active */ 1965 for (newNumLinksActive = i = 0; i < NG_PPP_MAX_LINKS; i++) { 1966 if (newConf->links[i].enableLink && priv->links[i].hook != NULL) 1967 newNumLinksActive++; 1968 if (!newConf->links[i].enableLink) 1969 continue; 1970 if (newConf->links[i].mru < MP_MIN_LINK_MRU) 1971 return (0); 1972 if (newConf->links[i].bandwidth == 0) 1973 return (0); 1974 if (newConf->links[i].bandwidth > NG_PPP_MAX_BANDWIDTH) 1975 return (0); 1976 if (newConf->links[i].latency > NG_PPP_MAX_LATENCY) 1977 return (0); 1978 } 1979 1980 /* Check bundle parameters */ 1981 if (newConf->bund.enableMultilink && newConf->bund.mrru < MP_MIN_MRRU) 1982 return (0); 1983 1984 /* Disallow changes to multi-link configuration while MP is active */ 1985 if (priv->numActiveLinks > 0 && newNumLinksActive > 0) { 1986 if (!priv->conf.enableMultilink 1987 != !newConf->bund.enableMultilink 1988 || !priv->conf.xmitShortSeq != !newConf->bund.xmitShortSeq 1989 || !priv->conf.recvShortSeq != !newConf->bund.recvShortSeq) 1990 return (0); 1991 } 1992 1993 /* At most one link can be active unless multi-link is enabled */ 1994 if (!newConf->bund.enableMultilink && newNumLinksActive > 1) 1995 return (0); 1996 1997 /* Configuration change would be valid */ 1998 return (1); 1999 } 2000 2001 /* 2002 * Free all entries in the fragment queue 2003 */ 2004 static void 2005 ng_ppp_frag_reset(node_p node) 2006 { 2007 const priv_p priv = NG_NODE_PRIVATE(node); 2008 struct ng_ppp_frag *qent, *qnext; 2009 2010 for (qent = TAILQ_FIRST(&priv->frags); qent; qent = qnext) { 2011 qnext = TAILQ_NEXT(qent, f_qent); 2012 NG_FREE_M(qent->data); 2013 FREE(qent, M_NETGRAPH_PPP); 2014 } 2015 TAILQ_INIT(&priv->frags); 2016 priv->qlen = 0; 2017 } 2018 2019 /* 2020 * Start fragment queue timer 2021 */ 2022 static void 2023 ng_ppp_start_frag_timer(node_p node) 2024 { 2025 const priv_p priv = NG_NODE_PRIVATE(node); 2026 2027 if (!(priv->fragTimer.c_flags & CALLOUT_PENDING)) 2028 ng_callout(&priv->fragTimer, node, NULL, MP_FRAGTIMER_INTERVAL, 2029 ng_ppp_frag_timeout, NULL, 0); 2030 } 2031 2032 /* 2033 * Stop fragment queue timer 2034 */ 2035 static void 2036 ng_ppp_stop_frag_timer(node_p node) 2037 { 2038 const priv_p priv = NG_NODE_PRIVATE(node); 2039 2040 if (priv->fragTimer.c_flags & CALLOUT_PENDING) 2041 ng_uncallout(&priv->fragTimer, node); 2042 } 2043