xref: /freebsd/sys/netgraph/ng_ppp.c (revision 5b664c7c1318bbcf41e535e196186b0fd0af6b82)
14cf49a43SJulian Elischer 
24cf49a43SJulian Elischer /*
34cf49a43SJulian Elischer  * ng_ppp.c
44cf49a43SJulian Elischer  *
54cf49a43SJulian Elischer  * Copyright (c) 1996-1999 Whistle Communications, Inc.
64cf49a43SJulian Elischer  * All rights reserved.
74cf49a43SJulian Elischer  *
84cf49a43SJulian Elischer  * Subject to the following obligations and disclaimer of warranty, use and
94cf49a43SJulian Elischer  * redistribution of this software, in source or object code forms, with or
104cf49a43SJulian Elischer  * without modifications are expressly permitted by Whistle Communications;
114cf49a43SJulian Elischer  * provided, however, that:
124cf49a43SJulian Elischer  * 1. Any and all reproductions of the source or object code must include the
134cf49a43SJulian Elischer  *    copyright notice above and the following disclaimer of warranties; and
144cf49a43SJulian Elischer  * 2. No rights are granted, in any manner or form, to use Whistle
154cf49a43SJulian Elischer  *    Communications, Inc. trademarks, including the mark "WHISTLE
164cf49a43SJulian Elischer  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
174cf49a43SJulian Elischer  *    such appears in the above copyright notice or in the software.
184cf49a43SJulian Elischer  *
194cf49a43SJulian Elischer  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
204cf49a43SJulian Elischer  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
214cf49a43SJulian Elischer  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
224cf49a43SJulian Elischer  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
234cf49a43SJulian Elischer  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
244cf49a43SJulian Elischer  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
254cf49a43SJulian Elischer  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
264cf49a43SJulian Elischer  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
274cf49a43SJulian Elischer  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
284cf49a43SJulian Elischer  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
294cf49a43SJulian Elischer  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
304cf49a43SJulian Elischer  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
314cf49a43SJulian Elischer  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
324cf49a43SJulian Elischer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
334cf49a43SJulian Elischer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
344cf49a43SJulian Elischer  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
354cf49a43SJulian Elischer  * OF SUCH DAMAGE.
364cf49a43SJulian Elischer  *
374cf49a43SJulian Elischer  * Author: Archie Cobbs <archie@whistle.com>
384cf49a43SJulian Elischer  *
394cf49a43SJulian Elischer  * $FreeBSD$
4074f5c6aaSJulian Elischer  * $Whistle: ng_ppp.c,v 1.24 1999/11/01 09:24:52 julian Exp $
414cf49a43SJulian Elischer  */
424cf49a43SJulian Elischer 
434cf49a43SJulian Elischer /*
443949bee8SArchie Cobbs  * PPP node type.
454cf49a43SJulian Elischer  */
464cf49a43SJulian Elischer 
474cf49a43SJulian Elischer #include <sys/param.h>
484cf49a43SJulian Elischer #include <sys/systm.h>
494cf49a43SJulian Elischer #include <sys/kernel.h>
504cf49a43SJulian Elischer #include <sys/conf.h>
514cf49a43SJulian Elischer #include <sys/mbuf.h>
524cf49a43SJulian Elischer #include <sys/malloc.h>
534cf49a43SJulian Elischer #include <sys/errno.h>
544cf49a43SJulian Elischer #include <sys/socket.h>
554cf49a43SJulian Elischer #include <sys/syslog.h>
565b664c7cSPoul-Henning Kamp #include <sys/ctype.h>
574cf49a43SJulian Elischer 
584cf49a43SJulian Elischer #include <netgraph/ng_message.h>
594cf49a43SJulian Elischer #include <netgraph/netgraph.h>
604cf49a43SJulian Elischer #include <netgraph/ng_ppp.h>
613949bee8SArchie Cobbs #include <netgraph/ng_vjc.h>
624cf49a43SJulian Elischer 
634cf49a43SJulian Elischer #define PROT_VALID(p)		(((p) & 0x0101) == 0x0001)
642b70adcbSArchie Cobbs #define PROT_COMPRESSABLE(p)	(((p) & 0xff00) == 0x0000)
654cf49a43SJulian Elischer 
663949bee8SArchie Cobbs /* Some PPP protocol numbers we're interested in */
673949bee8SArchie Cobbs #define PROT_APPLETALK		0x0029
683949bee8SArchie Cobbs #define PROT_COMPD		0x00fd
693949bee8SArchie Cobbs #define PROT_CRYPTD		0x0053
703949bee8SArchie Cobbs #define PROT_IP			0x0021
712b70adcbSArchie Cobbs #define PROT_IPX		0x002b
723949bee8SArchie Cobbs #define PROT_MP			0x003d
733949bee8SArchie Cobbs #define PROT_VJCOMP		0x002d
743949bee8SArchie Cobbs #define PROT_VJUNCOMP		0x002f
753949bee8SArchie Cobbs 
763949bee8SArchie Cobbs /* Multilink PPP definitions */
773949bee8SArchie Cobbs #define MP_MIN_MRRU		1500		/* per RFC 1990 */
783949bee8SArchie Cobbs #define MP_INITIAL_SEQ		0		/* per RFC 1990 */
793949bee8SArchie Cobbs #define MP_MIN_LINK_MRU		32
803949bee8SArchie Cobbs 
813949bee8SArchie Cobbs #define MP_MAX_SEQ_LINGER	64		/* max frags we will hold */
823949bee8SArchie Cobbs #define MP_INSANE_SEQ_JUMP	128		/* a sequence # jump too far */
833949bee8SArchie Cobbs #define MP_MIN_FRAG_LEN		6		/* don't frag smaller frames */
843949bee8SArchie Cobbs 
853949bee8SArchie Cobbs #define MP_SHORT_SEQ_MASK	0x00000fff	/* short seq # mask */
863949bee8SArchie Cobbs #define MP_SHORT_SEQ_HIBIT	0x00000800	/* short seq # high bit */
873949bee8SArchie Cobbs #define MP_SHORT_FIRST_FLAG	0x00008000	/* first fragment in frame */
883949bee8SArchie Cobbs #define MP_SHORT_LAST_FLAG	0x00004000	/* last fragment in frame */
893949bee8SArchie Cobbs 
903949bee8SArchie Cobbs #define MP_LONG_SEQ_MASK	0x00ffffff	/* long seq # mask */
913949bee8SArchie Cobbs #define MP_LONG_SEQ_HIBIT	0x00800000	/* long seq # high bit */
923949bee8SArchie Cobbs #define MP_LONG_FIRST_FLAG	0x80000000	/* first fragment in frame */
933949bee8SArchie Cobbs #define MP_LONG_LAST_FLAG	0x40000000	/* last fragment in frame */
943949bee8SArchie Cobbs 
953949bee8SArchie Cobbs #define MP_SEQ_MASK		(priv->conf.recvShortSeq ? \
963949bee8SArchie Cobbs 				    MP_SHORT_SEQ_MASK : MP_LONG_SEQ_MASK)
973949bee8SArchie Cobbs 
983949bee8SArchie Cobbs /* Sign extension of MP sequence numbers */
993949bee8SArchie Cobbs #define MP_SHORT_EXTEND(s)	(((s) & MP_SHORT_SEQ_HIBIT) ? \
1003949bee8SArchie Cobbs 				    ((s) | ~MP_SHORT_SEQ_MASK) : (s))
1013949bee8SArchie Cobbs #define MP_LONG_EXTEND(s)	(((s) & MP_LONG_SEQ_HIBIT) ? \
1023949bee8SArchie Cobbs 				    ((s) | ~MP_LONG_SEQ_MASK) : (s))
1033949bee8SArchie Cobbs 
1043949bee8SArchie Cobbs /* Comparision of MP sequence numbers */
1053949bee8SArchie Cobbs #define MP_SHORT_SEQ_DIFF(x,y)	(MP_SHORT_EXTEND(x) - MP_SHORT_EXTEND(y))
1063949bee8SArchie Cobbs #define MP_LONG_SEQ_DIFF(x,y)	(MP_LONG_EXTEND(x) - MP_LONG_EXTEND(y))
1073949bee8SArchie Cobbs 
1083949bee8SArchie Cobbs #define MP_SEQ_DIFF(x,y)	(priv->conf.recvShortSeq ? \
1093949bee8SArchie Cobbs 				    MP_SHORT_SEQ_DIFF((x), (y)) : \
1103949bee8SArchie Cobbs 				    MP_LONG_SEQ_DIFF((x), (y)))
1113949bee8SArchie Cobbs 
1123949bee8SArchie Cobbs /* We store incoming fragments this way */
1133949bee8SArchie Cobbs struct ng_ppp_frag {
1143949bee8SArchie Cobbs 	int				seq;
1153949bee8SArchie Cobbs 	u_char				first;
1163949bee8SArchie Cobbs 	u_char				last;
1173949bee8SArchie Cobbs 	struct mbuf			*data;
1183949bee8SArchie Cobbs 	meta_p				meta;
1193949bee8SArchie Cobbs 	CIRCLEQ_ENTRY(ng_ppp_frag)	f_qent;
1203949bee8SArchie Cobbs };
1213949bee8SArchie Cobbs 
1223949bee8SArchie Cobbs /* We keep track of link queue status this way */
1233949bee8SArchie Cobbs struct ng_ppp_link_qstat {
1243949bee8SArchie Cobbs 	struct timeval		lastWrite;	/* time of last write */
1253949bee8SArchie Cobbs 	int			bytesInQueue;	/* bytes in the queue then */
1263949bee8SArchie Cobbs };
1273949bee8SArchie Cobbs 
1283949bee8SArchie Cobbs /* We use integer indicies to refer to the non-link hooks */
1293949bee8SArchie Cobbs static const char *const ng_ppp_hook_names[] = {
1303949bee8SArchie Cobbs 	NG_PPP_HOOK_ATALK,
1313949bee8SArchie Cobbs #define HOOK_INDEX_ATALK		0
1323949bee8SArchie Cobbs 	NG_PPP_HOOK_BYPASS,
1333949bee8SArchie Cobbs #define HOOK_INDEX_BYPASS		1
1343949bee8SArchie Cobbs 	NG_PPP_HOOK_COMPRESS,
1353949bee8SArchie Cobbs #define HOOK_INDEX_COMPRESS		2
1363949bee8SArchie Cobbs 	NG_PPP_HOOK_ENCRYPT,
1373949bee8SArchie Cobbs #define HOOK_INDEX_ENCRYPT		3
1383949bee8SArchie Cobbs 	NG_PPP_HOOK_DECOMPRESS,
1393949bee8SArchie Cobbs #define HOOK_INDEX_DECOMPRESS		4
1403949bee8SArchie Cobbs 	NG_PPP_HOOK_DECRYPT,
1413949bee8SArchie Cobbs #define HOOK_INDEX_DECRYPT		5
1423949bee8SArchie Cobbs 	NG_PPP_HOOK_INET,
1433949bee8SArchie Cobbs #define HOOK_INDEX_INET			6
1443949bee8SArchie Cobbs 	NG_PPP_HOOK_IPX,
1453949bee8SArchie Cobbs #define HOOK_INDEX_IPX			7
1463949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_COMP,
1473949bee8SArchie Cobbs #define HOOK_INDEX_VJC_COMP		8
1483949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_IP,
1493949bee8SArchie Cobbs #define HOOK_INDEX_VJC_IP		9
1503949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_UNCOMP,
1513949bee8SArchie Cobbs #define HOOK_INDEX_VJC_UNCOMP		10
1523949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_VJIP,
1533949bee8SArchie Cobbs #define HOOK_INDEX_VJC_VJIP		11
1543949bee8SArchie Cobbs 	NULL
1553949bee8SArchie Cobbs #define HOOK_INDEX_MAX			12
1563949bee8SArchie Cobbs };
1573949bee8SArchie Cobbs 
1583949bee8SArchie Cobbs /* We store index numbers in the hook private pointer. The HOOK_INDEX()
1593949bee8SArchie Cobbs    for a hook is either the index (above) for normal hooks, or the ones
1603949bee8SArchie Cobbs    complement of the link number for link hooks. */
1613949bee8SArchie Cobbs #define HOOK_INDEX(hook)	(*((int16_t *) &(hook)->private))
1624cf49a43SJulian Elischer 
1634cf49a43SJulian Elischer /* Node private data */
1644cf49a43SJulian Elischer struct private {
1653949bee8SArchie Cobbs 	struct ng_ppp_node_config	conf;
1663949bee8SArchie Cobbs 	struct ng_ppp_link_stat		bundleStats;
1673949bee8SArchie Cobbs 	struct ng_ppp_link_stat		linkStats[NG_PPP_MAX_LINKS];
1683949bee8SArchie Cobbs 	hook_p				links[NG_PPP_MAX_LINKS];
1693949bee8SArchie Cobbs 	hook_p				hooks[HOOK_INDEX_MAX];
1703949bee8SArchie Cobbs 	u_char				vjCompHooked;
1713949bee8SArchie Cobbs 	u_char				allLinksEqual;
1723949bee8SArchie Cobbs 	u_short				activeLinks[NG_PPP_MAX_LINKS];
1733949bee8SArchie Cobbs 	u_int				numActiveLinks;
1743949bee8SArchie Cobbs 	u_int				lastLink;	/* for round robin */
1753949bee8SArchie Cobbs 	struct ng_ppp_link_qstat	qstat[NG_PPP_MAX_LINKS];
1763949bee8SArchie Cobbs 	CIRCLEQ_HEAD(ng_ppp_fraglist, ng_ppp_frag)
1773949bee8SArchie Cobbs 					frags;		/* incoming fragments */
1783949bee8SArchie Cobbs 	int				mpSeqOut;	/* next out MP seq # */
1794cf49a43SJulian Elischer };
1804cf49a43SJulian Elischer typedef struct private *priv_p;
1814cf49a43SJulian Elischer 
1824cf49a43SJulian Elischer /* Netgraph node methods */
18374f5c6aaSJulian Elischer static ng_constructor_t	ng_ppp_constructor;
18474f5c6aaSJulian Elischer static ng_rcvmsg_t	ng_ppp_rcvmsg;
18574f5c6aaSJulian Elischer static ng_shutdown_t	ng_ppp_rmnode;
18674f5c6aaSJulian Elischer static ng_newhook_t	ng_ppp_newhook;
18774f5c6aaSJulian Elischer static ng_rcvdata_t	ng_ppp_rcvdata;
18874f5c6aaSJulian Elischer static ng_disconnect_t	ng_ppp_disconnect;
1894cf49a43SJulian Elischer 
1903949bee8SArchie Cobbs /* Helper functions */
1913949bee8SArchie Cobbs static int	ng_ppp_input(node_p node, int ln, struct mbuf *m, meta_p meta);
1923949bee8SArchie Cobbs static int	ng_ppp_output(node_p node, int ln, struct mbuf *m, meta_p meta);
1933949bee8SArchie Cobbs static int	ng_ppp_mp_input(node_p nd, int ln, struct mbuf *m, meta_p meta);
1943949bee8SArchie Cobbs static int	ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta);
1953949bee8SArchie Cobbs static void	ng_ppp_mp_strategy(node_p node, int len, int *distrib);
1963949bee8SArchie Cobbs static int	ng_ppp_intcmp(const void *v1, const void *v2);
1973949bee8SArchie Cobbs static struct	mbuf *ng_ppp_addproto(struct mbuf *m, int proto, int compOK);
1983949bee8SArchie Cobbs static int	ng_ppp_config_valid(node_p node,
1993949bee8SArchie Cobbs 			const struct ng_ppp_node_config *newConf);
2003949bee8SArchie Cobbs static void	ng_ppp_update(node_p node, int newConf);
2013949bee8SArchie Cobbs static void	ng_ppp_free_frags(node_p node);
2024cf49a43SJulian Elischer 
2034cf49a43SJulian Elischer /* Node type descriptor */
2043949bee8SArchie Cobbs static struct ng_type ng_ppp_typestruct = {
2054cf49a43SJulian Elischer 	NG_VERSION,
2064cf49a43SJulian Elischer 	NG_PPP_NODE_TYPE,
2074cf49a43SJulian Elischer 	NULL,
2084cf49a43SJulian Elischer 	ng_ppp_constructor,
2094cf49a43SJulian Elischer 	ng_ppp_rcvmsg,
2104cf49a43SJulian Elischer 	ng_ppp_rmnode,
2114cf49a43SJulian Elischer 	ng_ppp_newhook,
2124cf49a43SJulian Elischer 	NULL,
2134cf49a43SJulian Elischer 	NULL,
2144cf49a43SJulian Elischer 	ng_ppp_rcvdata,
2154cf49a43SJulian Elischer 	ng_ppp_rcvdata,
2164cf49a43SJulian Elischer 	ng_ppp_disconnect
2174cf49a43SJulian Elischer };
2183949bee8SArchie Cobbs NETGRAPH_INIT(ppp, &ng_ppp_typestruct);
2194cf49a43SJulian Elischer 
2203949bee8SArchie Cobbs static int	*compareLatencies;		/* hack for ng_ppp_intcmp() */
2214cf49a43SJulian Elischer 
2224cf49a43SJulian Elischer #define ERROUT(x)	do { error = (x); goto done; } while (0)
2234cf49a43SJulian Elischer 
2244cf49a43SJulian Elischer /************************************************************************
2254cf49a43SJulian Elischer 			NETGRAPH NODE STUFF
2264cf49a43SJulian Elischer  ************************************************************************/
2274cf49a43SJulian Elischer 
2284cf49a43SJulian Elischer /*
2293949bee8SArchie Cobbs  * Node type constructor
2304cf49a43SJulian Elischer  */
2314cf49a43SJulian Elischer static int
2324cf49a43SJulian Elischer ng_ppp_constructor(node_p *nodep)
2334cf49a43SJulian Elischer {
2344cf49a43SJulian Elischer 	priv_p priv;
2354cf49a43SJulian Elischer 	int error;
2364cf49a43SJulian Elischer 
2374cf49a43SJulian Elischer 	/* Allocate private structure */
2384cf49a43SJulian Elischer 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK);
2394cf49a43SJulian Elischer 	if (priv == NULL)
2404cf49a43SJulian Elischer 		return (ENOMEM);
2414cf49a43SJulian Elischer 	bzero(priv, sizeof(*priv));
2424cf49a43SJulian Elischer 
2434cf49a43SJulian Elischer 	/* Call generic node constructor */
2443949bee8SArchie Cobbs 	if ((error = ng_make_node_common(&ng_ppp_typestruct, nodep))) {
2454cf49a43SJulian Elischer 		FREE(priv, M_NETGRAPH);
2464cf49a43SJulian Elischer 		return (error);
2474cf49a43SJulian Elischer 	}
2484cf49a43SJulian Elischer 	(*nodep)->private = priv;
2494cf49a43SJulian Elischer 
2503949bee8SArchie Cobbs 	/* Initialize state */
2513949bee8SArchie Cobbs 	CIRCLEQ_INIT(&priv->frags);
2523949bee8SArchie Cobbs 
2534cf49a43SJulian Elischer 	/* Done */
2544cf49a43SJulian Elischer 	return (0);
2554cf49a43SJulian Elischer }
2564cf49a43SJulian Elischer 
2574cf49a43SJulian Elischer /*
2584cf49a43SJulian Elischer  * Give our OK for a hook to be added
2594cf49a43SJulian Elischer  */
2604cf49a43SJulian Elischer static int
2614cf49a43SJulian Elischer ng_ppp_newhook(node_p node, hook_p hook, const char *name)
2624cf49a43SJulian Elischer {
2634cf49a43SJulian Elischer 	const priv_p priv = node->private;
2643949bee8SArchie Cobbs 	int linkNum = -1;
2653949bee8SArchie Cobbs 	hook_p *hookPtr = NULL;
2663949bee8SArchie Cobbs 	int hookIndex = -1;
2674cf49a43SJulian Elischer 
2683949bee8SArchie Cobbs 	/* Figure out which hook it is */
2693949bee8SArchie Cobbs 	if (strncmp(name, NG_PPP_HOOK_LINK_PREFIX,	/* a link hook? */
2703949bee8SArchie Cobbs 	    strlen(NG_PPP_HOOK_LINK_PREFIX)) == 0) {
2712b70adcbSArchie Cobbs 		const char *cp, *eptr;
2723949bee8SArchie Cobbs 
2732b70adcbSArchie Cobbs 		cp = name + strlen(NG_PPP_HOOK_LINK_PREFIX);
2742b70adcbSArchie Cobbs 		if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
2754cf49a43SJulian Elischer 			return (EINVAL);
2762b70adcbSArchie Cobbs 		linkNum = (int)strtoul(cp, &eptr, 10);
2772b70adcbSArchie Cobbs 		if (*eptr != '\0' || linkNum < 0 || linkNum >= NG_PPP_MAX_LINKS)
2783949bee8SArchie Cobbs 			return (EINVAL);
2793949bee8SArchie Cobbs 		hookPtr = &priv->links[linkNum];
2803949bee8SArchie Cobbs 		hookIndex = ~linkNum;
2813949bee8SArchie Cobbs 	} else {				/* must be a non-link hook */
2823949bee8SArchie Cobbs 		int i;
2834cf49a43SJulian Elischer 
2843949bee8SArchie Cobbs 		for (i = 0; ng_ppp_hook_names[i] != NULL; i++) {
2853949bee8SArchie Cobbs 			if (strcmp(name, ng_ppp_hook_names[i]) == 0) {
2863949bee8SArchie Cobbs 				hookPtr = &priv->hooks[i];
2873949bee8SArchie Cobbs 				hookIndex = i;
2883949bee8SArchie Cobbs 				break;
2893949bee8SArchie Cobbs 			}
2903949bee8SArchie Cobbs 		}
2913949bee8SArchie Cobbs 		if (ng_ppp_hook_names[i] == NULL)
2923949bee8SArchie Cobbs 			return (EINVAL);	/* no such hook */
2933949bee8SArchie Cobbs 	}
2943949bee8SArchie Cobbs 
2953949bee8SArchie Cobbs 	/* See if hook is already connected */
2963949bee8SArchie Cobbs 	if (*hookPtr != NULL)
2974cf49a43SJulian Elischer 		return (EISCONN);
2984cf49a43SJulian Elischer 
2993949bee8SArchie Cobbs 	/* Disallow more than one link unless multilink is enabled */
3003949bee8SArchie Cobbs 	if (linkNum != -1 && priv->conf.links[linkNum].enableLink
3013949bee8SArchie Cobbs 	    && !priv->conf.enableMultilink && priv->numActiveLinks >= 1)
3023949bee8SArchie Cobbs 		return (ENODEV);
3034cf49a43SJulian Elischer 
3044cf49a43SJulian Elischer 	/* OK */
3053949bee8SArchie Cobbs 	*hookPtr = hook;
3063949bee8SArchie Cobbs 	HOOK_INDEX(hook) = hookIndex;
3073949bee8SArchie Cobbs 	ng_ppp_update(node, 0);
3084cf49a43SJulian Elischer 	return (0);
3094cf49a43SJulian Elischer }
3104cf49a43SJulian Elischer 
3114cf49a43SJulian Elischer /*
3124cf49a43SJulian Elischer  * Receive a control message
3134cf49a43SJulian Elischer  */
3144cf49a43SJulian Elischer static int
3154cf49a43SJulian Elischer ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg,
3164cf49a43SJulian Elischer 	      const char *raddr, struct ng_mesg **rptr)
3174cf49a43SJulian Elischer {
3184cf49a43SJulian Elischer 	const priv_p priv = node->private;
3194cf49a43SJulian Elischer 	struct ng_mesg *resp = NULL;
3204cf49a43SJulian Elischer 	int error = 0;
3214cf49a43SJulian Elischer 
3224cf49a43SJulian Elischer 	switch (msg->header.typecookie) {
3234cf49a43SJulian Elischer 	case NGM_PPP_COOKIE:
3244cf49a43SJulian Elischer 		switch (msg->header.cmd) {
3253949bee8SArchie Cobbs 		case NGM_PPP_SET_CONFIG:
3263949bee8SArchie Cobbs 		    {
3273949bee8SArchie Cobbs 			struct ng_ppp_node_config *const newConf =
3283949bee8SArchie Cobbs 				(struct ng_ppp_node_config *) msg->data;
3293949bee8SArchie Cobbs 
3303949bee8SArchie Cobbs 			/* Check for invalid or illegal config */
3313949bee8SArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
3324cf49a43SJulian Elischer 				ERROUT(EINVAL);
3333949bee8SArchie Cobbs 			if (!ng_ppp_config_valid(node, newConf))
3343949bee8SArchie Cobbs 				ERROUT(EINVAL);
3353949bee8SArchie Cobbs 			priv->conf = *newConf;
3363949bee8SArchie Cobbs 			ng_ppp_update(node, 1);
3374cf49a43SJulian Elischer 			break;
3383949bee8SArchie Cobbs 		    }
3393949bee8SArchie Cobbs 		case NGM_PPP_GET_CONFIG:
3403949bee8SArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT);
3414cf49a43SJulian Elischer 			if (resp == NULL)
3424cf49a43SJulian Elischer 				ERROUT(ENOMEM);
3433949bee8SArchie Cobbs 			bcopy(&priv->conf, resp->data, sizeof(priv->conf));
3444cf49a43SJulian Elischer 			break;
3453949bee8SArchie Cobbs 		case NGM_PPP_GET_LINK_STATS:
3463949bee8SArchie Cobbs 		case NGM_PPP_CLR_LINK_STATS:
3473949bee8SArchie Cobbs 		    {
3483949bee8SArchie Cobbs 			struct ng_ppp_link_stat *stats;
3493949bee8SArchie Cobbs 			u_int16_t linkNum;
3503949bee8SArchie Cobbs 
3513949bee8SArchie Cobbs 			if (msg->header.arglen != sizeof(u_int16_t))
3523949bee8SArchie Cobbs 				ERROUT(EINVAL);
3533949bee8SArchie Cobbs 			linkNum = *((u_int16_t *) msg->data);
3543949bee8SArchie Cobbs 			if (linkNum >= NG_PPP_MAX_LINKS
3553949bee8SArchie Cobbs 			    && linkNum != NG_PPP_BUNDLE_LINKNUM)
3563949bee8SArchie Cobbs 				ERROUT(EINVAL);
3573949bee8SArchie Cobbs 			stats = (linkNum == NG_PPP_BUNDLE_LINKNUM) ?
3583949bee8SArchie Cobbs 				&priv->bundleStats : &priv->linkStats[linkNum];
3593949bee8SArchie Cobbs 			if (msg->header.cmd == NGM_PPP_GET_LINK_STATS) {
3603949bee8SArchie Cobbs 				NG_MKRESPONSE(resp, msg,
3613949bee8SArchie Cobbs 				    sizeof(struct ng_ppp_link_stat), M_NOWAIT);
3623949bee8SArchie Cobbs 				if (resp == NULL)
3633949bee8SArchie Cobbs 					ERROUT(ENOMEM);
3643949bee8SArchie Cobbs 				bcopy(stats, resp->data, sizeof(*stats));
3653949bee8SArchie Cobbs 			} else
3663949bee8SArchie Cobbs 				bzero(stats, sizeof(*stats));
3674cf49a43SJulian Elischer 			break;
3683949bee8SArchie Cobbs 		    }
3694cf49a43SJulian Elischer 		default:
3704cf49a43SJulian Elischer 			error = EINVAL;
3714cf49a43SJulian Elischer 			break;
3724cf49a43SJulian Elischer 		}
3734cf49a43SJulian Elischer 		break;
3743949bee8SArchie Cobbs 	case NGM_VJC_COOKIE:
3753949bee8SArchie Cobbs 	   {
3763949bee8SArchie Cobbs 	       char path[NG_PATHLEN + 1];
3773949bee8SArchie Cobbs 	       node_p origNode;
3783949bee8SArchie Cobbs 
3793949bee8SArchie Cobbs 	       if ((error = ng_path2node(node, raddr, &origNode, NULL)) != 0)
3803949bee8SArchie Cobbs 		       ERROUT(error);
3813949bee8SArchie Cobbs 	       snprintf(path, sizeof(path), "[%lx]:%s",
3823949bee8SArchie Cobbs 		   (long) node, NG_PPP_HOOK_VJC_IP);
3833949bee8SArchie Cobbs 	       return ng_send_msg(origNode, msg, path, rptr);
3843949bee8SArchie Cobbs 	       break;
3853949bee8SArchie Cobbs 	   }
3864cf49a43SJulian Elischer 	default:
3874cf49a43SJulian Elischer 		error = EINVAL;
3884cf49a43SJulian Elischer 		break;
3894cf49a43SJulian Elischer 	}
3904cf49a43SJulian Elischer 	if (rptr)
3914cf49a43SJulian Elischer 		*rptr = resp;
3924cf49a43SJulian Elischer 	else if (resp)
3934cf49a43SJulian Elischer 		FREE(resp, M_NETGRAPH);
3944cf49a43SJulian Elischer 
3954cf49a43SJulian Elischer done:
3964cf49a43SJulian Elischer 	FREE(msg, M_NETGRAPH);
3974cf49a43SJulian Elischer 	return (error);
3984cf49a43SJulian Elischer }
3994cf49a43SJulian Elischer 
4004cf49a43SJulian Elischer /*
4014cf49a43SJulian Elischer  * Receive data on a hook
4024cf49a43SJulian Elischer  */
4034cf49a43SJulian Elischer static int
4044cf49a43SJulian Elischer ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
4054cf49a43SJulian Elischer {
4064cf49a43SJulian Elischer 	const node_p node = hook->node;
4074cf49a43SJulian Elischer 	const priv_p priv = node->private;
4083949bee8SArchie Cobbs 	const int index = HOOK_INDEX(hook);
4093949bee8SArchie Cobbs 	u_int16_t linkNum = NG_PPP_BUNDLE_LINKNUM;
4103949bee8SArchie Cobbs 	hook_p outHook = NULL;
4113949bee8SArchie Cobbs 	int proto = 0, error;
4124cf49a43SJulian Elischer 
4133949bee8SArchie Cobbs 	/* Did it come from a link hook? */
4143949bee8SArchie Cobbs 	if (index < 0) {
4154cf49a43SJulian Elischer 
4163949bee8SArchie Cobbs 		/* Is link active? */
4173949bee8SArchie Cobbs 		linkNum = (u_int16_t)~index;
4183949bee8SArchie Cobbs 		KASSERT(linkNum < NG_PPP_MAX_LINKS,
4193949bee8SArchie Cobbs 		    ("%s: bogus index 0x%x", __FUNCTION__, index));
4203949bee8SArchie Cobbs 		if (!priv->conf.links[linkNum].enableLink) {
4214cf49a43SJulian Elischer 			NG_FREE_DATA(m, meta);
4223949bee8SArchie Cobbs 			return (ENXIO);
4233949bee8SArchie Cobbs 		}
4243949bee8SArchie Cobbs 
4253949bee8SArchie Cobbs 		/* Stats */
4263949bee8SArchie Cobbs 		priv->linkStats[linkNum].recvFrames++;
4273949bee8SArchie Cobbs 		priv->linkStats[linkNum].recvOctets += m->m_pkthdr.len;
4283949bee8SArchie Cobbs 
4293949bee8SArchie Cobbs 		/* Dispatch incoming frame */
4303949bee8SArchie Cobbs 		return ng_ppp_input(node, linkNum, m, meta);
4313949bee8SArchie Cobbs 	}
4323949bee8SArchie Cobbs 
4333949bee8SArchie Cobbs 	/* Get protocol & check if data allowed from this hook */
4343949bee8SArchie Cobbs 	switch (index) {
4353949bee8SArchie Cobbs 
4363949bee8SArchie Cobbs 	/* Downwards flowing data */
4373949bee8SArchie Cobbs 	case HOOK_INDEX_ATALK:
4383949bee8SArchie Cobbs 		if (!priv->conf.enableAtalk) {
4393949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4403949bee8SArchie Cobbs 			return (ENXIO);
4413949bee8SArchie Cobbs 		}
4423949bee8SArchie Cobbs 		proto = PROT_APPLETALK;
4433949bee8SArchie Cobbs 		break;
4443949bee8SArchie Cobbs 	case HOOK_INDEX_IPX:
4453949bee8SArchie Cobbs 		if (!priv->conf.enableIPX) {
4463949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4473949bee8SArchie Cobbs 			return (ENXIO);
4483949bee8SArchie Cobbs 		}
4493949bee8SArchie Cobbs 		proto = PROT_IPX;
4503949bee8SArchie Cobbs 		break;
4513949bee8SArchie Cobbs 	case HOOK_INDEX_INET:
4523949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_VJIP:
4533949bee8SArchie Cobbs 		if (!priv->conf.enableIP) {
4543949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4553949bee8SArchie Cobbs 			return (ENXIO);
4563949bee8SArchie Cobbs 		}
4573949bee8SArchie Cobbs 		proto = PROT_IP;
4583949bee8SArchie Cobbs 		break;
4593949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_COMP:
4603949bee8SArchie Cobbs 		if (!priv->conf.enableVJCompression) {
4613949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4623949bee8SArchie Cobbs 			return (ENXIO);
4633949bee8SArchie Cobbs 		}
4643949bee8SArchie Cobbs 		proto = PROT_VJCOMP;
4653949bee8SArchie Cobbs 		break;
4663949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_UNCOMP:
4673949bee8SArchie Cobbs 		if (!priv->conf.enableVJCompression) {
4683949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4693949bee8SArchie Cobbs 			return (ENXIO);
4703949bee8SArchie Cobbs 		}
4713949bee8SArchie Cobbs 		proto = PROT_VJUNCOMP;
4723949bee8SArchie Cobbs 		break;
4733949bee8SArchie Cobbs 	case HOOK_INDEX_COMPRESS:
4743949bee8SArchie Cobbs 		if (!priv->conf.enableCompression) {
4753949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4763949bee8SArchie Cobbs 			return (ENXIO);
4773949bee8SArchie Cobbs 		}
4783949bee8SArchie Cobbs 		proto = PROT_COMPD;
4793949bee8SArchie Cobbs 		break;
4803949bee8SArchie Cobbs 	case HOOK_INDEX_ENCRYPT:
4813949bee8SArchie Cobbs 		if (!priv->conf.enableEncryption) {
4823949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4833949bee8SArchie Cobbs 			return (ENXIO);
4843949bee8SArchie Cobbs 		}
4853949bee8SArchie Cobbs 		proto = PROT_CRYPTD;
4863949bee8SArchie Cobbs 		break;
4873949bee8SArchie Cobbs 	case HOOK_INDEX_BYPASS:
4883949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 4) {
4893949bee8SArchie Cobbs 			NG_FREE_META(meta);
4903949bee8SArchie Cobbs 			return (EINVAL);
4913949bee8SArchie Cobbs 		}
4923949bee8SArchie Cobbs 		if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
4933949bee8SArchie Cobbs 			NG_FREE_META(meta);
4943949bee8SArchie Cobbs 			return (ENOBUFS);
4953949bee8SArchie Cobbs 		}
4963949bee8SArchie Cobbs 		linkNum = ntohs(mtod(m, u_int16_t *)[0]);
4973949bee8SArchie Cobbs 		proto = ntohs(mtod(m, u_int16_t *)[1]);
4983949bee8SArchie Cobbs 		m_adj(m, 4);
4993949bee8SArchie Cobbs 		if (linkNum >= NG_PPP_MAX_LINKS
5003949bee8SArchie Cobbs 		    && linkNum != NG_PPP_BUNDLE_LINKNUM)
5013949bee8SArchie Cobbs 			return (EINVAL);
5023949bee8SArchie Cobbs 		break;
5033949bee8SArchie Cobbs 
5043949bee8SArchie Cobbs 	/* Upwards flowing data */
5053949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_IP:
5063949bee8SArchie Cobbs 		if (!priv->conf.enableVJDecompression) {
5073949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5083949bee8SArchie Cobbs 			return (ENXIO);
5093949bee8SArchie Cobbs 		}
5103949bee8SArchie Cobbs 		break;
5113949bee8SArchie Cobbs 	case HOOK_INDEX_DECOMPRESS:
5123949bee8SArchie Cobbs 		if (!priv->conf.enableDecompression) {
5133949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5143949bee8SArchie Cobbs 			return (ENXIO);
5153949bee8SArchie Cobbs 		}
5163949bee8SArchie Cobbs 		break;
5173949bee8SArchie Cobbs 	case HOOK_INDEX_DECRYPT:
5183949bee8SArchie Cobbs 		if (!priv->conf.enableDecryption) {
5193949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5203949bee8SArchie Cobbs 			return (ENXIO);
5213949bee8SArchie Cobbs 		}
5223949bee8SArchie Cobbs 		break;
5233949bee8SArchie Cobbs 	default:
5243949bee8SArchie Cobbs 		KASSERT(0, ("%s: bogus index 0x%x", __FUNCTION__, index));
5253949bee8SArchie Cobbs 	}
5263949bee8SArchie Cobbs 
5273949bee8SArchie Cobbs 	/* Now figure out what to do with the frame */
5283949bee8SArchie Cobbs 	switch (index) {
5293949bee8SArchie Cobbs 	case HOOK_INDEX_INET:
5303949bee8SArchie Cobbs 		if (priv->conf.enableVJCompression && priv->vjCompHooked) {
5313949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_IP];
5323949bee8SArchie Cobbs 			break;
5333949bee8SArchie Cobbs 		}
5343949bee8SArchie Cobbs 		/* FALLTHROUGH */
5353949bee8SArchie Cobbs 	case HOOK_INDEX_ATALK:
5363949bee8SArchie Cobbs 	case HOOK_INDEX_IPX:
5373949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_COMP:
5383949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_UNCOMP:
5393949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_VJIP:
5403949bee8SArchie Cobbs 		if (priv->conf.enableCompression
5413949bee8SArchie Cobbs 		    && priv->hooks[HOOK_INDEX_COMPRESS] != NULL) {
5423949bee8SArchie Cobbs 			if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) {
5433949bee8SArchie Cobbs 				NG_FREE_META(meta);
5443949bee8SArchie Cobbs 				return (ENOBUFS);
5453949bee8SArchie Cobbs 			}
5463949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_COMPRESS];
5473949bee8SArchie Cobbs 			break;
5483949bee8SArchie Cobbs 		}
5493949bee8SArchie Cobbs 		/* FALLTHROUGH */
5503949bee8SArchie Cobbs 	case HOOK_INDEX_COMPRESS:
5513949bee8SArchie Cobbs 		if (priv->conf.enableEncryption
5523949bee8SArchie Cobbs 		    && priv->hooks[HOOK_INDEX_ENCRYPT] != NULL) {
5533949bee8SArchie Cobbs 			if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) {
5543949bee8SArchie Cobbs 				NG_FREE_META(meta);
5553949bee8SArchie Cobbs 				return (ENOBUFS);
5563949bee8SArchie Cobbs 			}
5573949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_ENCRYPT];
5583949bee8SArchie Cobbs 			break;
5593949bee8SArchie Cobbs 		}
5603949bee8SArchie Cobbs 		/* FALLTHROUGH */
5613949bee8SArchie Cobbs 	case HOOK_INDEX_ENCRYPT:
5623949bee8SArchie Cobbs 	case HOOK_INDEX_BYPASS:
5633949bee8SArchie Cobbs 		if ((m = ng_ppp_addproto(m, proto,
5643949bee8SArchie Cobbs 		    linkNum == NG_PPP_BUNDLE_LINKNUM
5653949bee8SArchie Cobbs 		      || priv->conf.links[linkNum].enableProtoComp)) == NULL) {
5663949bee8SArchie Cobbs 			NG_FREE_META(meta);
5673949bee8SArchie Cobbs 			return (ENOBUFS);
5683949bee8SArchie Cobbs 		}
5693949bee8SArchie Cobbs 		return ng_ppp_output(node, NG_PPP_BUNDLE_LINKNUM, m, meta);
5703949bee8SArchie Cobbs 
5713949bee8SArchie Cobbs 	/* Incoming data */
5723949bee8SArchie Cobbs 	case HOOK_INDEX_DECRYPT:
5733949bee8SArchie Cobbs 	case HOOK_INDEX_DECOMPRESS:
5743949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_IP:
5753949bee8SArchie Cobbs 		return ng_ppp_input(node, NG_PPP_BUNDLE_LINKNUM, m, meta);
5763949bee8SArchie Cobbs 	}
5773949bee8SArchie Cobbs 
5783949bee8SArchie Cobbs 	/* Send packet out hook */
5793949bee8SArchie Cobbs 	NG_SEND_DATA(error, outHook, m, meta);
5804cf49a43SJulian Elischer 	return (error);
5814cf49a43SJulian Elischer }
5824cf49a43SJulian Elischer 
5834cf49a43SJulian Elischer /*
5844cf49a43SJulian Elischer  * Destroy node
5854cf49a43SJulian Elischer  */
5864cf49a43SJulian Elischer static int
5874cf49a43SJulian Elischer ng_ppp_rmnode(node_p node)
5884cf49a43SJulian Elischer {
5894cf49a43SJulian Elischer 	const priv_p priv = node->private;
5904cf49a43SJulian Elischer 
5914cf49a43SJulian Elischer 	/* Take down netgraph node */
5924cf49a43SJulian Elischer 	node->flags |= NG_INVALID;
5934cf49a43SJulian Elischer 	ng_cutlinks(node);
5944cf49a43SJulian Elischer 	ng_unname(node);
5954cf49a43SJulian Elischer 	bzero(priv, sizeof(*priv));
5964cf49a43SJulian Elischer 	FREE(priv, M_NETGRAPH);
5974cf49a43SJulian Elischer 	node->private = NULL;
5984cf49a43SJulian Elischer 	ng_unref(node);		/* let the node escape */
5994cf49a43SJulian Elischer 	return (0);
6004cf49a43SJulian Elischer }
6014cf49a43SJulian Elischer 
6024cf49a43SJulian Elischer /*
6034cf49a43SJulian Elischer  * Hook disconnection
6044cf49a43SJulian Elischer  */
6054cf49a43SJulian Elischer static int
6064cf49a43SJulian Elischer ng_ppp_disconnect(hook_p hook)
6074cf49a43SJulian Elischer {
6084cf49a43SJulian Elischer 	if (hook->node->numhooks == 0)
6094cf49a43SJulian Elischer 		ng_rmnode(hook->node);
6104cf49a43SJulian Elischer 	return (0);
6114cf49a43SJulian Elischer }
6124cf49a43SJulian Elischer 
6134cf49a43SJulian Elischer /************************************************************************
6144cf49a43SJulian Elischer 			HELPER STUFF
6154cf49a43SJulian Elischer  ************************************************************************/
6164cf49a43SJulian Elischer 
6174cf49a43SJulian Elischer /*
6183949bee8SArchie Cobbs  * Handle an incoming frame.  Extract the PPP protocol number
6193949bee8SArchie Cobbs  * and dispatch accordingly.
6204cf49a43SJulian Elischer  */
6214cf49a43SJulian Elischer static int
6223949bee8SArchie Cobbs ng_ppp_input(node_p node, int linkNum, struct mbuf *m, meta_p meta)
6234cf49a43SJulian Elischer {
6243949bee8SArchie Cobbs 	const priv_p priv = node->private;
6253949bee8SArchie Cobbs 	hook_p outHook = NULL;
6263949bee8SArchie Cobbs 	int proto, error;
6274cf49a43SJulian Elischer 
6283949bee8SArchie Cobbs 	/* Extract protocol number */
6293949bee8SArchie Cobbs 	for (proto = 0; !PROT_VALID(proto) && m->m_pkthdr.len > 0; ) {
6303949bee8SArchie Cobbs 		if (m->m_len < 1 && (m = m_pullup(m, 1)) == NULL) {
6313949bee8SArchie Cobbs 			NG_FREE_META(meta);
6323949bee8SArchie Cobbs 			return (ENOBUFS);
6334cf49a43SJulian Elischer 		}
6343949bee8SArchie Cobbs 		proto = (proto << 8) + *mtod(m, u_char *);
6353949bee8SArchie Cobbs 		m_adj(m, 1);
6363949bee8SArchie Cobbs 	}
6373949bee8SArchie Cobbs 	if (!PROT_VALID(proto)) {
6383949bee8SArchie Cobbs 		if (linkNum == NG_PPP_BUNDLE_LINKNUM)
6393949bee8SArchie Cobbs 			priv->bundleStats.badProtos++;
6403949bee8SArchie Cobbs 		else
6413949bee8SArchie Cobbs 			priv->linkStats[linkNum].badProtos++;
6423949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
6433949bee8SArchie Cobbs 		return (EINVAL);
6443949bee8SArchie Cobbs 	}
6453949bee8SArchie Cobbs 
6463949bee8SArchie Cobbs 	/* Check protocol */
6473949bee8SArchie Cobbs 	switch (proto) {
6483949bee8SArchie Cobbs 	case PROT_COMPD:
6493949bee8SArchie Cobbs 		if (priv->conf.enableDecompression)
6503949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_DECOMPRESS];
6513949bee8SArchie Cobbs 		break;
6523949bee8SArchie Cobbs 	case PROT_CRYPTD:
6533949bee8SArchie Cobbs 		if (priv->conf.enableDecryption)
6543949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_DECRYPT];
6553949bee8SArchie Cobbs 		break;
6563949bee8SArchie Cobbs 	case PROT_VJCOMP:
6573949bee8SArchie Cobbs 		if (priv->conf.enableVJDecompression && priv->vjCompHooked)
6583949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_COMP];
6593949bee8SArchie Cobbs 		break;
6603949bee8SArchie Cobbs 	case PROT_VJUNCOMP:
6613949bee8SArchie Cobbs 		if (priv->conf.enableVJDecompression && priv->vjCompHooked)
6623949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_UNCOMP];
6633949bee8SArchie Cobbs 		break;
6643949bee8SArchie Cobbs 	case PROT_MP:
6653949bee8SArchie Cobbs 		if (priv->conf.enableMultilink) {
6663949bee8SArchie Cobbs 			NG_FREE_META(meta);
6673949bee8SArchie Cobbs 			return ng_ppp_mp_input(node, linkNum, m, meta);
6683949bee8SArchie Cobbs 		}
6693949bee8SArchie Cobbs 		break;
6703949bee8SArchie Cobbs 	case PROT_APPLETALK:
6713949bee8SArchie Cobbs 		if (priv->conf.enableAtalk)
6723949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_ATALK];
6733949bee8SArchie Cobbs 		break;
6743949bee8SArchie Cobbs 	case PROT_IPX:
6753949bee8SArchie Cobbs 		if (priv->conf.enableIPX)
6763949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_IPX];
6773949bee8SArchie Cobbs 		break;
6783949bee8SArchie Cobbs 	case PROT_IP:
6793949bee8SArchie Cobbs 		if (priv->conf.enableIP)
6803949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_INET];
6813949bee8SArchie Cobbs 		break;
6823949bee8SArchie Cobbs 	}
6833949bee8SArchie Cobbs 
6843949bee8SArchie Cobbs 	/* For unknown/inactive protocols, forward out the bypass hook */
6853949bee8SArchie Cobbs 	if (outHook == NULL) {
6863949bee8SArchie Cobbs 		M_PREPEND(m, 4, M_NOWAIT);
6873949bee8SArchie Cobbs 		if (m == NULL || (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL))
6883949bee8SArchie Cobbs 			return (ENOBUFS);
6893949bee8SArchie Cobbs 		mtod(m, u_int16_t *)[0] = htons(linkNum);
6903949bee8SArchie Cobbs 		mtod(m, u_int16_t *)[1] = htons(proto);
6913949bee8SArchie Cobbs 		outHook = priv->hooks[HOOK_INDEX_BYPASS];
6923949bee8SArchie Cobbs 	}
6933949bee8SArchie Cobbs 
6943949bee8SArchie Cobbs 	/* Forward frame */
6953949bee8SArchie Cobbs 	NG_SEND_DATA(error, outHook, m, meta);
6963949bee8SArchie Cobbs 	return (error);
6974cf49a43SJulian Elischer }
6984cf49a43SJulian Elischer 
6994cf49a43SJulian Elischer /*
7003949bee8SArchie Cobbs  * Deliver a frame out a link, either a real one or NG_PPP_BUNDLE_LINKNUM
7014cf49a43SJulian Elischer  */
7023949bee8SArchie Cobbs static int
7033949bee8SArchie Cobbs ng_ppp_output(node_p node, int linkNum, struct mbuf *m, meta_p meta)
7044cf49a43SJulian Elischer {
7053949bee8SArchie Cobbs 	const priv_p priv = node->private;
7063949bee8SArchie Cobbs 	int error;
7074cf49a43SJulian Elischer 
7083949bee8SArchie Cobbs 	/* Check for bundle virtual link */
7093949bee8SArchie Cobbs 	if (linkNum == NG_PPP_BUNDLE_LINKNUM) {
7103949bee8SArchie Cobbs 		if (priv->conf.enableMultilink)
7113949bee8SArchie Cobbs 			return ng_ppp_mp_output(node, m, meta);
7123949bee8SArchie Cobbs 		linkNum = priv->activeLinks[0];
7134cf49a43SJulian Elischer 	}
7143949bee8SArchie Cobbs 
7153949bee8SArchie Cobbs 	/* Check link status */
7163949bee8SArchie Cobbs 	if (!priv->conf.links[linkNum].enableLink)
7173949bee8SArchie Cobbs 		return (ENXIO);
7183949bee8SArchie Cobbs 	if (priv->links[linkNum] == NULL) {
7193949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
7203949bee8SArchie Cobbs 		return (ENETDOWN);
7213949bee8SArchie Cobbs 	}
7223949bee8SArchie Cobbs 
7233949bee8SArchie Cobbs 	/* Deliver frame */
7243949bee8SArchie Cobbs 	NG_SEND_DATA(error, priv->links[linkNum], m, meta);
725fb1fc8abSArchie Cobbs 
726fb1fc8abSArchie Cobbs 	/* Update stats and 'bytes in queue' counter */
727fb1fc8abSArchie Cobbs 	if (error == 0) {
728fb1fc8abSArchie Cobbs 		priv->linkStats[linkNum].xmitFrames++;
729fb1fc8abSArchie Cobbs 		priv->linkStats[linkNum].xmitOctets += m->m_pkthdr.len;
730fb1fc8abSArchie Cobbs 		priv->qstat[linkNum].bytesInQueue += m->m_pkthdr.len;
731fb1fc8abSArchie Cobbs 		microtime(&priv->qstat[linkNum].lastWrite);
732fb1fc8abSArchie Cobbs 	}
7333949bee8SArchie Cobbs 	return error;
7343949bee8SArchie Cobbs }
7353949bee8SArchie Cobbs 
7363949bee8SArchie Cobbs /*
7373949bee8SArchie Cobbs  * Handle an incoming multi-link fragment
7383949bee8SArchie Cobbs  */
7393949bee8SArchie Cobbs static int
7403949bee8SArchie Cobbs ng_ppp_mp_input(node_p node, int linkNum, struct mbuf *m, meta_p meta)
7413949bee8SArchie Cobbs {
7423949bee8SArchie Cobbs 	const priv_p priv = node->private;
7433949bee8SArchie Cobbs 	struct ng_ppp_frag frag0, *frag = &frag0;
7443949bee8SArchie Cobbs 	struct ng_ppp_frag *qent, *qnext;
7453949bee8SArchie Cobbs 	struct ng_ppp_frag *first = NULL, *last = NULL;
7463949bee8SArchie Cobbs 	int diff, highSeq, nextSeq;
7473949bee8SArchie Cobbs 	struct mbuf *tail;
7483949bee8SArchie Cobbs 
7493949bee8SArchie Cobbs 	/* Extract fragment information from MP header */
7503949bee8SArchie Cobbs 	if (priv->conf.recvShortSeq) {
7513949bee8SArchie Cobbs 		u_int16_t shdr;
7523949bee8SArchie Cobbs 
7533949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 2) {
7543949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
7553949bee8SArchie Cobbs 			return (EINVAL);
7563949bee8SArchie Cobbs 		}
7573949bee8SArchie Cobbs 		if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) {
7583949bee8SArchie Cobbs 			NG_FREE_META(meta);
7593949bee8SArchie Cobbs 			return (ENOBUFS);
7603949bee8SArchie Cobbs 		}
7613949bee8SArchie Cobbs 		shdr = ntohs(*mtod(m, u_int16_t *));
7623949bee8SArchie Cobbs 		frag->seq = shdr & MP_SHORT_SEQ_MASK;
7633949bee8SArchie Cobbs 		frag->first = (shdr & MP_SHORT_FIRST_FLAG) != 0;
7643949bee8SArchie Cobbs 		frag->last = (shdr & MP_SHORT_LAST_FLAG) != 0;
7653949bee8SArchie Cobbs 		highSeq = CIRCLEQ_EMPTY(&priv->frags) ?
7663949bee8SArchie Cobbs 		    frag->seq : CIRCLEQ_FIRST(&priv->frags)->seq;
7673949bee8SArchie Cobbs 		diff = MP_SHORT_SEQ_DIFF(frag->seq, highSeq);
7683949bee8SArchie Cobbs 		m_adj(m, 2);
7693949bee8SArchie Cobbs 	} else {
7703949bee8SArchie Cobbs 		u_int32_t lhdr;
7713949bee8SArchie Cobbs 
7723949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 4) {
7733949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
7743949bee8SArchie Cobbs 			return (EINVAL);
7753949bee8SArchie Cobbs 		}
7763949bee8SArchie Cobbs 		if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
7773949bee8SArchie Cobbs 			NG_FREE_META(meta);
7783949bee8SArchie Cobbs 			return (ENOBUFS);
7793949bee8SArchie Cobbs 		}
7803949bee8SArchie Cobbs 		lhdr = ntohl(*mtod(m, u_int32_t *));
7813949bee8SArchie Cobbs 		frag->seq = lhdr & MP_LONG_SEQ_MASK;
7823949bee8SArchie Cobbs 		frag->first = (lhdr & MP_LONG_FIRST_FLAG) != 0;
7833949bee8SArchie Cobbs 		frag->last = (lhdr & MP_LONG_LAST_FLAG) != 0;
7843949bee8SArchie Cobbs 		highSeq = CIRCLEQ_EMPTY(&priv->frags) ?
7853949bee8SArchie Cobbs 		    frag->seq : CIRCLEQ_FIRST(&priv->frags)->seq;
7863949bee8SArchie Cobbs 		diff = MP_LONG_SEQ_DIFF(frag->seq, highSeq);
7873949bee8SArchie Cobbs 		m_adj(m, 4);
7883949bee8SArchie Cobbs 	}
7893949bee8SArchie Cobbs 	frag->data = m;
7903949bee8SArchie Cobbs 	frag->meta = meta;
7913949bee8SArchie Cobbs 
7923949bee8SArchie Cobbs 	/* If the sequence number makes a large jump, empty the queue */
7933949bee8SArchie Cobbs 	if (diff <= -MP_INSANE_SEQ_JUMP || diff >= MP_INSANE_SEQ_JUMP)
7943949bee8SArchie Cobbs 		ng_ppp_free_frags(node);
7953949bee8SArchie Cobbs 
7963949bee8SArchie Cobbs 	/* Optimization: handle a frame that's all in one fragment */
7973949bee8SArchie Cobbs 	if (frag->first && frag->last)
7983949bee8SArchie Cobbs 		return ng_ppp_input(node, NG_PPP_BUNDLE_LINKNUM, m, meta);
7993949bee8SArchie Cobbs 
8003949bee8SArchie Cobbs 	/* Allocate a new frag struct for the queue */
8013949bee8SArchie Cobbs 	MALLOC(frag, struct ng_ppp_frag *, sizeof(*frag), M_NETGRAPH, M_NOWAIT);
8023949bee8SArchie Cobbs 	if (frag == NULL) {
8033949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
8043949bee8SArchie Cobbs 		return (ENOMEM);
8053949bee8SArchie Cobbs 	}
8063949bee8SArchie Cobbs 	*frag = frag0;
8073949bee8SArchie Cobbs 	meta = NULL;
8083949bee8SArchie Cobbs 	m = NULL;
8093949bee8SArchie Cobbs 
8103949bee8SArchie Cobbs 	/* Add fragment to queue, which is reverse sorted by sequence number */
8113949bee8SArchie Cobbs 	CIRCLEQ_FOREACH(qent, &priv->frags, f_qent) {
8123949bee8SArchie Cobbs 		diff = MP_SEQ_DIFF(frag->seq, qent->seq);
8133949bee8SArchie Cobbs 		if (diff > 0) {
8143949bee8SArchie Cobbs 			CIRCLEQ_INSERT_BEFORE(&priv->frags, qent, frag, f_qent);
8153949bee8SArchie Cobbs 			break;
8163949bee8SArchie Cobbs 		} else if (diff == 0) {	     /* should never happen! */
8173949bee8SArchie Cobbs 			log(LOG_ERR, "%s: rec'd dup MP fragment\n", node->name);
8183949bee8SArchie Cobbs 			if (linkNum == NG_PPP_BUNDLE_LINKNUM)
8193949bee8SArchie Cobbs 				priv->linkStats[linkNum].dupFragments++;
8203949bee8SArchie Cobbs 			else
8213949bee8SArchie Cobbs 				priv->bundleStats.dupFragments++;
8223949bee8SArchie Cobbs 			NG_FREE_DATA(frag->data, frag->meta);
8233949bee8SArchie Cobbs 			FREE(frag, M_NETGRAPH);
8243949bee8SArchie Cobbs 			return (EINVAL);
8253949bee8SArchie Cobbs 		}
8263949bee8SArchie Cobbs 	}
8273949bee8SArchie Cobbs 	if (qent == NULL)
8283949bee8SArchie Cobbs 		CIRCLEQ_INSERT_TAIL(&priv->frags, frag, f_qent);
8293949bee8SArchie Cobbs 
8303949bee8SArchie Cobbs 	/* Find the first fragment in the possibly newly completed frame */
8313949bee8SArchie Cobbs 	for (nextSeq = frag->seq, qent = frag;
8323949bee8SArchie Cobbs 	    qent != (void *) &priv->frags;
8333949bee8SArchie Cobbs 	    qent = CIRCLEQ_PREV(qent, f_qent)) {
8343949bee8SArchie Cobbs 		if (qent->seq != nextSeq)
8353949bee8SArchie Cobbs 			goto pruneQueue;
8363949bee8SArchie Cobbs 		if (qent->first) {
8373949bee8SArchie Cobbs 			first = qent;
8383949bee8SArchie Cobbs 			break;
8393949bee8SArchie Cobbs 		}
8403949bee8SArchie Cobbs 		nextSeq = (nextSeq + 1) & MP_SEQ_MASK;
8413949bee8SArchie Cobbs 	}
8423949bee8SArchie Cobbs 
8433949bee8SArchie Cobbs 	/* Find the last fragment in the possibly newly completed frame */
8443949bee8SArchie Cobbs 	for (nextSeq = frag->seq, qent = frag;
8453949bee8SArchie Cobbs 	    qent != (void *) &priv->frags;
8463949bee8SArchie Cobbs 	    qent = CIRCLEQ_NEXT(qent, f_qent)) {
8473949bee8SArchie Cobbs 		if (qent->seq != nextSeq)
8483949bee8SArchie Cobbs 			goto pruneQueue;
8493949bee8SArchie Cobbs 		if (qent->last) {
8503949bee8SArchie Cobbs 			last = qent;
8513949bee8SArchie Cobbs 			break;
8523949bee8SArchie Cobbs 		}
8533949bee8SArchie Cobbs 		nextSeq = (nextSeq - 1) & MP_SEQ_MASK;
8543949bee8SArchie Cobbs 	}
8553949bee8SArchie Cobbs 
8563949bee8SArchie Cobbs 	/* We have a complete frame, extract it from the queue */
8573949bee8SArchie Cobbs 	for (tail = NULL, qent = first; qent != NULL; qent = qnext) {
8583949bee8SArchie Cobbs 		qnext = CIRCLEQ_PREV(qent, f_qent);
8593949bee8SArchie Cobbs 		CIRCLEQ_REMOVE(&priv->frags, qent, f_qent);
8603949bee8SArchie Cobbs 		if (tail == NULL) {
8613949bee8SArchie Cobbs 			tail = m = qent->data;
8623949bee8SArchie Cobbs 			meta = qent->meta;	/* inherit first frag's meta */
8633949bee8SArchie Cobbs 		} else {
8643949bee8SArchie Cobbs 			m->m_pkthdr.len += qent->data->m_pkthdr.len;
8653949bee8SArchie Cobbs 			tail->m_next = qent->data;
8663949bee8SArchie Cobbs 			NG_FREE_META(qent->meta); /* drop other frag's metas */
8673949bee8SArchie Cobbs 		}
8683949bee8SArchie Cobbs 		while (tail->m_next != NULL)
8693949bee8SArchie Cobbs 			tail = tail->m_next;
8703949bee8SArchie Cobbs 		if (qent == last)
8713949bee8SArchie Cobbs 			qnext = NULL;
8723949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
8733949bee8SArchie Cobbs 	}
8743949bee8SArchie Cobbs 
8753949bee8SArchie Cobbs pruneQueue:
8763949bee8SArchie Cobbs 	/* Prune out stale entries in the queue */
8773949bee8SArchie Cobbs 	for (qent = CIRCLEQ_LAST(&priv->frags);
8783949bee8SArchie Cobbs 	    qent != (void *) &priv->frags; qent = qnext) {
8793949bee8SArchie Cobbs 		if (MP_SEQ_DIFF(highSeq, qent->seq) <= MP_MAX_SEQ_LINGER)
8803949bee8SArchie Cobbs 			break;
8813949bee8SArchie Cobbs 		qnext = CIRCLEQ_PREV(qent, f_qent);
8823949bee8SArchie Cobbs 		CIRCLEQ_REMOVE(&priv->frags, qent, f_qent);
8833949bee8SArchie Cobbs 		NG_FREE_DATA(qent->data, qent->meta);
8843949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
8853949bee8SArchie Cobbs 	}
8863949bee8SArchie Cobbs 
8873949bee8SArchie Cobbs 	/* Deliver newly completed frame, if any */
8883949bee8SArchie Cobbs 	return m ? ng_ppp_input(node, NG_PPP_BUNDLE_LINKNUM, m, meta) : 0;
8893949bee8SArchie Cobbs }
8903949bee8SArchie Cobbs 
8913949bee8SArchie Cobbs /*
8923949bee8SArchie Cobbs  * Deliver a frame out on the bundle, i.e., figure out how to fragment
8933949bee8SArchie Cobbs  * the frame across the individual PPP links and do so.
8943949bee8SArchie Cobbs  */
8953949bee8SArchie Cobbs static int
8963949bee8SArchie Cobbs ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta)
8973949bee8SArchie Cobbs {
8983949bee8SArchie Cobbs 	const priv_p priv = node->private;
8993949bee8SArchie Cobbs 	int distrib[NG_PPP_MAX_LINKS];
9003949bee8SArchie Cobbs 	int firstFragment;
9013949bee8SArchie Cobbs 	int activeLinkNum;
9023949bee8SArchie Cobbs 
9033949bee8SArchie Cobbs 	/* At least one link must be active */
9043949bee8SArchie Cobbs 	if (priv->numActiveLinks == 0) {
9053949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
9063949bee8SArchie Cobbs 		return (ENETDOWN);
9073949bee8SArchie Cobbs 	}
9083949bee8SArchie Cobbs 
9093949bee8SArchie Cobbs 	/* Round-robin strategy */
9103949bee8SArchie Cobbs 	if (priv->conf.enableRoundRobin || m->m_pkthdr.len < MP_MIN_FRAG_LEN) {
9113949bee8SArchie Cobbs 		activeLinkNum = priv->lastLink++ % priv->numActiveLinks;
9123949bee8SArchie Cobbs 		bzero(&distrib, priv->numActiveLinks * sizeof(distrib[0]));
9133949bee8SArchie Cobbs 		distrib[activeLinkNum] = m->m_pkthdr.len;
9143949bee8SArchie Cobbs 		goto deliver;
9153949bee8SArchie Cobbs 	}
9163949bee8SArchie Cobbs 
9173949bee8SArchie Cobbs 	/* Strategy when all links are equivalent (optimize the common case) */
9183949bee8SArchie Cobbs 	if (priv->allLinksEqual) {
9193949bee8SArchie Cobbs 		const int fraction = m->m_pkthdr.len / priv->numActiveLinks;
9203949bee8SArchie Cobbs 		int i, remain;
9213949bee8SArchie Cobbs 
9223949bee8SArchie Cobbs 		for (i = 0; i < priv->numActiveLinks; i++)
9233949bee8SArchie Cobbs 			distrib[priv->lastLink++ % priv->numActiveLinks]
9243949bee8SArchie Cobbs 			    = fraction;
9253949bee8SArchie Cobbs 		remain = m->m_pkthdr.len - (fraction * priv->numActiveLinks);
9263949bee8SArchie Cobbs 		while (remain > 0) {
9273949bee8SArchie Cobbs 			distrib[priv->lastLink++ % priv->numActiveLinks]++;
9283949bee8SArchie Cobbs 			remain--;
9293949bee8SArchie Cobbs 		}
9303949bee8SArchie Cobbs 		goto deliver;
9313949bee8SArchie Cobbs 	}
9323949bee8SArchie Cobbs 
9333949bee8SArchie Cobbs 	/* Strategy when all links are not equivalent */
9343949bee8SArchie Cobbs 	ng_ppp_mp_strategy(node, m->m_pkthdr.len, distrib);
9353949bee8SArchie Cobbs 
9363949bee8SArchie Cobbs deliver:
9373949bee8SArchie Cobbs 	/* Update stats */
9383949bee8SArchie Cobbs 	priv->bundleStats.xmitFrames++;
9393949bee8SArchie Cobbs 	priv->bundleStats.xmitOctets += m->m_pkthdr.len;
9403949bee8SArchie Cobbs 
9413949bee8SArchie Cobbs 	/* Send alloted portions of frame out on the link(s) */
9423949bee8SArchie Cobbs 	for (firstFragment = 1, activeLinkNum = priv->numActiveLinks - 1;
9433949bee8SArchie Cobbs 	    activeLinkNum >= 0; activeLinkNum--) {
9443949bee8SArchie Cobbs 		const int linkNum = priv->activeLinks[activeLinkNum];
9453949bee8SArchie Cobbs 
9463949bee8SArchie Cobbs 		/* Deliver fragment(s) out the next link */
9473949bee8SArchie Cobbs 		for ( ; distrib[activeLinkNum] > 0; firstFragment = 0) {
9483949bee8SArchie Cobbs 			int len, lastFragment, error;
9493949bee8SArchie Cobbs 			struct mbuf *m2;
9503949bee8SArchie Cobbs 			meta_p meta2;
9513949bee8SArchie Cobbs 
9523949bee8SArchie Cobbs 			/* Calculate fragment length; don't exceed link MTU */
9533949bee8SArchie Cobbs 			len = distrib[activeLinkNum];
9543949bee8SArchie Cobbs 			if (len > priv->conf.links[linkNum].mru)
9553949bee8SArchie Cobbs 				len = priv->conf.links[linkNum].mru;
9563949bee8SArchie Cobbs 			distrib[activeLinkNum] -= len;
9573949bee8SArchie Cobbs 			lastFragment = (len == m->m_pkthdr.len);
9583949bee8SArchie Cobbs 
9593949bee8SArchie Cobbs 			/* Split off next fragment as "m2" */
9603949bee8SArchie Cobbs 			m2 = m;
9613949bee8SArchie Cobbs 			if (!lastFragment) {
9623949bee8SArchie Cobbs 				struct mbuf *n = m_split(m, len, M_NOWAIT);
9633949bee8SArchie Cobbs 
9643949bee8SArchie Cobbs 				if (n == NULL) {
9653949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
9663949bee8SArchie Cobbs 					return (ENOMEM);
9673949bee8SArchie Cobbs 				}
9683949bee8SArchie Cobbs 				m = n;
9693949bee8SArchie Cobbs 			}
9703949bee8SArchie Cobbs 
9713949bee8SArchie Cobbs 			/* Prepend MP header */
9723949bee8SArchie Cobbs 			if (priv->conf.xmitShortSeq) {
9733949bee8SArchie Cobbs 				u_int16_t shdr;
9743949bee8SArchie Cobbs 
9753949bee8SArchie Cobbs 				M_PREPEND(m2, 2, M_NOWAIT);
9763949bee8SArchie Cobbs 				if (m2 == NULL
9773949bee8SArchie Cobbs 				    || (m2->m_len < 2
9783949bee8SArchie Cobbs 				      && (m2 = m_pullup(m2, 2)) == NULL)) {
9793949bee8SArchie Cobbs 					if (!lastFragment)
9803949bee8SArchie Cobbs 						m_freem(m);
9813949bee8SArchie Cobbs 					NG_FREE_META(meta);
9823949bee8SArchie Cobbs 					return (ENOBUFS);
9833949bee8SArchie Cobbs 				}
9843949bee8SArchie Cobbs 				shdr = priv->mpSeqOut;
9853949bee8SArchie Cobbs 				priv->mpSeqOut =
9863949bee8SArchie Cobbs 				    (priv->mpSeqOut + 1) % MP_SHORT_SEQ_MASK;
9873949bee8SArchie Cobbs 				if (firstFragment)
9883949bee8SArchie Cobbs 					shdr |= MP_SHORT_FIRST_FLAG;
9893949bee8SArchie Cobbs 				if (lastFragment)
9903949bee8SArchie Cobbs 					shdr |= MP_SHORT_LAST_FLAG;
9913949bee8SArchie Cobbs 				*mtod(m2, u_int16_t *) = htons(shdr);
9923949bee8SArchie Cobbs 			} else {
9933949bee8SArchie Cobbs 				u_int32_t lhdr;
9943949bee8SArchie Cobbs 
9953949bee8SArchie Cobbs 				M_PREPEND(m2, 4, M_NOWAIT);
9963949bee8SArchie Cobbs 				if (m2 == NULL
9973949bee8SArchie Cobbs 				    || (m2->m_len < 4
9983949bee8SArchie Cobbs 				      && (m2 = m_pullup(m2, 4)) == NULL)) {
9993949bee8SArchie Cobbs 					if (!lastFragment)
10003949bee8SArchie Cobbs 						m_freem(m);
10013949bee8SArchie Cobbs 					NG_FREE_META(meta);
10023949bee8SArchie Cobbs 					return (ENOBUFS);
10033949bee8SArchie Cobbs 				}
10043949bee8SArchie Cobbs 				lhdr = priv->mpSeqOut;
10053949bee8SArchie Cobbs 				priv->mpSeqOut =
10063949bee8SArchie Cobbs 				    (priv->mpSeqOut + 1) % MP_LONG_SEQ_MASK;
10073949bee8SArchie Cobbs 				if (firstFragment)
10083949bee8SArchie Cobbs 					lhdr |= MP_LONG_FIRST_FLAG;
10093949bee8SArchie Cobbs 				if (lastFragment)
10103949bee8SArchie Cobbs 					lhdr |= MP_LONG_LAST_FLAG;
10113949bee8SArchie Cobbs 				*mtod(m2, u_int32_t *) = htonl(lhdr);
10123949bee8SArchie Cobbs 			}
10133949bee8SArchie Cobbs 
10143949bee8SArchie Cobbs 			/* Add MP protocol number */
10153949bee8SArchie Cobbs 			m2 = ng_ppp_addproto(m, PROT_MP,
10163949bee8SArchie Cobbs 			    priv->conf.links[linkNum].enableProtoComp);
10173949bee8SArchie Cobbs 			if (m2 == NULL) {
10183949bee8SArchie Cobbs 				if (!lastFragment)
10193949bee8SArchie Cobbs 					m_freem(m);
10203949bee8SArchie Cobbs 				NG_FREE_META(meta);
10213949bee8SArchie Cobbs 				return (ENOBUFS);
10223949bee8SArchie Cobbs 			}
10233949bee8SArchie Cobbs 
10243949bee8SArchie Cobbs 			/* Copy the meta information, if any */
10253949bee8SArchie Cobbs 			if (meta != NULL && !lastFragment) {
10263949bee8SArchie Cobbs 				MALLOC(meta2, meta_p,
10273949bee8SArchie Cobbs 				    meta->used_len, M_NETGRAPH, M_NOWAIT);
10283949bee8SArchie Cobbs 				if (meta2 == NULL) {
10293949bee8SArchie Cobbs 					m_freem(m2);
10303949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
10313949bee8SArchie Cobbs 					return (ENOMEM);
10323949bee8SArchie Cobbs 				}
10333949bee8SArchie Cobbs 				meta2->allocated_len = meta->used_len;
10343949bee8SArchie Cobbs 				bcopy(meta, meta2, meta->used_len);
10353949bee8SArchie Cobbs 			} else
10363949bee8SArchie Cobbs 				meta2 = meta;
10373949bee8SArchie Cobbs 
10383949bee8SArchie Cobbs 			/* Send fragment */
10393949bee8SArchie Cobbs 			error = ng_ppp_output(node, linkNum, m2, meta2);
10403949bee8SArchie Cobbs 
10413949bee8SArchie Cobbs 			/* Abort for error */
10423949bee8SArchie Cobbs 			if (error != 0) {
10433949bee8SArchie Cobbs 				if (!lastFragment)
10443949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
10453949bee8SArchie Cobbs 				return (error);
10463949bee8SArchie Cobbs 			}
10473949bee8SArchie Cobbs 		}
10483949bee8SArchie Cobbs 	}
10493949bee8SArchie Cobbs 
10503949bee8SArchie Cobbs 	/* Done */
10513949bee8SArchie Cobbs 	return (0);
10523949bee8SArchie Cobbs }
10533949bee8SArchie Cobbs 
10543949bee8SArchie Cobbs /*
10553949bee8SArchie Cobbs  * Computing the optimal fragmentation
10563949bee8SArchie Cobbs  * -----------------------------------
10573949bee8SArchie Cobbs  *
10583949bee8SArchie Cobbs  * This routine tries to compute the optimal fragmentation pattern based
10593949bee8SArchie Cobbs  * on each link's latency, bandwidth, and calculated additional latency.
10603949bee8SArchie Cobbs  * The latter quantity is the additional latency caused by previously
10613949bee8SArchie Cobbs  * written data that has not been transmitted yet.
10623949bee8SArchie Cobbs  *
10633949bee8SArchie Cobbs  * This algorithm is only useful when not all of the links have the
10643949bee8SArchie Cobbs  * same latency and bandwidth values.
10653949bee8SArchie Cobbs  *
10663949bee8SArchie Cobbs  * The essential idea is to make the last bit of each fragment of the
10673949bee8SArchie Cobbs  * frame arrive at the opposite end at the exact same time. This greedy
10683949bee8SArchie Cobbs  * algorithm is optimal, in that no other scheduling could result in any
10693949bee8SArchie Cobbs  * packet arriving any sooner unless packets are delivered out of order.
10703949bee8SArchie Cobbs  *
10713949bee8SArchie Cobbs  * Suppose link i has bandwidth b_i (in tens of bytes per milisecond) and
10723949bee8SArchie Cobbs  * latency l_i (in miliseconds). Consider the function function f_i(t)
10733949bee8SArchie Cobbs  * which is equal to the number of bytes that will have arrived at
10743949bee8SArchie Cobbs  * the peer after t miliseconds if we start writing continuously at
10753949bee8SArchie Cobbs  * time t = 0. Then f_i(t) = b_i * (t - l_i) = ((b_i * t) - (l_i * b_i).
10763949bee8SArchie Cobbs  * That is, f_i(t) is a line with slope b_i and y-intersect -(l_i * b_i).
10773949bee8SArchie Cobbs  * Note that the y-intersect is always <= zero because latency can't be
10783949bee8SArchie Cobbs  * negative.  Note also that really the function is f_i(t) except when
10793949bee8SArchie Cobbs  * f_i(t) is negative, in which case the function is zero.  To take
10803949bee8SArchie Cobbs  * care of this, let Q_i(t) = { if (f_i(t) > 0) return 1; else return 0; }.
10813949bee8SArchie Cobbs  * So the actual number of bytes that will have arrived at the peer after
10823949bee8SArchie Cobbs  * t miliseconds is f_i(t) * Q_i(t).
10833949bee8SArchie Cobbs  *
10843949bee8SArchie Cobbs  * At any given time, each link has some additional latency a_i >= 0
10853949bee8SArchie Cobbs  * due to previously written fragment(s) which are still in the queue.
10863949bee8SArchie Cobbs  * This value is easily computed from the time since last transmission,
10873949bee8SArchie Cobbs  * the previous latency value, the number of bytes written, and the
10883949bee8SArchie Cobbs  * link's bandwidth.
10893949bee8SArchie Cobbs  *
10903949bee8SArchie Cobbs  * Assume that l_i includes any a_i already, and that the links are
10913949bee8SArchie Cobbs  * sorted by latency, so that l_i <= l_{i+1}.
10923949bee8SArchie Cobbs  *
10933949bee8SArchie Cobbs  * Let N be the total number of bytes in the current frame we are sending.
10943949bee8SArchie Cobbs  *
10953949bee8SArchie Cobbs  * Suppose we were to start writing bytes at time t = 0 on all links
10963949bee8SArchie Cobbs  * simultaneously, which is the most we can possibly do.  Then let
10973949bee8SArchie Cobbs  * F(t) be equal to the total number of bytes received by the peer
10983949bee8SArchie Cobbs  * after t miliseconds. Then F(t) = Sum_i (f_i(t) * Q_i(t)).
10993949bee8SArchie Cobbs  *
11003949bee8SArchie Cobbs  * Our goal is simply this: fragment the frame across the links such
11013949bee8SArchie Cobbs  * that the peer is able to reconstruct the completed frame as soon as
11023949bee8SArchie Cobbs  * possible, i.e., at the least possible value of t. Call this value t_0.
11033949bee8SArchie Cobbs  *
11043949bee8SArchie Cobbs  * Then it follows that F(t_0) = N. Our strategy is first to find the value
11053949bee8SArchie Cobbs  * of t_0, and then deduce how many bytes to write to each link.
11063949bee8SArchie Cobbs  *
11073949bee8SArchie Cobbs  * Rewriting F(t_0):
11083949bee8SArchie Cobbs  *
11093949bee8SArchie Cobbs  *   t_0 = ( N + Sum_i ( l_i * b_i * Q_i(t_0) ) ) / Sum_i ( b_i * Q_i(t_0) )
11103949bee8SArchie Cobbs  *
11113949bee8SArchie Cobbs  * Now, we note that Q_i(t) is constant for l_i <= t <= l_{i+1}. t_0 will
11123949bee8SArchie Cobbs  * lie in one of these ranges.  To find it, we just need to find the i such
11133949bee8SArchie Cobbs  * that F(l_i) <= N <= F(l_{i+1}).  Then we compute all the constant values
11143949bee8SArchie Cobbs  * for Q_i() in this range, plug in the remaining values, solving for t_0.
11153949bee8SArchie Cobbs  *
11163949bee8SArchie Cobbs  * Once t_0 is known, then the number of bytes to send on link i is
11173949bee8SArchie Cobbs  * just f_i(t_0) * Q_i(t_0).
11183949bee8SArchie Cobbs  *
11193949bee8SArchie Cobbs  * In other words, we start allocating bytes to the links one at a time.
11203949bee8SArchie Cobbs  * We keep adding links until the frame is completely sent.  Some links
11213949bee8SArchie Cobbs  * may not get any bytes because their latency is too high.
11223949bee8SArchie Cobbs  *
11233949bee8SArchie Cobbs  * Is all this work really worth the trouble?  Depends on the situation.
11243949bee8SArchie Cobbs  * The bigger the ratio of computer speed to link speed, and the more
11253949bee8SArchie Cobbs  * important total bundle latency is (e.g., for interactive response time),
11263949bee8SArchie Cobbs  * the more it's worth it.  There is however the cost of calling this
11273949bee8SArchie Cobbs  * function for every frame.  The running time is O(n^2) where n is the
11283949bee8SArchie Cobbs  * number of links that receive a non-zero number of bytes.
11293949bee8SArchie Cobbs  *
11303949bee8SArchie Cobbs  * Since latency is measured in miliseconds, the "resolution" of this
11313949bee8SArchie Cobbs  * algorithm is one milisecond.
11323949bee8SArchie Cobbs  *
11333949bee8SArchie Cobbs  * To avoid this algorithm altogether, configure all links to have the
11343949bee8SArchie Cobbs  * same latency and bandwidth.
11353949bee8SArchie Cobbs  */
11363949bee8SArchie Cobbs static void
11373949bee8SArchie Cobbs ng_ppp_mp_strategy(node_p node, int len, int *distrib)
11383949bee8SArchie Cobbs {
11393949bee8SArchie Cobbs 	const priv_p priv = node->private;
11403949bee8SArchie Cobbs 	int latency[NG_PPP_MAX_LINKS];
11413949bee8SArchie Cobbs 	int sortByLatency[NG_PPP_MAX_LINKS];
11423949bee8SArchie Cobbs 	int activeLinkNum, linkNum;
11433949bee8SArchie Cobbs 	int t0, total, topSum, botSum;
11443949bee8SArchie Cobbs 	struct timeval now;
11453949bee8SArchie Cobbs 	int i, numFragments;
11463949bee8SArchie Cobbs 
11473949bee8SArchie Cobbs 	/* If only one link, this gets real easy */
11483949bee8SArchie Cobbs 	if (priv->numActiveLinks == 1) {
11493949bee8SArchie Cobbs 		distrib[0] = len;
11503949bee8SArchie Cobbs 		return;
11513949bee8SArchie Cobbs 	}
11523949bee8SArchie Cobbs 
11533949bee8SArchie Cobbs 	/* Get current time */
1154fb1fc8abSArchie Cobbs 	microtime(&now);
11553949bee8SArchie Cobbs 
11563949bee8SArchie Cobbs 	/* Compute latencies for each link at this point in time */
11573949bee8SArchie Cobbs 	for (activeLinkNum = 0;
11583949bee8SArchie Cobbs 	    activeLinkNum < priv->numActiveLinks; activeLinkNum++) {
11593949bee8SArchie Cobbs 		struct timeval diff;
11603949bee8SArchie Cobbs 		int xmitBytes;
11613949bee8SArchie Cobbs 
11623949bee8SArchie Cobbs 		/* Start with base latency value */
11633949bee8SArchie Cobbs 		linkNum = priv->activeLinks[activeLinkNum];
11643949bee8SArchie Cobbs 		latency[activeLinkNum] = priv->conf.links[linkNum].latency;
11653949bee8SArchie Cobbs 		sortByLatency[activeLinkNum] = activeLinkNum;	/* see below */
11663949bee8SArchie Cobbs 
11673949bee8SArchie Cobbs 		/* Any additional latency? */
11683949bee8SArchie Cobbs 		if (priv->qstat[activeLinkNum].bytesInQueue == 0)
11693949bee8SArchie Cobbs 			continue;
11703949bee8SArchie Cobbs 
11713949bee8SArchie Cobbs 		/* Compute time delta since last write */
11723949bee8SArchie Cobbs 		diff = now;
11733949bee8SArchie Cobbs 		timevalsub(&diff, &priv->qstat[activeLinkNum].lastWrite);
11743949bee8SArchie Cobbs 		if (now.tv_sec < 0 || diff.tv_sec >= 10) {	/* sanity */
11753949bee8SArchie Cobbs 			priv->qstat[activeLinkNum].bytesInQueue = 0;
11763949bee8SArchie Cobbs 			continue;
11773949bee8SArchie Cobbs 		}
11783949bee8SArchie Cobbs 
11793949bee8SArchie Cobbs 		/* How many bytes could have transmitted since last write? */
11803949bee8SArchie Cobbs 		xmitBytes = priv->conf.links[linkNum].bandwidth * diff.tv_sec
11813949bee8SArchie Cobbs 		    + (priv->conf.links[linkNum].bandwidth
11823949bee8SArchie Cobbs 			* (diff.tv_usec / 1000)) / 100;
11833949bee8SArchie Cobbs 		priv->qstat[activeLinkNum].bytesInQueue -= xmitBytes;
11843949bee8SArchie Cobbs 		if (priv->qstat[activeLinkNum].bytesInQueue < 0)
11853949bee8SArchie Cobbs 			priv->qstat[activeLinkNum].bytesInQueue = 0;
11863949bee8SArchie Cobbs 		else
11873949bee8SArchie Cobbs 			latency[activeLinkNum] +=
11883949bee8SArchie Cobbs 			    (100 * priv->qstat[activeLinkNum].bytesInQueue)
11893949bee8SArchie Cobbs 				/ priv->conf.links[linkNum].bandwidth;
11903949bee8SArchie Cobbs 	}
11913949bee8SArchie Cobbs 
11923949bee8SArchie Cobbs 	/* Sort links by latency */
11933949bee8SArchie Cobbs 	compareLatencies = latency;
11943949bee8SArchie Cobbs 	qsort(sortByLatency,
11953949bee8SArchie Cobbs 	    priv->numActiveLinks, sizeof(*sortByLatency), ng_ppp_intcmp);
11963949bee8SArchie Cobbs 	compareLatencies = NULL;
11973949bee8SArchie Cobbs 
11983949bee8SArchie Cobbs 	/* Find the interval we need (add links in sortByLatency[] order) */
11993949bee8SArchie Cobbs 	for (numFragments = 1;
12003949bee8SArchie Cobbs 	    numFragments < priv->numActiveLinks; numFragments++) {
12013949bee8SArchie Cobbs 		for (total = i = 0; i < numFragments; i++) {
12023949bee8SArchie Cobbs 			int flowTime;
12033949bee8SArchie Cobbs 
12043949bee8SArchie Cobbs 			flowTime = latency[sortByLatency[numFragments]]
12053949bee8SArchie Cobbs 			    - latency[sortByLatency[i]];
12063949bee8SArchie Cobbs 			total += ((flowTime * priv->conf.links[
12073949bee8SArchie Cobbs 			    priv->activeLinks[sortByLatency[i]]].bandwidth)
12083949bee8SArchie Cobbs 			    	+ 99) / 100;
12093949bee8SArchie Cobbs 		}
12103949bee8SArchie Cobbs 		if (total >= len)
12113949bee8SArchie Cobbs 			break;
12123949bee8SArchie Cobbs 	}
12133949bee8SArchie Cobbs 
12143949bee8SArchie Cobbs 	/* Solve for t_0 in that interval */
12153949bee8SArchie Cobbs 	for (topSum = botSum = i = 0; i < numFragments; i++) {
12163949bee8SArchie Cobbs 		int bw = priv->conf.links[
12173949bee8SArchie Cobbs 		    priv->activeLinks[sortByLatency[i]]].bandwidth;
12183949bee8SArchie Cobbs 
12193949bee8SArchie Cobbs 		topSum += latency[sortByLatency[i]] * bw;	/* / 100 */
12203949bee8SArchie Cobbs 		botSum += bw;					/* / 100 */
12213949bee8SArchie Cobbs 	}
12223949bee8SArchie Cobbs 	t0 = ((len * 100) + topSum + botSum / 2) / botSum;
12233949bee8SArchie Cobbs 
12243949bee8SArchie Cobbs 	/* Compute f_i(t_0) all i */
12253949bee8SArchie Cobbs 	bzero(distrib, priv->numActiveLinks * sizeof(*distrib));
12263949bee8SArchie Cobbs 	for (total = i = 0; i < numFragments; i++) {
12273949bee8SArchie Cobbs 		int bw = priv->conf.links[
12283949bee8SArchie Cobbs 		    priv->activeLinks[sortByLatency[i]]].bandwidth;
12293949bee8SArchie Cobbs 
12303949bee8SArchie Cobbs 		distrib[sortByLatency[i]] =
12313949bee8SArchie Cobbs 		    (bw * (t0 - latency[sortByLatency[i]]) + 50) / 100;
12323949bee8SArchie Cobbs 		total += distrib[sortByLatency[i]];
12333949bee8SArchie Cobbs 	}
12343949bee8SArchie Cobbs 
1235fb1fc8abSArchie Cobbs 	/* Deal with any rounding error */
1236fb1fc8abSArchie Cobbs 	if (total < len) {
12373949bee8SArchie Cobbs 		int fast = 0;
12383949bee8SArchie Cobbs 
1239fb1fc8abSArchie Cobbs 		/* Find the fastest link */
12403949bee8SArchie Cobbs 		for (i = 1; i < numFragments; i++) {
12413949bee8SArchie Cobbs 			if (priv->conf.links[
12423949bee8SArchie Cobbs 			      priv->activeLinks[sortByLatency[i]]].bandwidth >
12433949bee8SArchie Cobbs 			    priv->conf.links[
12443949bee8SArchie Cobbs 			      priv->activeLinks[sortByLatency[fast]]].bandwidth)
12453949bee8SArchie Cobbs 				fast = i;
12463949bee8SArchie Cobbs 		}
12473949bee8SArchie Cobbs 		distrib[sortByLatency[fast]] += len - total;
1248fb1fc8abSArchie Cobbs 	} else while (total > len) {
1249fb1fc8abSArchie Cobbs 		int delta, slow = 0;
12503949bee8SArchie Cobbs 
1251fb1fc8abSArchie Cobbs 		/* Find the slowest link that still has bytes to remove */
1252fb1fc8abSArchie Cobbs 		for (i = 1; i < numFragments; i++) {
1253fb1fc8abSArchie Cobbs 			if (distrib[sortByLatency[slow]] == 0
1254fb1fc8abSArchie Cobbs 			  || (distrib[sortByLatency[i]] > 0
1255fb1fc8abSArchie Cobbs 			    && priv->conf.links[priv->activeLinks[
1256fb1fc8abSArchie Cobbs 					sortByLatency[i]]].bandwidth <
1257fb1fc8abSArchie Cobbs 			      priv->conf.links[priv->activeLinks[
1258fb1fc8abSArchie Cobbs 					sortByLatency[slow]]].bandwidth))
1259fb1fc8abSArchie Cobbs 				slow = i;
1260fb1fc8abSArchie Cobbs 		}
1261fb1fc8abSArchie Cobbs 		delta = total - len;
1262fb1fc8abSArchie Cobbs 		if (delta > distrib[sortByLatency[slow]])
1263fb1fc8abSArchie Cobbs 			delta = distrib[sortByLatency[slow]];
1264fb1fc8abSArchie Cobbs 		distrib[sortByLatency[slow]] -= delta;
1265fb1fc8abSArchie Cobbs 		total -= delta;
12663949bee8SArchie Cobbs 	}
12673949bee8SArchie Cobbs }
12683949bee8SArchie Cobbs 
12693949bee8SArchie Cobbs /*
12703949bee8SArchie Cobbs  * Compare two integers
12713949bee8SArchie Cobbs  */
12723949bee8SArchie Cobbs static int
12733949bee8SArchie Cobbs ng_ppp_intcmp(const void *v1, const void *v2)
12743949bee8SArchie Cobbs {
12753949bee8SArchie Cobbs 	const int index1 = *((const int *) v1);
12763949bee8SArchie Cobbs 	const int index2 = *((const int *) v2);
12773949bee8SArchie Cobbs 
12783949bee8SArchie Cobbs 	return compareLatencies[index1] - compareLatencies[index2];
12793949bee8SArchie Cobbs }
12803949bee8SArchie Cobbs 
12813949bee8SArchie Cobbs /*
12823949bee8SArchie Cobbs  * Prepend a possibly compressed PPP protocol number in front of a frame
12833949bee8SArchie Cobbs  */
12843949bee8SArchie Cobbs static struct mbuf *
12853949bee8SArchie Cobbs ng_ppp_addproto(struct mbuf *m, int proto, int compOK)
12863949bee8SArchie Cobbs {
12872b70adcbSArchie Cobbs 	int psize = (PROT_COMPRESSABLE(proto) && compOK) ? 1 : 2;
12883949bee8SArchie Cobbs 
12893949bee8SArchie Cobbs 	/* Add protocol number */
12903949bee8SArchie Cobbs 	M_PREPEND(m, psize, M_NOWAIT);
12913949bee8SArchie Cobbs 	if (m == NULL || (m->m_len < psize && (m = m_pullup(m, psize)) == NULL))
12924cf49a43SJulian Elischer 		return (NULL);
12933949bee8SArchie Cobbs 	if (psize == 1)
12943949bee8SArchie Cobbs 		*mtod(m, u_char *) = (u_char)proto;
12953949bee8SArchie Cobbs 	else
12963949bee8SArchie Cobbs 		*mtod(m, u_int16_t *) = htons((u_int16_t)proto);
12973949bee8SArchie Cobbs 	return (m);
12983949bee8SArchie Cobbs }
12993949bee8SArchie Cobbs 
13003949bee8SArchie Cobbs /*
13013949bee8SArchie Cobbs  * Update private information that is derived from other private information
13023949bee8SArchie Cobbs  */
13033949bee8SArchie Cobbs static void
13043949bee8SArchie Cobbs ng_ppp_update(node_p node, int newConf)
13053949bee8SArchie Cobbs {
13063949bee8SArchie Cobbs 	const priv_p priv = node->private;
13073949bee8SArchie Cobbs 	int i;
13083949bee8SArchie Cobbs 
13093949bee8SArchie Cobbs 	/* Update active status for VJ Compression */
13103949bee8SArchie Cobbs 	priv->vjCompHooked = priv->hooks[HOOK_INDEX_VJC_IP] != NULL
13113949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_COMP] != NULL
13123949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_UNCOMP] != NULL
13133949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_VJIP] != NULL;
13143949bee8SArchie Cobbs 
13153949bee8SArchie Cobbs 	/* Increase latency for each link an amount equal to one MP header */
13163949bee8SArchie Cobbs 	if (newConf) {
13173949bee8SArchie Cobbs 		for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
13183949bee8SArchie Cobbs 			int hdrBytes;
13193949bee8SArchie Cobbs 
13203949bee8SArchie Cobbs 			hdrBytes = (priv->conf.links[i].enableProtoComp ? 1 : 2)
13213949bee8SArchie Cobbs 			    + (priv->conf.xmitShortSeq ? 2 : 4);
13223949bee8SArchie Cobbs 			priv->conf.links[i].latency +=
13233949bee8SArchie Cobbs 			    ((hdrBytes * priv->conf.links[i].bandwidth) + 50)
13243949bee8SArchie Cobbs 				/ 100;
13253949bee8SArchie Cobbs 		}
13263949bee8SArchie Cobbs 	}
13273949bee8SArchie Cobbs 
13283949bee8SArchie Cobbs 	/* Update list of active links */
13293949bee8SArchie Cobbs 	bzero(&priv->activeLinks, sizeof(priv->activeLinks));
13303949bee8SArchie Cobbs 	priv->numActiveLinks = 0;
13313949bee8SArchie Cobbs 	priv->allLinksEqual = 1;
13323949bee8SArchie Cobbs 	for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
13333949bee8SArchie Cobbs 		if (priv->conf.links[i].enableLink && priv->links[i] != NULL) {
13343949bee8SArchie Cobbs 			priv->activeLinks[priv->numActiveLinks++] = i;
13353949bee8SArchie Cobbs 			if (priv->conf.links[i].latency
13363949bee8SArchie Cobbs 				  != priv->conf.links[0].latency
13373949bee8SArchie Cobbs 			    || priv->conf.links[i].bandwidth
13383949bee8SArchie Cobbs 				  != priv->conf.links[0].bandwidth)
13393949bee8SArchie Cobbs 				priv->allLinksEqual = 0;
13403949bee8SArchie Cobbs 		}
13413949bee8SArchie Cobbs 	}
13423949bee8SArchie Cobbs 
13433949bee8SArchie Cobbs 	/* Reset MP state if no longer active */
13443949bee8SArchie Cobbs 	if (!priv->conf.enableMultilink || priv->numActiveLinks == 0) {
13453949bee8SArchie Cobbs 		ng_ppp_free_frags(node);
13463949bee8SArchie Cobbs 		priv->mpSeqOut = MP_INITIAL_SEQ;
13473949bee8SArchie Cobbs 		bzero(&priv->qstat, sizeof(priv->qstat));
13483949bee8SArchie Cobbs 	}
13493949bee8SArchie Cobbs }
13503949bee8SArchie Cobbs 
13513949bee8SArchie Cobbs /*
13523949bee8SArchie Cobbs  * Determine if a new configuration would represent a valid change
13533949bee8SArchie Cobbs  * from the current configuration and link activity status.
13543949bee8SArchie Cobbs  */
13553949bee8SArchie Cobbs static int
13563949bee8SArchie Cobbs ng_ppp_config_valid(node_p node, const struct ng_ppp_node_config *newConf)
13573949bee8SArchie Cobbs {
13583949bee8SArchie Cobbs 	const priv_p priv = node->private;
13593949bee8SArchie Cobbs 	int i, newNumLinksActive;
13603949bee8SArchie Cobbs 
13613949bee8SArchie Cobbs 	/* Check per-link config and count how many links would be active */
13623949bee8SArchie Cobbs 	for (newNumLinksActive = i = 0; i < NG_PPP_MAX_LINKS; i++) {
13633949bee8SArchie Cobbs 		if (newConf->links[i].mru < MP_MIN_LINK_MRU)
13643949bee8SArchie Cobbs 			return (0);
13653949bee8SArchie Cobbs 		if (newConf->links[i].bandwidth == 0)
13663949bee8SArchie Cobbs 			return (0);
13673949bee8SArchie Cobbs 		if (newConf->links[i].bandwidth > NG_PPP_MAX_BANDWIDTH)
13683949bee8SArchie Cobbs 			return (0);
13693949bee8SArchie Cobbs 		if (newConf->links[i].latency > NG_PPP_MAX_LATENCY)
13703949bee8SArchie Cobbs 			return (0);
13713949bee8SArchie Cobbs 		if (newConf->links[i].enableLink && priv->links[i] != NULL)
13723949bee8SArchie Cobbs 			newNumLinksActive++;
13733949bee8SArchie Cobbs 	}
13743949bee8SArchie Cobbs 
13753949bee8SArchie Cobbs 	/* Check bundle parameters */
13763949bee8SArchie Cobbs 	if (priv->conf.enableMultilink && newConf->mrru < MP_MIN_MRRU)
13773949bee8SArchie Cobbs 		return (0);
13783949bee8SArchie Cobbs 
13793949bee8SArchie Cobbs 	/* At most one link can be active unless multi-link is enabled */
13803949bee8SArchie Cobbs 	if (!newConf->enableMultilink && newNumLinksActive > 1)
13813949bee8SArchie Cobbs 		return (0);
13823949bee8SArchie Cobbs 
13833949bee8SArchie Cobbs 	/* Disallow changes to multi-link configuration while MP is active */
13843949bee8SArchie Cobbs 	if (priv->numActiveLinks > 0 && newNumLinksActive > 0) {
13853949bee8SArchie Cobbs 		if (!priv->conf.enableMultilink != !newConf->enableMultilink
13863949bee8SArchie Cobbs 		    || !priv->conf.xmitShortSeq != !newConf->xmitShortSeq
13873949bee8SArchie Cobbs 		    || !priv->conf.recvShortSeq != !newConf->recvShortSeq)
13883949bee8SArchie Cobbs 			return (0);
13893949bee8SArchie Cobbs 	}
13903949bee8SArchie Cobbs 
13913949bee8SArchie Cobbs 	/* Configuration change is valid */
13923949bee8SArchie Cobbs 	return (1);
13933949bee8SArchie Cobbs }
13943949bee8SArchie Cobbs 
13953949bee8SArchie Cobbs /*
13963949bee8SArchie Cobbs  * Free all entries in the fragment queue
13973949bee8SArchie Cobbs  */
13983949bee8SArchie Cobbs static void
13993949bee8SArchie Cobbs ng_ppp_free_frags(node_p node)
14003949bee8SArchie Cobbs {
14013949bee8SArchie Cobbs 	const priv_p priv = node->private;
14023949bee8SArchie Cobbs 	struct ng_ppp_frag *qent, *next;
14033949bee8SArchie Cobbs 
14043949bee8SArchie Cobbs 	for (qent = CIRCLEQ_FIRST(&priv->frags);
14053949bee8SArchie Cobbs 	    qent != (void *) &priv->frags; qent = next) {
14063949bee8SArchie Cobbs 		next = CIRCLEQ_NEXT(qent, f_qent);
14073949bee8SArchie Cobbs 		NG_FREE_DATA(qent->data, qent->meta);
14083949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
14093949bee8SArchie Cobbs 	}
14103949bee8SArchie Cobbs 	CIRCLEQ_INIT(&priv->frags);
14114cf49a43SJulian Elischer }
14124cf49a43SJulian Elischer 
1413