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