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