xref: /freebsd/sys/netgraph/ng_ppp.c (revision fb1fc8abee42ce78be962950f7cf728449308c71)
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>
564cf49a43SJulian Elischer 
574cf49a43SJulian Elischer #include <netgraph/ng_message.h>
584cf49a43SJulian Elischer #include <netgraph/netgraph.h>
594cf49a43SJulian Elischer #include <netgraph/ng_ppp.h>
603949bee8SArchie Cobbs #include <netgraph/ng_vjc.h>
614cf49a43SJulian Elischer 
624cf49a43SJulian Elischer #define PROT_VALID(p)		(((p) & 0x0101) == 0x0001)
633949bee8SArchie Cobbs #define PROT_COMPRESSIBLE(p)	(((p) & 0xff00) == 0x0000)
644cf49a43SJulian Elischer 
653949bee8SArchie Cobbs /* Some PPP protocol numbers we're interested in */
663949bee8SArchie Cobbs #define PROT_APPLETALK		0x0029
673949bee8SArchie Cobbs #define PROT_COMPD		0x00fd
683949bee8SArchie Cobbs #define PROT_CRYPTD		0x0053
693949bee8SArchie Cobbs #define PROT_IP			0x0021
703949bee8SArchie Cobbs #define PROT_IPX		0x002B
713949bee8SArchie Cobbs #define PROT_MP			0x003d
723949bee8SArchie Cobbs #define PROT_VJCOMP		0x002d
733949bee8SArchie Cobbs #define PROT_VJUNCOMP		0x002f
743949bee8SArchie Cobbs 
753949bee8SArchie Cobbs /* Multilink PPP definitions */
763949bee8SArchie Cobbs #define MP_MIN_MRRU		1500		/* per RFC 1990 */
773949bee8SArchie Cobbs #define MP_INITIAL_SEQ		0		/* per RFC 1990 */
783949bee8SArchie Cobbs #define MP_MIN_LINK_MRU		32
793949bee8SArchie Cobbs 
803949bee8SArchie Cobbs #define MP_MAX_SEQ_LINGER	64		/* max frags we will hold */
813949bee8SArchie Cobbs #define MP_INSANE_SEQ_JUMP	128		/* a sequence # jump too far */
823949bee8SArchie Cobbs #define MP_MIN_FRAG_LEN		6		/* don't frag smaller frames */
833949bee8SArchie Cobbs 
843949bee8SArchie Cobbs #define MP_SHORT_SEQ_MASK	0x00000fff	/* short seq # mask */
853949bee8SArchie Cobbs #define MP_SHORT_SEQ_HIBIT	0x00000800	/* short seq # high bit */
863949bee8SArchie Cobbs #define MP_SHORT_FIRST_FLAG	0x00008000	/* first fragment in frame */
873949bee8SArchie Cobbs #define MP_SHORT_LAST_FLAG	0x00004000	/* last fragment in frame */
883949bee8SArchie Cobbs 
893949bee8SArchie Cobbs #define MP_LONG_SEQ_MASK	0x00ffffff	/* long seq # mask */
903949bee8SArchie Cobbs #define MP_LONG_SEQ_HIBIT	0x00800000	/* long seq # high bit */
913949bee8SArchie Cobbs #define MP_LONG_FIRST_FLAG	0x80000000	/* first fragment in frame */
923949bee8SArchie Cobbs #define MP_LONG_LAST_FLAG	0x40000000	/* last fragment in frame */
933949bee8SArchie Cobbs 
943949bee8SArchie Cobbs #define MP_SEQ_MASK		(priv->conf.recvShortSeq ? \
953949bee8SArchie Cobbs 				    MP_SHORT_SEQ_MASK : MP_LONG_SEQ_MASK)
963949bee8SArchie Cobbs 
973949bee8SArchie Cobbs /* Sign extension of MP sequence numbers */
983949bee8SArchie Cobbs #define MP_SHORT_EXTEND(s)	(((s) & MP_SHORT_SEQ_HIBIT) ? \
993949bee8SArchie Cobbs 				    ((s) | ~MP_SHORT_SEQ_MASK) : (s))
1003949bee8SArchie Cobbs #define MP_LONG_EXTEND(s)	(((s) & MP_LONG_SEQ_HIBIT) ? \
1013949bee8SArchie Cobbs 				    ((s) | ~MP_LONG_SEQ_MASK) : (s))
1023949bee8SArchie Cobbs 
1033949bee8SArchie Cobbs /* Comparision of MP sequence numbers */
1043949bee8SArchie Cobbs #define MP_SHORT_SEQ_DIFF(x,y)	(MP_SHORT_EXTEND(x) - MP_SHORT_EXTEND(y))
1053949bee8SArchie Cobbs #define MP_LONG_SEQ_DIFF(x,y)	(MP_LONG_EXTEND(x) - MP_LONG_EXTEND(y))
1063949bee8SArchie Cobbs 
1073949bee8SArchie Cobbs #define MP_SEQ_DIFF(x,y)	(priv->conf.recvShortSeq ? \
1083949bee8SArchie Cobbs 				    MP_SHORT_SEQ_DIFF((x), (y)) : \
1093949bee8SArchie Cobbs 				    MP_LONG_SEQ_DIFF((x), (y)))
1103949bee8SArchie Cobbs 
1113949bee8SArchie Cobbs /* We store incoming fragments this way */
1123949bee8SArchie Cobbs struct ng_ppp_frag {
1133949bee8SArchie Cobbs 	int				seq;
1143949bee8SArchie Cobbs 	u_char				first;
1153949bee8SArchie Cobbs 	u_char				last;
1163949bee8SArchie Cobbs 	struct mbuf			*data;
1173949bee8SArchie Cobbs 	meta_p				meta;
1183949bee8SArchie Cobbs 	CIRCLEQ_ENTRY(ng_ppp_frag)	f_qent;
1193949bee8SArchie Cobbs };
1203949bee8SArchie Cobbs 
1213949bee8SArchie Cobbs /* We keep track of link queue status this way */
1223949bee8SArchie Cobbs struct ng_ppp_link_qstat {
1233949bee8SArchie Cobbs 	struct timeval		lastWrite;	/* time of last write */
1243949bee8SArchie Cobbs 	int			bytesInQueue;	/* bytes in the queue then */
1253949bee8SArchie Cobbs };
1263949bee8SArchie Cobbs 
1273949bee8SArchie Cobbs /* We use integer indicies to refer to the non-link hooks */
1283949bee8SArchie Cobbs static const char *const ng_ppp_hook_names[] = {
1293949bee8SArchie Cobbs 	NG_PPP_HOOK_ATALK,
1303949bee8SArchie Cobbs #define HOOK_INDEX_ATALK		0
1313949bee8SArchie Cobbs 	NG_PPP_HOOK_BYPASS,
1323949bee8SArchie Cobbs #define HOOK_INDEX_BYPASS		1
1333949bee8SArchie Cobbs 	NG_PPP_HOOK_COMPRESS,
1343949bee8SArchie Cobbs #define HOOK_INDEX_COMPRESS		2
1353949bee8SArchie Cobbs 	NG_PPP_HOOK_ENCRYPT,
1363949bee8SArchie Cobbs #define HOOK_INDEX_ENCRYPT		3
1373949bee8SArchie Cobbs 	NG_PPP_HOOK_DECOMPRESS,
1383949bee8SArchie Cobbs #define HOOK_INDEX_DECOMPRESS		4
1393949bee8SArchie Cobbs 	NG_PPP_HOOK_DECRYPT,
1403949bee8SArchie Cobbs #define HOOK_INDEX_DECRYPT		5
1413949bee8SArchie Cobbs 	NG_PPP_HOOK_INET,
1423949bee8SArchie Cobbs #define HOOK_INDEX_INET			6
1433949bee8SArchie Cobbs 	NG_PPP_HOOK_IPX,
1443949bee8SArchie Cobbs #define HOOK_INDEX_IPX			7
1453949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_COMP,
1463949bee8SArchie Cobbs #define HOOK_INDEX_VJC_COMP		8
1473949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_IP,
1483949bee8SArchie Cobbs #define HOOK_INDEX_VJC_IP		9
1493949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_UNCOMP,
1503949bee8SArchie Cobbs #define HOOK_INDEX_VJC_UNCOMP		10
1513949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_VJIP,
1523949bee8SArchie Cobbs #define HOOK_INDEX_VJC_VJIP		11
1533949bee8SArchie Cobbs 	NULL
1543949bee8SArchie Cobbs #define HOOK_INDEX_MAX			12
1553949bee8SArchie Cobbs };
1563949bee8SArchie Cobbs 
1573949bee8SArchie Cobbs /* We store index numbers in the hook private pointer. The HOOK_INDEX()
1583949bee8SArchie Cobbs    for a hook is either the index (above) for normal hooks, or the ones
1593949bee8SArchie Cobbs    complement of the link number for link hooks. */
1603949bee8SArchie Cobbs #define HOOK_INDEX(hook)	(*((int16_t *) &(hook)->private))
1614cf49a43SJulian Elischer 
1624cf49a43SJulian Elischer /* Node private data */
1634cf49a43SJulian Elischer struct private {
1643949bee8SArchie Cobbs 	struct ng_ppp_node_config	conf;
1653949bee8SArchie Cobbs 	struct ng_ppp_link_stat		bundleStats;
1663949bee8SArchie Cobbs 	struct ng_ppp_link_stat		linkStats[NG_PPP_MAX_LINKS];
1673949bee8SArchie Cobbs 	hook_p				links[NG_PPP_MAX_LINKS];
1683949bee8SArchie Cobbs 	hook_p				hooks[HOOK_INDEX_MAX];
1693949bee8SArchie Cobbs 	u_char				vjCompHooked;
1703949bee8SArchie Cobbs 	u_char				allLinksEqual;
1713949bee8SArchie Cobbs 	u_short				activeLinks[NG_PPP_MAX_LINKS];
1723949bee8SArchie Cobbs 	u_int				numActiveLinks;
1733949bee8SArchie Cobbs 	u_int				lastLink;	/* for round robin */
1743949bee8SArchie Cobbs 	struct ng_ppp_link_qstat	qstat[NG_PPP_MAX_LINKS];
1753949bee8SArchie Cobbs 	CIRCLEQ_HEAD(ng_ppp_fraglist, ng_ppp_frag)
1763949bee8SArchie Cobbs 					frags;		/* incoming fragments */
1773949bee8SArchie Cobbs 	int				mpSeqOut;	/* next out MP seq # */
1784cf49a43SJulian Elischer };
1794cf49a43SJulian Elischer typedef struct private *priv_p;
1804cf49a43SJulian Elischer 
1814cf49a43SJulian Elischer /* Netgraph node methods */
18274f5c6aaSJulian Elischer static ng_constructor_t	ng_ppp_constructor;
18374f5c6aaSJulian Elischer static ng_rcvmsg_t	ng_ppp_rcvmsg;
18474f5c6aaSJulian Elischer static ng_shutdown_t	ng_ppp_rmnode;
18574f5c6aaSJulian Elischer static ng_newhook_t	ng_ppp_newhook;
18674f5c6aaSJulian Elischer static ng_rcvdata_t	ng_ppp_rcvdata;
18774f5c6aaSJulian Elischer static ng_disconnect_t	ng_ppp_disconnect;
1884cf49a43SJulian Elischer 
1893949bee8SArchie Cobbs /* Helper functions */
1903949bee8SArchie Cobbs static int	ng_ppp_input(node_p node, int ln, struct mbuf *m, meta_p meta);
1913949bee8SArchie Cobbs static int	ng_ppp_output(node_p node, int ln, struct mbuf *m, meta_p meta);
1923949bee8SArchie Cobbs static int	ng_ppp_mp_input(node_p nd, int ln, struct mbuf *m, meta_p meta);
1933949bee8SArchie Cobbs static int	ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta);
1943949bee8SArchie Cobbs static void	ng_ppp_mp_strategy(node_p node, int len, int *distrib);
1953949bee8SArchie Cobbs static int	ng_ppp_intcmp(const void *v1, const void *v2);
1963949bee8SArchie Cobbs static struct	mbuf *ng_ppp_addproto(struct mbuf *m, int proto, int compOK);
1973949bee8SArchie Cobbs static int	ng_ppp_config_valid(node_p node,
1983949bee8SArchie Cobbs 			const struct ng_ppp_node_config *newConf);
1993949bee8SArchie Cobbs static void	ng_ppp_update(node_p node, int newConf);
2003949bee8SArchie Cobbs static void	ng_ppp_free_frags(node_p node);
2014cf49a43SJulian Elischer 
2024cf49a43SJulian Elischer /* Node type descriptor */
2033949bee8SArchie Cobbs static struct ng_type ng_ppp_typestruct = {
2044cf49a43SJulian Elischer 	NG_VERSION,
2054cf49a43SJulian Elischer 	NG_PPP_NODE_TYPE,
2064cf49a43SJulian Elischer 	NULL,
2074cf49a43SJulian Elischer 	ng_ppp_constructor,
2084cf49a43SJulian Elischer 	ng_ppp_rcvmsg,
2094cf49a43SJulian Elischer 	ng_ppp_rmnode,
2104cf49a43SJulian Elischer 	ng_ppp_newhook,
2114cf49a43SJulian Elischer 	NULL,
2124cf49a43SJulian Elischer 	NULL,
2134cf49a43SJulian Elischer 	ng_ppp_rcvdata,
2144cf49a43SJulian Elischer 	ng_ppp_rcvdata,
2154cf49a43SJulian Elischer 	ng_ppp_disconnect
2164cf49a43SJulian Elischer };
2173949bee8SArchie Cobbs NETGRAPH_INIT(ppp, &ng_ppp_typestruct);
2184cf49a43SJulian Elischer 
2193949bee8SArchie Cobbs static int	*compareLatencies;		/* hack for ng_ppp_intcmp() */
2204cf49a43SJulian Elischer 
2214cf49a43SJulian Elischer #define ERROUT(x)	do { error = (x); goto done; } while (0)
2224cf49a43SJulian Elischer 
2234cf49a43SJulian Elischer /************************************************************************
2244cf49a43SJulian Elischer 			NETGRAPH NODE STUFF
2254cf49a43SJulian Elischer  ************************************************************************/
2264cf49a43SJulian Elischer 
2274cf49a43SJulian Elischer /*
2283949bee8SArchie Cobbs  * Node type constructor
2294cf49a43SJulian Elischer  */
2304cf49a43SJulian Elischer static int
2314cf49a43SJulian Elischer ng_ppp_constructor(node_p *nodep)
2324cf49a43SJulian Elischer {
2334cf49a43SJulian Elischer 	priv_p priv;
2344cf49a43SJulian Elischer 	int error;
2354cf49a43SJulian Elischer 
2364cf49a43SJulian Elischer 	/* Allocate private structure */
2374cf49a43SJulian Elischer 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK);
2384cf49a43SJulian Elischer 	if (priv == NULL)
2394cf49a43SJulian Elischer 		return (ENOMEM);
2404cf49a43SJulian Elischer 	bzero(priv, sizeof(*priv));
2414cf49a43SJulian Elischer 
2424cf49a43SJulian Elischer 	/* Call generic node constructor */
2433949bee8SArchie Cobbs 	if ((error = ng_make_node_common(&ng_ppp_typestruct, nodep))) {
2444cf49a43SJulian Elischer 		FREE(priv, M_NETGRAPH);
2454cf49a43SJulian Elischer 		return (error);
2464cf49a43SJulian Elischer 	}
2474cf49a43SJulian Elischer 	(*nodep)->private = priv;
2484cf49a43SJulian Elischer 
2493949bee8SArchie Cobbs 	/* Initialize state */
2503949bee8SArchie Cobbs 	CIRCLEQ_INIT(&priv->frags);
2513949bee8SArchie Cobbs 
2524cf49a43SJulian Elischer 	/* Done */
2534cf49a43SJulian Elischer 	return (0);
2544cf49a43SJulian Elischer }
2554cf49a43SJulian Elischer 
2564cf49a43SJulian Elischer /*
2574cf49a43SJulian Elischer  * Give our OK for a hook to be added
2584cf49a43SJulian Elischer  */
2594cf49a43SJulian Elischer static int
2604cf49a43SJulian Elischer ng_ppp_newhook(node_p node, hook_p hook, const char *name)
2614cf49a43SJulian Elischer {
2624cf49a43SJulian Elischer 	const priv_p priv = node->private;
2633949bee8SArchie Cobbs 	int linkNum = -1;
2643949bee8SArchie Cobbs 	hook_p *hookPtr = NULL;
2653949bee8SArchie Cobbs 	int hookIndex = -1;
2664cf49a43SJulian Elischer 
2673949bee8SArchie Cobbs 	/* Figure out which hook it is */
2683949bee8SArchie Cobbs 	if (strncmp(name, NG_PPP_HOOK_LINK_PREFIX,	/* a link hook? */
2693949bee8SArchie Cobbs 	    strlen(NG_PPP_HOOK_LINK_PREFIX)) == 0) {
2703949bee8SArchie Cobbs 		int gotDigit = 0;
2713949bee8SArchie Cobbs 		const char *cp;
2723949bee8SArchie Cobbs 
2733949bee8SArchie Cobbs 		for (cp = name + strlen(NG_PPP_HOOK_LINK_PREFIX);
2743949bee8SArchie Cobbs 		    *cp >= '0' && *cp <= '9'; cp++) {
2753949bee8SArchie Cobbs 			if (!gotDigit) {
2763949bee8SArchie Cobbs 				if (*cp == '0')		/* no leading zeros */
2774cf49a43SJulian Elischer 					return (EINVAL);
2783949bee8SArchie Cobbs 				linkNum = *cp - '0';
2793949bee8SArchie Cobbs 				gotDigit = 1;
2803949bee8SArchie Cobbs 			} else
2813949bee8SArchie Cobbs 				linkNum = (linkNum * 10) + (*cp - '0');
2823949bee8SArchie Cobbs 			if (linkNum >= NG_PPP_MAX_LINKS)
2833949bee8SArchie Cobbs 				return (EINVAL);
2843949bee8SArchie Cobbs 		}
2853949bee8SArchie Cobbs 		if (!gotDigit || *cp != '\0')
2863949bee8SArchie Cobbs 			return (EINVAL);
2873949bee8SArchie Cobbs 		hookPtr = &priv->links[linkNum];
2883949bee8SArchie Cobbs 		hookIndex = ~linkNum;
2893949bee8SArchie Cobbs 	} else {				/* must be a non-link hook */
2903949bee8SArchie Cobbs 		int i;
2914cf49a43SJulian Elischer 
2923949bee8SArchie Cobbs 		for (i = 0; ng_ppp_hook_names[i] != NULL; i++) {
2933949bee8SArchie Cobbs 			if (strcmp(name, ng_ppp_hook_names[i]) == 0) {
2943949bee8SArchie Cobbs 				hookPtr = &priv->hooks[i];
2953949bee8SArchie Cobbs 				hookIndex = i;
2963949bee8SArchie Cobbs 				break;
2973949bee8SArchie Cobbs 			}
2983949bee8SArchie Cobbs 		}
2993949bee8SArchie Cobbs 		if (ng_ppp_hook_names[i] == NULL)
3003949bee8SArchie Cobbs 			return (EINVAL);	/* no such hook */
3013949bee8SArchie Cobbs 	}
3023949bee8SArchie Cobbs 
3033949bee8SArchie Cobbs 	/* See if hook is already connected */
3043949bee8SArchie Cobbs 	if (*hookPtr != NULL)
3054cf49a43SJulian Elischer 		return (EISCONN);
3064cf49a43SJulian Elischer 
3073949bee8SArchie Cobbs 	/* Disallow more than one link unless multilink is enabled */
3083949bee8SArchie Cobbs 	if (linkNum != -1 && priv->conf.links[linkNum].enableLink
3093949bee8SArchie Cobbs 	    && !priv->conf.enableMultilink && priv->numActiveLinks >= 1)
3103949bee8SArchie Cobbs 		return (ENODEV);
3114cf49a43SJulian Elischer 
3124cf49a43SJulian Elischer 	/* OK */
3133949bee8SArchie Cobbs 	*hookPtr = hook;
3143949bee8SArchie Cobbs 	HOOK_INDEX(hook) = hookIndex;
3153949bee8SArchie Cobbs 	ng_ppp_update(node, 0);
3164cf49a43SJulian Elischer 	return (0);
3174cf49a43SJulian Elischer }
3184cf49a43SJulian Elischer 
3194cf49a43SJulian Elischer /*
3204cf49a43SJulian Elischer  * Receive a control message
3214cf49a43SJulian Elischer  */
3224cf49a43SJulian Elischer static int
3234cf49a43SJulian Elischer ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg,
3244cf49a43SJulian Elischer 	      const char *raddr, struct ng_mesg **rptr)
3254cf49a43SJulian Elischer {
3264cf49a43SJulian Elischer 	const priv_p priv = node->private;
3274cf49a43SJulian Elischer 	struct ng_mesg *resp = NULL;
3284cf49a43SJulian Elischer 	int error = 0;
3294cf49a43SJulian Elischer 
3304cf49a43SJulian Elischer 	switch (msg->header.typecookie) {
3314cf49a43SJulian Elischer 	case NGM_PPP_COOKIE:
3324cf49a43SJulian Elischer 		switch (msg->header.cmd) {
3333949bee8SArchie Cobbs 		case NGM_PPP_SET_CONFIG:
3343949bee8SArchie Cobbs 		    {
3353949bee8SArchie Cobbs 			struct ng_ppp_node_config *const newConf =
3363949bee8SArchie Cobbs 				(struct ng_ppp_node_config *) msg->data;
3373949bee8SArchie Cobbs 
3383949bee8SArchie Cobbs 			/* Check for invalid or illegal config */
3393949bee8SArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
3404cf49a43SJulian Elischer 				ERROUT(EINVAL);
3413949bee8SArchie Cobbs 			if (!ng_ppp_config_valid(node, newConf))
3423949bee8SArchie Cobbs 				ERROUT(EINVAL);
3433949bee8SArchie Cobbs 			priv->conf = *newConf;
3443949bee8SArchie Cobbs 			ng_ppp_update(node, 1);
3454cf49a43SJulian Elischer 			break;
3463949bee8SArchie Cobbs 		    }
3473949bee8SArchie Cobbs 		case NGM_PPP_GET_CONFIG:
3483949bee8SArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT);
3494cf49a43SJulian Elischer 			if (resp == NULL)
3504cf49a43SJulian Elischer 				ERROUT(ENOMEM);
3513949bee8SArchie Cobbs 			bcopy(&priv->conf, resp->data, sizeof(priv->conf));
3524cf49a43SJulian Elischer 			break;
3533949bee8SArchie Cobbs 		case NGM_PPP_GET_LINK_STATS:
3543949bee8SArchie Cobbs 		case NGM_PPP_CLR_LINK_STATS:
3553949bee8SArchie Cobbs 		    {
3563949bee8SArchie Cobbs 			struct ng_ppp_link_stat *stats;
3573949bee8SArchie Cobbs 			u_int16_t linkNum;
3583949bee8SArchie Cobbs 
3593949bee8SArchie Cobbs 			if (msg->header.arglen != sizeof(u_int16_t))
3603949bee8SArchie Cobbs 				ERROUT(EINVAL);
3613949bee8SArchie Cobbs 			linkNum = *((u_int16_t *) msg->data);
3623949bee8SArchie Cobbs 			if (linkNum >= NG_PPP_MAX_LINKS
3633949bee8SArchie Cobbs 			    && linkNum != NG_PPP_BUNDLE_LINKNUM)
3643949bee8SArchie Cobbs 				ERROUT(EINVAL);
3653949bee8SArchie Cobbs 			stats = (linkNum == NG_PPP_BUNDLE_LINKNUM) ?
3663949bee8SArchie Cobbs 				&priv->bundleStats : &priv->linkStats[linkNum];
3673949bee8SArchie Cobbs 			if (msg->header.cmd == NGM_PPP_GET_LINK_STATS) {
3683949bee8SArchie Cobbs 				NG_MKRESPONSE(resp, msg,
3693949bee8SArchie Cobbs 				    sizeof(struct ng_ppp_link_stat), M_NOWAIT);
3703949bee8SArchie Cobbs 				if (resp == NULL)
3713949bee8SArchie Cobbs 					ERROUT(ENOMEM);
3723949bee8SArchie Cobbs 				bcopy(stats, resp->data, sizeof(*stats));
3733949bee8SArchie Cobbs 			} else
3743949bee8SArchie Cobbs 				bzero(stats, sizeof(*stats));
3754cf49a43SJulian Elischer 			break;
3763949bee8SArchie Cobbs 		    }
3774cf49a43SJulian Elischer 		default:
3784cf49a43SJulian Elischer 			error = EINVAL;
3794cf49a43SJulian Elischer 			break;
3804cf49a43SJulian Elischer 		}
3814cf49a43SJulian Elischer 		break;
3823949bee8SArchie Cobbs 	case NGM_VJC_COOKIE:
3833949bee8SArchie Cobbs 	   {
3843949bee8SArchie Cobbs 	       char path[NG_PATHLEN + 1];
3853949bee8SArchie Cobbs 	       node_p origNode;
3863949bee8SArchie Cobbs 
3873949bee8SArchie Cobbs 	       if ((error = ng_path2node(node, raddr, &origNode, NULL)) != 0)
3883949bee8SArchie Cobbs 		       ERROUT(error);
3893949bee8SArchie Cobbs 	       snprintf(path, sizeof(path), "[%lx]:%s",
3903949bee8SArchie Cobbs 		   (long) node, NG_PPP_HOOK_VJC_IP);
3913949bee8SArchie Cobbs 	       return ng_send_msg(origNode, msg, path, rptr);
3923949bee8SArchie Cobbs 	       break;
3933949bee8SArchie Cobbs 	   }
3944cf49a43SJulian Elischer 	default:
3954cf49a43SJulian Elischer 		error = EINVAL;
3964cf49a43SJulian Elischer 		break;
3974cf49a43SJulian Elischer 	}
3984cf49a43SJulian Elischer 	if (rptr)
3994cf49a43SJulian Elischer 		*rptr = resp;
4004cf49a43SJulian Elischer 	else if (resp)
4014cf49a43SJulian Elischer 		FREE(resp, M_NETGRAPH);
4024cf49a43SJulian Elischer 
4034cf49a43SJulian Elischer done:
4044cf49a43SJulian Elischer 	FREE(msg, M_NETGRAPH);
4054cf49a43SJulian Elischer 	return (error);
4064cf49a43SJulian Elischer }
4074cf49a43SJulian Elischer 
4084cf49a43SJulian Elischer /*
4094cf49a43SJulian Elischer  * Receive data on a hook
4104cf49a43SJulian Elischer  */
4114cf49a43SJulian Elischer static int
4124cf49a43SJulian Elischer ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
4134cf49a43SJulian Elischer {
4144cf49a43SJulian Elischer 	const node_p node = hook->node;
4154cf49a43SJulian Elischer 	const priv_p priv = node->private;
4163949bee8SArchie Cobbs 	const int index = HOOK_INDEX(hook);
4173949bee8SArchie Cobbs 	u_int16_t linkNum = NG_PPP_BUNDLE_LINKNUM;
4183949bee8SArchie Cobbs 	hook_p outHook = NULL;
4193949bee8SArchie Cobbs 	int proto = 0, error;
4204cf49a43SJulian Elischer 
4213949bee8SArchie Cobbs 	/* Did it come from a link hook? */
4223949bee8SArchie Cobbs 	if (index < 0) {
4234cf49a43SJulian Elischer 
4243949bee8SArchie Cobbs 		/* Is link active? */
4253949bee8SArchie Cobbs 		linkNum = (u_int16_t)~index;
4263949bee8SArchie Cobbs 		KASSERT(linkNum < NG_PPP_MAX_LINKS,
4273949bee8SArchie Cobbs 		    ("%s: bogus index 0x%x", __FUNCTION__, index));
4283949bee8SArchie Cobbs 		if (!priv->conf.links[linkNum].enableLink) {
4294cf49a43SJulian Elischer 			NG_FREE_DATA(m, meta);
4303949bee8SArchie Cobbs 			return (ENXIO);
4313949bee8SArchie Cobbs 		}
4323949bee8SArchie Cobbs 
4333949bee8SArchie Cobbs 		/* Stats */
4343949bee8SArchie Cobbs 		priv->linkStats[linkNum].recvFrames++;
4353949bee8SArchie Cobbs 		priv->linkStats[linkNum].recvOctets += m->m_pkthdr.len;
4363949bee8SArchie Cobbs 
4373949bee8SArchie Cobbs 		/* Dispatch incoming frame */
4383949bee8SArchie Cobbs 		return ng_ppp_input(node, linkNum, m, meta);
4393949bee8SArchie Cobbs 	}
4403949bee8SArchie Cobbs 
4413949bee8SArchie Cobbs 	/* Get protocol & check if data allowed from this hook */
4423949bee8SArchie Cobbs 	switch (index) {
4433949bee8SArchie Cobbs 
4443949bee8SArchie Cobbs 	/* Downwards flowing data */
4453949bee8SArchie Cobbs 	case HOOK_INDEX_ATALK:
4463949bee8SArchie Cobbs 		if (!priv->conf.enableAtalk) {
4473949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4483949bee8SArchie Cobbs 			return (ENXIO);
4493949bee8SArchie Cobbs 		}
4503949bee8SArchie Cobbs 		proto = PROT_APPLETALK;
4513949bee8SArchie Cobbs 		break;
4523949bee8SArchie Cobbs 	case HOOK_INDEX_IPX:
4533949bee8SArchie Cobbs 		if (!priv->conf.enableIPX) {
4543949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4553949bee8SArchie Cobbs 			return (ENXIO);
4563949bee8SArchie Cobbs 		}
4573949bee8SArchie Cobbs 		proto = PROT_IPX;
4583949bee8SArchie Cobbs 		break;
4593949bee8SArchie Cobbs 	case HOOK_INDEX_INET:
4603949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_VJIP:
4613949bee8SArchie Cobbs 		if (!priv->conf.enableIP) {
4623949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4633949bee8SArchie Cobbs 			return (ENXIO);
4643949bee8SArchie Cobbs 		}
4653949bee8SArchie Cobbs 		proto = PROT_IP;
4663949bee8SArchie Cobbs 		break;
4673949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_COMP:
4683949bee8SArchie Cobbs 		if (!priv->conf.enableVJCompression) {
4693949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4703949bee8SArchie Cobbs 			return (ENXIO);
4713949bee8SArchie Cobbs 		}
4723949bee8SArchie Cobbs 		proto = PROT_VJCOMP;
4733949bee8SArchie Cobbs 		break;
4743949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_UNCOMP:
4753949bee8SArchie Cobbs 		if (!priv->conf.enableVJCompression) {
4763949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4773949bee8SArchie Cobbs 			return (ENXIO);
4783949bee8SArchie Cobbs 		}
4793949bee8SArchie Cobbs 		proto = PROT_VJUNCOMP;
4803949bee8SArchie Cobbs 		break;
4813949bee8SArchie Cobbs 	case HOOK_INDEX_COMPRESS:
4823949bee8SArchie Cobbs 		if (!priv->conf.enableCompression) {
4833949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4843949bee8SArchie Cobbs 			return (ENXIO);
4853949bee8SArchie Cobbs 		}
4863949bee8SArchie Cobbs 		proto = PROT_COMPD;
4873949bee8SArchie Cobbs 		break;
4883949bee8SArchie Cobbs 	case HOOK_INDEX_ENCRYPT:
4893949bee8SArchie Cobbs 		if (!priv->conf.enableEncryption) {
4903949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4913949bee8SArchie Cobbs 			return (ENXIO);
4923949bee8SArchie Cobbs 		}
4933949bee8SArchie Cobbs 		proto = PROT_CRYPTD;
4943949bee8SArchie Cobbs 		break;
4953949bee8SArchie Cobbs 	case HOOK_INDEX_BYPASS:
4963949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 4) {
4973949bee8SArchie Cobbs 			NG_FREE_META(meta);
4983949bee8SArchie Cobbs 			return (EINVAL);
4993949bee8SArchie Cobbs 		}
5003949bee8SArchie Cobbs 		if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
5013949bee8SArchie Cobbs 			NG_FREE_META(meta);
5023949bee8SArchie Cobbs 			return (ENOBUFS);
5033949bee8SArchie Cobbs 		}
5043949bee8SArchie Cobbs 		linkNum = ntohs(mtod(m, u_int16_t *)[0]);
5053949bee8SArchie Cobbs 		proto = ntohs(mtod(m, u_int16_t *)[1]);
5063949bee8SArchie Cobbs 		m_adj(m, 4);
5073949bee8SArchie Cobbs 		if (linkNum >= NG_PPP_MAX_LINKS
5083949bee8SArchie Cobbs 		    && linkNum != NG_PPP_BUNDLE_LINKNUM)
5093949bee8SArchie Cobbs 			return (EINVAL);
5103949bee8SArchie Cobbs 		break;
5113949bee8SArchie Cobbs 
5123949bee8SArchie Cobbs 	/* Upwards flowing data */
5133949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_IP:
5143949bee8SArchie Cobbs 		if (!priv->conf.enableVJDecompression) {
5153949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5163949bee8SArchie Cobbs 			return (ENXIO);
5173949bee8SArchie Cobbs 		}
5183949bee8SArchie Cobbs 		break;
5193949bee8SArchie Cobbs 	case HOOK_INDEX_DECOMPRESS:
5203949bee8SArchie Cobbs 		if (!priv->conf.enableDecompression) {
5213949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5223949bee8SArchie Cobbs 			return (ENXIO);
5233949bee8SArchie Cobbs 		}
5243949bee8SArchie Cobbs 		break;
5253949bee8SArchie Cobbs 	case HOOK_INDEX_DECRYPT:
5263949bee8SArchie Cobbs 		if (!priv->conf.enableDecryption) {
5273949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5283949bee8SArchie Cobbs 			return (ENXIO);
5293949bee8SArchie Cobbs 		}
5303949bee8SArchie Cobbs 		break;
5313949bee8SArchie Cobbs 	default:
5323949bee8SArchie Cobbs 		KASSERT(0, ("%s: bogus index 0x%x", __FUNCTION__, index));
5333949bee8SArchie Cobbs 	}
5343949bee8SArchie Cobbs 
5353949bee8SArchie Cobbs 	/* Now figure out what to do with the frame */
5363949bee8SArchie Cobbs 	switch (index) {
5373949bee8SArchie Cobbs 	case HOOK_INDEX_INET:
5383949bee8SArchie Cobbs 		if (priv->conf.enableVJCompression && priv->vjCompHooked) {
5393949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_IP];
5403949bee8SArchie Cobbs 			break;
5413949bee8SArchie Cobbs 		}
5423949bee8SArchie Cobbs 		/* FALLTHROUGH */
5433949bee8SArchie Cobbs 	case HOOK_INDEX_ATALK:
5443949bee8SArchie Cobbs 	case HOOK_INDEX_IPX:
5453949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_COMP:
5463949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_UNCOMP:
5473949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_VJIP:
5483949bee8SArchie Cobbs 		if (priv->conf.enableCompression
5493949bee8SArchie Cobbs 		    && priv->hooks[HOOK_INDEX_COMPRESS] != NULL) {
5503949bee8SArchie Cobbs 			if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) {
5513949bee8SArchie Cobbs 				NG_FREE_META(meta);
5523949bee8SArchie Cobbs 				return (ENOBUFS);
5533949bee8SArchie Cobbs 			}
5543949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_COMPRESS];
5553949bee8SArchie Cobbs 			break;
5563949bee8SArchie Cobbs 		}
5573949bee8SArchie Cobbs 		/* FALLTHROUGH */
5583949bee8SArchie Cobbs 	case HOOK_INDEX_COMPRESS:
5593949bee8SArchie Cobbs 		if (priv->conf.enableEncryption
5603949bee8SArchie Cobbs 		    && priv->hooks[HOOK_INDEX_ENCRYPT] != NULL) {
5613949bee8SArchie Cobbs 			if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) {
5623949bee8SArchie Cobbs 				NG_FREE_META(meta);
5633949bee8SArchie Cobbs 				return (ENOBUFS);
5643949bee8SArchie Cobbs 			}
5653949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_ENCRYPT];
5663949bee8SArchie Cobbs 			break;
5673949bee8SArchie Cobbs 		}
5683949bee8SArchie Cobbs 		/* FALLTHROUGH */
5693949bee8SArchie Cobbs 	case HOOK_INDEX_ENCRYPT:
5703949bee8SArchie Cobbs 	case HOOK_INDEX_BYPASS:
5713949bee8SArchie Cobbs 		if ((m = ng_ppp_addproto(m, proto,
5723949bee8SArchie Cobbs 		    linkNum == NG_PPP_BUNDLE_LINKNUM
5733949bee8SArchie Cobbs 		      || priv->conf.links[linkNum].enableProtoComp)) == NULL) {
5743949bee8SArchie Cobbs 			NG_FREE_META(meta);
5753949bee8SArchie Cobbs 			return (ENOBUFS);
5763949bee8SArchie Cobbs 		}
5773949bee8SArchie Cobbs 		return ng_ppp_output(node, NG_PPP_BUNDLE_LINKNUM, m, meta);
5783949bee8SArchie Cobbs 
5793949bee8SArchie Cobbs 	/* Incoming data */
5803949bee8SArchie Cobbs 	case HOOK_INDEX_DECRYPT:
5813949bee8SArchie Cobbs 	case HOOK_INDEX_DECOMPRESS:
5823949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_IP:
5833949bee8SArchie Cobbs 		return ng_ppp_input(node, NG_PPP_BUNDLE_LINKNUM, m, meta);
5843949bee8SArchie Cobbs 	}
5853949bee8SArchie Cobbs 
5863949bee8SArchie Cobbs 	/* Send packet out hook */
5873949bee8SArchie Cobbs 	NG_SEND_DATA(error, outHook, m, meta);
5884cf49a43SJulian Elischer 	return (error);
5894cf49a43SJulian Elischer }
5904cf49a43SJulian Elischer 
5914cf49a43SJulian Elischer /*
5924cf49a43SJulian Elischer  * Destroy node
5934cf49a43SJulian Elischer  */
5944cf49a43SJulian Elischer static int
5954cf49a43SJulian Elischer ng_ppp_rmnode(node_p node)
5964cf49a43SJulian Elischer {
5974cf49a43SJulian Elischer 	const priv_p priv = node->private;
5984cf49a43SJulian Elischer 
5994cf49a43SJulian Elischer 	/* Take down netgraph node */
6004cf49a43SJulian Elischer 	node->flags |= NG_INVALID;
6014cf49a43SJulian Elischer 	ng_cutlinks(node);
6024cf49a43SJulian Elischer 	ng_unname(node);
6034cf49a43SJulian Elischer 	bzero(priv, sizeof(*priv));
6044cf49a43SJulian Elischer 	FREE(priv, M_NETGRAPH);
6054cf49a43SJulian Elischer 	node->private = NULL;
6064cf49a43SJulian Elischer 	ng_unref(node);		/* let the node escape */
6074cf49a43SJulian Elischer 	return (0);
6084cf49a43SJulian Elischer }
6094cf49a43SJulian Elischer 
6104cf49a43SJulian Elischer /*
6114cf49a43SJulian Elischer  * Hook disconnection
6124cf49a43SJulian Elischer  */
6134cf49a43SJulian Elischer static int
6144cf49a43SJulian Elischer ng_ppp_disconnect(hook_p hook)
6154cf49a43SJulian Elischer {
6164cf49a43SJulian Elischer 	if (hook->node->numhooks == 0)
6174cf49a43SJulian Elischer 		ng_rmnode(hook->node);
6184cf49a43SJulian Elischer 	return (0);
6194cf49a43SJulian Elischer }
6204cf49a43SJulian Elischer 
6214cf49a43SJulian Elischer /************************************************************************
6224cf49a43SJulian Elischer 			HELPER STUFF
6234cf49a43SJulian Elischer  ************************************************************************/
6244cf49a43SJulian Elischer 
6254cf49a43SJulian Elischer /*
6263949bee8SArchie Cobbs  * Handle an incoming frame.  Extract the PPP protocol number
6273949bee8SArchie Cobbs  * and dispatch accordingly.
6284cf49a43SJulian Elischer  */
6294cf49a43SJulian Elischer static int
6303949bee8SArchie Cobbs ng_ppp_input(node_p node, int linkNum, struct mbuf *m, meta_p meta)
6314cf49a43SJulian Elischer {
6323949bee8SArchie Cobbs 	const priv_p priv = node->private;
6333949bee8SArchie Cobbs 	hook_p outHook = NULL;
6343949bee8SArchie Cobbs 	int proto, error;
6354cf49a43SJulian Elischer 
6363949bee8SArchie Cobbs 	/* Extract protocol number */
6373949bee8SArchie Cobbs 	for (proto = 0; !PROT_VALID(proto) && m->m_pkthdr.len > 0; ) {
6383949bee8SArchie Cobbs 		if (m->m_len < 1 && (m = m_pullup(m, 1)) == NULL) {
6393949bee8SArchie Cobbs 			NG_FREE_META(meta);
6403949bee8SArchie Cobbs 			return (ENOBUFS);
6414cf49a43SJulian Elischer 		}
6423949bee8SArchie Cobbs 		proto = (proto << 8) + *mtod(m, u_char *);
6433949bee8SArchie Cobbs 		m_adj(m, 1);
6443949bee8SArchie Cobbs 	}
6453949bee8SArchie Cobbs 	if (!PROT_VALID(proto)) {
6463949bee8SArchie Cobbs 		if (linkNum == NG_PPP_BUNDLE_LINKNUM)
6473949bee8SArchie Cobbs 			priv->bundleStats.badProtos++;
6483949bee8SArchie Cobbs 		else
6493949bee8SArchie Cobbs 			priv->linkStats[linkNum].badProtos++;
6503949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
6513949bee8SArchie Cobbs 		return (EINVAL);
6523949bee8SArchie Cobbs 	}
6533949bee8SArchie Cobbs 
6543949bee8SArchie Cobbs 	/* Check protocol */
6553949bee8SArchie Cobbs 	switch (proto) {
6563949bee8SArchie Cobbs 	case PROT_COMPD:
6573949bee8SArchie Cobbs 		if (priv->conf.enableDecompression)
6583949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_DECOMPRESS];
6593949bee8SArchie Cobbs 		break;
6603949bee8SArchie Cobbs 	case PROT_CRYPTD:
6613949bee8SArchie Cobbs 		if (priv->conf.enableDecryption)
6623949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_DECRYPT];
6633949bee8SArchie Cobbs 		break;
6643949bee8SArchie Cobbs 	case PROT_VJCOMP:
6653949bee8SArchie Cobbs 		if (priv->conf.enableVJDecompression && priv->vjCompHooked)
6663949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_COMP];
6673949bee8SArchie Cobbs 		break;
6683949bee8SArchie Cobbs 	case PROT_VJUNCOMP:
6693949bee8SArchie Cobbs 		if (priv->conf.enableVJDecompression && priv->vjCompHooked)
6703949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_UNCOMP];
6713949bee8SArchie Cobbs 		break;
6723949bee8SArchie Cobbs 	case PROT_MP:
6733949bee8SArchie Cobbs 		if (priv->conf.enableMultilink) {
6743949bee8SArchie Cobbs 			NG_FREE_META(meta);
6753949bee8SArchie Cobbs 			return ng_ppp_mp_input(node, linkNum, m, meta);
6763949bee8SArchie Cobbs 		}
6773949bee8SArchie Cobbs 		break;
6783949bee8SArchie Cobbs 	case PROT_APPLETALK:
6793949bee8SArchie Cobbs 		if (priv->conf.enableAtalk)
6803949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_ATALK];
6813949bee8SArchie Cobbs 		break;
6823949bee8SArchie Cobbs 	case PROT_IPX:
6833949bee8SArchie Cobbs 		if (priv->conf.enableIPX)
6843949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_IPX];
6853949bee8SArchie Cobbs 		break;
6863949bee8SArchie Cobbs 	case PROT_IP:
6873949bee8SArchie Cobbs 		if (priv->conf.enableIP)
6883949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_INET];
6893949bee8SArchie Cobbs 		break;
6903949bee8SArchie Cobbs 	}
6913949bee8SArchie Cobbs 
6923949bee8SArchie Cobbs 	/* For unknown/inactive protocols, forward out the bypass hook */
6933949bee8SArchie Cobbs 	if (outHook == NULL) {
6943949bee8SArchie Cobbs 		M_PREPEND(m, 4, M_NOWAIT);
6953949bee8SArchie Cobbs 		if (m == NULL || (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL))
6963949bee8SArchie Cobbs 			return (ENOBUFS);
6973949bee8SArchie Cobbs 		mtod(m, u_int16_t *)[0] = htons(linkNum);
6983949bee8SArchie Cobbs 		mtod(m, u_int16_t *)[1] = htons(proto);
6993949bee8SArchie Cobbs 		outHook = priv->hooks[HOOK_INDEX_BYPASS];
7003949bee8SArchie Cobbs 	}
7013949bee8SArchie Cobbs 
7023949bee8SArchie Cobbs 	/* Forward frame */
7033949bee8SArchie Cobbs 	NG_SEND_DATA(error, outHook, m, meta);
7043949bee8SArchie Cobbs 	return (error);
7054cf49a43SJulian Elischer }
7064cf49a43SJulian Elischer 
7074cf49a43SJulian Elischer /*
7083949bee8SArchie Cobbs  * Deliver a frame out a link, either a real one or NG_PPP_BUNDLE_LINKNUM
7094cf49a43SJulian Elischer  */
7103949bee8SArchie Cobbs static int
7113949bee8SArchie Cobbs ng_ppp_output(node_p node, int linkNum, struct mbuf *m, meta_p meta)
7124cf49a43SJulian Elischer {
7133949bee8SArchie Cobbs 	const priv_p priv = node->private;
7143949bee8SArchie Cobbs 	int error;
7154cf49a43SJulian Elischer 
7163949bee8SArchie Cobbs 	/* Check for bundle virtual link */
7173949bee8SArchie Cobbs 	if (linkNum == NG_PPP_BUNDLE_LINKNUM) {
7183949bee8SArchie Cobbs 		if (priv->conf.enableMultilink)
7193949bee8SArchie Cobbs 			return ng_ppp_mp_output(node, m, meta);
7203949bee8SArchie Cobbs 		linkNum = priv->activeLinks[0];
7214cf49a43SJulian Elischer 	}
7223949bee8SArchie Cobbs 
7233949bee8SArchie Cobbs 	/* Check link status */
7243949bee8SArchie Cobbs 	if (!priv->conf.links[linkNum].enableLink)
7253949bee8SArchie Cobbs 		return (ENXIO);
7263949bee8SArchie Cobbs 	if (priv->links[linkNum] == NULL) {
7273949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
7283949bee8SArchie Cobbs 		return (ENETDOWN);
7293949bee8SArchie Cobbs 	}
7303949bee8SArchie Cobbs 
7313949bee8SArchie Cobbs 	/* Deliver frame */
7323949bee8SArchie Cobbs 	NG_SEND_DATA(error, priv->links[linkNum], m, meta);
733fb1fc8abSArchie Cobbs 
734fb1fc8abSArchie Cobbs 	/* Update stats and 'bytes in queue' counter */
735fb1fc8abSArchie Cobbs 	if (error == 0) {
736fb1fc8abSArchie Cobbs 		priv->linkStats[linkNum].xmitFrames++;
737fb1fc8abSArchie Cobbs 		priv->linkStats[linkNum].xmitOctets += m->m_pkthdr.len;
738fb1fc8abSArchie Cobbs 		priv->qstat[linkNum].bytesInQueue += m->m_pkthdr.len;
739fb1fc8abSArchie Cobbs 		microtime(&priv->qstat[linkNum].lastWrite);
740fb1fc8abSArchie Cobbs 	}
7413949bee8SArchie Cobbs 	return error;
7423949bee8SArchie Cobbs }
7433949bee8SArchie Cobbs 
7443949bee8SArchie Cobbs /*
7453949bee8SArchie Cobbs  * Handle an incoming multi-link fragment
7463949bee8SArchie Cobbs  */
7473949bee8SArchie Cobbs static int
7483949bee8SArchie Cobbs ng_ppp_mp_input(node_p node, int linkNum, struct mbuf *m, meta_p meta)
7493949bee8SArchie Cobbs {
7503949bee8SArchie Cobbs 	const priv_p priv = node->private;
7513949bee8SArchie Cobbs 	struct ng_ppp_frag frag0, *frag = &frag0;
7523949bee8SArchie Cobbs 	struct ng_ppp_frag *qent, *qnext;
7533949bee8SArchie Cobbs 	struct ng_ppp_frag *first = NULL, *last = NULL;
7543949bee8SArchie Cobbs 	int diff, highSeq, nextSeq;
7553949bee8SArchie Cobbs 	struct mbuf *tail;
7563949bee8SArchie Cobbs 
7573949bee8SArchie Cobbs 	/* Extract fragment information from MP header */
7583949bee8SArchie Cobbs 	if (priv->conf.recvShortSeq) {
7593949bee8SArchie Cobbs 		u_int16_t shdr;
7603949bee8SArchie Cobbs 
7613949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 2) {
7623949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
7633949bee8SArchie Cobbs 			return (EINVAL);
7643949bee8SArchie Cobbs 		}
7653949bee8SArchie Cobbs 		if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) {
7663949bee8SArchie Cobbs 			NG_FREE_META(meta);
7673949bee8SArchie Cobbs 			return (ENOBUFS);
7683949bee8SArchie Cobbs 		}
7693949bee8SArchie Cobbs 		shdr = ntohs(*mtod(m, u_int16_t *));
7703949bee8SArchie Cobbs 		frag->seq = shdr & MP_SHORT_SEQ_MASK;
7713949bee8SArchie Cobbs 		frag->first = (shdr & MP_SHORT_FIRST_FLAG) != 0;
7723949bee8SArchie Cobbs 		frag->last = (shdr & MP_SHORT_LAST_FLAG) != 0;
7733949bee8SArchie Cobbs 		highSeq = CIRCLEQ_EMPTY(&priv->frags) ?
7743949bee8SArchie Cobbs 		    frag->seq : CIRCLEQ_FIRST(&priv->frags)->seq;
7753949bee8SArchie Cobbs 		diff = MP_SHORT_SEQ_DIFF(frag->seq, highSeq);
7763949bee8SArchie Cobbs 		m_adj(m, 2);
7773949bee8SArchie Cobbs 	} else {
7783949bee8SArchie Cobbs 		u_int32_t lhdr;
7793949bee8SArchie Cobbs 
7803949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 4) {
7813949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
7823949bee8SArchie Cobbs 			return (EINVAL);
7833949bee8SArchie Cobbs 		}
7843949bee8SArchie Cobbs 		if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
7853949bee8SArchie Cobbs 			NG_FREE_META(meta);
7863949bee8SArchie Cobbs 			return (ENOBUFS);
7873949bee8SArchie Cobbs 		}
7883949bee8SArchie Cobbs 		lhdr = ntohl(*mtod(m, u_int32_t *));
7893949bee8SArchie Cobbs 		frag->seq = lhdr & MP_LONG_SEQ_MASK;
7903949bee8SArchie Cobbs 		frag->first = (lhdr & MP_LONG_FIRST_FLAG) != 0;
7913949bee8SArchie Cobbs 		frag->last = (lhdr & MP_LONG_LAST_FLAG) != 0;
7923949bee8SArchie Cobbs 		highSeq = CIRCLEQ_EMPTY(&priv->frags) ?
7933949bee8SArchie Cobbs 		    frag->seq : CIRCLEQ_FIRST(&priv->frags)->seq;
7943949bee8SArchie Cobbs 		diff = MP_LONG_SEQ_DIFF(frag->seq, highSeq);
7953949bee8SArchie Cobbs 		m_adj(m, 4);
7963949bee8SArchie Cobbs 	}
7973949bee8SArchie Cobbs 	frag->data = m;
7983949bee8SArchie Cobbs 	frag->meta = meta;
7993949bee8SArchie Cobbs 
8003949bee8SArchie Cobbs 	/* If the sequence number makes a large jump, empty the queue */
8013949bee8SArchie Cobbs 	if (diff <= -MP_INSANE_SEQ_JUMP || diff >= MP_INSANE_SEQ_JUMP)
8023949bee8SArchie Cobbs 		ng_ppp_free_frags(node);
8033949bee8SArchie Cobbs 
8043949bee8SArchie Cobbs 	/* Optimization: handle a frame that's all in one fragment */
8053949bee8SArchie Cobbs 	if (frag->first && frag->last)
8063949bee8SArchie Cobbs 		return ng_ppp_input(node, NG_PPP_BUNDLE_LINKNUM, m, meta);
8073949bee8SArchie Cobbs 
8083949bee8SArchie Cobbs 	/* Allocate a new frag struct for the queue */
8093949bee8SArchie Cobbs 	MALLOC(frag, struct ng_ppp_frag *, sizeof(*frag), M_NETGRAPH, M_NOWAIT);
8103949bee8SArchie Cobbs 	if (frag == NULL) {
8113949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
8123949bee8SArchie Cobbs 		return (ENOMEM);
8133949bee8SArchie Cobbs 	}
8143949bee8SArchie Cobbs 	*frag = frag0;
8153949bee8SArchie Cobbs 	meta = NULL;
8163949bee8SArchie Cobbs 	m = NULL;
8173949bee8SArchie Cobbs 
8183949bee8SArchie Cobbs 	/* Add fragment to queue, which is reverse sorted by sequence number */
8193949bee8SArchie Cobbs 	CIRCLEQ_FOREACH(qent, &priv->frags, f_qent) {
8203949bee8SArchie Cobbs 		diff = MP_SEQ_DIFF(frag->seq, qent->seq);
8213949bee8SArchie Cobbs 		if (diff > 0) {
8223949bee8SArchie Cobbs 			CIRCLEQ_INSERT_BEFORE(&priv->frags, qent, frag, f_qent);
8233949bee8SArchie Cobbs 			break;
8243949bee8SArchie Cobbs 		} else if (diff == 0) {	     /* should never happen! */
8253949bee8SArchie Cobbs 			log(LOG_ERR, "%s: rec'd dup MP fragment\n", node->name);
8263949bee8SArchie Cobbs 			if (linkNum == NG_PPP_BUNDLE_LINKNUM)
8273949bee8SArchie Cobbs 				priv->linkStats[linkNum].dupFragments++;
8283949bee8SArchie Cobbs 			else
8293949bee8SArchie Cobbs 				priv->bundleStats.dupFragments++;
8303949bee8SArchie Cobbs 			NG_FREE_DATA(frag->data, frag->meta);
8313949bee8SArchie Cobbs 			FREE(frag, M_NETGRAPH);
8323949bee8SArchie Cobbs 			return (EINVAL);
8333949bee8SArchie Cobbs 		}
8343949bee8SArchie Cobbs 	}
8353949bee8SArchie Cobbs 	if (qent == NULL)
8363949bee8SArchie Cobbs 		CIRCLEQ_INSERT_TAIL(&priv->frags, frag, f_qent);
8373949bee8SArchie Cobbs 
8383949bee8SArchie Cobbs 	/* Find the first fragment in the possibly newly completed frame */
8393949bee8SArchie Cobbs 	for (nextSeq = frag->seq, qent = frag;
8403949bee8SArchie Cobbs 	    qent != (void *) &priv->frags;
8413949bee8SArchie Cobbs 	    qent = CIRCLEQ_PREV(qent, f_qent)) {
8423949bee8SArchie Cobbs 		if (qent->seq != nextSeq)
8433949bee8SArchie Cobbs 			goto pruneQueue;
8443949bee8SArchie Cobbs 		if (qent->first) {
8453949bee8SArchie Cobbs 			first = qent;
8463949bee8SArchie Cobbs 			break;
8473949bee8SArchie Cobbs 		}
8483949bee8SArchie Cobbs 		nextSeq = (nextSeq + 1) & MP_SEQ_MASK;
8493949bee8SArchie Cobbs 	}
8503949bee8SArchie Cobbs 
8513949bee8SArchie Cobbs 	/* Find the last fragment in the possibly newly completed frame */
8523949bee8SArchie Cobbs 	for (nextSeq = frag->seq, qent = frag;
8533949bee8SArchie Cobbs 	    qent != (void *) &priv->frags;
8543949bee8SArchie Cobbs 	    qent = CIRCLEQ_NEXT(qent, f_qent)) {
8553949bee8SArchie Cobbs 		if (qent->seq != nextSeq)
8563949bee8SArchie Cobbs 			goto pruneQueue;
8573949bee8SArchie Cobbs 		if (qent->last) {
8583949bee8SArchie Cobbs 			last = qent;
8593949bee8SArchie Cobbs 			break;
8603949bee8SArchie Cobbs 		}
8613949bee8SArchie Cobbs 		nextSeq = (nextSeq - 1) & MP_SEQ_MASK;
8623949bee8SArchie Cobbs 	}
8633949bee8SArchie Cobbs 
8643949bee8SArchie Cobbs 	/* We have a complete frame, extract it from the queue */
8653949bee8SArchie Cobbs 	for (tail = NULL, qent = first; qent != NULL; qent = qnext) {
8663949bee8SArchie Cobbs 		qnext = CIRCLEQ_PREV(qent, f_qent);
8673949bee8SArchie Cobbs 		CIRCLEQ_REMOVE(&priv->frags, qent, f_qent);
8683949bee8SArchie Cobbs 		if (tail == NULL) {
8693949bee8SArchie Cobbs 			tail = m = qent->data;
8703949bee8SArchie Cobbs 			meta = qent->meta;	/* inherit first frag's meta */
8713949bee8SArchie Cobbs 		} else {
8723949bee8SArchie Cobbs 			m->m_pkthdr.len += qent->data->m_pkthdr.len;
8733949bee8SArchie Cobbs 			tail->m_next = qent->data;
8743949bee8SArchie Cobbs 			NG_FREE_META(qent->meta); /* drop other frag's metas */
8753949bee8SArchie Cobbs 		}
8763949bee8SArchie Cobbs 		while (tail->m_next != NULL)
8773949bee8SArchie Cobbs 			tail = tail->m_next;
8783949bee8SArchie Cobbs 		if (qent == last)
8793949bee8SArchie Cobbs 			qnext = NULL;
8803949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
8813949bee8SArchie Cobbs 	}
8823949bee8SArchie Cobbs 
8833949bee8SArchie Cobbs pruneQueue:
8843949bee8SArchie Cobbs 	/* Prune out stale entries in the queue */
8853949bee8SArchie Cobbs 	for (qent = CIRCLEQ_LAST(&priv->frags);
8863949bee8SArchie Cobbs 	    qent != (void *) &priv->frags; qent = qnext) {
8873949bee8SArchie Cobbs 		if (MP_SEQ_DIFF(highSeq, qent->seq) <= MP_MAX_SEQ_LINGER)
8883949bee8SArchie Cobbs 			break;
8893949bee8SArchie Cobbs 		qnext = CIRCLEQ_PREV(qent, f_qent);
8903949bee8SArchie Cobbs 		CIRCLEQ_REMOVE(&priv->frags, qent, f_qent);
8913949bee8SArchie Cobbs 		NG_FREE_DATA(qent->data, qent->meta);
8923949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
8933949bee8SArchie Cobbs 	}
8943949bee8SArchie Cobbs 
8953949bee8SArchie Cobbs 	/* Deliver newly completed frame, if any */
8963949bee8SArchie Cobbs 	return m ? ng_ppp_input(node, NG_PPP_BUNDLE_LINKNUM, m, meta) : 0;
8973949bee8SArchie Cobbs }
8983949bee8SArchie Cobbs 
8993949bee8SArchie Cobbs /*
9003949bee8SArchie Cobbs  * Deliver a frame out on the bundle, i.e., figure out how to fragment
9013949bee8SArchie Cobbs  * the frame across the individual PPP links and do so.
9023949bee8SArchie Cobbs  */
9033949bee8SArchie Cobbs static int
9043949bee8SArchie Cobbs ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta)
9053949bee8SArchie Cobbs {
9063949bee8SArchie Cobbs 	const priv_p priv = node->private;
9073949bee8SArchie Cobbs 	int distrib[NG_PPP_MAX_LINKS];
9083949bee8SArchie Cobbs 	int firstFragment;
9093949bee8SArchie Cobbs 	int activeLinkNum;
9103949bee8SArchie Cobbs 
9113949bee8SArchie Cobbs 	/* At least one link must be active */
9123949bee8SArchie Cobbs 	if (priv->numActiveLinks == 0) {
9133949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
9143949bee8SArchie Cobbs 		return (ENETDOWN);
9153949bee8SArchie Cobbs 	}
9163949bee8SArchie Cobbs 
9173949bee8SArchie Cobbs 	/* Round-robin strategy */
9183949bee8SArchie Cobbs 	if (priv->conf.enableRoundRobin || m->m_pkthdr.len < MP_MIN_FRAG_LEN) {
9193949bee8SArchie Cobbs 		activeLinkNum = priv->lastLink++ % priv->numActiveLinks;
9203949bee8SArchie Cobbs 		bzero(&distrib, priv->numActiveLinks * sizeof(distrib[0]));
9213949bee8SArchie Cobbs 		distrib[activeLinkNum] = m->m_pkthdr.len;
9223949bee8SArchie Cobbs 		goto deliver;
9233949bee8SArchie Cobbs 	}
9243949bee8SArchie Cobbs 
9253949bee8SArchie Cobbs 	/* Strategy when all links are equivalent (optimize the common case) */
9263949bee8SArchie Cobbs 	if (priv->allLinksEqual) {
9273949bee8SArchie Cobbs 		const int fraction = m->m_pkthdr.len / priv->numActiveLinks;
9283949bee8SArchie Cobbs 		int i, remain;
9293949bee8SArchie Cobbs 
9303949bee8SArchie Cobbs 		for (i = 0; i < priv->numActiveLinks; i++)
9313949bee8SArchie Cobbs 			distrib[priv->lastLink++ % priv->numActiveLinks]
9323949bee8SArchie Cobbs 			    = fraction;
9333949bee8SArchie Cobbs 		remain = m->m_pkthdr.len - (fraction * priv->numActiveLinks);
9343949bee8SArchie Cobbs 		while (remain > 0) {
9353949bee8SArchie Cobbs 			distrib[priv->lastLink++ % priv->numActiveLinks]++;
9363949bee8SArchie Cobbs 			remain--;
9373949bee8SArchie Cobbs 		}
9383949bee8SArchie Cobbs 		goto deliver;
9393949bee8SArchie Cobbs 	}
9403949bee8SArchie Cobbs 
9413949bee8SArchie Cobbs 	/* Strategy when all links are not equivalent */
9423949bee8SArchie Cobbs 	ng_ppp_mp_strategy(node, m->m_pkthdr.len, distrib);
9433949bee8SArchie Cobbs 
9443949bee8SArchie Cobbs deliver:
9453949bee8SArchie Cobbs 	/* Update stats */
9463949bee8SArchie Cobbs 	priv->bundleStats.xmitFrames++;
9473949bee8SArchie Cobbs 	priv->bundleStats.xmitOctets += m->m_pkthdr.len;
9483949bee8SArchie Cobbs 
9493949bee8SArchie Cobbs 	/* Send alloted portions of frame out on the link(s) */
9503949bee8SArchie Cobbs 	for (firstFragment = 1, activeLinkNum = priv->numActiveLinks - 1;
9513949bee8SArchie Cobbs 	    activeLinkNum >= 0; activeLinkNum--) {
9523949bee8SArchie Cobbs 		const int linkNum = priv->activeLinks[activeLinkNum];
9533949bee8SArchie Cobbs 
9543949bee8SArchie Cobbs 		/* Deliver fragment(s) out the next link */
9553949bee8SArchie Cobbs 		for ( ; distrib[activeLinkNum] > 0; firstFragment = 0) {
9563949bee8SArchie Cobbs 			int len, lastFragment, error;
9573949bee8SArchie Cobbs 			struct mbuf *m2;
9583949bee8SArchie Cobbs 			meta_p meta2;
9593949bee8SArchie Cobbs 
9603949bee8SArchie Cobbs 			/* Calculate fragment length; don't exceed link MTU */
9613949bee8SArchie Cobbs 			len = distrib[activeLinkNum];
9623949bee8SArchie Cobbs 			if (len > priv->conf.links[linkNum].mru)
9633949bee8SArchie Cobbs 				len = priv->conf.links[linkNum].mru;
9643949bee8SArchie Cobbs 			distrib[activeLinkNum] -= len;
9653949bee8SArchie Cobbs 			lastFragment = (len == m->m_pkthdr.len);
9663949bee8SArchie Cobbs 
9673949bee8SArchie Cobbs 			/* Split off next fragment as "m2" */
9683949bee8SArchie Cobbs 			m2 = m;
9693949bee8SArchie Cobbs 			if (!lastFragment) {
9703949bee8SArchie Cobbs 				struct mbuf *n = m_split(m, len, M_NOWAIT);
9713949bee8SArchie Cobbs 
9723949bee8SArchie Cobbs 				if (n == NULL) {
9733949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
9743949bee8SArchie Cobbs 					return (ENOMEM);
9753949bee8SArchie Cobbs 				}
9763949bee8SArchie Cobbs 				m = n;
9773949bee8SArchie Cobbs 			}
9783949bee8SArchie Cobbs 
9793949bee8SArchie Cobbs 			/* Prepend MP header */
9803949bee8SArchie Cobbs 			if (priv->conf.xmitShortSeq) {
9813949bee8SArchie Cobbs 				u_int16_t shdr;
9823949bee8SArchie Cobbs 
9833949bee8SArchie Cobbs 				M_PREPEND(m2, 2, M_NOWAIT);
9843949bee8SArchie Cobbs 				if (m2 == NULL
9853949bee8SArchie Cobbs 				    || (m2->m_len < 2
9863949bee8SArchie Cobbs 				      && (m2 = m_pullup(m2, 2)) == NULL)) {
9873949bee8SArchie Cobbs 					if (!lastFragment)
9883949bee8SArchie Cobbs 						m_freem(m);
9893949bee8SArchie Cobbs 					NG_FREE_META(meta);
9903949bee8SArchie Cobbs 					return (ENOBUFS);
9913949bee8SArchie Cobbs 				}
9923949bee8SArchie Cobbs 				shdr = priv->mpSeqOut;
9933949bee8SArchie Cobbs 				priv->mpSeqOut =
9943949bee8SArchie Cobbs 				    (priv->mpSeqOut + 1) % MP_SHORT_SEQ_MASK;
9953949bee8SArchie Cobbs 				if (firstFragment)
9963949bee8SArchie Cobbs 					shdr |= MP_SHORT_FIRST_FLAG;
9973949bee8SArchie Cobbs 				if (lastFragment)
9983949bee8SArchie Cobbs 					shdr |= MP_SHORT_LAST_FLAG;
9993949bee8SArchie Cobbs 				*mtod(m2, u_int16_t *) = htons(shdr);
10003949bee8SArchie Cobbs 			} else {
10013949bee8SArchie Cobbs 				u_int32_t lhdr;
10023949bee8SArchie Cobbs 
10033949bee8SArchie Cobbs 				M_PREPEND(m2, 4, M_NOWAIT);
10043949bee8SArchie Cobbs 				if (m2 == NULL
10053949bee8SArchie Cobbs 				    || (m2->m_len < 4
10063949bee8SArchie Cobbs 				      && (m2 = m_pullup(m2, 4)) == NULL)) {
10073949bee8SArchie Cobbs 					if (!lastFragment)
10083949bee8SArchie Cobbs 						m_freem(m);
10093949bee8SArchie Cobbs 					NG_FREE_META(meta);
10103949bee8SArchie Cobbs 					return (ENOBUFS);
10113949bee8SArchie Cobbs 				}
10123949bee8SArchie Cobbs 				lhdr = priv->mpSeqOut;
10133949bee8SArchie Cobbs 				priv->mpSeqOut =
10143949bee8SArchie Cobbs 				    (priv->mpSeqOut + 1) % MP_LONG_SEQ_MASK;
10153949bee8SArchie Cobbs 				if (firstFragment)
10163949bee8SArchie Cobbs 					lhdr |= MP_LONG_FIRST_FLAG;
10173949bee8SArchie Cobbs 				if (lastFragment)
10183949bee8SArchie Cobbs 					lhdr |= MP_LONG_LAST_FLAG;
10193949bee8SArchie Cobbs 				*mtod(m2, u_int32_t *) = htonl(lhdr);
10203949bee8SArchie Cobbs 			}
10213949bee8SArchie Cobbs 
10223949bee8SArchie Cobbs 			/* Add MP protocol number */
10233949bee8SArchie Cobbs 			m2 = ng_ppp_addproto(m, PROT_MP,
10243949bee8SArchie Cobbs 			    priv->conf.links[linkNum].enableProtoComp);
10253949bee8SArchie Cobbs 			if (m2 == NULL) {
10263949bee8SArchie Cobbs 				if (!lastFragment)
10273949bee8SArchie Cobbs 					m_freem(m);
10283949bee8SArchie Cobbs 				NG_FREE_META(meta);
10293949bee8SArchie Cobbs 				return (ENOBUFS);
10303949bee8SArchie Cobbs 			}
10313949bee8SArchie Cobbs 
10323949bee8SArchie Cobbs 			/* Copy the meta information, if any */
10333949bee8SArchie Cobbs 			if (meta != NULL && !lastFragment) {
10343949bee8SArchie Cobbs 				MALLOC(meta2, meta_p,
10353949bee8SArchie Cobbs 				    meta->used_len, M_NETGRAPH, M_NOWAIT);
10363949bee8SArchie Cobbs 				if (meta2 == NULL) {
10373949bee8SArchie Cobbs 					m_freem(m2);
10383949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
10393949bee8SArchie Cobbs 					return (ENOMEM);
10403949bee8SArchie Cobbs 				}
10413949bee8SArchie Cobbs 				meta2->allocated_len = meta->used_len;
10423949bee8SArchie Cobbs 				bcopy(meta, meta2, meta->used_len);
10433949bee8SArchie Cobbs 			} else
10443949bee8SArchie Cobbs 				meta2 = meta;
10453949bee8SArchie Cobbs 
10463949bee8SArchie Cobbs 			/* Send fragment */
10473949bee8SArchie Cobbs 			error = ng_ppp_output(node, linkNum, m2, meta2);
10483949bee8SArchie Cobbs 
10493949bee8SArchie Cobbs 			/* Abort for error */
10503949bee8SArchie Cobbs 			if (error != 0) {
10513949bee8SArchie Cobbs 				if (!lastFragment)
10523949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
10533949bee8SArchie Cobbs 				return (error);
10543949bee8SArchie Cobbs 			}
10553949bee8SArchie Cobbs 		}
10563949bee8SArchie Cobbs 	}
10573949bee8SArchie Cobbs 
10583949bee8SArchie Cobbs 	/* Done */
10593949bee8SArchie Cobbs 	return (0);
10603949bee8SArchie Cobbs }
10613949bee8SArchie Cobbs 
10623949bee8SArchie Cobbs /*
10633949bee8SArchie Cobbs  * Computing the optimal fragmentation
10643949bee8SArchie Cobbs  * -----------------------------------
10653949bee8SArchie Cobbs  *
10663949bee8SArchie Cobbs  * This routine tries to compute the optimal fragmentation pattern based
10673949bee8SArchie Cobbs  * on each link's latency, bandwidth, and calculated additional latency.
10683949bee8SArchie Cobbs  * The latter quantity is the additional latency caused by previously
10693949bee8SArchie Cobbs  * written data that has not been transmitted yet.
10703949bee8SArchie Cobbs  *
10713949bee8SArchie Cobbs  * This algorithm is only useful when not all of the links have the
10723949bee8SArchie Cobbs  * same latency and bandwidth values.
10733949bee8SArchie Cobbs  *
10743949bee8SArchie Cobbs  * The essential idea is to make the last bit of each fragment of the
10753949bee8SArchie Cobbs  * frame arrive at the opposite end at the exact same time. This greedy
10763949bee8SArchie Cobbs  * algorithm is optimal, in that no other scheduling could result in any
10773949bee8SArchie Cobbs  * packet arriving any sooner unless packets are delivered out of order.
10783949bee8SArchie Cobbs  *
10793949bee8SArchie Cobbs  * Suppose link i has bandwidth b_i (in tens of bytes per milisecond) and
10803949bee8SArchie Cobbs  * latency l_i (in miliseconds). Consider the function function f_i(t)
10813949bee8SArchie Cobbs  * which is equal to the number of bytes that will have arrived at
10823949bee8SArchie Cobbs  * the peer after t miliseconds if we start writing continuously at
10833949bee8SArchie Cobbs  * time t = 0. Then f_i(t) = b_i * (t - l_i) = ((b_i * t) - (l_i * b_i).
10843949bee8SArchie Cobbs  * That is, f_i(t) is a line with slope b_i and y-intersect -(l_i * b_i).
10853949bee8SArchie Cobbs  * Note that the y-intersect is always <= zero because latency can't be
10863949bee8SArchie Cobbs  * negative.  Note also that really the function is f_i(t) except when
10873949bee8SArchie Cobbs  * f_i(t) is negative, in which case the function is zero.  To take
10883949bee8SArchie Cobbs  * care of this, let Q_i(t) = { if (f_i(t) > 0) return 1; else return 0; }.
10893949bee8SArchie Cobbs  * So the actual number of bytes that will have arrived at the peer after
10903949bee8SArchie Cobbs  * t miliseconds is f_i(t) * Q_i(t).
10913949bee8SArchie Cobbs  *
10923949bee8SArchie Cobbs  * At any given time, each link has some additional latency a_i >= 0
10933949bee8SArchie Cobbs  * due to previously written fragment(s) which are still in the queue.
10943949bee8SArchie Cobbs  * This value is easily computed from the time since last transmission,
10953949bee8SArchie Cobbs  * the previous latency value, the number of bytes written, and the
10963949bee8SArchie Cobbs  * link's bandwidth.
10973949bee8SArchie Cobbs  *
10983949bee8SArchie Cobbs  * Assume that l_i includes any a_i already, and that the links are
10993949bee8SArchie Cobbs  * sorted by latency, so that l_i <= l_{i+1}.
11003949bee8SArchie Cobbs  *
11013949bee8SArchie Cobbs  * Let N be the total number of bytes in the current frame we are sending.
11023949bee8SArchie Cobbs  *
11033949bee8SArchie Cobbs  * Suppose we were to start writing bytes at time t = 0 on all links
11043949bee8SArchie Cobbs  * simultaneously, which is the most we can possibly do.  Then let
11053949bee8SArchie Cobbs  * F(t) be equal to the total number of bytes received by the peer
11063949bee8SArchie Cobbs  * after t miliseconds. Then F(t) = Sum_i (f_i(t) * Q_i(t)).
11073949bee8SArchie Cobbs  *
11083949bee8SArchie Cobbs  * Our goal is simply this: fragment the frame across the links such
11093949bee8SArchie Cobbs  * that the peer is able to reconstruct the completed frame as soon as
11103949bee8SArchie Cobbs  * possible, i.e., at the least possible value of t. Call this value t_0.
11113949bee8SArchie Cobbs  *
11123949bee8SArchie Cobbs  * Then it follows that F(t_0) = N. Our strategy is first to find the value
11133949bee8SArchie Cobbs  * of t_0, and then deduce how many bytes to write to each link.
11143949bee8SArchie Cobbs  *
11153949bee8SArchie Cobbs  * Rewriting F(t_0):
11163949bee8SArchie Cobbs  *
11173949bee8SArchie Cobbs  *   t_0 = ( N + Sum_i ( l_i * b_i * Q_i(t_0) ) ) / Sum_i ( b_i * Q_i(t_0) )
11183949bee8SArchie Cobbs  *
11193949bee8SArchie Cobbs  * Now, we note that Q_i(t) is constant for l_i <= t <= l_{i+1}. t_0 will
11203949bee8SArchie Cobbs  * lie in one of these ranges.  To find it, we just need to find the i such
11213949bee8SArchie Cobbs  * that F(l_i) <= N <= F(l_{i+1}).  Then we compute all the constant values
11223949bee8SArchie Cobbs  * for Q_i() in this range, plug in the remaining values, solving for t_0.
11233949bee8SArchie Cobbs  *
11243949bee8SArchie Cobbs  * Once t_0 is known, then the number of bytes to send on link i is
11253949bee8SArchie Cobbs  * just f_i(t_0) * Q_i(t_0).
11263949bee8SArchie Cobbs  *
11273949bee8SArchie Cobbs  * In other words, we start allocating bytes to the links one at a time.
11283949bee8SArchie Cobbs  * We keep adding links until the frame is completely sent.  Some links
11293949bee8SArchie Cobbs  * may not get any bytes because their latency is too high.
11303949bee8SArchie Cobbs  *
11313949bee8SArchie Cobbs  * Is all this work really worth the trouble?  Depends on the situation.
11323949bee8SArchie Cobbs  * The bigger the ratio of computer speed to link speed, and the more
11333949bee8SArchie Cobbs  * important total bundle latency is (e.g., for interactive response time),
11343949bee8SArchie Cobbs  * the more it's worth it.  There is however the cost of calling this
11353949bee8SArchie Cobbs  * function for every frame.  The running time is O(n^2) where n is the
11363949bee8SArchie Cobbs  * number of links that receive a non-zero number of bytes.
11373949bee8SArchie Cobbs  *
11383949bee8SArchie Cobbs  * Since latency is measured in miliseconds, the "resolution" of this
11393949bee8SArchie Cobbs  * algorithm is one milisecond.
11403949bee8SArchie Cobbs  *
11413949bee8SArchie Cobbs  * To avoid this algorithm altogether, configure all links to have the
11423949bee8SArchie Cobbs  * same latency and bandwidth.
11433949bee8SArchie Cobbs  */
11443949bee8SArchie Cobbs static void
11453949bee8SArchie Cobbs ng_ppp_mp_strategy(node_p node, int len, int *distrib)
11463949bee8SArchie Cobbs {
11473949bee8SArchie Cobbs 	const priv_p priv = node->private;
11483949bee8SArchie Cobbs 	int latency[NG_PPP_MAX_LINKS];
11493949bee8SArchie Cobbs 	int sortByLatency[NG_PPP_MAX_LINKS];
11503949bee8SArchie Cobbs 	int activeLinkNum, linkNum;
11513949bee8SArchie Cobbs 	int t0, total, topSum, botSum;
11523949bee8SArchie Cobbs 	struct timeval now;
11533949bee8SArchie Cobbs 	int i, numFragments;
11543949bee8SArchie Cobbs 
11553949bee8SArchie Cobbs 	/* If only one link, this gets real easy */
11563949bee8SArchie Cobbs 	if (priv->numActiveLinks == 1) {
11573949bee8SArchie Cobbs 		distrib[0] = len;
11583949bee8SArchie Cobbs 		return;
11593949bee8SArchie Cobbs 	}
11603949bee8SArchie Cobbs 
11613949bee8SArchie Cobbs 	/* Get current time */
1162fb1fc8abSArchie Cobbs 	microtime(&now);
11633949bee8SArchie Cobbs 
11643949bee8SArchie Cobbs 	/* Compute latencies for each link at this point in time */
11653949bee8SArchie Cobbs 	for (activeLinkNum = 0;
11663949bee8SArchie Cobbs 	    activeLinkNum < priv->numActiveLinks; activeLinkNum++) {
11673949bee8SArchie Cobbs 		struct timeval diff;
11683949bee8SArchie Cobbs 		int xmitBytes;
11693949bee8SArchie Cobbs 
11703949bee8SArchie Cobbs 		/* Start with base latency value */
11713949bee8SArchie Cobbs 		linkNum = priv->activeLinks[activeLinkNum];
11723949bee8SArchie Cobbs 		latency[activeLinkNum] = priv->conf.links[linkNum].latency;
11733949bee8SArchie Cobbs 		sortByLatency[activeLinkNum] = activeLinkNum;	/* see below */
11743949bee8SArchie Cobbs 
11753949bee8SArchie Cobbs 		/* Any additional latency? */
11763949bee8SArchie Cobbs 		if (priv->qstat[activeLinkNum].bytesInQueue == 0)
11773949bee8SArchie Cobbs 			continue;
11783949bee8SArchie Cobbs 
11793949bee8SArchie Cobbs 		/* Compute time delta since last write */
11803949bee8SArchie Cobbs 		diff = now;
11813949bee8SArchie Cobbs 		timevalsub(&diff, &priv->qstat[activeLinkNum].lastWrite);
11823949bee8SArchie Cobbs 		if (now.tv_sec < 0 || diff.tv_sec >= 10) {	/* sanity */
11833949bee8SArchie Cobbs 			priv->qstat[activeLinkNum].bytesInQueue = 0;
11843949bee8SArchie Cobbs 			continue;
11853949bee8SArchie Cobbs 		}
11863949bee8SArchie Cobbs 
11873949bee8SArchie Cobbs 		/* How many bytes could have transmitted since last write? */
11883949bee8SArchie Cobbs 		xmitBytes = priv->conf.links[linkNum].bandwidth * diff.tv_sec
11893949bee8SArchie Cobbs 		    + (priv->conf.links[linkNum].bandwidth
11903949bee8SArchie Cobbs 			* (diff.tv_usec / 1000)) / 100;
11913949bee8SArchie Cobbs 		priv->qstat[activeLinkNum].bytesInQueue -= xmitBytes;
11923949bee8SArchie Cobbs 		if (priv->qstat[activeLinkNum].bytesInQueue < 0)
11933949bee8SArchie Cobbs 			priv->qstat[activeLinkNum].bytesInQueue = 0;
11943949bee8SArchie Cobbs 		else
11953949bee8SArchie Cobbs 			latency[activeLinkNum] +=
11963949bee8SArchie Cobbs 			    (100 * priv->qstat[activeLinkNum].bytesInQueue)
11973949bee8SArchie Cobbs 				/ priv->conf.links[linkNum].bandwidth;
11983949bee8SArchie Cobbs 	}
11993949bee8SArchie Cobbs 
12003949bee8SArchie Cobbs 	/* Sort links by latency */
12013949bee8SArchie Cobbs 	compareLatencies = latency;
12023949bee8SArchie Cobbs 	qsort(sortByLatency,
12033949bee8SArchie Cobbs 	    priv->numActiveLinks, sizeof(*sortByLatency), ng_ppp_intcmp);
12043949bee8SArchie Cobbs 	compareLatencies = NULL;
12053949bee8SArchie Cobbs 
12063949bee8SArchie Cobbs 	/* Find the interval we need (add links in sortByLatency[] order) */
12073949bee8SArchie Cobbs 	for (numFragments = 1;
12083949bee8SArchie Cobbs 	    numFragments < priv->numActiveLinks; numFragments++) {
12093949bee8SArchie Cobbs 		for (total = i = 0; i < numFragments; i++) {
12103949bee8SArchie Cobbs 			int flowTime;
12113949bee8SArchie Cobbs 
12123949bee8SArchie Cobbs 			flowTime = latency[sortByLatency[numFragments]]
12133949bee8SArchie Cobbs 			    - latency[sortByLatency[i]];
12143949bee8SArchie Cobbs 			total += ((flowTime * priv->conf.links[
12153949bee8SArchie Cobbs 			    priv->activeLinks[sortByLatency[i]]].bandwidth)
12163949bee8SArchie Cobbs 			    	+ 99) / 100;
12173949bee8SArchie Cobbs 		}
12183949bee8SArchie Cobbs 		if (total >= len)
12193949bee8SArchie Cobbs 			break;
12203949bee8SArchie Cobbs 	}
12213949bee8SArchie Cobbs 
12223949bee8SArchie Cobbs 	/* Solve for t_0 in that interval */
12233949bee8SArchie Cobbs 	for (topSum = botSum = i = 0; i < numFragments; i++) {
12243949bee8SArchie Cobbs 		int bw = priv->conf.links[
12253949bee8SArchie Cobbs 		    priv->activeLinks[sortByLatency[i]]].bandwidth;
12263949bee8SArchie Cobbs 
12273949bee8SArchie Cobbs 		topSum += latency[sortByLatency[i]] * bw;	/* / 100 */
12283949bee8SArchie Cobbs 		botSum += bw;					/* / 100 */
12293949bee8SArchie Cobbs 	}
12303949bee8SArchie Cobbs 	t0 = ((len * 100) + topSum + botSum / 2) / botSum;
12313949bee8SArchie Cobbs 
12323949bee8SArchie Cobbs 	/* Compute f_i(t_0) all i */
12333949bee8SArchie Cobbs 	bzero(distrib, priv->numActiveLinks * sizeof(*distrib));
12343949bee8SArchie Cobbs 	for (total = i = 0; i < numFragments; i++) {
12353949bee8SArchie Cobbs 		int bw = priv->conf.links[
12363949bee8SArchie Cobbs 		    priv->activeLinks[sortByLatency[i]]].bandwidth;
12373949bee8SArchie Cobbs 
12383949bee8SArchie Cobbs 		distrib[sortByLatency[i]] =
12393949bee8SArchie Cobbs 		    (bw * (t0 - latency[sortByLatency[i]]) + 50) / 100;
12403949bee8SArchie Cobbs 		total += distrib[sortByLatency[i]];
12413949bee8SArchie Cobbs 	}
12423949bee8SArchie Cobbs 
1243fb1fc8abSArchie Cobbs 	/* Deal with any rounding error */
1244fb1fc8abSArchie Cobbs 	if (total < len) {
12453949bee8SArchie Cobbs 		int fast = 0;
12463949bee8SArchie Cobbs 
1247fb1fc8abSArchie Cobbs 		/* Find the fastest link */
12483949bee8SArchie Cobbs 		for (i = 1; i < numFragments; i++) {
12493949bee8SArchie Cobbs 			if (priv->conf.links[
12503949bee8SArchie Cobbs 			      priv->activeLinks[sortByLatency[i]]].bandwidth >
12513949bee8SArchie Cobbs 			    priv->conf.links[
12523949bee8SArchie Cobbs 			      priv->activeLinks[sortByLatency[fast]]].bandwidth)
12533949bee8SArchie Cobbs 				fast = i;
12543949bee8SArchie Cobbs 		}
12553949bee8SArchie Cobbs 		distrib[sortByLatency[fast]] += len - total;
1256fb1fc8abSArchie Cobbs 	} else while (total > len) {
1257fb1fc8abSArchie Cobbs 		int delta, slow = 0;
12583949bee8SArchie Cobbs 
1259fb1fc8abSArchie Cobbs 		/* Find the slowest link that still has bytes to remove */
1260fb1fc8abSArchie Cobbs 		for (i = 1; i < numFragments; i++) {
1261fb1fc8abSArchie Cobbs 			if (distrib[sortByLatency[slow]] == 0
1262fb1fc8abSArchie Cobbs 			  || (distrib[sortByLatency[i]] > 0
1263fb1fc8abSArchie Cobbs 			    && priv->conf.links[priv->activeLinks[
1264fb1fc8abSArchie Cobbs 					sortByLatency[i]]].bandwidth <
1265fb1fc8abSArchie Cobbs 			      priv->conf.links[priv->activeLinks[
1266fb1fc8abSArchie Cobbs 					sortByLatency[slow]]].bandwidth))
1267fb1fc8abSArchie Cobbs 				slow = i;
1268fb1fc8abSArchie Cobbs 		}
1269fb1fc8abSArchie Cobbs 		delta = total - len;
1270fb1fc8abSArchie Cobbs 		if (delta > distrib[sortByLatency[slow]])
1271fb1fc8abSArchie Cobbs 			delta = distrib[sortByLatency[slow]];
1272fb1fc8abSArchie Cobbs 		distrib[sortByLatency[slow]] -= delta;
1273fb1fc8abSArchie Cobbs 		total -= delta;
12743949bee8SArchie Cobbs 	}
12753949bee8SArchie Cobbs }
12763949bee8SArchie Cobbs 
12773949bee8SArchie Cobbs /*
12783949bee8SArchie Cobbs  * Compare two integers
12793949bee8SArchie Cobbs  */
12803949bee8SArchie Cobbs static int
12813949bee8SArchie Cobbs ng_ppp_intcmp(const void *v1, const void *v2)
12823949bee8SArchie Cobbs {
12833949bee8SArchie Cobbs 	const int index1 = *((const int *) v1);
12843949bee8SArchie Cobbs 	const int index2 = *((const int *) v2);
12853949bee8SArchie Cobbs 
12863949bee8SArchie Cobbs 	return compareLatencies[index1] - compareLatencies[index2];
12873949bee8SArchie Cobbs }
12883949bee8SArchie Cobbs 
12893949bee8SArchie Cobbs /*
12903949bee8SArchie Cobbs  * Prepend a possibly compressed PPP protocol number in front of a frame
12913949bee8SArchie Cobbs  */
12923949bee8SArchie Cobbs static struct mbuf *
12933949bee8SArchie Cobbs ng_ppp_addproto(struct mbuf *m, int proto, int compOK)
12943949bee8SArchie Cobbs {
12953949bee8SArchie Cobbs 	int psize = (PROT_COMPRESSIBLE(proto) && compOK) ? 1 : 2;
12963949bee8SArchie Cobbs 
12973949bee8SArchie Cobbs 	/* Add protocol number */
12983949bee8SArchie Cobbs 	M_PREPEND(m, psize, M_NOWAIT);
12993949bee8SArchie Cobbs 	if (m == NULL || (m->m_len < psize && (m = m_pullup(m, psize)) == NULL))
13004cf49a43SJulian Elischer 		return (NULL);
13013949bee8SArchie Cobbs 	if (psize == 1)
13023949bee8SArchie Cobbs 		*mtod(m, u_char *) = (u_char)proto;
13033949bee8SArchie Cobbs 	else
13043949bee8SArchie Cobbs 		*mtod(m, u_int16_t *) = htons((u_int16_t)proto);
13053949bee8SArchie Cobbs 	return (m);
13063949bee8SArchie Cobbs }
13073949bee8SArchie Cobbs 
13083949bee8SArchie Cobbs /*
13093949bee8SArchie Cobbs  * Update private information that is derived from other private information
13103949bee8SArchie Cobbs  */
13113949bee8SArchie Cobbs static void
13123949bee8SArchie Cobbs ng_ppp_update(node_p node, int newConf)
13133949bee8SArchie Cobbs {
13143949bee8SArchie Cobbs 	const priv_p priv = node->private;
13153949bee8SArchie Cobbs 	int i;
13163949bee8SArchie Cobbs 
13173949bee8SArchie Cobbs 	/* Update active status for VJ Compression */
13183949bee8SArchie Cobbs 	priv->vjCompHooked = priv->hooks[HOOK_INDEX_VJC_IP] != NULL
13193949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_COMP] != NULL
13203949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_UNCOMP] != NULL
13213949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_VJIP] != NULL;
13223949bee8SArchie Cobbs 
13233949bee8SArchie Cobbs 	/* Increase latency for each link an amount equal to one MP header */
13243949bee8SArchie Cobbs 	if (newConf) {
13253949bee8SArchie Cobbs 		for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
13263949bee8SArchie Cobbs 			int hdrBytes;
13273949bee8SArchie Cobbs 
13283949bee8SArchie Cobbs 			hdrBytes = (priv->conf.links[i].enableProtoComp ? 1 : 2)
13293949bee8SArchie Cobbs 			    + (priv->conf.xmitShortSeq ? 2 : 4);
13303949bee8SArchie Cobbs 			priv->conf.links[i].latency +=
13313949bee8SArchie Cobbs 			    ((hdrBytes * priv->conf.links[i].bandwidth) + 50)
13323949bee8SArchie Cobbs 				/ 100;
13333949bee8SArchie Cobbs 		}
13343949bee8SArchie Cobbs 	}
13353949bee8SArchie Cobbs 
13363949bee8SArchie Cobbs 	/* Update list of active links */
13373949bee8SArchie Cobbs 	bzero(&priv->activeLinks, sizeof(priv->activeLinks));
13383949bee8SArchie Cobbs 	priv->numActiveLinks = 0;
13393949bee8SArchie Cobbs 	priv->allLinksEqual = 1;
13403949bee8SArchie Cobbs 	for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
13413949bee8SArchie Cobbs 		if (priv->conf.links[i].enableLink && priv->links[i] != NULL) {
13423949bee8SArchie Cobbs 			priv->activeLinks[priv->numActiveLinks++] = i;
13433949bee8SArchie Cobbs 			if (priv->conf.links[i].latency
13443949bee8SArchie Cobbs 				  != priv->conf.links[0].latency
13453949bee8SArchie Cobbs 			    || priv->conf.links[i].bandwidth
13463949bee8SArchie Cobbs 				  != priv->conf.links[0].bandwidth)
13473949bee8SArchie Cobbs 				priv->allLinksEqual = 0;
13483949bee8SArchie Cobbs 		}
13493949bee8SArchie Cobbs 	}
13503949bee8SArchie Cobbs 
13513949bee8SArchie Cobbs 	/* Reset MP state if no longer active */
13523949bee8SArchie Cobbs 	if (!priv->conf.enableMultilink || priv->numActiveLinks == 0) {
13533949bee8SArchie Cobbs 		ng_ppp_free_frags(node);
13543949bee8SArchie Cobbs 		priv->mpSeqOut = MP_INITIAL_SEQ;
13553949bee8SArchie Cobbs 		bzero(&priv->qstat, sizeof(priv->qstat));
13563949bee8SArchie Cobbs 	}
13573949bee8SArchie Cobbs }
13583949bee8SArchie Cobbs 
13593949bee8SArchie Cobbs /*
13603949bee8SArchie Cobbs  * Determine if a new configuration would represent a valid change
13613949bee8SArchie Cobbs  * from the current configuration and link activity status.
13623949bee8SArchie Cobbs  */
13633949bee8SArchie Cobbs static int
13643949bee8SArchie Cobbs ng_ppp_config_valid(node_p node, const struct ng_ppp_node_config *newConf)
13653949bee8SArchie Cobbs {
13663949bee8SArchie Cobbs 	const priv_p priv = node->private;
13673949bee8SArchie Cobbs 	int i, newNumLinksActive;
13683949bee8SArchie Cobbs 
13693949bee8SArchie Cobbs 	/* Check per-link config and count how many links would be active */
13703949bee8SArchie Cobbs 	for (newNumLinksActive = i = 0; i < NG_PPP_MAX_LINKS; i++) {
13713949bee8SArchie Cobbs 		if (newConf->links[i].mru < MP_MIN_LINK_MRU)
13723949bee8SArchie Cobbs 			return (0);
13733949bee8SArchie Cobbs 		if (newConf->links[i].bandwidth == 0)
13743949bee8SArchie Cobbs 			return (0);
13753949bee8SArchie Cobbs 		if (newConf->links[i].bandwidth > NG_PPP_MAX_BANDWIDTH)
13763949bee8SArchie Cobbs 			return (0);
13773949bee8SArchie Cobbs 		if (newConf->links[i].latency > NG_PPP_MAX_LATENCY)
13783949bee8SArchie Cobbs 			return (0);
13793949bee8SArchie Cobbs 		if (newConf->links[i].enableLink && priv->links[i] != NULL)
13803949bee8SArchie Cobbs 			newNumLinksActive++;
13813949bee8SArchie Cobbs 	}
13823949bee8SArchie Cobbs 
13833949bee8SArchie Cobbs 	/* Check bundle parameters */
13843949bee8SArchie Cobbs 	if (priv->conf.enableMultilink && newConf->mrru < MP_MIN_MRRU)
13853949bee8SArchie Cobbs 		return (0);
13863949bee8SArchie Cobbs 
13873949bee8SArchie Cobbs 	/* At most one link can be active unless multi-link is enabled */
13883949bee8SArchie Cobbs 	if (!newConf->enableMultilink && newNumLinksActive > 1)
13893949bee8SArchie Cobbs 		return (0);
13903949bee8SArchie Cobbs 
13913949bee8SArchie Cobbs 	/* Disallow changes to multi-link configuration while MP is active */
13923949bee8SArchie Cobbs 	if (priv->numActiveLinks > 0 && newNumLinksActive > 0) {
13933949bee8SArchie Cobbs 		if (!priv->conf.enableMultilink != !newConf->enableMultilink
13943949bee8SArchie Cobbs 		    || !priv->conf.xmitShortSeq != !newConf->xmitShortSeq
13953949bee8SArchie Cobbs 		    || !priv->conf.recvShortSeq != !newConf->recvShortSeq)
13963949bee8SArchie Cobbs 			return (0);
13973949bee8SArchie Cobbs 	}
13983949bee8SArchie Cobbs 
13993949bee8SArchie Cobbs 	/* Configuration change is valid */
14003949bee8SArchie Cobbs 	return (1);
14013949bee8SArchie Cobbs }
14023949bee8SArchie Cobbs 
14033949bee8SArchie Cobbs /*
14043949bee8SArchie Cobbs  * Free all entries in the fragment queue
14053949bee8SArchie Cobbs  */
14063949bee8SArchie Cobbs static void
14073949bee8SArchie Cobbs ng_ppp_free_frags(node_p node)
14083949bee8SArchie Cobbs {
14093949bee8SArchie Cobbs 	const priv_p priv = node->private;
14103949bee8SArchie Cobbs 	struct ng_ppp_frag *qent, *next;
14113949bee8SArchie Cobbs 
14123949bee8SArchie Cobbs 	for (qent = CIRCLEQ_FIRST(&priv->frags);
14133949bee8SArchie Cobbs 	    qent != (void *) &priv->frags; qent = next) {
14143949bee8SArchie Cobbs 		next = CIRCLEQ_NEXT(qent, f_qent);
14153949bee8SArchie Cobbs 		NG_FREE_DATA(qent->data, qent->meta);
14163949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
14173949bee8SArchie Cobbs 	}
14183949bee8SArchie Cobbs 	CIRCLEQ_INIT(&priv->frags);
14194cf49a43SJulian Elischer }
14204cf49a43SJulian Elischer 
1421