xref: /freebsd/sys/netgraph/ng_ppp.c (revision 62838fae89bb09d966b5e345bb7039d63458c251)
14cf49a43SJulian Elischer 
24cf49a43SJulian Elischer /*
34cf49a43SJulian Elischer  * ng_ppp.c
44cf49a43SJulian Elischer  *
54cf49a43SJulian Elischer  * Copyright (c) 1996-1999 Whistle Communications, Inc.
64cf49a43SJulian Elischer  * All rights reserved.
74cf49a43SJulian Elischer  *
84cf49a43SJulian Elischer  * Subject to the following obligations and disclaimer of warranty, use and
94cf49a43SJulian Elischer  * redistribution of this software, in source or object code forms, with or
104cf49a43SJulian Elischer  * without modifications are expressly permitted by Whistle Communications;
114cf49a43SJulian Elischer  * provided, however, that:
124cf49a43SJulian Elischer  * 1. Any and all reproductions of the source or object code must include the
134cf49a43SJulian Elischer  *    copyright notice above and the following disclaimer of warranties; and
144cf49a43SJulian Elischer  * 2. No rights are granted, in any manner or form, to use Whistle
154cf49a43SJulian Elischer  *    Communications, Inc. trademarks, including the mark "WHISTLE
164cf49a43SJulian Elischer  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
174cf49a43SJulian Elischer  *    such appears in the above copyright notice or in the software.
184cf49a43SJulian Elischer  *
194cf49a43SJulian Elischer  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
204cf49a43SJulian Elischer  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
214cf49a43SJulian Elischer  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
224cf49a43SJulian Elischer  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
234cf49a43SJulian Elischer  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
244cf49a43SJulian Elischer  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
254cf49a43SJulian Elischer  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
264cf49a43SJulian Elischer  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
274cf49a43SJulian Elischer  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
284cf49a43SJulian Elischer  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
294cf49a43SJulian Elischer  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
304cf49a43SJulian Elischer  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
314cf49a43SJulian Elischer  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
324cf49a43SJulian Elischer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
334cf49a43SJulian Elischer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
344cf49a43SJulian Elischer  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
354cf49a43SJulian Elischer  * OF SUCH DAMAGE.
364cf49a43SJulian Elischer  *
374cf49a43SJulian Elischer  * Author: Archie Cobbs <archie@whistle.com>
384cf49a43SJulian Elischer  *
394cf49a43SJulian Elischer  * $FreeBSD$
4074f5c6aaSJulian Elischer  * $Whistle: ng_ppp.c,v 1.24 1999/11/01 09:24:52 julian Exp $
414cf49a43SJulian Elischer  */
424cf49a43SJulian Elischer 
434cf49a43SJulian Elischer /*
443949bee8SArchie Cobbs  * PPP node type.
454cf49a43SJulian Elischer  */
464cf49a43SJulian Elischer 
474cf49a43SJulian Elischer #include <sys/param.h>
484cf49a43SJulian Elischer #include <sys/systm.h>
494cf49a43SJulian Elischer #include <sys/kernel.h>
504cf49a43SJulian Elischer #include <sys/conf.h>
514cf49a43SJulian Elischer #include <sys/mbuf.h>
524cf49a43SJulian Elischer #include <sys/malloc.h>
534cf49a43SJulian Elischer #include <sys/errno.h>
544cf49a43SJulian Elischer #include <sys/socket.h>
554cf49a43SJulian Elischer #include <sys/syslog.h>
565b664c7cSPoul-Henning Kamp #include <sys/ctype.h>
574cf49a43SJulian Elischer 
584cf49a43SJulian Elischer #include <netgraph/ng_message.h>
594cf49a43SJulian Elischer #include <netgraph/netgraph.h>
604cf49a43SJulian Elischer #include <netgraph/ng_ppp.h>
613949bee8SArchie Cobbs #include <netgraph/ng_vjc.h>
624cf49a43SJulian Elischer 
634cf49a43SJulian Elischer #define PROT_VALID(p)		(((p) & 0x0101) == 0x0001)
642b70adcbSArchie Cobbs #define PROT_COMPRESSABLE(p)	(((p) & 0xff00) == 0x0000)
654cf49a43SJulian Elischer 
663949bee8SArchie Cobbs /* Some PPP protocol numbers we're interested in */
673949bee8SArchie Cobbs #define PROT_APPLETALK		0x0029
683949bee8SArchie Cobbs #define PROT_COMPD		0x00fd
693949bee8SArchie Cobbs #define PROT_CRYPTD		0x0053
703949bee8SArchie Cobbs #define PROT_IP			0x0021
712b70adcbSArchie Cobbs #define PROT_IPX		0x002b
72d690a6e7SArchie Cobbs #define PROT_LCP		0xc021
733949bee8SArchie Cobbs #define PROT_MP			0x003d
743949bee8SArchie Cobbs #define PROT_VJCOMP		0x002d
753949bee8SArchie Cobbs #define PROT_VJUNCOMP		0x002f
763949bee8SArchie Cobbs 
773949bee8SArchie Cobbs /* Multilink PPP definitions */
783949bee8SArchie Cobbs #define MP_MIN_MRRU		1500		/* per RFC 1990 */
793949bee8SArchie Cobbs #define MP_INITIAL_SEQ		0		/* per RFC 1990 */
803949bee8SArchie Cobbs #define MP_MIN_LINK_MRU		32
813949bee8SArchie Cobbs 
823949bee8SArchie Cobbs #define MP_MAX_SEQ_LINGER	64		/* max frags we will hold */
833949bee8SArchie Cobbs #define MP_INSANE_SEQ_JUMP	128		/* a sequence # jump too far */
843949bee8SArchie Cobbs #define MP_MIN_FRAG_LEN		6		/* don't frag smaller frames */
853949bee8SArchie Cobbs 
863949bee8SArchie Cobbs #define MP_SHORT_SEQ_MASK	0x00000fff	/* short seq # mask */
873949bee8SArchie Cobbs #define MP_SHORT_SEQ_HIBIT	0x00000800	/* short seq # high bit */
883949bee8SArchie Cobbs #define MP_SHORT_FIRST_FLAG	0x00008000	/* first fragment in frame */
893949bee8SArchie Cobbs #define MP_SHORT_LAST_FLAG	0x00004000	/* last fragment in frame */
903949bee8SArchie Cobbs 
913949bee8SArchie Cobbs #define MP_LONG_SEQ_MASK	0x00ffffff	/* long seq # mask */
923949bee8SArchie Cobbs #define MP_LONG_SEQ_HIBIT	0x00800000	/* long seq # high bit */
933949bee8SArchie Cobbs #define MP_LONG_FIRST_FLAG	0x80000000	/* first fragment in frame */
943949bee8SArchie Cobbs #define MP_LONG_LAST_FLAG	0x40000000	/* last fragment in frame */
953949bee8SArchie Cobbs 
96d690a6e7SArchie Cobbs #define MP_SEQ_MASK(priv)	((priv)->conf.recvShortSeq ? \
973949bee8SArchie Cobbs 				    MP_SHORT_SEQ_MASK : MP_LONG_SEQ_MASK)
983949bee8SArchie Cobbs 
993949bee8SArchie Cobbs /* Sign extension of MP sequence numbers */
1003949bee8SArchie Cobbs #define MP_SHORT_EXTEND(s)	(((s) & MP_SHORT_SEQ_HIBIT) ? \
1013949bee8SArchie Cobbs 				    ((s) | ~MP_SHORT_SEQ_MASK) : (s))
1023949bee8SArchie Cobbs #define MP_LONG_EXTEND(s)	(((s) & MP_LONG_SEQ_HIBIT) ? \
1033949bee8SArchie Cobbs 				    ((s) | ~MP_LONG_SEQ_MASK) : (s))
1043949bee8SArchie Cobbs 
1053949bee8SArchie Cobbs /* Comparision of MP sequence numbers */
1063949bee8SArchie Cobbs #define MP_SHORT_SEQ_DIFF(x,y)	(MP_SHORT_EXTEND(x) - MP_SHORT_EXTEND(y))
1073949bee8SArchie Cobbs #define MP_LONG_SEQ_DIFF(x,y)	(MP_LONG_EXTEND(x) - MP_LONG_EXTEND(y))
1083949bee8SArchie Cobbs 
109d690a6e7SArchie Cobbs #define MP_SEQ_DIFF(priv,x,y)	((priv)->conf.recvShortSeq ? \
1103949bee8SArchie Cobbs 				    MP_SHORT_SEQ_DIFF((x), (y)) : \
1113949bee8SArchie Cobbs 				    MP_LONG_SEQ_DIFF((x), (y)))
1123949bee8SArchie Cobbs 
1133949bee8SArchie Cobbs /* We store incoming fragments this way */
1143949bee8SArchie Cobbs struct ng_ppp_frag {
1153949bee8SArchie Cobbs 	int				seq;
1163949bee8SArchie Cobbs 	u_char				first;
1173949bee8SArchie Cobbs 	u_char				last;
1183949bee8SArchie Cobbs 	struct mbuf			*data;
1193949bee8SArchie Cobbs 	meta_p				meta;
1203949bee8SArchie Cobbs 	CIRCLEQ_ENTRY(ng_ppp_frag)	f_qent;
1213949bee8SArchie Cobbs };
1223949bee8SArchie Cobbs 
1233949bee8SArchie Cobbs /* We keep track of link queue status this way */
1243949bee8SArchie Cobbs struct ng_ppp_link_qstat {
1253949bee8SArchie Cobbs 	struct timeval		lastWrite;	/* time of last write */
1263949bee8SArchie Cobbs 	int			bytesInQueue;	/* bytes in the queue then */
1273949bee8SArchie Cobbs };
1283949bee8SArchie Cobbs 
1293949bee8SArchie Cobbs /* We use integer indicies to refer to the non-link hooks */
1303949bee8SArchie Cobbs static const char *const ng_ppp_hook_names[] = {
1313949bee8SArchie Cobbs 	NG_PPP_HOOK_ATALK,
1323949bee8SArchie Cobbs #define HOOK_INDEX_ATALK		0
1333949bee8SArchie Cobbs 	NG_PPP_HOOK_BYPASS,
1343949bee8SArchie Cobbs #define HOOK_INDEX_BYPASS		1
1353949bee8SArchie Cobbs 	NG_PPP_HOOK_COMPRESS,
1363949bee8SArchie Cobbs #define HOOK_INDEX_COMPRESS		2
1373949bee8SArchie Cobbs 	NG_PPP_HOOK_ENCRYPT,
1383949bee8SArchie Cobbs #define HOOK_INDEX_ENCRYPT		3
1393949bee8SArchie Cobbs 	NG_PPP_HOOK_DECOMPRESS,
1403949bee8SArchie Cobbs #define HOOK_INDEX_DECOMPRESS		4
1413949bee8SArchie Cobbs 	NG_PPP_HOOK_DECRYPT,
1423949bee8SArchie Cobbs #define HOOK_INDEX_DECRYPT		5
1433949bee8SArchie Cobbs 	NG_PPP_HOOK_INET,
1443949bee8SArchie Cobbs #define HOOK_INDEX_INET			6
1453949bee8SArchie Cobbs 	NG_PPP_HOOK_IPX,
1463949bee8SArchie Cobbs #define HOOK_INDEX_IPX			7
1473949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_COMP,
1483949bee8SArchie Cobbs #define HOOK_INDEX_VJC_COMP		8
1493949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_IP,
1503949bee8SArchie Cobbs #define HOOK_INDEX_VJC_IP		9
1513949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_UNCOMP,
1523949bee8SArchie Cobbs #define HOOK_INDEX_VJC_UNCOMP		10
1533949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_VJIP,
1543949bee8SArchie Cobbs #define HOOK_INDEX_VJC_VJIP		11
1553949bee8SArchie Cobbs 	NULL
1563949bee8SArchie Cobbs #define HOOK_INDEX_MAX			12
1573949bee8SArchie Cobbs };
1583949bee8SArchie Cobbs 
1593949bee8SArchie Cobbs /* We store index numbers in the hook private pointer. The HOOK_INDEX()
1603949bee8SArchie Cobbs    for a hook is either the index (above) for normal hooks, or the ones
1613949bee8SArchie Cobbs    complement of the link number for link hooks. */
1623949bee8SArchie Cobbs #define HOOK_INDEX(hook)	(*((int16_t *) &(hook)->private))
1634cf49a43SJulian Elischer 
1644cf49a43SJulian Elischer /* Node private data */
16562838faeSArchie Cobbs struct ng_ppp_private {
1663949bee8SArchie Cobbs 	struct ng_ppp_node_config	conf;
1673949bee8SArchie Cobbs 	struct ng_ppp_link_stat		bundleStats;
1683949bee8SArchie Cobbs 	struct ng_ppp_link_stat		linkStats[NG_PPP_MAX_LINKS];
1693949bee8SArchie Cobbs 	hook_p				links[NG_PPP_MAX_LINKS];
1703949bee8SArchie Cobbs 	hook_p				hooks[HOOK_INDEX_MAX];
1713949bee8SArchie Cobbs 	u_char				vjCompHooked;
1723949bee8SArchie Cobbs 	u_char				allLinksEqual;
1733949bee8SArchie Cobbs 	u_short				activeLinks[NG_PPP_MAX_LINKS];
1743949bee8SArchie Cobbs 	u_int				numActiveLinks;
1753949bee8SArchie Cobbs 	u_int				lastLink;	/* for round robin */
1763949bee8SArchie Cobbs 	struct ng_ppp_link_qstat	qstat[NG_PPP_MAX_LINKS];
1773949bee8SArchie Cobbs 	CIRCLEQ_HEAD(ng_ppp_fraglist, ng_ppp_frag)
1783949bee8SArchie Cobbs 					frags;		/* incoming fragments */
1793949bee8SArchie Cobbs 	int				mpSeqOut;	/* next out MP seq # */
1804cf49a43SJulian Elischer };
18162838faeSArchie Cobbs typedef struct ng_ppp_private *priv_p;
1824cf49a43SJulian Elischer 
1834cf49a43SJulian Elischer /* Netgraph node methods */
18474f5c6aaSJulian Elischer static ng_constructor_t	ng_ppp_constructor;
18574f5c6aaSJulian Elischer static ng_rcvmsg_t	ng_ppp_rcvmsg;
18674f5c6aaSJulian Elischer static ng_shutdown_t	ng_ppp_rmnode;
18774f5c6aaSJulian Elischer static ng_newhook_t	ng_ppp_newhook;
18874f5c6aaSJulian Elischer static ng_rcvdata_t	ng_ppp_rcvdata;
18974f5c6aaSJulian Elischer static ng_disconnect_t	ng_ppp_disconnect;
1904cf49a43SJulian Elischer 
1913949bee8SArchie Cobbs /* Helper functions */
192e149c4e2SArchie Cobbs static int	ng_ppp_input(node_p node, int bypass,
193e149c4e2SArchie Cobbs 			int linkNum, struct mbuf *m, meta_p meta);
194d690a6e7SArchie Cobbs static int	ng_ppp_output(node_p node, int bypass, int proto,
195e149c4e2SArchie Cobbs 			int linkNum, struct mbuf *m, meta_p meta);
196e149c4e2SArchie Cobbs static int	ng_ppp_mp_input(node_p node, int linkNum,
197e149c4e2SArchie Cobbs 			struct mbuf *m, meta_p meta);
1983949bee8SArchie Cobbs static int	ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta);
1993949bee8SArchie Cobbs static void	ng_ppp_mp_strategy(node_p node, int len, int *distrib);
2003949bee8SArchie Cobbs static int	ng_ppp_intcmp(const void *v1, const void *v2);
2013949bee8SArchie Cobbs static struct	mbuf *ng_ppp_addproto(struct mbuf *m, int proto, int compOK);
202d690a6e7SArchie Cobbs static struct	mbuf *ng_ppp_prepend(struct mbuf *m, const void *buf, int len);
2033949bee8SArchie Cobbs static int	ng_ppp_config_valid(node_p node,
2043949bee8SArchie Cobbs 			const struct ng_ppp_node_config *newConf);
2053949bee8SArchie Cobbs static void	ng_ppp_update(node_p node, int newConf);
2063949bee8SArchie Cobbs static void	ng_ppp_free_frags(node_p node);
2074cf49a43SJulian Elischer 
2084cf49a43SJulian Elischer /* Node type descriptor */
2093949bee8SArchie Cobbs static struct ng_type ng_ppp_typestruct = {
2104cf49a43SJulian Elischer 	NG_VERSION,
2114cf49a43SJulian Elischer 	NG_PPP_NODE_TYPE,
2124cf49a43SJulian Elischer 	NULL,
2134cf49a43SJulian Elischer 	ng_ppp_constructor,
2144cf49a43SJulian Elischer 	ng_ppp_rcvmsg,
2154cf49a43SJulian Elischer 	ng_ppp_rmnode,
2164cf49a43SJulian Elischer 	ng_ppp_newhook,
2174cf49a43SJulian Elischer 	NULL,
2184cf49a43SJulian Elischer 	NULL,
2194cf49a43SJulian Elischer 	ng_ppp_rcvdata,
2204cf49a43SJulian Elischer 	ng_ppp_rcvdata,
2214cf49a43SJulian Elischer 	ng_ppp_disconnect
2224cf49a43SJulian Elischer };
2233949bee8SArchie Cobbs NETGRAPH_INIT(ppp, &ng_ppp_typestruct);
2244cf49a43SJulian Elischer 
2253949bee8SArchie Cobbs static int	*compareLatencies;		/* hack for ng_ppp_intcmp() */
2264cf49a43SJulian Elischer 
227d690a6e7SArchie Cobbs /* Address and control field header */
228d690a6e7SArchie Cobbs static const u_char	ng_ppp_acf[2] = { 0xff, 0x03 };
229d690a6e7SArchie Cobbs 
2304cf49a43SJulian Elischer #define ERROUT(x)	do { error = (x); goto done; } while (0)
2314cf49a43SJulian Elischer 
2324cf49a43SJulian Elischer /************************************************************************
2334cf49a43SJulian Elischer 			NETGRAPH NODE STUFF
2344cf49a43SJulian Elischer  ************************************************************************/
2354cf49a43SJulian Elischer 
2364cf49a43SJulian Elischer /*
2373949bee8SArchie Cobbs  * Node type constructor
2384cf49a43SJulian Elischer  */
2394cf49a43SJulian Elischer static int
2404cf49a43SJulian Elischer ng_ppp_constructor(node_p *nodep)
2414cf49a43SJulian Elischer {
2424cf49a43SJulian Elischer 	priv_p priv;
2434cf49a43SJulian Elischer 	int error;
2444cf49a43SJulian Elischer 
2454cf49a43SJulian Elischer 	/* Allocate private structure */
2464cf49a43SJulian Elischer 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK);
2474cf49a43SJulian Elischer 	if (priv == NULL)
2484cf49a43SJulian Elischer 		return (ENOMEM);
2494cf49a43SJulian Elischer 	bzero(priv, sizeof(*priv));
2504cf49a43SJulian Elischer 
2514cf49a43SJulian Elischer 	/* Call generic node constructor */
2523949bee8SArchie Cobbs 	if ((error = ng_make_node_common(&ng_ppp_typestruct, nodep))) {
2534cf49a43SJulian Elischer 		FREE(priv, M_NETGRAPH);
2544cf49a43SJulian Elischer 		return (error);
2554cf49a43SJulian Elischer 	}
2564cf49a43SJulian Elischer 	(*nodep)->private = priv;
2574cf49a43SJulian Elischer 
2583949bee8SArchie Cobbs 	/* Initialize state */
2593949bee8SArchie Cobbs 	CIRCLEQ_INIT(&priv->frags);
2603949bee8SArchie Cobbs 
2614cf49a43SJulian Elischer 	/* Done */
2624cf49a43SJulian Elischer 	return (0);
2634cf49a43SJulian Elischer }
2644cf49a43SJulian Elischer 
2654cf49a43SJulian Elischer /*
2664cf49a43SJulian Elischer  * Give our OK for a hook to be added
2674cf49a43SJulian Elischer  */
2684cf49a43SJulian Elischer static int
2694cf49a43SJulian Elischer ng_ppp_newhook(node_p node, hook_p hook, const char *name)
2704cf49a43SJulian Elischer {
2714cf49a43SJulian Elischer 	const priv_p priv = node->private;
2723949bee8SArchie Cobbs 	int linkNum = -1;
2733949bee8SArchie Cobbs 	hook_p *hookPtr = NULL;
2743949bee8SArchie Cobbs 	int hookIndex = -1;
2754cf49a43SJulian Elischer 
2763949bee8SArchie Cobbs 	/* Figure out which hook it is */
2773949bee8SArchie Cobbs 	if (strncmp(name, NG_PPP_HOOK_LINK_PREFIX,	/* a link hook? */
2783949bee8SArchie Cobbs 	    strlen(NG_PPP_HOOK_LINK_PREFIX)) == 0) {
2792b70adcbSArchie Cobbs 		const char *cp, *eptr;
2803949bee8SArchie Cobbs 
2812b70adcbSArchie Cobbs 		cp = name + strlen(NG_PPP_HOOK_LINK_PREFIX);
2822b70adcbSArchie Cobbs 		if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
2834cf49a43SJulian Elischer 			return (EINVAL);
2842b70adcbSArchie Cobbs 		linkNum = (int)strtoul(cp, &eptr, 10);
2852b70adcbSArchie Cobbs 		if (*eptr != '\0' || linkNum < 0 || linkNum >= NG_PPP_MAX_LINKS)
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:
355e149c4e2SArchie Cobbs 		case NGM_PPP_GETCLR_LINK_STATS:
3563949bee8SArchie Cobbs 		    {
3573949bee8SArchie Cobbs 			struct ng_ppp_link_stat *stats;
3583949bee8SArchie Cobbs 			u_int16_t linkNum;
3593949bee8SArchie Cobbs 
3603949bee8SArchie Cobbs 			if (msg->header.arglen != sizeof(u_int16_t))
3613949bee8SArchie Cobbs 				ERROUT(EINVAL);
3623949bee8SArchie Cobbs 			linkNum = *((u_int16_t *) msg->data);
3633949bee8SArchie Cobbs 			if (linkNum >= NG_PPP_MAX_LINKS
3643949bee8SArchie Cobbs 			    && linkNum != NG_PPP_BUNDLE_LINKNUM)
3653949bee8SArchie Cobbs 				ERROUT(EINVAL);
3663949bee8SArchie Cobbs 			stats = (linkNum == NG_PPP_BUNDLE_LINKNUM) ?
3673949bee8SArchie Cobbs 				&priv->bundleStats : &priv->linkStats[linkNum];
368e149c4e2SArchie Cobbs 			if (msg->header.cmd != NGM_PPP_CLR_LINK_STATS) {
3693949bee8SArchie Cobbs 				NG_MKRESPONSE(resp, msg,
3703949bee8SArchie Cobbs 				    sizeof(struct ng_ppp_link_stat), M_NOWAIT);
3713949bee8SArchie Cobbs 				if (resp == NULL)
3723949bee8SArchie Cobbs 					ERROUT(ENOMEM);
3733949bee8SArchie Cobbs 				bcopy(stats, resp->data, sizeof(*stats));
374e149c4e2SArchie Cobbs 			}
375e149c4e2SArchie Cobbs 			if (msg->header.cmd != NGM_PPP_GET_LINK_STATS)
3763949bee8SArchie Cobbs 				bzero(stats, sizeof(*stats));
3774cf49a43SJulian Elischer 			break;
3783949bee8SArchie Cobbs 		    }
3794cf49a43SJulian Elischer 		default:
3804cf49a43SJulian Elischer 			error = EINVAL;
3814cf49a43SJulian Elischer 			break;
3824cf49a43SJulian Elischer 		}
3834cf49a43SJulian Elischer 		break;
3843949bee8SArchie Cobbs 	case NGM_VJC_COOKIE:
3853949bee8SArchie Cobbs 	   {
3863949bee8SArchie Cobbs 	       char path[NG_PATHLEN + 1];
3873949bee8SArchie Cobbs 	       node_p origNode;
3883949bee8SArchie Cobbs 
3893949bee8SArchie Cobbs 	       if ((error = ng_path2node(node, raddr, &origNode, NULL)) != 0)
3903949bee8SArchie Cobbs 		       ERROUT(error);
3913949bee8SArchie Cobbs 	       snprintf(path, sizeof(path), "[%lx]:%s",
3923949bee8SArchie Cobbs 		   (long) node, NG_PPP_HOOK_VJC_IP);
3933949bee8SArchie Cobbs 	       return ng_send_msg(origNode, msg, path, rptr);
3943949bee8SArchie Cobbs 	       break;
3953949bee8SArchie Cobbs 	   }
3964cf49a43SJulian Elischer 	default:
3974cf49a43SJulian Elischer 		error = EINVAL;
3984cf49a43SJulian Elischer 		break;
3994cf49a43SJulian Elischer 	}
4004cf49a43SJulian Elischer 	if (rptr)
4014cf49a43SJulian Elischer 		*rptr = resp;
4024cf49a43SJulian Elischer 	else if (resp)
4034cf49a43SJulian Elischer 		FREE(resp, M_NETGRAPH);
4044cf49a43SJulian Elischer 
4054cf49a43SJulian Elischer done:
4064cf49a43SJulian Elischer 	FREE(msg, M_NETGRAPH);
4074cf49a43SJulian Elischer 	return (error);
4084cf49a43SJulian Elischer }
4094cf49a43SJulian Elischer 
4104cf49a43SJulian Elischer /*
4114cf49a43SJulian Elischer  * Receive data on a hook
4124cf49a43SJulian Elischer  */
4134cf49a43SJulian Elischer static int
4144cf49a43SJulian Elischer ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
4154cf49a43SJulian Elischer {
4164cf49a43SJulian Elischer 	const node_p node = hook->node;
4174cf49a43SJulian Elischer 	const priv_p priv = node->private;
4183949bee8SArchie Cobbs 	const int index = HOOK_INDEX(hook);
4193949bee8SArchie Cobbs 	u_int16_t linkNum = NG_PPP_BUNDLE_LINKNUM;
4203949bee8SArchie Cobbs 	hook_p outHook = NULL;
4213949bee8SArchie Cobbs 	int proto = 0, error;
4224cf49a43SJulian Elischer 
4233949bee8SArchie Cobbs 	/* Did it come from a link hook? */
4243949bee8SArchie Cobbs 	if (index < 0) {
4254cf49a43SJulian Elischer 
426e149c4e2SArchie Cobbs 		/* Convert index into a link number */
4273949bee8SArchie Cobbs 		linkNum = (u_int16_t)~index;
4283949bee8SArchie Cobbs 		KASSERT(linkNum < NG_PPP_MAX_LINKS,
4293949bee8SArchie Cobbs 		    ("%s: bogus index 0x%x", __FUNCTION__, index));
4303949bee8SArchie Cobbs 
4313949bee8SArchie Cobbs 		/* Stats */
4323949bee8SArchie Cobbs 		priv->linkStats[linkNum].recvFrames++;
4333949bee8SArchie Cobbs 		priv->linkStats[linkNum].recvOctets += m->m_pkthdr.len;
4343949bee8SArchie Cobbs 
435d690a6e7SArchie Cobbs 		/* Strip address and control fields, if present */
436d690a6e7SArchie Cobbs 		if (m->m_pkthdr.len >= 2) {
437d690a6e7SArchie Cobbs 			if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) {
438d690a6e7SArchie Cobbs 				NG_FREE_DATA(m, meta);
439d690a6e7SArchie Cobbs 				return (ENOBUFS);
440d690a6e7SArchie Cobbs 			}
441d690a6e7SArchie Cobbs 			if (bcmp(mtod(m, u_char *), &ng_ppp_acf, 2) == 0)
442d690a6e7SArchie Cobbs 				m_adj(m, 2);
443d690a6e7SArchie Cobbs 		}
444d690a6e7SArchie Cobbs 
445e149c4e2SArchie Cobbs 		/* Dispatch incoming frame (if not enabled, to bypass) */
446e149c4e2SArchie Cobbs 		return ng_ppp_input(node,
447e149c4e2SArchie Cobbs 		    !priv->conf.links[linkNum].enableLink, linkNum, m, meta);
4483949bee8SArchie Cobbs 	}
4493949bee8SArchie Cobbs 
4503949bee8SArchie Cobbs 	/* Get protocol & check if data allowed from this hook */
4513949bee8SArchie Cobbs 	switch (index) {
4523949bee8SArchie Cobbs 
453e149c4e2SArchie Cobbs 	/* Outgoing data */
4543949bee8SArchie Cobbs 	case HOOK_INDEX_ATALK:
4553949bee8SArchie Cobbs 		if (!priv->conf.enableAtalk) {
4563949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4573949bee8SArchie Cobbs 			return (ENXIO);
4583949bee8SArchie Cobbs 		}
4593949bee8SArchie Cobbs 		proto = PROT_APPLETALK;
4603949bee8SArchie Cobbs 		break;
4613949bee8SArchie Cobbs 	case HOOK_INDEX_IPX:
4623949bee8SArchie Cobbs 		if (!priv->conf.enableIPX) {
4633949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4643949bee8SArchie Cobbs 			return (ENXIO);
4653949bee8SArchie Cobbs 		}
4663949bee8SArchie Cobbs 		proto = PROT_IPX;
4673949bee8SArchie Cobbs 		break;
4683949bee8SArchie Cobbs 	case HOOK_INDEX_INET:
4693949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_VJIP:
4703949bee8SArchie Cobbs 		if (!priv->conf.enableIP) {
4713949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4723949bee8SArchie Cobbs 			return (ENXIO);
4733949bee8SArchie Cobbs 		}
4743949bee8SArchie Cobbs 		proto = PROT_IP;
4753949bee8SArchie Cobbs 		break;
4763949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_COMP:
4773949bee8SArchie Cobbs 		if (!priv->conf.enableVJCompression) {
4783949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4793949bee8SArchie Cobbs 			return (ENXIO);
4803949bee8SArchie Cobbs 		}
4813949bee8SArchie Cobbs 		proto = PROT_VJCOMP;
4823949bee8SArchie Cobbs 		break;
4833949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_UNCOMP:
4843949bee8SArchie Cobbs 		if (!priv->conf.enableVJCompression) {
4853949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4863949bee8SArchie Cobbs 			return (ENXIO);
4873949bee8SArchie Cobbs 		}
4883949bee8SArchie Cobbs 		proto = PROT_VJUNCOMP;
4893949bee8SArchie Cobbs 		break;
4903949bee8SArchie Cobbs 	case HOOK_INDEX_COMPRESS:
4913949bee8SArchie Cobbs 		if (!priv->conf.enableCompression) {
4923949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
4933949bee8SArchie Cobbs 			return (ENXIO);
4943949bee8SArchie Cobbs 		}
4953949bee8SArchie Cobbs 		proto = PROT_COMPD;
4963949bee8SArchie Cobbs 		break;
4973949bee8SArchie Cobbs 	case HOOK_INDEX_ENCRYPT:
4983949bee8SArchie Cobbs 		if (!priv->conf.enableEncryption) {
4993949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5003949bee8SArchie Cobbs 			return (ENXIO);
5013949bee8SArchie Cobbs 		}
5023949bee8SArchie Cobbs 		proto = PROT_CRYPTD;
5033949bee8SArchie Cobbs 		break;
5043949bee8SArchie Cobbs 	case HOOK_INDEX_BYPASS:
5053949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 4) {
5063949bee8SArchie Cobbs 			NG_FREE_META(meta);
5073949bee8SArchie Cobbs 			return (EINVAL);
5083949bee8SArchie Cobbs 		}
5093949bee8SArchie Cobbs 		if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
5103949bee8SArchie Cobbs 			NG_FREE_META(meta);
5113949bee8SArchie Cobbs 			return (ENOBUFS);
5123949bee8SArchie Cobbs 		}
5133949bee8SArchie Cobbs 		linkNum = ntohs(mtod(m, u_int16_t *)[0]);
5143949bee8SArchie Cobbs 		proto = ntohs(mtod(m, u_int16_t *)[1]);
5153949bee8SArchie Cobbs 		m_adj(m, 4);
5163949bee8SArchie Cobbs 		if (linkNum >= NG_PPP_MAX_LINKS
5173949bee8SArchie Cobbs 		    && linkNum != NG_PPP_BUNDLE_LINKNUM)
5183949bee8SArchie Cobbs 			return (EINVAL);
5193949bee8SArchie Cobbs 		break;
5203949bee8SArchie Cobbs 
521e149c4e2SArchie Cobbs 	/* Incoming data */
5223949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_IP:
523e149c4e2SArchie Cobbs 		if (!priv->conf.enableIP || !priv->conf.enableVJDecompression) {
5243949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5253949bee8SArchie Cobbs 			return (ENXIO);
5263949bee8SArchie Cobbs 		}
5273949bee8SArchie Cobbs 		break;
5283949bee8SArchie Cobbs 	case HOOK_INDEX_DECOMPRESS:
5293949bee8SArchie Cobbs 		if (!priv->conf.enableDecompression) {
5303949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5313949bee8SArchie Cobbs 			return (ENXIO);
5323949bee8SArchie Cobbs 		}
5333949bee8SArchie Cobbs 		break;
5343949bee8SArchie Cobbs 	case HOOK_INDEX_DECRYPT:
5353949bee8SArchie Cobbs 		if (!priv->conf.enableDecryption) {
5363949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5373949bee8SArchie Cobbs 			return (ENXIO);
5383949bee8SArchie Cobbs 		}
5393949bee8SArchie Cobbs 		break;
5403949bee8SArchie Cobbs 	default:
541e149c4e2SArchie Cobbs 		panic("%s: bogus index 0x%x", __FUNCTION__, index);
5423949bee8SArchie Cobbs 	}
5433949bee8SArchie Cobbs 
5443949bee8SArchie Cobbs 	/* Now figure out what to do with the frame */
5453949bee8SArchie Cobbs 	switch (index) {
546e149c4e2SArchie Cobbs 
547e149c4e2SArchie Cobbs 	/* Outgoing data */
5483949bee8SArchie Cobbs 	case HOOK_INDEX_INET:
5493949bee8SArchie Cobbs 		if (priv->conf.enableVJCompression && priv->vjCompHooked) {
5503949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_IP];
5513949bee8SArchie Cobbs 			break;
5523949bee8SArchie Cobbs 		}
5533949bee8SArchie Cobbs 		/* FALLTHROUGH */
5543949bee8SArchie Cobbs 	case HOOK_INDEX_ATALK:
5553949bee8SArchie Cobbs 	case HOOK_INDEX_IPX:
5563949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_COMP:
5573949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_UNCOMP:
5583949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_VJIP:
5593949bee8SArchie Cobbs 		if (priv->conf.enableCompression
5603949bee8SArchie Cobbs 		    && priv->hooks[HOOK_INDEX_COMPRESS] != 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_COMPRESS];
5663949bee8SArchie Cobbs 			break;
5673949bee8SArchie Cobbs 		}
5683949bee8SArchie Cobbs 		/* FALLTHROUGH */
5693949bee8SArchie Cobbs 	case HOOK_INDEX_COMPRESS:
5703949bee8SArchie Cobbs 		if (priv->conf.enableEncryption
5713949bee8SArchie Cobbs 		    && priv->hooks[HOOK_INDEX_ENCRYPT] != NULL) {
5723949bee8SArchie Cobbs 			if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) {
5733949bee8SArchie Cobbs 				NG_FREE_META(meta);
5743949bee8SArchie Cobbs 				return (ENOBUFS);
5753949bee8SArchie Cobbs 			}
5763949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_ENCRYPT];
5773949bee8SArchie Cobbs 			break;
5783949bee8SArchie Cobbs 		}
5793949bee8SArchie Cobbs 		/* FALLTHROUGH */
5803949bee8SArchie Cobbs 	case HOOK_INDEX_ENCRYPT:
5810e11d0f3SArchie Cobbs 		return ng_ppp_output(node, 0,
582d690a6e7SArchie Cobbs 		    proto, NG_PPP_BUNDLE_LINKNUM, m, meta);
5833949bee8SArchie Cobbs 
5840e11d0f3SArchie Cobbs 	case HOOK_INDEX_BYPASS:
5850e11d0f3SArchie Cobbs 		return ng_ppp_output(node, 1, proto, linkNum, m, meta);
5860e11d0f3SArchie Cobbs 
5873949bee8SArchie Cobbs 	/* Incoming data */
5883949bee8SArchie Cobbs 	case HOOK_INDEX_DECRYPT:
5893949bee8SArchie Cobbs 	case HOOK_INDEX_DECOMPRESS:
590e149c4e2SArchie Cobbs 		return ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta);
591e149c4e2SArchie Cobbs 
5923949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_IP:
593e149c4e2SArchie Cobbs 		outHook = priv->hooks[HOOK_INDEX_INET];
594e149c4e2SArchie Cobbs 		break;
5953949bee8SArchie Cobbs 	}
5963949bee8SArchie Cobbs 
5973949bee8SArchie Cobbs 	/* Send packet out hook */
5983949bee8SArchie Cobbs 	NG_SEND_DATA(error, outHook, m, meta);
5994cf49a43SJulian Elischer 	return (error);
6004cf49a43SJulian Elischer }
6014cf49a43SJulian Elischer 
6024cf49a43SJulian Elischer /*
6034cf49a43SJulian Elischer  * Destroy node
6044cf49a43SJulian Elischer  */
6054cf49a43SJulian Elischer static int
6064cf49a43SJulian Elischer ng_ppp_rmnode(node_p node)
6074cf49a43SJulian Elischer {
6084cf49a43SJulian Elischer 	const priv_p priv = node->private;
6094cf49a43SJulian Elischer 
6104cf49a43SJulian Elischer 	/* Take down netgraph node */
6114cf49a43SJulian Elischer 	node->flags |= NG_INVALID;
6124cf49a43SJulian Elischer 	ng_cutlinks(node);
6134cf49a43SJulian Elischer 	ng_unname(node);
614e149c4e2SArchie Cobbs 	ng_ppp_free_frags(node);
6154cf49a43SJulian Elischer 	bzero(priv, sizeof(*priv));
6164cf49a43SJulian Elischer 	FREE(priv, M_NETGRAPH);
6174cf49a43SJulian Elischer 	node->private = NULL;
6184cf49a43SJulian Elischer 	ng_unref(node);		/* let the node escape */
6194cf49a43SJulian Elischer 	return (0);
6204cf49a43SJulian Elischer }
6214cf49a43SJulian Elischer 
6224cf49a43SJulian Elischer /*
6234cf49a43SJulian Elischer  * Hook disconnection
6244cf49a43SJulian Elischer  */
6254cf49a43SJulian Elischer static int
6264cf49a43SJulian Elischer ng_ppp_disconnect(hook_p hook)
6274cf49a43SJulian Elischer {
62862838faeSArchie Cobbs 	const node_p node = hook->node;
62962838faeSArchie Cobbs 	const priv_p priv = node->private;
63062838faeSArchie Cobbs 	const int index = HOOK_INDEX(hook);
63162838faeSArchie Cobbs 
63262838faeSArchie Cobbs 	/* Zero out hook pointer */
63362838faeSArchie Cobbs 	if (index < 0)
63462838faeSArchie Cobbs 		priv->links[~index] = NULL;
63562838faeSArchie Cobbs 	else
63662838faeSArchie Cobbs 		priv->hooks[index] = NULL;
63762838faeSArchie Cobbs 
63862838faeSArchie Cobbs 	/* Update derived info (or go away if no hooks left) */
63962838faeSArchie Cobbs 	if (node->numhooks > 0)
64062838faeSArchie Cobbs 		ng_ppp_update(node, 0);
64162838faeSArchie Cobbs 	else
6424cf49a43SJulian Elischer 		ng_rmnode(hook->node);
6434cf49a43SJulian Elischer 	return (0);
6444cf49a43SJulian Elischer }
6454cf49a43SJulian Elischer 
6464cf49a43SJulian Elischer /************************************************************************
6474cf49a43SJulian Elischer 			HELPER STUFF
6484cf49a43SJulian Elischer  ************************************************************************/
6494cf49a43SJulian Elischer 
6504cf49a43SJulian Elischer /*
6513949bee8SArchie Cobbs  * Handle an incoming frame.  Extract the PPP protocol number
6523949bee8SArchie Cobbs  * and dispatch accordingly.
6534cf49a43SJulian Elischer  */
6544cf49a43SJulian Elischer static int
655e149c4e2SArchie Cobbs ng_ppp_input(node_p node, int bypass, int linkNum, struct mbuf *m, meta_p meta)
6564cf49a43SJulian Elischer {
6573949bee8SArchie Cobbs 	const priv_p priv = node->private;
6583949bee8SArchie Cobbs 	hook_p outHook = NULL;
6593949bee8SArchie Cobbs 	int proto, error;
6604cf49a43SJulian Elischer 
6613949bee8SArchie Cobbs 	/* Extract protocol number */
6623949bee8SArchie Cobbs 	for (proto = 0; !PROT_VALID(proto) && m->m_pkthdr.len > 0; ) {
6633949bee8SArchie Cobbs 		if (m->m_len < 1 && (m = m_pullup(m, 1)) == NULL) {
6643949bee8SArchie Cobbs 			NG_FREE_META(meta);
6653949bee8SArchie Cobbs 			return (ENOBUFS);
6664cf49a43SJulian Elischer 		}
6673949bee8SArchie Cobbs 		proto = (proto << 8) + *mtod(m, u_char *);
6683949bee8SArchie Cobbs 		m_adj(m, 1);
6693949bee8SArchie Cobbs 	}
6703949bee8SArchie Cobbs 	if (!PROT_VALID(proto)) {
6713949bee8SArchie Cobbs 		if (linkNum == NG_PPP_BUNDLE_LINKNUM)
6723949bee8SArchie Cobbs 			priv->bundleStats.badProtos++;
6733949bee8SArchie Cobbs 		else
6743949bee8SArchie Cobbs 			priv->linkStats[linkNum].badProtos++;
6753949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
6763949bee8SArchie Cobbs 		return (EINVAL);
6773949bee8SArchie Cobbs 	}
6783949bee8SArchie Cobbs 
679e149c4e2SArchie Cobbs 	/* Bypass frame? */
680e149c4e2SArchie Cobbs 	if (bypass)
681e149c4e2SArchie Cobbs 		goto bypass;
682e149c4e2SArchie Cobbs 
6833949bee8SArchie Cobbs 	/* Check protocol */
6843949bee8SArchie Cobbs 	switch (proto) {
6853949bee8SArchie Cobbs 	case PROT_COMPD:
6863949bee8SArchie Cobbs 		if (priv->conf.enableDecompression)
6873949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_DECOMPRESS];
6883949bee8SArchie Cobbs 		break;
6893949bee8SArchie Cobbs 	case PROT_CRYPTD:
6903949bee8SArchie Cobbs 		if (priv->conf.enableDecryption)
6913949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_DECRYPT];
6923949bee8SArchie Cobbs 		break;
6933949bee8SArchie Cobbs 	case PROT_VJCOMP:
6943949bee8SArchie Cobbs 		if (priv->conf.enableVJDecompression && priv->vjCompHooked)
6953949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_COMP];
6963949bee8SArchie Cobbs 		break;
6973949bee8SArchie Cobbs 	case PROT_VJUNCOMP:
6983949bee8SArchie Cobbs 		if (priv->conf.enableVJDecompression && priv->vjCompHooked)
6993949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_UNCOMP];
7003949bee8SArchie Cobbs 		break;
7013949bee8SArchie Cobbs 	case PROT_MP:
7020e11d0f3SArchie Cobbs 		if (priv->conf.enableMultilink)
7033949bee8SArchie Cobbs 			return ng_ppp_mp_input(node, linkNum, m, meta);
7043949bee8SArchie Cobbs 		break;
7053949bee8SArchie Cobbs 	case PROT_APPLETALK:
7063949bee8SArchie Cobbs 		if (priv->conf.enableAtalk)
7073949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_ATALK];
7083949bee8SArchie Cobbs 		break;
7093949bee8SArchie Cobbs 	case PROT_IPX:
7103949bee8SArchie Cobbs 		if (priv->conf.enableIPX)
7113949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_IPX];
7123949bee8SArchie Cobbs 		break;
7133949bee8SArchie Cobbs 	case PROT_IP:
7143949bee8SArchie Cobbs 		if (priv->conf.enableIP)
7153949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_INET];
7163949bee8SArchie Cobbs 		break;
7173949bee8SArchie Cobbs 	}
7183949bee8SArchie Cobbs 
7193949bee8SArchie Cobbs 	/* For unknown/inactive protocols, forward out the bypass hook */
720e149c4e2SArchie Cobbs bypass:
7213949bee8SArchie Cobbs 	if (outHook == NULL) {
722d690a6e7SArchie Cobbs 		u_int16_t hdr[2];
723d690a6e7SArchie Cobbs 
724d690a6e7SArchie Cobbs 		hdr[0] = htons(linkNum);
725d690a6e7SArchie Cobbs 		hdr[1] = htons((u_int16_t)proto);
726d690a6e7SArchie Cobbs 		if ((m = ng_ppp_prepend(m, &hdr, 4)) == NULL)
7273949bee8SArchie Cobbs 			return (ENOBUFS);
7283949bee8SArchie Cobbs 		outHook = priv->hooks[HOOK_INDEX_BYPASS];
7293949bee8SArchie Cobbs 	}
7303949bee8SArchie Cobbs 
7313949bee8SArchie Cobbs 	/* Forward frame */
7323949bee8SArchie Cobbs 	NG_SEND_DATA(error, outHook, m, meta);
7333949bee8SArchie Cobbs 	return (error);
7344cf49a43SJulian Elischer }
7354cf49a43SJulian Elischer 
7364cf49a43SJulian Elischer /*
7373949bee8SArchie Cobbs  * Deliver a frame out a link, either a real one or NG_PPP_BUNDLE_LINKNUM
7380e11d0f3SArchie Cobbs  * If the link is not enabled then ENXIO is returned, unless "bypass" is != 0.
7394cf49a43SJulian Elischer  */
7403949bee8SArchie Cobbs static int
741d690a6e7SArchie Cobbs ng_ppp_output(node_p node, int bypass,
742d690a6e7SArchie Cobbs 	int proto, int linkNum, struct mbuf *m, meta_p meta)
7434cf49a43SJulian Elischer {
7443949bee8SArchie Cobbs 	const priv_p priv = node->private;
745e149c4e2SArchie Cobbs 	int len, error;
7464cf49a43SJulian Elischer 
747d690a6e7SArchie Cobbs 	/* If not doing MP, map bundle virtual link to (the only) link */
748d690a6e7SArchie Cobbs 	if (linkNum == NG_PPP_BUNDLE_LINKNUM && !priv->conf.enableMultilink)
7493949bee8SArchie Cobbs 		linkNum = priv->activeLinks[0];
7503949bee8SArchie Cobbs 
751d690a6e7SArchie Cobbs 	/* Check link status (if real) */
7520e11d0f3SArchie Cobbs 	if (linkNum != NG_PPP_BUNDLE_LINKNUM) {
7530e11d0f3SArchie Cobbs 		if (!bypass && !priv->conf.links[linkNum].enableLink)
7543949bee8SArchie Cobbs 			return (ENXIO);
7553949bee8SArchie Cobbs 		if (priv->links[linkNum] == NULL) {
7563949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
7573949bee8SArchie Cobbs 			return (ENETDOWN);
7583949bee8SArchie Cobbs 		}
7590e11d0f3SArchie Cobbs 	}
7603949bee8SArchie Cobbs 
761d690a6e7SArchie Cobbs 	/* Prepend protocol number, possibly compressed */
762d690a6e7SArchie Cobbs 	if ((m = ng_ppp_addproto(m, proto,
763d690a6e7SArchie Cobbs 	    linkNum == NG_PPP_BUNDLE_LINKNUM
764d690a6e7SArchie Cobbs 	      || priv->conf.links[linkNum].enableProtoComp)) == NULL) {
765d690a6e7SArchie Cobbs 		NG_FREE_META(meta);
766d690a6e7SArchie Cobbs 		return (ENOBUFS);
767d690a6e7SArchie Cobbs 	}
768d690a6e7SArchie Cobbs 
769d690a6e7SArchie Cobbs 	/* Special handling for the MP virtual link */
770d690a6e7SArchie Cobbs 	if (linkNum == NG_PPP_BUNDLE_LINKNUM)
771d690a6e7SArchie Cobbs 		return ng_ppp_mp_output(node, m, meta);
772d690a6e7SArchie Cobbs 
773d690a6e7SArchie Cobbs 	/* Prepend address and control field (unless compressed) */
774d690a6e7SArchie Cobbs 	if (proto == PROT_LCP || !priv->conf.links[linkNum].enableACFComp) {
775d690a6e7SArchie Cobbs 		if ((m = ng_ppp_prepend(m, &ng_ppp_acf, 2)) == NULL) {
776d690a6e7SArchie Cobbs 			NG_FREE_META(meta);
777d690a6e7SArchie Cobbs 			return (ENOBUFS);
778d690a6e7SArchie Cobbs 		}
779d690a6e7SArchie Cobbs 	}
780d690a6e7SArchie Cobbs 
7813949bee8SArchie Cobbs 	/* Deliver frame */
782e149c4e2SArchie Cobbs 	len = m->m_pkthdr.len;
7833949bee8SArchie Cobbs 	NG_SEND_DATA(error, priv->links[linkNum], m, meta);
784fb1fc8abSArchie Cobbs 
785fb1fc8abSArchie Cobbs 	/* Update stats and 'bytes in queue' counter */
786fb1fc8abSArchie Cobbs 	if (error == 0) {
787fb1fc8abSArchie Cobbs 		priv->linkStats[linkNum].xmitFrames++;
788e149c4e2SArchie Cobbs 		priv->linkStats[linkNum].xmitOctets += len;
789e149c4e2SArchie Cobbs 		priv->qstat[linkNum].bytesInQueue += len;
790fb1fc8abSArchie Cobbs 		microtime(&priv->qstat[linkNum].lastWrite);
791fb1fc8abSArchie Cobbs 	}
7923949bee8SArchie Cobbs 	return error;
7933949bee8SArchie Cobbs }
7943949bee8SArchie Cobbs 
7953949bee8SArchie Cobbs /*
7963949bee8SArchie Cobbs  * Handle an incoming multi-link fragment
7973949bee8SArchie Cobbs  */
7983949bee8SArchie Cobbs static int
7993949bee8SArchie Cobbs ng_ppp_mp_input(node_p node, int linkNum, struct mbuf *m, meta_p meta)
8003949bee8SArchie Cobbs {
8013949bee8SArchie Cobbs 	const priv_p priv = node->private;
8023949bee8SArchie Cobbs 	struct ng_ppp_frag frag0, *frag = &frag0;
8033949bee8SArchie Cobbs 	struct ng_ppp_frag *qent, *qnext;
8043949bee8SArchie Cobbs 	struct ng_ppp_frag *first = NULL, *last = NULL;
8053949bee8SArchie Cobbs 	int diff, highSeq, nextSeq;
8063949bee8SArchie Cobbs 	struct mbuf *tail;
8073949bee8SArchie Cobbs 
8083949bee8SArchie Cobbs 	/* Extract fragment information from MP header */
8093949bee8SArchie Cobbs 	if (priv->conf.recvShortSeq) {
8103949bee8SArchie Cobbs 		u_int16_t shdr;
8113949bee8SArchie Cobbs 
8123949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 2) {
8133949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
8143949bee8SArchie Cobbs 			return (EINVAL);
8153949bee8SArchie Cobbs 		}
8163949bee8SArchie Cobbs 		if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) {
8173949bee8SArchie Cobbs 			NG_FREE_META(meta);
8183949bee8SArchie Cobbs 			return (ENOBUFS);
8193949bee8SArchie Cobbs 		}
8203949bee8SArchie Cobbs 		shdr = ntohs(*mtod(m, u_int16_t *));
8213949bee8SArchie Cobbs 		frag->seq = shdr & MP_SHORT_SEQ_MASK;
8223949bee8SArchie Cobbs 		frag->first = (shdr & MP_SHORT_FIRST_FLAG) != 0;
8233949bee8SArchie Cobbs 		frag->last = (shdr & MP_SHORT_LAST_FLAG) != 0;
8243949bee8SArchie Cobbs 		highSeq = CIRCLEQ_EMPTY(&priv->frags) ?
8253949bee8SArchie Cobbs 		    frag->seq : CIRCLEQ_FIRST(&priv->frags)->seq;
8263949bee8SArchie Cobbs 		diff = MP_SHORT_SEQ_DIFF(frag->seq, highSeq);
8273949bee8SArchie Cobbs 		m_adj(m, 2);
8283949bee8SArchie Cobbs 	} else {
8293949bee8SArchie Cobbs 		u_int32_t lhdr;
8303949bee8SArchie Cobbs 
8313949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 4) {
8323949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
8333949bee8SArchie Cobbs 			return (EINVAL);
8343949bee8SArchie Cobbs 		}
8353949bee8SArchie Cobbs 		if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
8363949bee8SArchie Cobbs 			NG_FREE_META(meta);
8373949bee8SArchie Cobbs 			return (ENOBUFS);
8383949bee8SArchie Cobbs 		}
8393949bee8SArchie Cobbs 		lhdr = ntohl(*mtod(m, u_int32_t *));
8403949bee8SArchie Cobbs 		frag->seq = lhdr & MP_LONG_SEQ_MASK;
8413949bee8SArchie Cobbs 		frag->first = (lhdr & MP_LONG_FIRST_FLAG) != 0;
8423949bee8SArchie Cobbs 		frag->last = (lhdr & MP_LONG_LAST_FLAG) != 0;
8433949bee8SArchie Cobbs 		highSeq = CIRCLEQ_EMPTY(&priv->frags) ?
8443949bee8SArchie Cobbs 		    frag->seq : CIRCLEQ_FIRST(&priv->frags)->seq;
8453949bee8SArchie Cobbs 		diff = MP_LONG_SEQ_DIFF(frag->seq, highSeq);
8463949bee8SArchie Cobbs 		m_adj(m, 4);
8473949bee8SArchie Cobbs 	}
8483949bee8SArchie Cobbs 	frag->data = m;
8493949bee8SArchie Cobbs 	frag->meta = meta;
8503949bee8SArchie Cobbs 
8513949bee8SArchie Cobbs 	/* If the sequence number makes a large jump, empty the queue */
8523949bee8SArchie Cobbs 	if (diff <= -MP_INSANE_SEQ_JUMP || diff >= MP_INSANE_SEQ_JUMP)
8533949bee8SArchie Cobbs 		ng_ppp_free_frags(node);
8543949bee8SArchie Cobbs 
8553949bee8SArchie Cobbs 	/* Optimization: handle a frame that's all in one fragment */
8563949bee8SArchie Cobbs 	if (frag->first && frag->last)
857e149c4e2SArchie Cobbs 		return ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta);
8583949bee8SArchie Cobbs 
8593949bee8SArchie Cobbs 	/* Allocate a new frag struct for the queue */
8603949bee8SArchie Cobbs 	MALLOC(frag, struct ng_ppp_frag *, sizeof(*frag), M_NETGRAPH, M_NOWAIT);
8613949bee8SArchie Cobbs 	if (frag == NULL) {
8623949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
8633949bee8SArchie Cobbs 		return (ENOMEM);
8643949bee8SArchie Cobbs 	}
8653949bee8SArchie Cobbs 	*frag = frag0;
8663949bee8SArchie Cobbs 	meta = NULL;
8673949bee8SArchie Cobbs 	m = NULL;
8683949bee8SArchie Cobbs 
8693949bee8SArchie Cobbs 	/* Add fragment to queue, which is reverse sorted by sequence number */
8703949bee8SArchie Cobbs 	CIRCLEQ_FOREACH(qent, &priv->frags, f_qent) {
871d690a6e7SArchie Cobbs 		diff = MP_SEQ_DIFF(priv, frag->seq, qent->seq);
8723949bee8SArchie Cobbs 		if (diff > 0) {
8733949bee8SArchie Cobbs 			CIRCLEQ_INSERT_BEFORE(&priv->frags, qent, frag, f_qent);
8743949bee8SArchie Cobbs 			break;
8753949bee8SArchie Cobbs 		} else if (diff == 0) {	     /* should never happen! */
8763949bee8SArchie Cobbs 			log(LOG_ERR, "%s: rec'd dup MP fragment\n", node->name);
8773949bee8SArchie Cobbs 			if (linkNum == NG_PPP_BUNDLE_LINKNUM)
8783949bee8SArchie Cobbs 				priv->linkStats[linkNum].dupFragments++;
8793949bee8SArchie Cobbs 			else
8803949bee8SArchie Cobbs 				priv->bundleStats.dupFragments++;
8813949bee8SArchie Cobbs 			NG_FREE_DATA(frag->data, frag->meta);
8823949bee8SArchie Cobbs 			FREE(frag, M_NETGRAPH);
8833949bee8SArchie Cobbs 			return (EINVAL);
8843949bee8SArchie Cobbs 		}
8853949bee8SArchie Cobbs 	}
8863949bee8SArchie Cobbs 	if (qent == NULL)
8873949bee8SArchie Cobbs 		CIRCLEQ_INSERT_TAIL(&priv->frags, frag, f_qent);
8883949bee8SArchie Cobbs 
8893949bee8SArchie Cobbs 	/* Find the first fragment in the possibly newly completed frame */
8903949bee8SArchie Cobbs 	for (nextSeq = frag->seq, qent = frag;
8913949bee8SArchie Cobbs 	    qent != (void *) &priv->frags;
8923949bee8SArchie Cobbs 	    qent = CIRCLEQ_PREV(qent, f_qent)) {
8933949bee8SArchie Cobbs 		if (qent->seq != nextSeq)
8943949bee8SArchie Cobbs 			goto pruneQueue;
8953949bee8SArchie Cobbs 		if (qent->first) {
8963949bee8SArchie Cobbs 			first = qent;
8973949bee8SArchie Cobbs 			break;
8983949bee8SArchie Cobbs 		}
899d690a6e7SArchie Cobbs 		nextSeq = (nextSeq - 1) & MP_SEQ_MASK(priv);
9003949bee8SArchie Cobbs 	}
9013949bee8SArchie Cobbs 
9023949bee8SArchie Cobbs 	/* Find the last fragment in the possibly newly completed frame */
9033949bee8SArchie Cobbs 	for (nextSeq = frag->seq, qent = frag;
9043949bee8SArchie Cobbs 	    qent != (void *) &priv->frags;
9053949bee8SArchie Cobbs 	    qent = CIRCLEQ_NEXT(qent, f_qent)) {
9063949bee8SArchie Cobbs 		if (qent->seq != nextSeq)
9073949bee8SArchie Cobbs 			goto pruneQueue;
9083949bee8SArchie Cobbs 		if (qent->last) {
9093949bee8SArchie Cobbs 			last = qent;
9103949bee8SArchie Cobbs 			break;
9113949bee8SArchie Cobbs 		}
912d690a6e7SArchie Cobbs 		nextSeq = (nextSeq + 1) & MP_SEQ_MASK(priv);
9133949bee8SArchie Cobbs 	}
9143949bee8SArchie Cobbs 
9153949bee8SArchie Cobbs 	/* We have a complete frame, extract it from the queue */
9163949bee8SArchie Cobbs 	for (tail = NULL, qent = first; qent != NULL; qent = qnext) {
9173949bee8SArchie Cobbs 		qnext = CIRCLEQ_PREV(qent, f_qent);
9183949bee8SArchie Cobbs 		CIRCLEQ_REMOVE(&priv->frags, qent, f_qent);
9193949bee8SArchie Cobbs 		if (tail == NULL) {
9203949bee8SArchie Cobbs 			tail = m = qent->data;
9213949bee8SArchie Cobbs 			meta = qent->meta;	/* inherit first frag's meta */
9223949bee8SArchie Cobbs 		} else {
9233949bee8SArchie Cobbs 			m->m_pkthdr.len += qent->data->m_pkthdr.len;
9243949bee8SArchie Cobbs 			tail->m_next = qent->data;
925d690a6e7SArchie Cobbs 			NG_FREE_META(qent->meta); /* drop other frags' metas */
9263949bee8SArchie Cobbs 		}
9273949bee8SArchie Cobbs 		while (tail->m_next != NULL)
9283949bee8SArchie Cobbs 			tail = tail->m_next;
9293949bee8SArchie Cobbs 		if (qent == last)
9303949bee8SArchie Cobbs 			qnext = NULL;
9313949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
9323949bee8SArchie Cobbs 	}
9333949bee8SArchie Cobbs 
9343949bee8SArchie Cobbs pruneQueue:
9353949bee8SArchie Cobbs 	/* Prune out stale entries in the queue */
9363949bee8SArchie Cobbs 	for (qent = CIRCLEQ_LAST(&priv->frags);
9373949bee8SArchie Cobbs 	    qent != (void *) &priv->frags; qent = qnext) {
938d690a6e7SArchie Cobbs 		if (MP_SEQ_DIFF(priv, highSeq, qent->seq) <= MP_MAX_SEQ_LINGER)
9393949bee8SArchie Cobbs 			break;
9403949bee8SArchie Cobbs 		qnext = CIRCLEQ_PREV(qent, f_qent);
9413949bee8SArchie Cobbs 		CIRCLEQ_REMOVE(&priv->frags, qent, f_qent);
9423949bee8SArchie Cobbs 		NG_FREE_DATA(qent->data, qent->meta);
9433949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
9443949bee8SArchie Cobbs 	}
9453949bee8SArchie Cobbs 
9463949bee8SArchie Cobbs 	/* Deliver newly completed frame, if any */
947e149c4e2SArchie Cobbs 	return m ? ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta) : 0;
9483949bee8SArchie Cobbs }
9493949bee8SArchie Cobbs 
9503949bee8SArchie Cobbs /*
9513949bee8SArchie Cobbs  * Deliver a frame out on the bundle, i.e., figure out how to fragment
9523949bee8SArchie Cobbs  * the frame across the individual PPP links and do so.
9533949bee8SArchie Cobbs  */
9543949bee8SArchie Cobbs static int
9553949bee8SArchie Cobbs ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta)
9563949bee8SArchie Cobbs {
9573949bee8SArchie Cobbs 	const priv_p priv = node->private;
9583949bee8SArchie Cobbs 	int distrib[NG_PPP_MAX_LINKS];
9593949bee8SArchie Cobbs 	int firstFragment;
9603949bee8SArchie Cobbs 	int activeLinkNum;
9613949bee8SArchie Cobbs 
9623949bee8SArchie Cobbs 	/* At least one link must be active */
9633949bee8SArchie Cobbs 	if (priv->numActiveLinks == 0) {
9643949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
9653949bee8SArchie Cobbs 		return (ENETDOWN);
9663949bee8SArchie Cobbs 	}
9673949bee8SArchie Cobbs 
9683949bee8SArchie Cobbs 	/* Round-robin strategy */
9693949bee8SArchie Cobbs 	if (priv->conf.enableRoundRobin || m->m_pkthdr.len < MP_MIN_FRAG_LEN) {
9703949bee8SArchie Cobbs 		activeLinkNum = priv->lastLink++ % priv->numActiveLinks;
9713949bee8SArchie Cobbs 		bzero(&distrib, priv->numActiveLinks * sizeof(distrib[0]));
9723949bee8SArchie Cobbs 		distrib[activeLinkNum] = m->m_pkthdr.len;
9733949bee8SArchie Cobbs 		goto deliver;
9743949bee8SArchie Cobbs 	}
9753949bee8SArchie Cobbs 
9763949bee8SArchie Cobbs 	/* Strategy when all links are equivalent (optimize the common case) */
9773949bee8SArchie Cobbs 	if (priv->allLinksEqual) {
9783949bee8SArchie Cobbs 		const int fraction = m->m_pkthdr.len / priv->numActiveLinks;
9793949bee8SArchie Cobbs 		int i, remain;
9803949bee8SArchie Cobbs 
9813949bee8SArchie Cobbs 		for (i = 0; i < priv->numActiveLinks; i++)
9823949bee8SArchie Cobbs 			distrib[priv->lastLink++ % priv->numActiveLinks]
9833949bee8SArchie Cobbs 			    = fraction;
9843949bee8SArchie Cobbs 		remain = m->m_pkthdr.len - (fraction * priv->numActiveLinks);
9853949bee8SArchie Cobbs 		while (remain > 0) {
9863949bee8SArchie Cobbs 			distrib[priv->lastLink++ % priv->numActiveLinks]++;
9873949bee8SArchie Cobbs 			remain--;
9883949bee8SArchie Cobbs 		}
9893949bee8SArchie Cobbs 		goto deliver;
9903949bee8SArchie Cobbs 	}
9913949bee8SArchie Cobbs 
9923949bee8SArchie Cobbs 	/* Strategy when all links are not equivalent */
9933949bee8SArchie Cobbs 	ng_ppp_mp_strategy(node, m->m_pkthdr.len, distrib);
9943949bee8SArchie Cobbs 
9953949bee8SArchie Cobbs deliver:
9963949bee8SArchie Cobbs 	/* Update stats */
9973949bee8SArchie Cobbs 	priv->bundleStats.xmitFrames++;
9983949bee8SArchie Cobbs 	priv->bundleStats.xmitOctets += m->m_pkthdr.len;
9993949bee8SArchie Cobbs 
10003949bee8SArchie Cobbs 	/* Send alloted portions of frame out on the link(s) */
10013949bee8SArchie Cobbs 	for (firstFragment = 1, activeLinkNum = priv->numActiveLinks - 1;
10023949bee8SArchie Cobbs 	    activeLinkNum >= 0; activeLinkNum--) {
10033949bee8SArchie Cobbs 		const int linkNum = priv->activeLinks[activeLinkNum];
10043949bee8SArchie Cobbs 
10053949bee8SArchie Cobbs 		/* Deliver fragment(s) out the next link */
10063949bee8SArchie Cobbs 		for ( ; distrib[activeLinkNum] > 0; firstFragment = 0) {
10073949bee8SArchie Cobbs 			int len, lastFragment, error;
10083949bee8SArchie Cobbs 			struct mbuf *m2;
10093949bee8SArchie Cobbs 			meta_p meta2;
10103949bee8SArchie Cobbs 
10113949bee8SArchie Cobbs 			/* Calculate fragment length; don't exceed link MTU */
10123949bee8SArchie Cobbs 			len = distrib[activeLinkNum];
10133949bee8SArchie Cobbs 			if (len > priv->conf.links[linkNum].mru)
10143949bee8SArchie Cobbs 				len = priv->conf.links[linkNum].mru;
10153949bee8SArchie Cobbs 			distrib[activeLinkNum] -= len;
10163949bee8SArchie Cobbs 			lastFragment = (len == m->m_pkthdr.len);
10173949bee8SArchie Cobbs 
10183949bee8SArchie Cobbs 			/* Split off next fragment as "m2" */
10193949bee8SArchie Cobbs 			m2 = m;
10203949bee8SArchie Cobbs 			if (!lastFragment) {
10213949bee8SArchie Cobbs 				struct mbuf *n = m_split(m, len, M_NOWAIT);
10223949bee8SArchie Cobbs 
10233949bee8SArchie Cobbs 				if (n == NULL) {
10243949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
10253949bee8SArchie Cobbs 					return (ENOMEM);
10263949bee8SArchie Cobbs 				}
10273949bee8SArchie Cobbs 				m = n;
10283949bee8SArchie Cobbs 			}
10293949bee8SArchie Cobbs 
10303949bee8SArchie Cobbs 			/* Prepend MP header */
10313949bee8SArchie Cobbs 			if (priv->conf.xmitShortSeq) {
10323949bee8SArchie Cobbs 				u_int16_t shdr;
10333949bee8SArchie Cobbs 
10343949bee8SArchie Cobbs 				shdr = priv->mpSeqOut;
10353949bee8SArchie Cobbs 				priv->mpSeqOut =
10363949bee8SArchie Cobbs 				    (priv->mpSeqOut + 1) % MP_SHORT_SEQ_MASK;
10373949bee8SArchie Cobbs 				if (firstFragment)
10383949bee8SArchie Cobbs 					shdr |= MP_SHORT_FIRST_FLAG;
10393949bee8SArchie Cobbs 				if (lastFragment)
10403949bee8SArchie Cobbs 					shdr |= MP_SHORT_LAST_FLAG;
1041d690a6e7SArchie Cobbs 				shdr = htons(shdr);
1042d690a6e7SArchie Cobbs 				m2 = ng_ppp_prepend(m2, &shdr, 2);
10433949bee8SArchie Cobbs 			} else {
10443949bee8SArchie Cobbs 				u_int32_t lhdr;
10453949bee8SArchie Cobbs 
10463949bee8SArchie Cobbs 				lhdr = priv->mpSeqOut;
10473949bee8SArchie Cobbs 				priv->mpSeqOut =
10483949bee8SArchie Cobbs 				    (priv->mpSeqOut + 1) % MP_LONG_SEQ_MASK;
10493949bee8SArchie Cobbs 				if (firstFragment)
10503949bee8SArchie Cobbs 					lhdr |= MP_LONG_FIRST_FLAG;
10513949bee8SArchie Cobbs 				if (lastFragment)
10523949bee8SArchie Cobbs 					lhdr |= MP_LONG_LAST_FLAG;
1053d690a6e7SArchie Cobbs 				lhdr = htonl(lhdr);
1054d690a6e7SArchie Cobbs 				m2 = ng_ppp_prepend(m2, &lhdr, 4);
10553949bee8SArchie Cobbs 			}
10563949bee8SArchie Cobbs 			if (m2 == NULL) {
10573949bee8SArchie Cobbs 				if (!lastFragment)
10583949bee8SArchie Cobbs 					m_freem(m);
10593949bee8SArchie Cobbs 				NG_FREE_META(meta);
10603949bee8SArchie Cobbs 				return (ENOBUFS);
10613949bee8SArchie Cobbs 			}
10623949bee8SArchie Cobbs 
10633949bee8SArchie Cobbs 			/* Copy the meta information, if any */
10643949bee8SArchie Cobbs 			if (meta != NULL && !lastFragment) {
10653949bee8SArchie Cobbs 				MALLOC(meta2, meta_p,
10663949bee8SArchie Cobbs 				    meta->used_len, M_NETGRAPH, M_NOWAIT);
10673949bee8SArchie Cobbs 				if (meta2 == NULL) {
10683949bee8SArchie Cobbs 					m_freem(m2);
10693949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
10703949bee8SArchie Cobbs 					return (ENOMEM);
10713949bee8SArchie Cobbs 				}
10723949bee8SArchie Cobbs 				meta2->allocated_len = meta->used_len;
10733949bee8SArchie Cobbs 				bcopy(meta, meta2, meta->used_len);
10743949bee8SArchie Cobbs 			} else
10753949bee8SArchie Cobbs 				meta2 = meta;
10763949bee8SArchie Cobbs 
10773949bee8SArchie Cobbs 			/* Send fragment */
107862838faeSArchie Cobbs 			error = ng_ppp_output(node, 0,
107962838faeSArchie Cobbs 			    PROT_MP, linkNum, m2, meta2);
10803949bee8SArchie Cobbs 			if (error != 0) {
10813949bee8SArchie Cobbs 				if (!lastFragment)
10823949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
10833949bee8SArchie Cobbs 				return (error);
10843949bee8SArchie Cobbs 			}
10853949bee8SArchie Cobbs 		}
10863949bee8SArchie Cobbs 	}
10873949bee8SArchie Cobbs 
10883949bee8SArchie Cobbs 	/* Done */
10893949bee8SArchie Cobbs 	return (0);
10903949bee8SArchie Cobbs }
10913949bee8SArchie Cobbs 
10923949bee8SArchie Cobbs /*
10933949bee8SArchie Cobbs  * Computing the optimal fragmentation
10943949bee8SArchie Cobbs  * -----------------------------------
10953949bee8SArchie Cobbs  *
10963949bee8SArchie Cobbs  * This routine tries to compute the optimal fragmentation pattern based
10973949bee8SArchie Cobbs  * on each link's latency, bandwidth, and calculated additional latency.
10983949bee8SArchie Cobbs  * The latter quantity is the additional latency caused by previously
10993949bee8SArchie Cobbs  * written data that has not been transmitted yet.
11003949bee8SArchie Cobbs  *
11013949bee8SArchie Cobbs  * This algorithm is only useful when not all of the links have the
11023949bee8SArchie Cobbs  * same latency and bandwidth values.
11033949bee8SArchie Cobbs  *
11043949bee8SArchie Cobbs  * The essential idea is to make the last bit of each fragment of the
11053949bee8SArchie Cobbs  * frame arrive at the opposite end at the exact same time. This greedy
11063949bee8SArchie Cobbs  * algorithm is optimal, in that no other scheduling could result in any
11073949bee8SArchie Cobbs  * packet arriving any sooner unless packets are delivered out of order.
11083949bee8SArchie Cobbs  *
11093949bee8SArchie Cobbs  * Suppose link i has bandwidth b_i (in tens of bytes per milisecond) and
11103949bee8SArchie Cobbs  * latency l_i (in miliseconds). Consider the function function f_i(t)
11113949bee8SArchie Cobbs  * which is equal to the number of bytes that will have arrived at
11123949bee8SArchie Cobbs  * the peer after t miliseconds if we start writing continuously at
11133949bee8SArchie Cobbs  * time t = 0. Then f_i(t) = b_i * (t - l_i) = ((b_i * t) - (l_i * b_i).
11143949bee8SArchie Cobbs  * That is, f_i(t) is a line with slope b_i and y-intersect -(l_i * b_i).
11153949bee8SArchie Cobbs  * Note that the y-intersect is always <= zero because latency can't be
11163949bee8SArchie Cobbs  * negative.  Note also that really the function is f_i(t) except when
11173949bee8SArchie Cobbs  * f_i(t) is negative, in which case the function is zero.  To take
11183949bee8SArchie Cobbs  * care of this, let Q_i(t) = { if (f_i(t) > 0) return 1; else return 0; }.
11193949bee8SArchie Cobbs  * So the actual number of bytes that will have arrived at the peer after
11203949bee8SArchie Cobbs  * t miliseconds is f_i(t) * Q_i(t).
11213949bee8SArchie Cobbs  *
11223949bee8SArchie Cobbs  * At any given time, each link has some additional latency a_i >= 0
11233949bee8SArchie Cobbs  * due to previously written fragment(s) which are still in the queue.
11243949bee8SArchie Cobbs  * This value is easily computed from the time since last transmission,
11253949bee8SArchie Cobbs  * the previous latency value, the number of bytes written, and the
11263949bee8SArchie Cobbs  * link's bandwidth.
11273949bee8SArchie Cobbs  *
11283949bee8SArchie Cobbs  * Assume that l_i includes any a_i already, and that the links are
11293949bee8SArchie Cobbs  * sorted by latency, so that l_i <= l_{i+1}.
11303949bee8SArchie Cobbs  *
11313949bee8SArchie Cobbs  * Let N be the total number of bytes in the current frame we are sending.
11323949bee8SArchie Cobbs  *
11333949bee8SArchie Cobbs  * Suppose we were to start writing bytes at time t = 0 on all links
11343949bee8SArchie Cobbs  * simultaneously, which is the most we can possibly do.  Then let
11353949bee8SArchie Cobbs  * F(t) be equal to the total number of bytes received by the peer
11363949bee8SArchie Cobbs  * after t miliseconds. Then F(t) = Sum_i (f_i(t) * Q_i(t)).
11373949bee8SArchie Cobbs  *
11383949bee8SArchie Cobbs  * Our goal is simply this: fragment the frame across the links such
11393949bee8SArchie Cobbs  * that the peer is able to reconstruct the completed frame as soon as
11403949bee8SArchie Cobbs  * possible, i.e., at the least possible value of t. Call this value t_0.
11413949bee8SArchie Cobbs  *
11423949bee8SArchie Cobbs  * Then it follows that F(t_0) = N. Our strategy is first to find the value
11433949bee8SArchie Cobbs  * of t_0, and then deduce how many bytes to write to each link.
11443949bee8SArchie Cobbs  *
11453949bee8SArchie Cobbs  * Rewriting F(t_0):
11463949bee8SArchie Cobbs  *
11473949bee8SArchie Cobbs  *   t_0 = ( N + Sum_i ( l_i * b_i * Q_i(t_0) ) ) / Sum_i ( b_i * Q_i(t_0) )
11483949bee8SArchie Cobbs  *
11493949bee8SArchie Cobbs  * Now, we note that Q_i(t) is constant for l_i <= t <= l_{i+1}. t_0 will
11503949bee8SArchie Cobbs  * lie in one of these ranges.  To find it, we just need to find the i such
11513949bee8SArchie Cobbs  * that F(l_i) <= N <= F(l_{i+1}).  Then we compute all the constant values
11523949bee8SArchie Cobbs  * for Q_i() in this range, plug in the remaining values, solving for t_0.
11533949bee8SArchie Cobbs  *
11543949bee8SArchie Cobbs  * Once t_0 is known, then the number of bytes to send on link i is
11553949bee8SArchie Cobbs  * just f_i(t_0) * Q_i(t_0).
11563949bee8SArchie Cobbs  *
11573949bee8SArchie Cobbs  * In other words, we start allocating bytes to the links one at a time.
11583949bee8SArchie Cobbs  * We keep adding links until the frame is completely sent.  Some links
11593949bee8SArchie Cobbs  * may not get any bytes because their latency is too high.
11603949bee8SArchie Cobbs  *
11613949bee8SArchie Cobbs  * Is all this work really worth the trouble?  Depends on the situation.
11623949bee8SArchie Cobbs  * The bigger the ratio of computer speed to link speed, and the more
11633949bee8SArchie Cobbs  * important total bundle latency is (e.g., for interactive response time),
11643949bee8SArchie Cobbs  * the more it's worth it.  There is however the cost of calling this
11653949bee8SArchie Cobbs  * function for every frame.  The running time is O(n^2) where n is the
11663949bee8SArchie Cobbs  * number of links that receive a non-zero number of bytes.
11673949bee8SArchie Cobbs  *
11683949bee8SArchie Cobbs  * Since latency is measured in miliseconds, the "resolution" of this
11693949bee8SArchie Cobbs  * algorithm is one milisecond.
11703949bee8SArchie Cobbs  *
11713949bee8SArchie Cobbs  * To avoid this algorithm altogether, configure all links to have the
11723949bee8SArchie Cobbs  * same latency and bandwidth.
11733949bee8SArchie Cobbs  */
11743949bee8SArchie Cobbs static void
11753949bee8SArchie Cobbs ng_ppp_mp_strategy(node_p node, int len, int *distrib)
11763949bee8SArchie Cobbs {
11773949bee8SArchie Cobbs 	const priv_p priv = node->private;
11783949bee8SArchie Cobbs 	int latency[NG_PPP_MAX_LINKS];
11793949bee8SArchie Cobbs 	int sortByLatency[NG_PPP_MAX_LINKS];
11803949bee8SArchie Cobbs 	int activeLinkNum, linkNum;
11813949bee8SArchie Cobbs 	int t0, total, topSum, botSum;
11823949bee8SArchie Cobbs 	struct timeval now;
11833949bee8SArchie Cobbs 	int i, numFragments;
11843949bee8SArchie Cobbs 
11853949bee8SArchie Cobbs 	/* If only one link, this gets real easy */
11863949bee8SArchie Cobbs 	if (priv->numActiveLinks == 1) {
11873949bee8SArchie Cobbs 		distrib[0] = len;
11883949bee8SArchie Cobbs 		return;
11893949bee8SArchie Cobbs 	}
11903949bee8SArchie Cobbs 
11913949bee8SArchie Cobbs 	/* Get current time */
1192fb1fc8abSArchie Cobbs 	microtime(&now);
11933949bee8SArchie Cobbs 
11943949bee8SArchie Cobbs 	/* Compute latencies for each link at this point in time */
11953949bee8SArchie Cobbs 	for (activeLinkNum = 0;
11963949bee8SArchie Cobbs 	    activeLinkNum < priv->numActiveLinks; activeLinkNum++) {
11973949bee8SArchie Cobbs 		struct timeval diff;
11983949bee8SArchie Cobbs 		int xmitBytes;
11993949bee8SArchie Cobbs 
12003949bee8SArchie Cobbs 		/* Start with base latency value */
12013949bee8SArchie Cobbs 		linkNum = priv->activeLinks[activeLinkNum];
12023949bee8SArchie Cobbs 		latency[activeLinkNum] = priv->conf.links[linkNum].latency;
12033949bee8SArchie Cobbs 		sortByLatency[activeLinkNum] = activeLinkNum;	/* see below */
12043949bee8SArchie Cobbs 
12053949bee8SArchie Cobbs 		/* Any additional latency? */
12063949bee8SArchie Cobbs 		if (priv->qstat[activeLinkNum].bytesInQueue == 0)
12073949bee8SArchie Cobbs 			continue;
12083949bee8SArchie Cobbs 
12093949bee8SArchie Cobbs 		/* Compute time delta since last write */
12103949bee8SArchie Cobbs 		diff = now;
12113949bee8SArchie Cobbs 		timevalsub(&diff, &priv->qstat[activeLinkNum].lastWrite);
12123949bee8SArchie Cobbs 		if (now.tv_sec < 0 || diff.tv_sec >= 10) {	/* sanity */
12133949bee8SArchie Cobbs 			priv->qstat[activeLinkNum].bytesInQueue = 0;
12143949bee8SArchie Cobbs 			continue;
12153949bee8SArchie Cobbs 		}
12163949bee8SArchie Cobbs 
12173949bee8SArchie Cobbs 		/* How many bytes could have transmitted since last write? */
12183949bee8SArchie Cobbs 		xmitBytes = priv->conf.links[linkNum].bandwidth * diff.tv_sec
12193949bee8SArchie Cobbs 		    + (priv->conf.links[linkNum].bandwidth
12203949bee8SArchie Cobbs 			* (diff.tv_usec / 1000)) / 100;
12213949bee8SArchie Cobbs 		priv->qstat[activeLinkNum].bytesInQueue -= xmitBytes;
12223949bee8SArchie Cobbs 		if (priv->qstat[activeLinkNum].bytesInQueue < 0)
12233949bee8SArchie Cobbs 			priv->qstat[activeLinkNum].bytesInQueue = 0;
12243949bee8SArchie Cobbs 		else
12253949bee8SArchie Cobbs 			latency[activeLinkNum] +=
12263949bee8SArchie Cobbs 			    (100 * priv->qstat[activeLinkNum].bytesInQueue)
12273949bee8SArchie Cobbs 				/ priv->conf.links[linkNum].bandwidth;
12283949bee8SArchie Cobbs 	}
12293949bee8SArchie Cobbs 
12303949bee8SArchie Cobbs 	/* Sort links by latency */
12313949bee8SArchie Cobbs 	compareLatencies = latency;
12323949bee8SArchie Cobbs 	qsort(sortByLatency,
12333949bee8SArchie Cobbs 	    priv->numActiveLinks, sizeof(*sortByLatency), ng_ppp_intcmp);
12343949bee8SArchie Cobbs 	compareLatencies = NULL;
12353949bee8SArchie Cobbs 
12363949bee8SArchie Cobbs 	/* Find the interval we need (add links in sortByLatency[] order) */
12373949bee8SArchie Cobbs 	for (numFragments = 1;
12383949bee8SArchie Cobbs 	    numFragments < priv->numActiveLinks; numFragments++) {
12393949bee8SArchie Cobbs 		for (total = i = 0; i < numFragments; i++) {
12403949bee8SArchie Cobbs 			int flowTime;
12413949bee8SArchie Cobbs 
12423949bee8SArchie Cobbs 			flowTime = latency[sortByLatency[numFragments]]
12433949bee8SArchie Cobbs 			    - latency[sortByLatency[i]];
12443949bee8SArchie Cobbs 			total += ((flowTime * priv->conf.links[
12453949bee8SArchie Cobbs 			    priv->activeLinks[sortByLatency[i]]].bandwidth)
12463949bee8SArchie Cobbs 			    	+ 99) / 100;
12473949bee8SArchie Cobbs 		}
12483949bee8SArchie Cobbs 		if (total >= len)
12493949bee8SArchie Cobbs 			break;
12503949bee8SArchie Cobbs 	}
12513949bee8SArchie Cobbs 
12523949bee8SArchie Cobbs 	/* Solve for t_0 in that interval */
12533949bee8SArchie Cobbs 	for (topSum = botSum = i = 0; i < numFragments; i++) {
12543949bee8SArchie Cobbs 		int bw = priv->conf.links[
12553949bee8SArchie Cobbs 		    priv->activeLinks[sortByLatency[i]]].bandwidth;
12563949bee8SArchie Cobbs 
12573949bee8SArchie Cobbs 		topSum += latency[sortByLatency[i]] * bw;	/* / 100 */
12583949bee8SArchie Cobbs 		botSum += bw;					/* / 100 */
12593949bee8SArchie Cobbs 	}
12603949bee8SArchie Cobbs 	t0 = ((len * 100) + topSum + botSum / 2) / botSum;
12613949bee8SArchie Cobbs 
12623949bee8SArchie Cobbs 	/* Compute f_i(t_0) all i */
12633949bee8SArchie Cobbs 	bzero(distrib, priv->numActiveLinks * sizeof(*distrib));
12643949bee8SArchie Cobbs 	for (total = i = 0; i < numFragments; i++) {
12653949bee8SArchie Cobbs 		int bw = priv->conf.links[
12663949bee8SArchie Cobbs 		    priv->activeLinks[sortByLatency[i]]].bandwidth;
12673949bee8SArchie Cobbs 
12683949bee8SArchie Cobbs 		distrib[sortByLatency[i]] =
12693949bee8SArchie Cobbs 		    (bw * (t0 - latency[sortByLatency[i]]) + 50) / 100;
12703949bee8SArchie Cobbs 		total += distrib[sortByLatency[i]];
12713949bee8SArchie Cobbs 	}
12723949bee8SArchie Cobbs 
1273fb1fc8abSArchie Cobbs 	/* Deal with any rounding error */
1274fb1fc8abSArchie Cobbs 	if (total < len) {
12753949bee8SArchie Cobbs 		int fast = 0;
12763949bee8SArchie Cobbs 
1277fb1fc8abSArchie Cobbs 		/* Find the fastest link */
12783949bee8SArchie Cobbs 		for (i = 1; i < numFragments; i++) {
12793949bee8SArchie Cobbs 			if (priv->conf.links[
12803949bee8SArchie Cobbs 			      priv->activeLinks[sortByLatency[i]]].bandwidth >
12813949bee8SArchie Cobbs 			    priv->conf.links[
12823949bee8SArchie Cobbs 			      priv->activeLinks[sortByLatency[fast]]].bandwidth)
12833949bee8SArchie Cobbs 				fast = i;
12843949bee8SArchie Cobbs 		}
12853949bee8SArchie Cobbs 		distrib[sortByLatency[fast]] += len - total;
1286fb1fc8abSArchie Cobbs 	} else while (total > len) {
1287fb1fc8abSArchie Cobbs 		int delta, slow = 0;
12883949bee8SArchie Cobbs 
1289fb1fc8abSArchie Cobbs 		/* Find the slowest link that still has bytes to remove */
1290fb1fc8abSArchie Cobbs 		for (i = 1; i < numFragments; i++) {
1291fb1fc8abSArchie Cobbs 			if (distrib[sortByLatency[slow]] == 0
1292fb1fc8abSArchie Cobbs 			  || (distrib[sortByLatency[i]] > 0
1293fb1fc8abSArchie Cobbs 			    && priv->conf.links[priv->activeLinks[
1294fb1fc8abSArchie Cobbs 					sortByLatency[i]]].bandwidth <
1295fb1fc8abSArchie Cobbs 			      priv->conf.links[priv->activeLinks[
1296fb1fc8abSArchie Cobbs 					sortByLatency[slow]]].bandwidth))
1297fb1fc8abSArchie Cobbs 				slow = i;
1298fb1fc8abSArchie Cobbs 		}
1299fb1fc8abSArchie Cobbs 		delta = total - len;
1300fb1fc8abSArchie Cobbs 		if (delta > distrib[sortByLatency[slow]])
1301fb1fc8abSArchie Cobbs 			delta = distrib[sortByLatency[slow]];
1302fb1fc8abSArchie Cobbs 		distrib[sortByLatency[slow]] -= delta;
1303fb1fc8abSArchie Cobbs 		total -= delta;
13043949bee8SArchie Cobbs 	}
13053949bee8SArchie Cobbs }
13063949bee8SArchie Cobbs 
13073949bee8SArchie Cobbs /*
13083949bee8SArchie Cobbs  * Compare two integers
13093949bee8SArchie Cobbs  */
13103949bee8SArchie Cobbs static int
13113949bee8SArchie Cobbs ng_ppp_intcmp(const void *v1, const void *v2)
13123949bee8SArchie Cobbs {
13133949bee8SArchie Cobbs 	const int index1 = *((const int *) v1);
13143949bee8SArchie Cobbs 	const int index2 = *((const int *) v2);
13153949bee8SArchie Cobbs 
13163949bee8SArchie Cobbs 	return compareLatencies[index1] - compareLatencies[index2];
13173949bee8SArchie Cobbs }
13183949bee8SArchie Cobbs 
13193949bee8SArchie Cobbs /*
13203949bee8SArchie Cobbs  * Prepend a possibly compressed PPP protocol number in front of a frame
13213949bee8SArchie Cobbs  */
13223949bee8SArchie Cobbs static struct mbuf *
13233949bee8SArchie Cobbs ng_ppp_addproto(struct mbuf *m, int proto, int compOK)
13243949bee8SArchie Cobbs {
1325d690a6e7SArchie Cobbs 	if (compOK && PROT_COMPRESSABLE(proto)) {
1326d690a6e7SArchie Cobbs 		u_char pbyte = (u_char)proto;
13273949bee8SArchie Cobbs 
1328d690a6e7SArchie Cobbs 		return ng_ppp_prepend(m, &pbyte, 1);
1329d690a6e7SArchie Cobbs 	} else {
1330d690a6e7SArchie Cobbs 		u_int16_t pword = htons((u_int16_t)proto);
1331d690a6e7SArchie Cobbs 
1332d690a6e7SArchie Cobbs 		return ng_ppp_prepend(m, &pword, 2);
1333d690a6e7SArchie Cobbs 	}
1334d690a6e7SArchie Cobbs }
1335d690a6e7SArchie Cobbs 
1336d690a6e7SArchie Cobbs /*
1337d690a6e7SArchie Cobbs  * Prepend some bytes to an mbuf
1338d690a6e7SArchie Cobbs  */
1339d690a6e7SArchie Cobbs static struct mbuf *
1340d690a6e7SArchie Cobbs ng_ppp_prepend(struct mbuf *m, const void *buf, int len)
1341d690a6e7SArchie Cobbs {
1342d690a6e7SArchie Cobbs 	M_PREPEND(m, len, M_NOWAIT);
1343d690a6e7SArchie Cobbs 	if (m == NULL || (m->m_len < len && (m = m_pullup(m, len)) == NULL))
13444cf49a43SJulian Elischer 		return (NULL);
1345d690a6e7SArchie Cobbs 	bcopy(buf, mtod(m, u_char *), len);
13463949bee8SArchie Cobbs 	return (m);
13473949bee8SArchie Cobbs }
13483949bee8SArchie Cobbs 
13493949bee8SArchie Cobbs /*
13503949bee8SArchie Cobbs  * Update private information that is derived from other private information
13513949bee8SArchie Cobbs  */
13523949bee8SArchie Cobbs static void
13533949bee8SArchie Cobbs ng_ppp_update(node_p node, int newConf)
13543949bee8SArchie Cobbs {
13553949bee8SArchie Cobbs 	const priv_p priv = node->private;
13563949bee8SArchie Cobbs 	int i;
13573949bee8SArchie Cobbs 
13583949bee8SArchie Cobbs 	/* Update active status for VJ Compression */
13593949bee8SArchie Cobbs 	priv->vjCompHooked = priv->hooks[HOOK_INDEX_VJC_IP] != NULL
13603949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_COMP] != NULL
13613949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_UNCOMP] != NULL
13623949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_VJIP] != NULL;
13633949bee8SArchie Cobbs 
13643949bee8SArchie Cobbs 	/* Increase latency for each link an amount equal to one MP header */
13653949bee8SArchie Cobbs 	if (newConf) {
13663949bee8SArchie Cobbs 		for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
13673949bee8SArchie Cobbs 			int hdrBytes;
13683949bee8SArchie Cobbs 
1369d690a6e7SArchie Cobbs 			hdrBytes = (priv->conf.links[i].enableACFComp ? 0 : 2)
1370d690a6e7SArchie Cobbs 			    + (priv->conf.links[i].enableProtoComp ? 1 : 2)
13713949bee8SArchie Cobbs 			    + (priv->conf.xmitShortSeq ? 2 : 4);
13723949bee8SArchie Cobbs 			priv->conf.links[i].latency +=
13733949bee8SArchie Cobbs 			    ((hdrBytes * priv->conf.links[i].bandwidth) + 50)
13743949bee8SArchie Cobbs 				/ 100;
13753949bee8SArchie Cobbs 		}
13763949bee8SArchie Cobbs 	}
13773949bee8SArchie Cobbs 
13783949bee8SArchie Cobbs 	/* Update list of active links */
13793949bee8SArchie Cobbs 	bzero(&priv->activeLinks, sizeof(priv->activeLinks));
13803949bee8SArchie Cobbs 	priv->numActiveLinks = 0;
13813949bee8SArchie Cobbs 	priv->allLinksEqual = 1;
13823949bee8SArchie Cobbs 	for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
13830e11d0f3SArchie Cobbs 		struct ng_ppp_link_config *const lc = &priv->conf.links[i];
13840e11d0f3SArchie Cobbs 
13850e11d0f3SArchie Cobbs 		if (lc->enableLink && priv->links[i] != NULL) {
13863949bee8SArchie Cobbs 			priv->activeLinks[priv->numActiveLinks++] = i;
13870e11d0f3SArchie Cobbs 			if (lc->latency !=
13880e11d0f3SArchie Cobbs 			      priv->conf.links[priv->activeLinks[0]].latency
13890e11d0f3SArchie Cobbs 			  || lc->bandwidth !=
13900e11d0f3SArchie Cobbs 			      priv->conf.links[priv->activeLinks[0]].bandwidth)
13913949bee8SArchie Cobbs 				priv->allLinksEqual = 0;
13923949bee8SArchie Cobbs 		}
13933949bee8SArchie Cobbs 	}
13943949bee8SArchie Cobbs 
13950e11d0f3SArchie Cobbs 	/* Reset MP state if multi-link is no longer active */
13963949bee8SArchie Cobbs 	if (!priv->conf.enableMultilink || priv->numActiveLinks == 0) {
13973949bee8SArchie Cobbs 		ng_ppp_free_frags(node);
13983949bee8SArchie Cobbs 		priv->mpSeqOut = MP_INITIAL_SEQ;
13993949bee8SArchie Cobbs 		bzero(&priv->qstat, sizeof(priv->qstat));
14003949bee8SArchie Cobbs 	}
14013949bee8SArchie Cobbs }
14023949bee8SArchie Cobbs 
14033949bee8SArchie Cobbs /*
14043949bee8SArchie Cobbs  * Determine if a new configuration would represent a valid change
14053949bee8SArchie Cobbs  * from the current configuration and link activity status.
14063949bee8SArchie Cobbs  */
14073949bee8SArchie Cobbs static int
14083949bee8SArchie Cobbs ng_ppp_config_valid(node_p node, const struct ng_ppp_node_config *newConf)
14093949bee8SArchie Cobbs {
14103949bee8SArchie Cobbs 	const priv_p priv = node->private;
14113949bee8SArchie Cobbs 	int i, newNumLinksActive;
14123949bee8SArchie Cobbs 
14133949bee8SArchie Cobbs 	/* Check per-link config and count how many links would be active */
14143949bee8SArchie Cobbs 	for (newNumLinksActive = i = 0; i < NG_PPP_MAX_LINKS; i++) {
1415e149c4e2SArchie Cobbs 		if (newConf->links[i].enableLink && priv->links[i] != NULL)
1416e149c4e2SArchie Cobbs 			newNumLinksActive++;
1417e149c4e2SArchie Cobbs 		if (!newConf->links[i].enableLink)
1418e149c4e2SArchie Cobbs 			continue;
14193949bee8SArchie Cobbs 		if (newConf->links[i].mru < MP_MIN_LINK_MRU)
14203949bee8SArchie Cobbs 			return (0);
14213949bee8SArchie Cobbs 		if (newConf->links[i].bandwidth == 0)
14223949bee8SArchie Cobbs 			return (0);
14233949bee8SArchie Cobbs 		if (newConf->links[i].bandwidth > NG_PPP_MAX_BANDWIDTH)
14243949bee8SArchie Cobbs 			return (0);
14253949bee8SArchie Cobbs 		if (newConf->links[i].latency > NG_PPP_MAX_LATENCY)
14263949bee8SArchie Cobbs 			return (0);
14273949bee8SArchie Cobbs 	}
14283949bee8SArchie Cobbs 
14293949bee8SArchie Cobbs 	/* Check bundle parameters */
1430e149c4e2SArchie Cobbs 	if (newConf->enableMultilink && newConf->mrru < MP_MIN_MRRU)
14313949bee8SArchie Cobbs 		return (0);
14323949bee8SArchie Cobbs 
14333949bee8SArchie Cobbs 	/* Disallow changes to multi-link configuration while MP is active */
14343949bee8SArchie Cobbs 	if (priv->numActiveLinks > 0 && newNumLinksActive > 0) {
14353949bee8SArchie Cobbs 		if (!priv->conf.enableMultilink != !newConf->enableMultilink
14363949bee8SArchie Cobbs 		    || !priv->conf.xmitShortSeq != !newConf->xmitShortSeq
14373949bee8SArchie Cobbs 		    || !priv->conf.recvShortSeq != !newConf->recvShortSeq)
14383949bee8SArchie Cobbs 			return (0);
14393949bee8SArchie Cobbs 	}
14403949bee8SArchie Cobbs 
1441e149c4e2SArchie Cobbs 	/* At most one link can be active unless multi-link is enabled */
1442e149c4e2SArchie Cobbs 	if (!newConf->enableMultilink && newNumLinksActive > 1)
1443e149c4e2SArchie Cobbs 		return (0);
1444e149c4e2SArchie Cobbs 
1445e149c4e2SArchie Cobbs 	/* Configuration change would be valid */
14463949bee8SArchie Cobbs 	return (1);
14473949bee8SArchie Cobbs }
14483949bee8SArchie Cobbs 
14493949bee8SArchie Cobbs /*
14503949bee8SArchie Cobbs  * Free all entries in the fragment queue
14513949bee8SArchie Cobbs  */
14523949bee8SArchie Cobbs static void
14533949bee8SArchie Cobbs ng_ppp_free_frags(node_p node)
14543949bee8SArchie Cobbs {
14553949bee8SArchie Cobbs 	const priv_p priv = node->private;
14563949bee8SArchie Cobbs 	struct ng_ppp_frag *qent, *next;
14573949bee8SArchie Cobbs 
14583949bee8SArchie Cobbs 	for (qent = CIRCLEQ_FIRST(&priv->frags);
14593949bee8SArchie Cobbs 	    qent != (void *) &priv->frags; qent = next) {
14603949bee8SArchie Cobbs 		next = CIRCLEQ_NEXT(qent, f_qent);
14613949bee8SArchie Cobbs 		NG_FREE_DATA(qent->data, qent->meta);
14623949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
14633949bee8SArchie Cobbs 	}
14643949bee8SArchie Cobbs 	CIRCLEQ_INIT(&priv->frags);
14654cf49a43SJulian Elischer }
14664cf49a43SJulian Elischer 
1467