xref: /freebsd/sys/netgraph/ng_ppp.c (revision 2b70adcbbe52d78f5828fe5c14850f6817c83d4a)
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)
632b70adcbSArchie Cobbs #define PROT_COMPRESSABLE(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
702b70adcbSArchie 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) {
2702b70adcbSArchie Cobbs 		const char *cp, *eptr;
2713949bee8SArchie Cobbs 
2722b70adcbSArchie Cobbs 		cp = name + strlen(NG_PPP_HOOK_LINK_PREFIX);
2732b70adcbSArchie Cobbs 		if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
2744cf49a43SJulian Elischer 			return (EINVAL);
2752b70adcbSArchie Cobbs 		linkNum = (int)strtoul(cp, &eptr, 10);
2762b70adcbSArchie Cobbs 		if (*eptr != '\0' || linkNum < 0 || linkNum >= NG_PPP_MAX_LINKS)
2773949bee8SArchie Cobbs 			return (EINVAL);
2783949bee8SArchie Cobbs 		hookPtr = &priv->links[linkNum];
2793949bee8SArchie Cobbs 		hookIndex = ~linkNum;
2803949bee8SArchie Cobbs 	} else {				/* must be a non-link hook */
2813949bee8SArchie Cobbs 		int i;
2824cf49a43SJulian Elischer 
2833949bee8SArchie Cobbs 		for (i = 0; ng_ppp_hook_names[i] != NULL; i++) {
2843949bee8SArchie Cobbs 			if (strcmp(name, ng_ppp_hook_names[i]) == 0) {
2853949bee8SArchie Cobbs 				hookPtr = &priv->hooks[i];
2863949bee8SArchie Cobbs 				hookIndex = i;
2873949bee8SArchie Cobbs 				break;
2883949bee8SArchie Cobbs 			}
2893949bee8SArchie Cobbs 		}
2903949bee8SArchie Cobbs 		if (ng_ppp_hook_names[i] == NULL)
2913949bee8SArchie Cobbs 			return (EINVAL);	/* no such hook */
2923949bee8SArchie Cobbs 	}
2933949bee8SArchie Cobbs 
2943949bee8SArchie Cobbs 	/* See if hook is already connected */
2953949bee8SArchie Cobbs 	if (*hookPtr != NULL)
2964cf49a43SJulian Elischer 		return (EISCONN);
2974cf49a43SJulian Elischer 
2983949bee8SArchie Cobbs 	/* Disallow more than one link unless multilink is enabled */
2993949bee8SArchie Cobbs 	if (linkNum != -1 && priv->conf.links[linkNum].enableLink
3003949bee8SArchie Cobbs 	    && !priv->conf.enableMultilink && priv->numActiveLinks >= 1)
3013949bee8SArchie Cobbs 		return (ENODEV);
3024cf49a43SJulian Elischer 
3034cf49a43SJulian Elischer 	/* OK */
3043949bee8SArchie Cobbs 	*hookPtr = hook;
3053949bee8SArchie Cobbs 	HOOK_INDEX(hook) = hookIndex;
3063949bee8SArchie Cobbs 	ng_ppp_update(node, 0);
3074cf49a43SJulian Elischer 	return (0);
3084cf49a43SJulian Elischer }
3094cf49a43SJulian Elischer 
3104cf49a43SJulian Elischer /*
3114cf49a43SJulian Elischer  * Receive a control message
3124cf49a43SJulian Elischer  */
3134cf49a43SJulian Elischer static int
3144cf49a43SJulian Elischer ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg,
3154cf49a43SJulian Elischer 	      const char *raddr, struct ng_mesg **rptr)
3164cf49a43SJulian Elischer {
3174cf49a43SJulian Elischer 	const priv_p priv = node->private;
3184cf49a43SJulian Elischer 	struct ng_mesg *resp = NULL;
3194cf49a43SJulian Elischer 	int error = 0;
3204cf49a43SJulian Elischer 
3214cf49a43SJulian Elischer 	switch (msg->header.typecookie) {
3224cf49a43SJulian Elischer 	case NGM_PPP_COOKIE:
3234cf49a43SJulian Elischer 		switch (msg->header.cmd) {
3243949bee8SArchie Cobbs 		case NGM_PPP_SET_CONFIG:
3253949bee8SArchie Cobbs 		    {
3263949bee8SArchie Cobbs 			struct ng_ppp_node_config *const newConf =
3273949bee8SArchie Cobbs 				(struct ng_ppp_node_config *) msg->data;
3283949bee8SArchie Cobbs 
3293949bee8SArchie Cobbs 			/* Check for invalid or illegal config */
3303949bee8SArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
3314cf49a43SJulian Elischer 				ERROUT(EINVAL);
3323949bee8SArchie Cobbs 			if (!ng_ppp_config_valid(node, newConf))
3333949bee8SArchie Cobbs 				ERROUT(EINVAL);
3343949bee8SArchie Cobbs 			priv->conf = *newConf;
3353949bee8SArchie Cobbs 			ng_ppp_update(node, 1);
3364cf49a43SJulian Elischer 			break;
3373949bee8SArchie Cobbs 		    }
3383949bee8SArchie Cobbs 		case NGM_PPP_GET_CONFIG:
3393949bee8SArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT);
3404cf49a43SJulian Elischer 			if (resp == NULL)
3414cf49a43SJulian Elischer 				ERROUT(ENOMEM);
3423949bee8SArchie Cobbs 			bcopy(&priv->conf, resp->data, sizeof(priv->conf));
3434cf49a43SJulian Elischer 			break;
3443949bee8SArchie Cobbs 		case NGM_PPP_GET_LINK_STATS:
3453949bee8SArchie Cobbs 		case NGM_PPP_CLR_LINK_STATS:
3463949bee8SArchie Cobbs 		    {
3473949bee8SArchie Cobbs 			struct ng_ppp_link_stat *stats;
3483949bee8SArchie Cobbs 			u_int16_t linkNum;
3493949bee8SArchie Cobbs 
3503949bee8SArchie Cobbs 			if (msg->header.arglen != sizeof(u_int16_t))
3513949bee8SArchie Cobbs 				ERROUT(EINVAL);
3523949bee8SArchie Cobbs 			linkNum = *((u_int16_t *) msg->data);
3533949bee8SArchie Cobbs 			if (linkNum >= NG_PPP_MAX_LINKS
3543949bee8SArchie Cobbs 			    && linkNum != NG_PPP_BUNDLE_LINKNUM)
3553949bee8SArchie Cobbs 				ERROUT(EINVAL);
3563949bee8SArchie Cobbs 			stats = (linkNum == NG_PPP_BUNDLE_LINKNUM) ?
3573949bee8SArchie Cobbs 				&priv->bundleStats : &priv->linkStats[linkNum];
3583949bee8SArchie Cobbs 			if (msg->header.cmd == NGM_PPP_GET_LINK_STATS) {
3593949bee8SArchie Cobbs 				NG_MKRESPONSE(resp, msg,
3603949bee8SArchie Cobbs 				    sizeof(struct ng_ppp_link_stat), M_NOWAIT);
3613949bee8SArchie Cobbs 				if (resp == NULL)
3623949bee8SArchie Cobbs 					ERROUT(ENOMEM);
3633949bee8SArchie Cobbs 				bcopy(stats, resp->data, sizeof(*stats));
3643949bee8SArchie Cobbs 			} else
3653949bee8SArchie Cobbs 				bzero(stats, sizeof(*stats));
3664cf49a43SJulian Elischer 			break;
3673949bee8SArchie Cobbs 		    }
3684cf49a43SJulian Elischer 		default:
3694cf49a43SJulian Elischer 			error = EINVAL;
3704cf49a43SJulian Elischer 			break;
3714cf49a43SJulian Elischer 		}
3724cf49a43SJulian Elischer 		break;
3733949bee8SArchie Cobbs 	case NGM_VJC_COOKIE:
3743949bee8SArchie Cobbs 	   {
3753949bee8SArchie Cobbs 	       char path[NG_PATHLEN + 1];
3763949bee8SArchie Cobbs 	       node_p origNode;
3773949bee8SArchie Cobbs 
3783949bee8SArchie Cobbs 	       if ((error = ng_path2node(node, raddr, &origNode, NULL)) != 0)
3793949bee8SArchie Cobbs 		       ERROUT(error);
3803949bee8SArchie Cobbs 	       snprintf(path, sizeof(path), "[%lx]:%s",
3813949bee8SArchie Cobbs 		   (long) node, NG_PPP_HOOK_VJC_IP);
3823949bee8SArchie Cobbs 	       return ng_send_msg(origNode, msg, path, rptr);
3833949bee8SArchie Cobbs 	       break;
3843949bee8SArchie Cobbs 	   }
3854cf49a43SJulian Elischer 	default:
3864cf49a43SJulian Elischer 		error = EINVAL;
3874cf49a43SJulian Elischer 		break;
3884cf49a43SJulian Elischer 	}
3894cf49a43SJulian Elischer 	if (rptr)
3904cf49a43SJulian Elischer 		*rptr = resp;
3914cf49a43SJulian Elischer 	else if (resp)
3924cf49a43SJulian Elischer 		FREE(resp, M_NETGRAPH);
3934cf49a43SJulian Elischer 
3944cf49a43SJulian Elischer done:
3954cf49a43SJulian Elischer 	FREE(msg, M_NETGRAPH);
3964cf49a43SJulian Elischer 	return (error);
3974cf49a43SJulian Elischer }
3984cf49a43SJulian Elischer 
3994cf49a43SJulian Elischer /*
4004cf49a43SJulian Elischer  * Receive data on a hook
4014cf49a43SJulian Elischer  */
4024cf49a43SJulian Elischer static int
4034cf49a43SJulian Elischer ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
4044cf49a43SJulian Elischer {
4054cf49a43SJulian Elischer 	const node_p node = hook->node;
4064cf49a43SJulian Elischer 	const priv_p priv = node->private;
4073949bee8SArchie Cobbs 	const int index = HOOK_INDEX(hook);
4083949bee8SArchie Cobbs 	u_int16_t linkNum = NG_PPP_BUNDLE_LINKNUM;
4093949bee8SArchie Cobbs 	hook_p outHook = NULL;
4103949bee8SArchie Cobbs 	int proto = 0, error;
4114cf49a43SJulian Elischer 
4123949bee8SArchie Cobbs 	/* Did it come from a link hook? */
4133949bee8SArchie Cobbs 	if (index < 0) {
4144cf49a43SJulian Elischer 
4153949bee8SArchie Cobbs 		/* Is link active? */
4163949bee8SArchie Cobbs 		linkNum = (u_int16_t)~index;
4173949bee8SArchie Cobbs 		KASSERT(linkNum < NG_PPP_MAX_LINKS,
4183949bee8SArchie Cobbs 		    ("%s: bogus index 0x%x", __FUNCTION__, index));
4193949bee8SArchie Cobbs 		if (!priv->conf.links[linkNum].enableLink) {
4204cf49a43SJulian Elischer 			NG_FREE_DATA(m, meta);
4213949bee8SArchie Cobbs 			return (ENXIO);
4223949bee8SArchie Cobbs 		}
4233949bee8SArchie Cobbs 
4243949bee8SArchie Cobbs 		/* Stats */
4253949bee8SArchie Cobbs 		priv->linkStats[linkNum].recvFrames++;
4263949bee8SArchie Cobbs 		priv->linkStats[linkNum].recvOctets += m->m_pkthdr.len;
4273949bee8SArchie Cobbs 
4283949bee8SArchie Cobbs 		/* Dispatch incoming frame */
4293949bee8SArchie Cobbs 		return ng_ppp_input(node, linkNum, m, meta);
4303949bee8SArchie Cobbs 	}
4313949bee8SArchie Cobbs 
4323949bee8SArchie Cobbs 	/* Get protocol & check if data allowed from this hook */
4333949bee8SArchie Cobbs 	switch (index) {
4343949bee8SArchie Cobbs 
4353949bee8SArchie Cobbs 	/* Downwards flowing data */
4363949bee8SArchie Cobbs 	case HOOK_INDEX_ATALK:
4373949bee8SArchie Cobbs 		if (!priv->conf.enableAtalk) {
4383949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4393949bee8SArchie Cobbs 			return (ENXIO);
4403949bee8SArchie Cobbs 		}
4413949bee8SArchie Cobbs 		proto = PROT_APPLETALK;
4423949bee8SArchie Cobbs 		break;
4433949bee8SArchie Cobbs 	case HOOK_INDEX_IPX:
4443949bee8SArchie Cobbs 		if (!priv->conf.enableIPX) {
4453949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4463949bee8SArchie Cobbs 			return (ENXIO);
4473949bee8SArchie Cobbs 		}
4483949bee8SArchie Cobbs 		proto = PROT_IPX;
4493949bee8SArchie Cobbs 		break;
4503949bee8SArchie Cobbs 	case HOOK_INDEX_INET:
4513949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_VJIP:
4523949bee8SArchie Cobbs 		if (!priv->conf.enableIP) {
4533949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4543949bee8SArchie Cobbs 			return (ENXIO);
4553949bee8SArchie Cobbs 		}
4563949bee8SArchie Cobbs 		proto = PROT_IP;
4573949bee8SArchie Cobbs 		break;
4583949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_COMP:
4593949bee8SArchie Cobbs 		if (!priv->conf.enableVJCompression) {
4603949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4613949bee8SArchie Cobbs 			return (ENXIO);
4623949bee8SArchie Cobbs 		}
4633949bee8SArchie Cobbs 		proto = PROT_VJCOMP;
4643949bee8SArchie Cobbs 		break;
4653949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_UNCOMP:
4663949bee8SArchie Cobbs 		if (!priv->conf.enableVJCompression) {
4673949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4683949bee8SArchie Cobbs 			return (ENXIO);
4693949bee8SArchie Cobbs 		}
4703949bee8SArchie Cobbs 		proto = PROT_VJUNCOMP;
4713949bee8SArchie Cobbs 		break;
4723949bee8SArchie Cobbs 	case HOOK_INDEX_COMPRESS:
4733949bee8SArchie Cobbs 		if (!priv->conf.enableCompression) {
4743949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4753949bee8SArchie Cobbs 			return (ENXIO);
4763949bee8SArchie Cobbs 		}
4773949bee8SArchie Cobbs 		proto = PROT_COMPD;
4783949bee8SArchie Cobbs 		break;
4793949bee8SArchie Cobbs 	case HOOK_INDEX_ENCRYPT:
4803949bee8SArchie Cobbs 		if (!priv->conf.enableEncryption) {
4813949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4823949bee8SArchie Cobbs 			return (ENXIO);
4833949bee8SArchie Cobbs 		}
4843949bee8SArchie Cobbs 		proto = PROT_CRYPTD;
4853949bee8SArchie Cobbs 		break;
4863949bee8SArchie Cobbs 	case HOOK_INDEX_BYPASS:
4873949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 4) {
4883949bee8SArchie Cobbs 			NG_FREE_META(meta);
4893949bee8SArchie Cobbs 			return (EINVAL);
4903949bee8SArchie Cobbs 		}
4913949bee8SArchie Cobbs 		if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
4923949bee8SArchie Cobbs 			NG_FREE_META(meta);
4933949bee8SArchie Cobbs 			return (ENOBUFS);
4943949bee8SArchie Cobbs 		}
4953949bee8SArchie Cobbs 		linkNum = ntohs(mtod(m, u_int16_t *)[0]);
4963949bee8SArchie Cobbs 		proto = ntohs(mtod(m, u_int16_t *)[1]);
4973949bee8SArchie Cobbs 		m_adj(m, 4);
4983949bee8SArchie Cobbs 		if (linkNum >= NG_PPP_MAX_LINKS
4993949bee8SArchie Cobbs 		    && linkNum != NG_PPP_BUNDLE_LINKNUM)
5003949bee8SArchie Cobbs 			return (EINVAL);
5013949bee8SArchie Cobbs 		break;
5023949bee8SArchie Cobbs 
5033949bee8SArchie Cobbs 	/* Upwards flowing data */
5043949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_IP:
5053949bee8SArchie Cobbs 		if (!priv->conf.enableVJDecompression) {
5063949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5073949bee8SArchie Cobbs 			return (ENXIO);
5083949bee8SArchie Cobbs 		}
5093949bee8SArchie Cobbs 		break;
5103949bee8SArchie Cobbs 	case HOOK_INDEX_DECOMPRESS:
5113949bee8SArchie Cobbs 		if (!priv->conf.enableDecompression) {
5123949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5133949bee8SArchie Cobbs 			return (ENXIO);
5143949bee8SArchie Cobbs 		}
5153949bee8SArchie Cobbs 		break;
5163949bee8SArchie Cobbs 	case HOOK_INDEX_DECRYPT:
5173949bee8SArchie Cobbs 		if (!priv->conf.enableDecryption) {
5183949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5193949bee8SArchie Cobbs 			return (ENXIO);
5203949bee8SArchie Cobbs 		}
5213949bee8SArchie Cobbs 		break;
5223949bee8SArchie Cobbs 	default:
5233949bee8SArchie Cobbs 		KASSERT(0, ("%s: bogus index 0x%x", __FUNCTION__, index));
5243949bee8SArchie Cobbs 	}
5253949bee8SArchie Cobbs 
5263949bee8SArchie Cobbs 	/* Now figure out what to do with the frame */
5273949bee8SArchie Cobbs 	switch (index) {
5283949bee8SArchie Cobbs 	case HOOK_INDEX_INET:
5293949bee8SArchie Cobbs 		if (priv->conf.enableVJCompression && priv->vjCompHooked) {
5303949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_IP];
5313949bee8SArchie Cobbs 			break;
5323949bee8SArchie Cobbs 		}
5333949bee8SArchie Cobbs 		/* FALLTHROUGH */
5343949bee8SArchie Cobbs 	case HOOK_INDEX_ATALK:
5353949bee8SArchie Cobbs 	case HOOK_INDEX_IPX:
5363949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_COMP:
5373949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_UNCOMP:
5383949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_VJIP:
5393949bee8SArchie Cobbs 		if (priv->conf.enableCompression
5403949bee8SArchie Cobbs 		    && priv->hooks[HOOK_INDEX_COMPRESS] != NULL) {
5413949bee8SArchie Cobbs 			if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) {
5423949bee8SArchie Cobbs 				NG_FREE_META(meta);
5433949bee8SArchie Cobbs 				return (ENOBUFS);
5443949bee8SArchie Cobbs 			}
5453949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_COMPRESS];
5463949bee8SArchie Cobbs 			break;
5473949bee8SArchie Cobbs 		}
5483949bee8SArchie Cobbs 		/* FALLTHROUGH */
5493949bee8SArchie Cobbs 	case HOOK_INDEX_COMPRESS:
5503949bee8SArchie Cobbs 		if (priv->conf.enableEncryption
5513949bee8SArchie Cobbs 		    && priv->hooks[HOOK_INDEX_ENCRYPT] != NULL) {
5523949bee8SArchie Cobbs 			if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) {
5533949bee8SArchie Cobbs 				NG_FREE_META(meta);
5543949bee8SArchie Cobbs 				return (ENOBUFS);
5553949bee8SArchie Cobbs 			}
5563949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_ENCRYPT];
5573949bee8SArchie Cobbs 			break;
5583949bee8SArchie Cobbs 		}
5593949bee8SArchie Cobbs 		/* FALLTHROUGH */
5603949bee8SArchie Cobbs 	case HOOK_INDEX_ENCRYPT:
5613949bee8SArchie Cobbs 	case HOOK_INDEX_BYPASS:
5623949bee8SArchie Cobbs 		if ((m = ng_ppp_addproto(m, proto,
5633949bee8SArchie Cobbs 		    linkNum == NG_PPP_BUNDLE_LINKNUM
5643949bee8SArchie Cobbs 		      || priv->conf.links[linkNum].enableProtoComp)) == NULL) {
5653949bee8SArchie Cobbs 			NG_FREE_META(meta);
5663949bee8SArchie Cobbs 			return (ENOBUFS);
5673949bee8SArchie Cobbs 		}
5683949bee8SArchie Cobbs 		return ng_ppp_output(node, NG_PPP_BUNDLE_LINKNUM, m, meta);
5693949bee8SArchie Cobbs 
5703949bee8SArchie Cobbs 	/* Incoming data */
5713949bee8SArchie Cobbs 	case HOOK_INDEX_DECRYPT:
5723949bee8SArchie Cobbs 	case HOOK_INDEX_DECOMPRESS:
5733949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_IP:
5743949bee8SArchie Cobbs 		return ng_ppp_input(node, NG_PPP_BUNDLE_LINKNUM, m, meta);
5753949bee8SArchie Cobbs 	}
5763949bee8SArchie Cobbs 
5773949bee8SArchie Cobbs 	/* Send packet out hook */
5783949bee8SArchie Cobbs 	NG_SEND_DATA(error, outHook, m, meta);
5794cf49a43SJulian Elischer 	return (error);
5804cf49a43SJulian Elischer }
5814cf49a43SJulian Elischer 
5824cf49a43SJulian Elischer /*
5834cf49a43SJulian Elischer  * Destroy node
5844cf49a43SJulian Elischer  */
5854cf49a43SJulian Elischer static int
5864cf49a43SJulian Elischer ng_ppp_rmnode(node_p node)
5874cf49a43SJulian Elischer {
5884cf49a43SJulian Elischer 	const priv_p priv = node->private;
5894cf49a43SJulian Elischer 
5904cf49a43SJulian Elischer 	/* Take down netgraph node */
5914cf49a43SJulian Elischer 	node->flags |= NG_INVALID;
5924cf49a43SJulian Elischer 	ng_cutlinks(node);
5934cf49a43SJulian Elischer 	ng_unname(node);
5944cf49a43SJulian Elischer 	bzero(priv, sizeof(*priv));
5954cf49a43SJulian Elischer 	FREE(priv, M_NETGRAPH);
5964cf49a43SJulian Elischer 	node->private = NULL;
5974cf49a43SJulian Elischer 	ng_unref(node);		/* let the node escape */
5984cf49a43SJulian Elischer 	return (0);
5994cf49a43SJulian Elischer }
6004cf49a43SJulian Elischer 
6014cf49a43SJulian Elischer /*
6024cf49a43SJulian Elischer  * Hook disconnection
6034cf49a43SJulian Elischer  */
6044cf49a43SJulian Elischer static int
6054cf49a43SJulian Elischer ng_ppp_disconnect(hook_p hook)
6064cf49a43SJulian Elischer {
6074cf49a43SJulian Elischer 	if (hook->node->numhooks == 0)
6084cf49a43SJulian Elischer 		ng_rmnode(hook->node);
6094cf49a43SJulian Elischer 	return (0);
6104cf49a43SJulian Elischer }
6114cf49a43SJulian Elischer 
6124cf49a43SJulian Elischer /************************************************************************
6134cf49a43SJulian Elischer 			HELPER STUFF
6144cf49a43SJulian Elischer  ************************************************************************/
6154cf49a43SJulian Elischer 
6164cf49a43SJulian Elischer /*
6173949bee8SArchie Cobbs  * Handle an incoming frame.  Extract the PPP protocol number
6183949bee8SArchie Cobbs  * and dispatch accordingly.
6194cf49a43SJulian Elischer  */
6204cf49a43SJulian Elischer static int
6213949bee8SArchie Cobbs ng_ppp_input(node_p node, int linkNum, struct mbuf *m, meta_p meta)
6224cf49a43SJulian Elischer {
6233949bee8SArchie Cobbs 	const priv_p priv = node->private;
6243949bee8SArchie Cobbs 	hook_p outHook = NULL;
6253949bee8SArchie Cobbs 	int proto, error;
6264cf49a43SJulian Elischer 
6273949bee8SArchie Cobbs 	/* Extract protocol number */
6283949bee8SArchie Cobbs 	for (proto = 0; !PROT_VALID(proto) && m->m_pkthdr.len > 0; ) {
6293949bee8SArchie Cobbs 		if (m->m_len < 1 && (m = m_pullup(m, 1)) == NULL) {
6303949bee8SArchie Cobbs 			NG_FREE_META(meta);
6313949bee8SArchie Cobbs 			return (ENOBUFS);
6324cf49a43SJulian Elischer 		}
6333949bee8SArchie Cobbs 		proto = (proto << 8) + *mtod(m, u_char *);
6343949bee8SArchie Cobbs 		m_adj(m, 1);
6353949bee8SArchie Cobbs 	}
6363949bee8SArchie Cobbs 	if (!PROT_VALID(proto)) {
6373949bee8SArchie Cobbs 		if (linkNum == NG_PPP_BUNDLE_LINKNUM)
6383949bee8SArchie Cobbs 			priv->bundleStats.badProtos++;
6393949bee8SArchie Cobbs 		else
6403949bee8SArchie Cobbs 			priv->linkStats[linkNum].badProtos++;
6413949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
6423949bee8SArchie Cobbs 		return (EINVAL);
6433949bee8SArchie Cobbs 	}
6443949bee8SArchie Cobbs 
6453949bee8SArchie Cobbs 	/* Check protocol */
6463949bee8SArchie Cobbs 	switch (proto) {
6473949bee8SArchie Cobbs 	case PROT_COMPD:
6483949bee8SArchie Cobbs 		if (priv->conf.enableDecompression)
6493949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_DECOMPRESS];
6503949bee8SArchie Cobbs 		break;
6513949bee8SArchie Cobbs 	case PROT_CRYPTD:
6523949bee8SArchie Cobbs 		if (priv->conf.enableDecryption)
6533949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_DECRYPT];
6543949bee8SArchie Cobbs 		break;
6553949bee8SArchie Cobbs 	case PROT_VJCOMP:
6563949bee8SArchie Cobbs 		if (priv->conf.enableVJDecompression && priv->vjCompHooked)
6573949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_COMP];
6583949bee8SArchie Cobbs 		break;
6593949bee8SArchie Cobbs 	case PROT_VJUNCOMP:
6603949bee8SArchie Cobbs 		if (priv->conf.enableVJDecompression && priv->vjCompHooked)
6613949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_UNCOMP];
6623949bee8SArchie Cobbs 		break;
6633949bee8SArchie Cobbs 	case PROT_MP:
6643949bee8SArchie Cobbs 		if (priv->conf.enableMultilink) {
6653949bee8SArchie Cobbs 			NG_FREE_META(meta);
6663949bee8SArchie Cobbs 			return ng_ppp_mp_input(node, linkNum, m, meta);
6673949bee8SArchie Cobbs 		}
6683949bee8SArchie Cobbs 		break;
6693949bee8SArchie Cobbs 	case PROT_APPLETALK:
6703949bee8SArchie Cobbs 		if (priv->conf.enableAtalk)
6713949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_ATALK];
6723949bee8SArchie Cobbs 		break;
6733949bee8SArchie Cobbs 	case PROT_IPX:
6743949bee8SArchie Cobbs 		if (priv->conf.enableIPX)
6753949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_IPX];
6763949bee8SArchie Cobbs 		break;
6773949bee8SArchie Cobbs 	case PROT_IP:
6783949bee8SArchie Cobbs 		if (priv->conf.enableIP)
6793949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_INET];
6803949bee8SArchie Cobbs 		break;
6813949bee8SArchie Cobbs 	}
6823949bee8SArchie Cobbs 
6833949bee8SArchie Cobbs 	/* For unknown/inactive protocols, forward out the bypass hook */
6843949bee8SArchie Cobbs 	if (outHook == NULL) {
6853949bee8SArchie Cobbs 		M_PREPEND(m, 4, M_NOWAIT);
6863949bee8SArchie Cobbs 		if (m == NULL || (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL))
6873949bee8SArchie Cobbs 			return (ENOBUFS);
6883949bee8SArchie Cobbs 		mtod(m, u_int16_t *)[0] = htons(linkNum);
6893949bee8SArchie Cobbs 		mtod(m, u_int16_t *)[1] = htons(proto);
6903949bee8SArchie Cobbs 		outHook = priv->hooks[HOOK_INDEX_BYPASS];
6913949bee8SArchie Cobbs 	}
6923949bee8SArchie Cobbs 
6933949bee8SArchie Cobbs 	/* Forward frame */
6943949bee8SArchie Cobbs 	NG_SEND_DATA(error, outHook, m, meta);
6953949bee8SArchie Cobbs 	return (error);
6964cf49a43SJulian Elischer }
6974cf49a43SJulian Elischer 
6984cf49a43SJulian Elischer /*
6993949bee8SArchie Cobbs  * Deliver a frame out a link, either a real one or NG_PPP_BUNDLE_LINKNUM
7004cf49a43SJulian Elischer  */
7013949bee8SArchie Cobbs static int
7023949bee8SArchie Cobbs ng_ppp_output(node_p node, int linkNum, struct mbuf *m, meta_p meta)
7034cf49a43SJulian Elischer {
7043949bee8SArchie Cobbs 	const priv_p priv = node->private;
7053949bee8SArchie Cobbs 	int error;
7064cf49a43SJulian Elischer 
7073949bee8SArchie Cobbs 	/* Check for bundle virtual link */
7083949bee8SArchie Cobbs 	if (linkNum == NG_PPP_BUNDLE_LINKNUM) {
7093949bee8SArchie Cobbs 		if (priv->conf.enableMultilink)
7103949bee8SArchie Cobbs 			return ng_ppp_mp_output(node, m, meta);
7113949bee8SArchie Cobbs 		linkNum = priv->activeLinks[0];
7124cf49a43SJulian Elischer 	}
7133949bee8SArchie Cobbs 
7143949bee8SArchie Cobbs 	/* Check link status */
7153949bee8SArchie Cobbs 	if (!priv->conf.links[linkNum].enableLink)
7163949bee8SArchie Cobbs 		return (ENXIO);
7173949bee8SArchie Cobbs 	if (priv->links[linkNum] == NULL) {
7183949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
7193949bee8SArchie Cobbs 		return (ENETDOWN);
7203949bee8SArchie Cobbs 	}
7213949bee8SArchie Cobbs 
7223949bee8SArchie Cobbs 	/* Deliver frame */
7233949bee8SArchie Cobbs 	NG_SEND_DATA(error, priv->links[linkNum], m, meta);
724fb1fc8abSArchie Cobbs 
725fb1fc8abSArchie Cobbs 	/* Update stats and 'bytes in queue' counter */
726fb1fc8abSArchie Cobbs 	if (error == 0) {
727fb1fc8abSArchie Cobbs 		priv->linkStats[linkNum].xmitFrames++;
728fb1fc8abSArchie Cobbs 		priv->linkStats[linkNum].xmitOctets += m->m_pkthdr.len;
729fb1fc8abSArchie Cobbs 		priv->qstat[linkNum].bytesInQueue += m->m_pkthdr.len;
730fb1fc8abSArchie Cobbs 		microtime(&priv->qstat[linkNum].lastWrite);
731fb1fc8abSArchie Cobbs 	}
7323949bee8SArchie Cobbs 	return error;
7333949bee8SArchie Cobbs }
7343949bee8SArchie Cobbs 
7353949bee8SArchie Cobbs /*
7363949bee8SArchie Cobbs  * Handle an incoming multi-link fragment
7373949bee8SArchie Cobbs  */
7383949bee8SArchie Cobbs static int
7393949bee8SArchie Cobbs ng_ppp_mp_input(node_p node, int linkNum, struct mbuf *m, meta_p meta)
7403949bee8SArchie Cobbs {
7413949bee8SArchie Cobbs 	const priv_p priv = node->private;
7423949bee8SArchie Cobbs 	struct ng_ppp_frag frag0, *frag = &frag0;
7433949bee8SArchie Cobbs 	struct ng_ppp_frag *qent, *qnext;
7443949bee8SArchie Cobbs 	struct ng_ppp_frag *first = NULL, *last = NULL;
7453949bee8SArchie Cobbs 	int diff, highSeq, nextSeq;
7463949bee8SArchie Cobbs 	struct mbuf *tail;
7473949bee8SArchie Cobbs 
7483949bee8SArchie Cobbs 	/* Extract fragment information from MP header */
7493949bee8SArchie Cobbs 	if (priv->conf.recvShortSeq) {
7503949bee8SArchie Cobbs 		u_int16_t shdr;
7513949bee8SArchie Cobbs 
7523949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 2) {
7533949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
7543949bee8SArchie Cobbs 			return (EINVAL);
7553949bee8SArchie Cobbs 		}
7563949bee8SArchie Cobbs 		if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) {
7573949bee8SArchie Cobbs 			NG_FREE_META(meta);
7583949bee8SArchie Cobbs 			return (ENOBUFS);
7593949bee8SArchie Cobbs 		}
7603949bee8SArchie Cobbs 		shdr = ntohs(*mtod(m, u_int16_t *));
7613949bee8SArchie Cobbs 		frag->seq = shdr & MP_SHORT_SEQ_MASK;
7623949bee8SArchie Cobbs 		frag->first = (shdr & MP_SHORT_FIRST_FLAG) != 0;
7633949bee8SArchie Cobbs 		frag->last = (shdr & MP_SHORT_LAST_FLAG) != 0;
7643949bee8SArchie Cobbs 		highSeq = CIRCLEQ_EMPTY(&priv->frags) ?
7653949bee8SArchie Cobbs 		    frag->seq : CIRCLEQ_FIRST(&priv->frags)->seq;
7663949bee8SArchie Cobbs 		diff = MP_SHORT_SEQ_DIFF(frag->seq, highSeq);
7673949bee8SArchie Cobbs 		m_adj(m, 2);
7683949bee8SArchie Cobbs 	} else {
7693949bee8SArchie Cobbs 		u_int32_t lhdr;
7703949bee8SArchie Cobbs 
7713949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 4) {
7723949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
7733949bee8SArchie Cobbs 			return (EINVAL);
7743949bee8SArchie Cobbs 		}
7753949bee8SArchie Cobbs 		if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
7763949bee8SArchie Cobbs 			NG_FREE_META(meta);
7773949bee8SArchie Cobbs 			return (ENOBUFS);
7783949bee8SArchie Cobbs 		}
7793949bee8SArchie Cobbs 		lhdr = ntohl(*mtod(m, u_int32_t *));
7803949bee8SArchie Cobbs 		frag->seq = lhdr & MP_LONG_SEQ_MASK;
7813949bee8SArchie Cobbs 		frag->first = (lhdr & MP_LONG_FIRST_FLAG) != 0;
7823949bee8SArchie Cobbs 		frag->last = (lhdr & MP_LONG_LAST_FLAG) != 0;
7833949bee8SArchie Cobbs 		highSeq = CIRCLEQ_EMPTY(&priv->frags) ?
7843949bee8SArchie Cobbs 		    frag->seq : CIRCLEQ_FIRST(&priv->frags)->seq;
7853949bee8SArchie Cobbs 		diff = MP_LONG_SEQ_DIFF(frag->seq, highSeq);
7863949bee8SArchie Cobbs 		m_adj(m, 4);
7873949bee8SArchie Cobbs 	}
7883949bee8SArchie Cobbs 	frag->data = m;
7893949bee8SArchie Cobbs 	frag->meta = meta;
7903949bee8SArchie Cobbs 
7913949bee8SArchie Cobbs 	/* If the sequence number makes a large jump, empty the queue */
7923949bee8SArchie Cobbs 	if (diff <= -MP_INSANE_SEQ_JUMP || diff >= MP_INSANE_SEQ_JUMP)
7933949bee8SArchie Cobbs 		ng_ppp_free_frags(node);
7943949bee8SArchie Cobbs 
7953949bee8SArchie Cobbs 	/* Optimization: handle a frame that's all in one fragment */
7963949bee8SArchie Cobbs 	if (frag->first && frag->last)
7973949bee8SArchie Cobbs 		return ng_ppp_input(node, NG_PPP_BUNDLE_LINKNUM, m, meta);
7983949bee8SArchie Cobbs 
7993949bee8SArchie Cobbs 	/* Allocate a new frag struct for the queue */
8003949bee8SArchie Cobbs 	MALLOC(frag, struct ng_ppp_frag *, sizeof(*frag), M_NETGRAPH, M_NOWAIT);
8013949bee8SArchie Cobbs 	if (frag == NULL) {
8023949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
8033949bee8SArchie Cobbs 		return (ENOMEM);
8043949bee8SArchie Cobbs 	}
8053949bee8SArchie Cobbs 	*frag = frag0;
8063949bee8SArchie Cobbs 	meta = NULL;
8073949bee8SArchie Cobbs 	m = NULL;
8083949bee8SArchie Cobbs 
8093949bee8SArchie Cobbs 	/* Add fragment to queue, which is reverse sorted by sequence number */
8103949bee8SArchie Cobbs 	CIRCLEQ_FOREACH(qent, &priv->frags, f_qent) {
8113949bee8SArchie Cobbs 		diff = MP_SEQ_DIFF(frag->seq, qent->seq);
8123949bee8SArchie Cobbs 		if (diff > 0) {
8133949bee8SArchie Cobbs 			CIRCLEQ_INSERT_BEFORE(&priv->frags, qent, frag, f_qent);
8143949bee8SArchie Cobbs 			break;
8153949bee8SArchie Cobbs 		} else if (diff == 0) {	     /* should never happen! */
8163949bee8SArchie Cobbs 			log(LOG_ERR, "%s: rec'd dup MP fragment\n", node->name);
8173949bee8SArchie Cobbs 			if (linkNum == NG_PPP_BUNDLE_LINKNUM)
8183949bee8SArchie Cobbs 				priv->linkStats[linkNum].dupFragments++;
8193949bee8SArchie Cobbs 			else
8203949bee8SArchie Cobbs 				priv->bundleStats.dupFragments++;
8213949bee8SArchie Cobbs 			NG_FREE_DATA(frag->data, frag->meta);
8223949bee8SArchie Cobbs 			FREE(frag, M_NETGRAPH);
8233949bee8SArchie Cobbs 			return (EINVAL);
8243949bee8SArchie Cobbs 		}
8253949bee8SArchie Cobbs 	}
8263949bee8SArchie Cobbs 	if (qent == NULL)
8273949bee8SArchie Cobbs 		CIRCLEQ_INSERT_TAIL(&priv->frags, frag, f_qent);
8283949bee8SArchie Cobbs 
8293949bee8SArchie Cobbs 	/* Find the first fragment in the possibly newly completed frame */
8303949bee8SArchie Cobbs 	for (nextSeq = frag->seq, qent = frag;
8313949bee8SArchie Cobbs 	    qent != (void *) &priv->frags;
8323949bee8SArchie Cobbs 	    qent = CIRCLEQ_PREV(qent, f_qent)) {
8333949bee8SArchie Cobbs 		if (qent->seq != nextSeq)
8343949bee8SArchie Cobbs 			goto pruneQueue;
8353949bee8SArchie Cobbs 		if (qent->first) {
8363949bee8SArchie Cobbs 			first = qent;
8373949bee8SArchie Cobbs 			break;
8383949bee8SArchie Cobbs 		}
8393949bee8SArchie Cobbs 		nextSeq = (nextSeq + 1) & MP_SEQ_MASK;
8403949bee8SArchie Cobbs 	}
8413949bee8SArchie Cobbs 
8423949bee8SArchie Cobbs 	/* Find the last fragment in the possibly newly completed frame */
8433949bee8SArchie Cobbs 	for (nextSeq = frag->seq, qent = frag;
8443949bee8SArchie Cobbs 	    qent != (void *) &priv->frags;
8453949bee8SArchie Cobbs 	    qent = CIRCLEQ_NEXT(qent, f_qent)) {
8463949bee8SArchie Cobbs 		if (qent->seq != nextSeq)
8473949bee8SArchie Cobbs 			goto pruneQueue;
8483949bee8SArchie Cobbs 		if (qent->last) {
8493949bee8SArchie Cobbs 			last = qent;
8503949bee8SArchie Cobbs 			break;
8513949bee8SArchie Cobbs 		}
8523949bee8SArchie Cobbs 		nextSeq = (nextSeq - 1) & MP_SEQ_MASK;
8533949bee8SArchie Cobbs 	}
8543949bee8SArchie Cobbs 
8553949bee8SArchie Cobbs 	/* We have a complete frame, extract it from the queue */
8563949bee8SArchie Cobbs 	for (tail = NULL, qent = first; qent != NULL; qent = qnext) {
8573949bee8SArchie Cobbs 		qnext = CIRCLEQ_PREV(qent, f_qent);
8583949bee8SArchie Cobbs 		CIRCLEQ_REMOVE(&priv->frags, qent, f_qent);
8593949bee8SArchie Cobbs 		if (tail == NULL) {
8603949bee8SArchie Cobbs 			tail = m = qent->data;
8613949bee8SArchie Cobbs 			meta = qent->meta;	/* inherit first frag's meta */
8623949bee8SArchie Cobbs 		} else {
8633949bee8SArchie Cobbs 			m->m_pkthdr.len += qent->data->m_pkthdr.len;
8643949bee8SArchie Cobbs 			tail->m_next = qent->data;
8653949bee8SArchie Cobbs 			NG_FREE_META(qent->meta); /* drop other frag's metas */
8663949bee8SArchie Cobbs 		}
8673949bee8SArchie Cobbs 		while (tail->m_next != NULL)
8683949bee8SArchie Cobbs 			tail = tail->m_next;
8693949bee8SArchie Cobbs 		if (qent == last)
8703949bee8SArchie Cobbs 			qnext = NULL;
8713949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
8723949bee8SArchie Cobbs 	}
8733949bee8SArchie Cobbs 
8743949bee8SArchie Cobbs pruneQueue:
8753949bee8SArchie Cobbs 	/* Prune out stale entries in the queue */
8763949bee8SArchie Cobbs 	for (qent = CIRCLEQ_LAST(&priv->frags);
8773949bee8SArchie Cobbs 	    qent != (void *) &priv->frags; qent = qnext) {
8783949bee8SArchie Cobbs 		if (MP_SEQ_DIFF(highSeq, qent->seq) <= MP_MAX_SEQ_LINGER)
8793949bee8SArchie Cobbs 			break;
8803949bee8SArchie Cobbs 		qnext = CIRCLEQ_PREV(qent, f_qent);
8813949bee8SArchie Cobbs 		CIRCLEQ_REMOVE(&priv->frags, qent, f_qent);
8823949bee8SArchie Cobbs 		NG_FREE_DATA(qent->data, qent->meta);
8833949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
8843949bee8SArchie Cobbs 	}
8853949bee8SArchie Cobbs 
8863949bee8SArchie Cobbs 	/* Deliver newly completed frame, if any */
8873949bee8SArchie Cobbs 	return m ? ng_ppp_input(node, NG_PPP_BUNDLE_LINKNUM, m, meta) : 0;
8883949bee8SArchie Cobbs }
8893949bee8SArchie Cobbs 
8903949bee8SArchie Cobbs /*
8913949bee8SArchie Cobbs  * Deliver a frame out on the bundle, i.e., figure out how to fragment
8923949bee8SArchie Cobbs  * the frame across the individual PPP links and do so.
8933949bee8SArchie Cobbs  */
8943949bee8SArchie Cobbs static int
8953949bee8SArchie Cobbs ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta)
8963949bee8SArchie Cobbs {
8973949bee8SArchie Cobbs 	const priv_p priv = node->private;
8983949bee8SArchie Cobbs 	int distrib[NG_PPP_MAX_LINKS];
8993949bee8SArchie Cobbs 	int firstFragment;
9003949bee8SArchie Cobbs 	int activeLinkNum;
9013949bee8SArchie Cobbs 
9023949bee8SArchie Cobbs 	/* At least one link must be active */
9033949bee8SArchie Cobbs 	if (priv->numActiveLinks == 0) {
9043949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
9053949bee8SArchie Cobbs 		return (ENETDOWN);
9063949bee8SArchie Cobbs 	}
9073949bee8SArchie Cobbs 
9083949bee8SArchie Cobbs 	/* Round-robin strategy */
9093949bee8SArchie Cobbs 	if (priv->conf.enableRoundRobin || m->m_pkthdr.len < MP_MIN_FRAG_LEN) {
9103949bee8SArchie Cobbs 		activeLinkNum = priv->lastLink++ % priv->numActiveLinks;
9113949bee8SArchie Cobbs 		bzero(&distrib, priv->numActiveLinks * sizeof(distrib[0]));
9123949bee8SArchie Cobbs 		distrib[activeLinkNum] = m->m_pkthdr.len;
9133949bee8SArchie Cobbs 		goto deliver;
9143949bee8SArchie Cobbs 	}
9153949bee8SArchie Cobbs 
9163949bee8SArchie Cobbs 	/* Strategy when all links are equivalent (optimize the common case) */
9173949bee8SArchie Cobbs 	if (priv->allLinksEqual) {
9183949bee8SArchie Cobbs 		const int fraction = m->m_pkthdr.len / priv->numActiveLinks;
9193949bee8SArchie Cobbs 		int i, remain;
9203949bee8SArchie Cobbs 
9213949bee8SArchie Cobbs 		for (i = 0; i < priv->numActiveLinks; i++)
9223949bee8SArchie Cobbs 			distrib[priv->lastLink++ % priv->numActiveLinks]
9233949bee8SArchie Cobbs 			    = fraction;
9243949bee8SArchie Cobbs 		remain = m->m_pkthdr.len - (fraction * priv->numActiveLinks);
9253949bee8SArchie Cobbs 		while (remain > 0) {
9263949bee8SArchie Cobbs 			distrib[priv->lastLink++ % priv->numActiveLinks]++;
9273949bee8SArchie Cobbs 			remain--;
9283949bee8SArchie Cobbs 		}
9293949bee8SArchie Cobbs 		goto deliver;
9303949bee8SArchie Cobbs 	}
9313949bee8SArchie Cobbs 
9323949bee8SArchie Cobbs 	/* Strategy when all links are not equivalent */
9333949bee8SArchie Cobbs 	ng_ppp_mp_strategy(node, m->m_pkthdr.len, distrib);
9343949bee8SArchie Cobbs 
9353949bee8SArchie Cobbs deliver:
9363949bee8SArchie Cobbs 	/* Update stats */
9373949bee8SArchie Cobbs 	priv->bundleStats.xmitFrames++;
9383949bee8SArchie Cobbs 	priv->bundleStats.xmitOctets += m->m_pkthdr.len;
9393949bee8SArchie Cobbs 
9403949bee8SArchie Cobbs 	/* Send alloted portions of frame out on the link(s) */
9413949bee8SArchie Cobbs 	for (firstFragment = 1, activeLinkNum = priv->numActiveLinks - 1;
9423949bee8SArchie Cobbs 	    activeLinkNum >= 0; activeLinkNum--) {
9433949bee8SArchie Cobbs 		const int linkNum = priv->activeLinks[activeLinkNum];
9443949bee8SArchie Cobbs 
9453949bee8SArchie Cobbs 		/* Deliver fragment(s) out the next link */
9463949bee8SArchie Cobbs 		for ( ; distrib[activeLinkNum] > 0; firstFragment = 0) {
9473949bee8SArchie Cobbs 			int len, lastFragment, error;
9483949bee8SArchie Cobbs 			struct mbuf *m2;
9493949bee8SArchie Cobbs 			meta_p meta2;
9503949bee8SArchie Cobbs 
9513949bee8SArchie Cobbs 			/* Calculate fragment length; don't exceed link MTU */
9523949bee8SArchie Cobbs 			len = distrib[activeLinkNum];
9533949bee8SArchie Cobbs 			if (len > priv->conf.links[linkNum].mru)
9543949bee8SArchie Cobbs 				len = priv->conf.links[linkNum].mru;
9553949bee8SArchie Cobbs 			distrib[activeLinkNum] -= len;
9563949bee8SArchie Cobbs 			lastFragment = (len == m->m_pkthdr.len);
9573949bee8SArchie Cobbs 
9583949bee8SArchie Cobbs 			/* Split off next fragment as "m2" */
9593949bee8SArchie Cobbs 			m2 = m;
9603949bee8SArchie Cobbs 			if (!lastFragment) {
9613949bee8SArchie Cobbs 				struct mbuf *n = m_split(m, len, M_NOWAIT);
9623949bee8SArchie Cobbs 
9633949bee8SArchie Cobbs 				if (n == NULL) {
9643949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
9653949bee8SArchie Cobbs 					return (ENOMEM);
9663949bee8SArchie Cobbs 				}
9673949bee8SArchie Cobbs 				m = n;
9683949bee8SArchie Cobbs 			}
9693949bee8SArchie Cobbs 
9703949bee8SArchie Cobbs 			/* Prepend MP header */
9713949bee8SArchie Cobbs 			if (priv->conf.xmitShortSeq) {
9723949bee8SArchie Cobbs 				u_int16_t shdr;
9733949bee8SArchie Cobbs 
9743949bee8SArchie Cobbs 				M_PREPEND(m2, 2, M_NOWAIT);
9753949bee8SArchie Cobbs 				if (m2 == NULL
9763949bee8SArchie Cobbs 				    || (m2->m_len < 2
9773949bee8SArchie Cobbs 				      && (m2 = m_pullup(m2, 2)) == NULL)) {
9783949bee8SArchie Cobbs 					if (!lastFragment)
9793949bee8SArchie Cobbs 						m_freem(m);
9803949bee8SArchie Cobbs 					NG_FREE_META(meta);
9813949bee8SArchie Cobbs 					return (ENOBUFS);
9823949bee8SArchie Cobbs 				}
9833949bee8SArchie Cobbs 				shdr = priv->mpSeqOut;
9843949bee8SArchie Cobbs 				priv->mpSeqOut =
9853949bee8SArchie Cobbs 				    (priv->mpSeqOut + 1) % MP_SHORT_SEQ_MASK;
9863949bee8SArchie Cobbs 				if (firstFragment)
9873949bee8SArchie Cobbs 					shdr |= MP_SHORT_FIRST_FLAG;
9883949bee8SArchie Cobbs 				if (lastFragment)
9893949bee8SArchie Cobbs 					shdr |= MP_SHORT_LAST_FLAG;
9903949bee8SArchie Cobbs 				*mtod(m2, u_int16_t *) = htons(shdr);
9913949bee8SArchie Cobbs 			} else {
9923949bee8SArchie Cobbs 				u_int32_t lhdr;
9933949bee8SArchie Cobbs 
9943949bee8SArchie Cobbs 				M_PREPEND(m2, 4, M_NOWAIT);
9953949bee8SArchie Cobbs 				if (m2 == NULL
9963949bee8SArchie Cobbs 				    || (m2->m_len < 4
9973949bee8SArchie Cobbs 				      && (m2 = m_pullup(m2, 4)) == NULL)) {
9983949bee8SArchie Cobbs 					if (!lastFragment)
9993949bee8SArchie Cobbs 						m_freem(m);
10003949bee8SArchie Cobbs 					NG_FREE_META(meta);
10013949bee8SArchie Cobbs 					return (ENOBUFS);
10023949bee8SArchie Cobbs 				}
10033949bee8SArchie Cobbs 				lhdr = priv->mpSeqOut;
10043949bee8SArchie Cobbs 				priv->mpSeqOut =
10053949bee8SArchie Cobbs 				    (priv->mpSeqOut + 1) % MP_LONG_SEQ_MASK;
10063949bee8SArchie Cobbs 				if (firstFragment)
10073949bee8SArchie Cobbs 					lhdr |= MP_LONG_FIRST_FLAG;
10083949bee8SArchie Cobbs 				if (lastFragment)
10093949bee8SArchie Cobbs 					lhdr |= MP_LONG_LAST_FLAG;
10103949bee8SArchie Cobbs 				*mtod(m2, u_int32_t *) = htonl(lhdr);
10113949bee8SArchie Cobbs 			}
10123949bee8SArchie Cobbs 
10133949bee8SArchie Cobbs 			/* Add MP protocol number */
10143949bee8SArchie Cobbs 			m2 = ng_ppp_addproto(m, PROT_MP,
10153949bee8SArchie Cobbs 			    priv->conf.links[linkNum].enableProtoComp);
10163949bee8SArchie Cobbs 			if (m2 == NULL) {
10173949bee8SArchie Cobbs 				if (!lastFragment)
10183949bee8SArchie Cobbs 					m_freem(m);
10193949bee8SArchie Cobbs 				NG_FREE_META(meta);
10203949bee8SArchie Cobbs 				return (ENOBUFS);
10213949bee8SArchie Cobbs 			}
10223949bee8SArchie Cobbs 
10233949bee8SArchie Cobbs 			/* Copy the meta information, if any */
10243949bee8SArchie Cobbs 			if (meta != NULL && !lastFragment) {
10253949bee8SArchie Cobbs 				MALLOC(meta2, meta_p,
10263949bee8SArchie Cobbs 				    meta->used_len, M_NETGRAPH, M_NOWAIT);
10273949bee8SArchie Cobbs 				if (meta2 == NULL) {
10283949bee8SArchie Cobbs 					m_freem(m2);
10293949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
10303949bee8SArchie Cobbs 					return (ENOMEM);
10313949bee8SArchie Cobbs 				}
10323949bee8SArchie Cobbs 				meta2->allocated_len = meta->used_len;
10333949bee8SArchie Cobbs 				bcopy(meta, meta2, meta->used_len);
10343949bee8SArchie Cobbs 			} else
10353949bee8SArchie Cobbs 				meta2 = meta;
10363949bee8SArchie Cobbs 
10373949bee8SArchie Cobbs 			/* Send fragment */
10383949bee8SArchie Cobbs 			error = ng_ppp_output(node, linkNum, m2, meta2);
10393949bee8SArchie Cobbs 
10403949bee8SArchie Cobbs 			/* Abort for error */
10413949bee8SArchie Cobbs 			if (error != 0) {
10423949bee8SArchie Cobbs 				if (!lastFragment)
10433949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
10443949bee8SArchie Cobbs 				return (error);
10453949bee8SArchie Cobbs 			}
10463949bee8SArchie Cobbs 		}
10473949bee8SArchie Cobbs 	}
10483949bee8SArchie Cobbs 
10493949bee8SArchie Cobbs 	/* Done */
10503949bee8SArchie Cobbs 	return (0);
10513949bee8SArchie Cobbs }
10523949bee8SArchie Cobbs 
10533949bee8SArchie Cobbs /*
10543949bee8SArchie Cobbs  * Computing the optimal fragmentation
10553949bee8SArchie Cobbs  * -----------------------------------
10563949bee8SArchie Cobbs  *
10573949bee8SArchie Cobbs  * This routine tries to compute the optimal fragmentation pattern based
10583949bee8SArchie Cobbs  * on each link's latency, bandwidth, and calculated additional latency.
10593949bee8SArchie Cobbs  * The latter quantity is the additional latency caused by previously
10603949bee8SArchie Cobbs  * written data that has not been transmitted yet.
10613949bee8SArchie Cobbs  *
10623949bee8SArchie Cobbs  * This algorithm is only useful when not all of the links have the
10633949bee8SArchie Cobbs  * same latency and bandwidth values.
10643949bee8SArchie Cobbs  *
10653949bee8SArchie Cobbs  * The essential idea is to make the last bit of each fragment of the
10663949bee8SArchie Cobbs  * frame arrive at the opposite end at the exact same time. This greedy
10673949bee8SArchie Cobbs  * algorithm is optimal, in that no other scheduling could result in any
10683949bee8SArchie Cobbs  * packet arriving any sooner unless packets are delivered out of order.
10693949bee8SArchie Cobbs  *
10703949bee8SArchie Cobbs  * Suppose link i has bandwidth b_i (in tens of bytes per milisecond) and
10713949bee8SArchie Cobbs  * latency l_i (in miliseconds). Consider the function function f_i(t)
10723949bee8SArchie Cobbs  * which is equal to the number of bytes that will have arrived at
10733949bee8SArchie Cobbs  * the peer after t miliseconds if we start writing continuously at
10743949bee8SArchie Cobbs  * time t = 0. Then f_i(t) = b_i * (t - l_i) = ((b_i * t) - (l_i * b_i).
10753949bee8SArchie Cobbs  * That is, f_i(t) is a line with slope b_i and y-intersect -(l_i * b_i).
10763949bee8SArchie Cobbs  * Note that the y-intersect is always <= zero because latency can't be
10773949bee8SArchie Cobbs  * negative.  Note also that really the function is f_i(t) except when
10783949bee8SArchie Cobbs  * f_i(t) is negative, in which case the function is zero.  To take
10793949bee8SArchie Cobbs  * care of this, let Q_i(t) = { if (f_i(t) > 0) return 1; else return 0; }.
10803949bee8SArchie Cobbs  * So the actual number of bytes that will have arrived at the peer after
10813949bee8SArchie Cobbs  * t miliseconds is f_i(t) * Q_i(t).
10823949bee8SArchie Cobbs  *
10833949bee8SArchie Cobbs  * At any given time, each link has some additional latency a_i >= 0
10843949bee8SArchie Cobbs  * due to previously written fragment(s) which are still in the queue.
10853949bee8SArchie Cobbs  * This value is easily computed from the time since last transmission,
10863949bee8SArchie Cobbs  * the previous latency value, the number of bytes written, and the
10873949bee8SArchie Cobbs  * link's bandwidth.
10883949bee8SArchie Cobbs  *
10893949bee8SArchie Cobbs  * Assume that l_i includes any a_i already, and that the links are
10903949bee8SArchie Cobbs  * sorted by latency, so that l_i <= l_{i+1}.
10913949bee8SArchie Cobbs  *
10923949bee8SArchie Cobbs  * Let N be the total number of bytes in the current frame we are sending.
10933949bee8SArchie Cobbs  *
10943949bee8SArchie Cobbs  * Suppose we were to start writing bytes at time t = 0 on all links
10953949bee8SArchie Cobbs  * simultaneously, which is the most we can possibly do.  Then let
10963949bee8SArchie Cobbs  * F(t) be equal to the total number of bytes received by the peer
10973949bee8SArchie Cobbs  * after t miliseconds. Then F(t) = Sum_i (f_i(t) * Q_i(t)).
10983949bee8SArchie Cobbs  *
10993949bee8SArchie Cobbs  * Our goal is simply this: fragment the frame across the links such
11003949bee8SArchie Cobbs  * that the peer is able to reconstruct the completed frame as soon as
11013949bee8SArchie Cobbs  * possible, i.e., at the least possible value of t. Call this value t_0.
11023949bee8SArchie Cobbs  *
11033949bee8SArchie Cobbs  * Then it follows that F(t_0) = N. Our strategy is first to find the value
11043949bee8SArchie Cobbs  * of t_0, and then deduce how many bytes to write to each link.
11053949bee8SArchie Cobbs  *
11063949bee8SArchie Cobbs  * Rewriting F(t_0):
11073949bee8SArchie Cobbs  *
11083949bee8SArchie Cobbs  *   t_0 = ( N + Sum_i ( l_i * b_i * Q_i(t_0) ) ) / Sum_i ( b_i * Q_i(t_0) )
11093949bee8SArchie Cobbs  *
11103949bee8SArchie Cobbs  * Now, we note that Q_i(t) is constant for l_i <= t <= l_{i+1}. t_0 will
11113949bee8SArchie Cobbs  * lie in one of these ranges.  To find it, we just need to find the i such
11123949bee8SArchie Cobbs  * that F(l_i) <= N <= F(l_{i+1}).  Then we compute all the constant values
11133949bee8SArchie Cobbs  * for Q_i() in this range, plug in the remaining values, solving for t_0.
11143949bee8SArchie Cobbs  *
11153949bee8SArchie Cobbs  * Once t_0 is known, then the number of bytes to send on link i is
11163949bee8SArchie Cobbs  * just f_i(t_0) * Q_i(t_0).
11173949bee8SArchie Cobbs  *
11183949bee8SArchie Cobbs  * In other words, we start allocating bytes to the links one at a time.
11193949bee8SArchie Cobbs  * We keep adding links until the frame is completely sent.  Some links
11203949bee8SArchie Cobbs  * may not get any bytes because their latency is too high.
11213949bee8SArchie Cobbs  *
11223949bee8SArchie Cobbs  * Is all this work really worth the trouble?  Depends on the situation.
11233949bee8SArchie Cobbs  * The bigger the ratio of computer speed to link speed, and the more
11243949bee8SArchie Cobbs  * important total bundle latency is (e.g., for interactive response time),
11253949bee8SArchie Cobbs  * the more it's worth it.  There is however the cost of calling this
11263949bee8SArchie Cobbs  * function for every frame.  The running time is O(n^2) where n is the
11273949bee8SArchie Cobbs  * number of links that receive a non-zero number of bytes.
11283949bee8SArchie Cobbs  *
11293949bee8SArchie Cobbs  * Since latency is measured in miliseconds, the "resolution" of this
11303949bee8SArchie Cobbs  * algorithm is one milisecond.
11313949bee8SArchie Cobbs  *
11323949bee8SArchie Cobbs  * To avoid this algorithm altogether, configure all links to have the
11333949bee8SArchie Cobbs  * same latency and bandwidth.
11343949bee8SArchie Cobbs  */
11353949bee8SArchie Cobbs static void
11363949bee8SArchie Cobbs ng_ppp_mp_strategy(node_p node, int len, int *distrib)
11373949bee8SArchie Cobbs {
11383949bee8SArchie Cobbs 	const priv_p priv = node->private;
11393949bee8SArchie Cobbs 	int latency[NG_PPP_MAX_LINKS];
11403949bee8SArchie Cobbs 	int sortByLatency[NG_PPP_MAX_LINKS];
11413949bee8SArchie Cobbs 	int activeLinkNum, linkNum;
11423949bee8SArchie Cobbs 	int t0, total, topSum, botSum;
11433949bee8SArchie Cobbs 	struct timeval now;
11443949bee8SArchie Cobbs 	int i, numFragments;
11453949bee8SArchie Cobbs 
11463949bee8SArchie Cobbs 	/* If only one link, this gets real easy */
11473949bee8SArchie Cobbs 	if (priv->numActiveLinks == 1) {
11483949bee8SArchie Cobbs 		distrib[0] = len;
11493949bee8SArchie Cobbs 		return;
11503949bee8SArchie Cobbs 	}
11513949bee8SArchie Cobbs 
11523949bee8SArchie Cobbs 	/* Get current time */
1153fb1fc8abSArchie Cobbs 	microtime(&now);
11543949bee8SArchie Cobbs 
11553949bee8SArchie Cobbs 	/* Compute latencies for each link at this point in time */
11563949bee8SArchie Cobbs 	for (activeLinkNum = 0;
11573949bee8SArchie Cobbs 	    activeLinkNum < priv->numActiveLinks; activeLinkNum++) {
11583949bee8SArchie Cobbs 		struct timeval diff;
11593949bee8SArchie Cobbs 		int xmitBytes;
11603949bee8SArchie Cobbs 
11613949bee8SArchie Cobbs 		/* Start with base latency value */
11623949bee8SArchie Cobbs 		linkNum = priv->activeLinks[activeLinkNum];
11633949bee8SArchie Cobbs 		latency[activeLinkNum] = priv->conf.links[linkNum].latency;
11643949bee8SArchie Cobbs 		sortByLatency[activeLinkNum] = activeLinkNum;	/* see below */
11653949bee8SArchie Cobbs 
11663949bee8SArchie Cobbs 		/* Any additional latency? */
11673949bee8SArchie Cobbs 		if (priv->qstat[activeLinkNum].bytesInQueue == 0)
11683949bee8SArchie Cobbs 			continue;
11693949bee8SArchie Cobbs 
11703949bee8SArchie Cobbs 		/* Compute time delta since last write */
11713949bee8SArchie Cobbs 		diff = now;
11723949bee8SArchie Cobbs 		timevalsub(&diff, &priv->qstat[activeLinkNum].lastWrite);
11733949bee8SArchie Cobbs 		if (now.tv_sec < 0 || diff.tv_sec >= 10) {	/* sanity */
11743949bee8SArchie Cobbs 			priv->qstat[activeLinkNum].bytesInQueue = 0;
11753949bee8SArchie Cobbs 			continue;
11763949bee8SArchie Cobbs 		}
11773949bee8SArchie Cobbs 
11783949bee8SArchie Cobbs 		/* How many bytes could have transmitted since last write? */
11793949bee8SArchie Cobbs 		xmitBytes = priv->conf.links[linkNum].bandwidth * diff.tv_sec
11803949bee8SArchie Cobbs 		    + (priv->conf.links[linkNum].bandwidth
11813949bee8SArchie Cobbs 			* (diff.tv_usec / 1000)) / 100;
11823949bee8SArchie Cobbs 		priv->qstat[activeLinkNum].bytesInQueue -= xmitBytes;
11833949bee8SArchie Cobbs 		if (priv->qstat[activeLinkNum].bytesInQueue < 0)
11843949bee8SArchie Cobbs 			priv->qstat[activeLinkNum].bytesInQueue = 0;
11853949bee8SArchie Cobbs 		else
11863949bee8SArchie Cobbs 			latency[activeLinkNum] +=
11873949bee8SArchie Cobbs 			    (100 * priv->qstat[activeLinkNum].bytesInQueue)
11883949bee8SArchie Cobbs 				/ priv->conf.links[linkNum].bandwidth;
11893949bee8SArchie Cobbs 	}
11903949bee8SArchie Cobbs 
11913949bee8SArchie Cobbs 	/* Sort links by latency */
11923949bee8SArchie Cobbs 	compareLatencies = latency;
11933949bee8SArchie Cobbs 	qsort(sortByLatency,
11943949bee8SArchie Cobbs 	    priv->numActiveLinks, sizeof(*sortByLatency), ng_ppp_intcmp);
11953949bee8SArchie Cobbs 	compareLatencies = NULL;
11963949bee8SArchie Cobbs 
11973949bee8SArchie Cobbs 	/* Find the interval we need (add links in sortByLatency[] order) */
11983949bee8SArchie Cobbs 	for (numFragments = 1;
11993949bee8SArchie Cobbs 	    numFragments < priv->numActiveLinks; numFragments++) {
12003949bee8SArchie Cobbs 		for (total = i = 0; i < numFragments; i++) {
12013949bee8SArchie Cobbs 			int flowTime;
12023949bee8SArchie Cobbs 
12033949bee8SArchie Cobbs 			flowTime = latency[sortByLatency[numFragments]]
12043949bee8SArchie Cobbs 			    - latency[sortByLatency[i]];
12053949bee8SArchie Cobbs 			total += ((flowTime * priv->conf.links[
12063949bee8SArchie Cobbs 			    priv->activeLinks[sortByLatency[i]]].bandwidth)
12073949bee8SArchie Cobbs 			    	+ 99) / 100;
12083949bee8SArchie Cobbs 		}
12093949bee8SArchie Cobbs 		if (total >= len)
12103949bee8SArchie Cobbs 			break;
12113949bee8SArchie Cobbs 	}
12123949bee8SArchie Cobbs 
12133949bee8SArchie Cobbs 	/* Solve for t_0 in that interval */
12143949bee8SArchie Cobbs 	for (topSum = botSum = i = 0; i < numFragments; i++) {
12153949bee8SArchie Cobbs 		int bw = priv->conf.links[
12163949bee8SArchie Cobbs 		    priv->activeLinks[sortByLatency[i]]].bandwidth;
12173949bee8SArchie Cobbs 
12183949bee8SArchie Cobbs 		topSum += latency[sortByLatency[i]] * bw;	/* / 100 */
12193949bee8SArchie Cobbs 		botSum += bw;					/* / 100 */
12203949bee8SArchie Cobbs 	}
12213949bee8SArchie Cobbs 	t0 = ((len * 100) + topSum + botSum / 2) / botSum;
12223949bee8SArchie Cobbs 
12233949bee8SArchie Cobbs 	/* Compute f_i(t_0) all i */
12243949bee8SArchie Cobbs 	bzero(distrib, priv->numActiveLinks * sizeof(*distrib));
12253949bee8SArchie Cobbs 	for (total = i = 0; i < numFragments; i++) {
12263949bee8SArchie Cobbs 		int bw = priv->conf.links[
12273949bee8SArchie Cobbs 		    priv->activeLinks[sortByLatency[i]]].bandwidth;
12283949bee8SArchie Cobbs 
12293949bee8SArchie Cobbs 		distrib[sortByLatency[i]] =
12303949bee8SArchie Cobbs 		    (bw * (t0 - latency[sortByLatency[i]]) + 50) / 100;
12313949bee8SArchie Cobbs 		total += distrib[sortByLatency[i]];
12323949bee8SArchie Cobbs 	}
12333949bee8SArchie Cobbs 
1234fb1fc8abSArchie Cobbs 	/* Deal with any rounding error */
1235fb1fc8abSArchie Cobbs 	if (total < len) {
12363949bee8SArchie Cobbs 		int fast = 0;
12373949bee8SArchie Cobbs 
1238fb1fc8abSArchie Cobbs 		/* Find the fastest link */
12393949bee8SArchie Cobbs 		for (i = 1; i < numFragments; i++) {
12403949bee8SArchie Cobbs 			if (priv->conf.links[
12413949bee8SArchie Cobbs 			      priv->activeLinks[sortByLatency[i]]].bandwidth >
12423949bee8SArchie Cobbs 			    priv->conf.links[
12433949bee8SArchie Cobbs 			      priv->activeLinks[sortByLatency[fast]]].bandwidth)
12443949bee8SArchie Cobbs 				fast = i;
12453949bee8SArchie Cobbs 		}
12463949bee8SArchie Cobbs 		distrib[sortByLatency[fast]] += len - total;
1247fb1fc8abSArchie Cobbs 	} else while (total > len) {
1248fb1fc8abSArchie Cobbs 		int delta, slow = 0;
12493949bee8SArchie Cobbs 
1250fb1fc8abSArchie Cobbs 		/* Find the slowest link that still has bytes to remove */
1251fb1fc8abSArchie Cobbs 		for (i = 1; i < numFragments; i++) {
1252fb1fc8abSArchie Cobbs 			if (distrib[sortByLatency[slow]] == 0
1253fb1fc8abSArchie Cobbs 			  || (distrib[sortByLatency[i]] > 0
1254fb1fc8abSArchie Cobbs 			    && priv->conf.links[priv->activeLinks[
1255fb1fc8abSArchie Cobbs 					sortByLatency[i]]].bandwidth <
1256fb1fc8abSArchie Cobbs 			      priv->conf.links[priv->activeLinks[
1257fb1fc8abSArchie Cobbs 					sortByLatency[slow]]].bandwidth))
1258fb1fc8abSArchie Cobbs 				slow = i;
1259fb1fc8abSArchie Cobbs 		}
1260fb1fc8abSArchie Cobbs 		delta = total - len;
1261fb1fc8abSArchie Cobbs 		if (delta > distrib[sortByLatency[slow]])
1262fb1fc8abSArchie Cobbs 			delta = distrib[sortByLatency[slow]];
1263fb1fc8abSArchie Cobbs 		distrib[sortByLatency[slow]] -= delta;
1264fb1fc8abSArchie Cobbs 		total -= delta;
12653949bee8SArchie Cobbs 	}
12663949bee8SArchie Cobbs }
12673949bee8SArchie Cobbs 
12683949bee8SArchie Cobbs /*
12693949bee8SArchie Cobbs  * Compare two integers
12703949bee8SArchie Cobbs  */
12713949bee8SArchie Cobbs static int
12723949bee8SArchie Cobbs ng_ppp_intcmp(const void *v1, const void *v2)
12733949bee8SArchie Cobbs {
12743949bee8SArchie Cobbs 	const int index1 = *((const int *) v1);
12753949bee8SArchie Cobbs 	const int index2 = *((const int *) v2);
12763949bee8SArchie Cobbs 
12773949bee8SArchie Cobbs 	return compareLatencies[index1] - compareLatencies[index2];
12783949bee8SArchie Cobbs }
12793949bee8SArchie Cobbs 
12803949bee8SArchie Cobbs /*
12813949bee8SArchie Cobbs  * Prepend a possibly compressed PPP protocol number in front of a frame
12823949bee8SArchie Cobbs  */
12833949bee8SArchie Cobbs static struct mbuf *
12843949bee8SArchie Cobbs ng_ppp_addproto(struct mbuf *m, int proto, int compOK)
12853949bee8SArchie Cobbs {
12862b70adcbSArchie Cobbs 	int psize = (PROT_COMPRESSABLE(proto) && compOK) ? 1 : 2;
12873949bee8SArchie Cobbs 
12883949bee8SArchie Cobbs 	/* Add protocol number */
12893949bee8SArchie Cobbs 	M_PREPEND(m, psize, M_NOWAIT);
12903949bee8SArchie Cobbs 	if (m == NULL || (m->m_len < psize && (m = m_pullup(m, psize)) == NULL))
12914cf49a43SJulian Elischer 		return (NULL);
12923949bee8SArchie Cobbs 	if (psize == 1)
12933949bee8SArchie Cobbs 		*mtod(m, u_char *) = (u_char)proto;
12943949bee8SArchie Cobbs 	else
12953949bee8SArchie Cobbs 		*mtod(m, u_int16_t *) = htons((u_int16_t)proto);
12963949bee8SArchie Cobbs 	return (m);
12973949bee8SArchie Cobbs }
12983949bee8SArchie Cobbs 
12993949bee8SArchie Cobbs /*
13003949bee8SArchie Cobbs  * Update private information that is derived from other private information
13013949bee8SArchie Cobbs  */
13023949bee8SArchie Cobbs static void
13033949bee8SArchie Cobbs ng_ppp_update(node_p node, int newConf)
13043949bee8SArchie Cobbs {
13053949bee8SArchie Cobbs 	const priv_p priv = node->private;
13063949bee8SArchie Cobbs 	int i;
13073949bee8SArchie Cobbs 
13083949bee8SArchie Cobbs 	/* Update active status for VJ Compression */
13093949bee8SArchie Cobbs 	priv->vjCompHooked = priv->hooks[HOOK_INDEX_VJC_IP] != NULL
13103949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_COMP] != NULL
13113949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_UNCOMP] != NULL
13123949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_VJIP] != NULL;
13133949bee8SArchie Cobbs 
13143949bee8SArchie Cobbs 	/* Increase latency for each link an amount equal to one MP header */
13153949bee8SArchie Cobbs 	if (newConf) {
13163949bee8SArchie Cobbs 		for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
13173949bee8SArchie Cobbs 			int hdrBytes;
13183949bee8SArchie Cobbs 
13193949bee8SArchie Cobbs 			hdrBytes = (priv->conf.links[i].enableProtoComp ? 1 : 2)
13203949bee8SArchie Cobbs 			    + (priv->conf.xmitShortSeq ? 2 : 4);
13213949bee8SArchie Cobbs 			priv->conf.links[i].latency +=
13223949bee8SArchie Cobbs 			    ((hdrBytes * priv->conf.links[i].bandwidth) + 50)
13233949bee8SArchie Cobbs 				/ 100;
13243949bee8SArchie Cobbs 		}
13253949bee8SArchie Cobbs 	}
13263949bee8SArchie Cobbs 
13273949bee8SArchie Cobbs 	/* Update list of active links */
13283949bee8SArchie Cobbs 	bzero(&priv->activeLinks, sizeof(priv->activeLinks));
13293949bee8SArchie Cobbs 	priv->numActiveLinks = 0;
13303949bee8SArchie Cobbs 	priv->allLinksEqual = 1;
13313949bee8SArchie Cobbs 	for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
13323949bee8SArchie Cobbs 		if (priv->conf.links[i].enableLink && priv->links[i] != NULL) {
13333949bee8SArchie Cobbs 			priv->activeLinks[priv->numActiveLinks++] = i;
13343949bee8SArchie Cobbs 			if (priv->conf.links[i].latency
13353949bee8SArchie Cobbs 				  != priv->conf.links[0].latency
13363949bee8SArchie Cobbs 			    || priv->conf.links[i].bandwidth
13373949bee8SArchie Cobbs 				  != priv->conf.links[0].bandwidth)
13383949bee8SArchie Cobbs 				priv->allLinksEqual = 0;
13393949bee8SArchie Cobbs 		}
13403949bee8SArchie Cobbs 	}
13413949bee8SArchie Cobbs 
13423949bee8SArchie Cobbs 	/* Reset MP state if no longer active */
13433949bee8SArchie Cobbs 	if (!priv->conf.enableMultilink || priv->numActiveLinks == 0) {
13443949bee8SArchie Cobbs 		ng_ppp_free_frags(node);
13453949bee8SArchie Cobbs 		priv->mpSeqOut = MP_INITIAL_SEQ;
13463949bee8SArchie Cobbs 		bzero(&priv->qstat, sizeof(priv->qstat));
13473949bee8SArchie Cobbs 	}
13483949bee8SArchie Cobbs }
13493949bee8SArchie Cobbs 
13503949bee8SArchie Cobbs /*
13513949bee8SArchie Cobbs  * Determine if a new configuration would represent a valid change
13523949bee8SArchie Cobbs  * from the current configuration and link activity status.
13533949bee8SArchie Cobbs  */
13543949bee8SArchie Cobbs static int
13553949bee8SArchie Cobbs ng_ppp_config_valid(node_p node, const struct ng_ppp_node_config *newConf)
13563949bee8SArchie Cobbs {
13573949bee8SArchie Cobbs 	const priv_p priv = node->private;
13583949bee8SArchie Cobbs 	int i, newNumLinksActive;
13593949bee8SArchie Cobbs 
13603949bee8SArchie Cobbs 	/* Check per-link config and count how many links would be active */
13613949bee8SArchie Cobbs 	for (newNumLinksActive = i = 0; i < NG_PPP_MAX_LINKS; i++) {
13623949bee8SArchie Cobbs 		if (newConf->links[i].mru < MP_MIN_LINK_MRU)
13633949bee8SArchie Cobbs 			return (0);
13643949bee8SArchie Cobbs 		if (newConf->links[i].bandwidth == 0)
13653949bee8SArchie Cobbs 			return (0);
13663949bee8SArchie Cobbs 		if (newConf->links[i].bandwidth > NG_PPP_MAX_BANDWIDTH)
13673949bee8SArchie Cobbs 			return (0);
13683949bee8SArchie Cobbs 		if (newConf->links[i].latency > NG_PPP_MAX_LATENCY)
13693949bee8SArchie Cobbs 			return (0);
13703949bee8SArchie Cobbs 		if (newConf->links[i].enableLink && priv->links[i] != NULL)
13713949bee8SArchie Cobbs 			newNumLinksActive++;
13723949bee8SArchie Cobbs 	}
13733949bee8SArchie Cobbs 
13743949bee8SArchie Cobbs 	/* Check bundle parameters */
13753949bee8SArchie Cobbs 	if (priv->conf.enableMultilink && newConf->mrru < MP_MIN_MRRU)
13763949bee8SArchie Cobbs 		return (0);
13773949bee8SArchie Cobbs 
13783949bee8SArchie Cobbs 	/* At most one link can be active unless multi-link is enabled */
13793949bee8SArchie Cobbs 	if (!newConf->enableMultilink && newNumLinksActive > 1)
13803949bee8SArchie Cobbs 		return (0);
13813949bee8SArchie Cobbs 
13823949bee8SArchie Cobbs 	/* Disallow changes to multi-link configuration while MP is active */
13833949bee8SArchie Cobbs 	if (priv->numActiveLinks > 0 && newNumLinksActive > 0) {
13843949bee8SArchie Cobbs 		if (!priv->conf.enableMultilink != !newConf->enableMultilink
13853949bee8SArchie Cobbs 		    || !priv->conf.xmitShortSeq != !newConf->xmitShortSeq
13863949bee8SArchie Cobbs 		    || !priv->conf.recvShortSeq != !newConf->recvShortSeq)
13873949bee8SArchie Cobbs 			return (0);
13883949bee8SArchie Cobbs 	}
13893949bee8SArchie Cobbs 
13903949bee8SArchie Cobbs 	/* Configuration change is valid */
13913949bee8SArchie Cobbs 	return (1);
13923949bee8SArchie Cobbs }
13933949bee8SArchie Cobbs 
13943949bee8SArchie Cobbs /*
13953949bee8SArchie Cobbs  * Free all entries in the fragment queue
13963949bee8SArchie Cobbs  */
13973949bee8SArchie Cobbs static void
13983949bee8SArchie Cobbs ng_ppp_free_frags(node_p node)
13993949bee8SArchie Cobbs {
14003949bee8SArchie Cobbs 	const priv_p priv = node->private;
14013949bee8SArchie Cobbs 	struct ng_ppp_frag *qent, *next;
14023949bee8SArchie Cobbs 
14033949bee8SArchie Cobbs 	for (qent = CIRCLEQ_FIRST(&priv->frags);
14043949bee8SArchie Cobbs 	    qent != (void *) &priv->frags; qent = next) {
14053949bee8SArchie Cobbs 		next = CIRCLEQ_NEXT(qent, f_qent);
14063949bee8SArchie Cobbs 		NG_FREE_DATA(qent->data, qent->meta);
14073949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
14083949bee8SArchie Cobbs 	}
14093949bee8SArchie Cobbs 	CIRCLEQ_INIT(&priv->frags);
14104cf49a43SJulian Elischer }
14114cf49a43SJulian Elischer 
1412