xref: /freebsd/sys/netgraph/ng_ppp.c (revision f8307e1233657707bc582110f07373c96d91943b)
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>
60f8307e12SArchie Cobbs #include <netgraph/ng_parse.h>
614cf49a43SJulian Elischer #include <netgraph/ng_ppp.h>
623949bee8SArchie Cobbs #include <netgraph/ng_vjc.h>
634cf49a43SJulian Elischer 
644cf49a43SJulian Elischer #define PROT_VALID(p)		(((p) & 0x0101) == 0x0001)
652b70adcbSArchie Cobbs #define PROT_COMPRESSABLE(p)	(((p) & 0xff00) == 0x0000)
664cf49a43SJulian Elischer 
673949bee8SArchie Cobbs /* Some PPP protocol numbers we're interested in */
683949bee8SArchie Cobbs #define PROT_APPLETALK		0x0029
693949bee8SArchie Cobbs #define PROT_COMPD		0x00fd
703949bee8SArchie Cobbs #define PROT_CRYPTD		0x0053
713949bee8SArchie Cobbs #define PROT_IP			0x0021
722b70adcbSArchie Cobbs #define PROT_IPX		0x002b
73d690a6e7SArchie Cobbs #define PROT_LCP		0xc021
743949bee8SArchie Cobbs #define PROT_MP			0x003d
753949bee8SArchie Cobbs #define PROT_VJCOMP		0x002d
763949bee8SArchie Cobbs #define PROT_VJUNCOMP		0x002f
773949bee8SArchie Cobbs 
783949bee8SArchie Cobbs /* Multilink PPP definitions */
793949bee8SArchie Cobbs #define MP_MIN_MRRU		1500		/* per RFC 1990 */
803949bee8SArchie Cobbs #define MP_INITIAL_SEQ		0		/* per RFC 1990 */
813949bee8SArchie Cobbs #define MP_MIN_LINK_MRU		32
823949bee8SArchie Cobbs 
833949bee8SArchie Cobbs #define MP_MAX_SEQ_LINGER	64		/* max frags we will hold */
843949bee8SArchie Cobbs #define MP_INSANE_SEQ_JUMP	128		/* a sequence # jump too far */
853949bee8SArchie Cobbs #define MP_MIN_FRAG_LEN		6		/* don't frag smaller frames */
863949bee8SArchie Cobbs 
873949bee8SArchie Cobbs #define MP_SHORT_SEQ_MASK	0x00000fff	/* short seq # mask */
883949bee8SArchie Cobbs #define MP_SHORT_SEQ_HIBIT	0x00000800	/* short seq # high bit */
893949bee8SArchie Cobbs #define MP_SHORT_FIRST_FLAG	0x00008000	/* first fragment in frame */
903949bee8SArchie Cobbs #define MP_SHORT_LAST_FLAG	0x00004000	/* last fragment in frame */
913949bee8SArchie Cobbs 
923949bee8SArchie Cobbs #define MP_LONG_SEQ_MASK	0x00ffffff	/* long seq # mask */
933949bee8SArchie Cobbs #define MP_LONG_SEQ_HIBIT	0x00800000	/* long seq # high bit */
943949bee8SArchie Cobbs #define MP_LONG_FIRST_FLAG	0x80000000	/* first fragment in frame */
953949bee8SArchie Cobbs #define MP_LONG_LAST_FLAG	0x40000000	/* last fragment in frame */
963949bee8SArchie Cobbs 
97d690a6e7SArchie Cobbs #define MP_SEQ_MASK(priv)	((priv)->conf.recvShortSeq ? \
983949bee8SArchie Cobbs 				    MP_SHORT_SEQ_MASK : MP_LONG_SEQ_MASK)
993949bee8SArchie Cobbs 
1003949bee8SArchie Cobbs /* Sign extension of MP sequence numbers */
1013949bee8SArchie Cobbs #define MP_SHORT_EXTEND(s)	(((s) & MP_SHORT_SEQ_HIBIT) ? \
1023949bee8SArchie Cobbs 				    ((s) | ~MP_SHORT_SEQ_MASK) : (s))
1033949bee8SArchie Cobbs #define MP_LONG_EXTEND(s)	(((s) & MP_LONG_SEQ_HIBIT) ? \
1043949bee8SArchie Cobbs 				    ((s) | ~MP_LONG_SEQ_MASK) : (s))
1053949bee8SArchie Cobbs 
1063949bee8SArchie Cobbs /* Comparision of MP sequence numbers */
1073949bee8SArchie Cobbs #define MP_SHORT_SEQ_DIFF(x,y)	(MP_SHORT_EXTEND(x) - MP_SHORT_EXTEND(y))
1083949bee8SArchie Cobbs #define MP_LONG_SEQ_DIFF(x,y)	(MP_LONG_EXTEND(x) - MP_LONG_EXTEND(y))
1093949bee8SArchie Cobbs 
110d690a6e7SArchie Cobbs #define MP_SEQ_DIFF(priv,x,y)	((priv)->conf.recvShortSeq ? \
1113949bee8SArchie Cobbs 				    MP_SHORT_SEQ_DIFF((x), (y)) : \
1123949bee8SArchie Cobbs 				    MP_LONG_SEQ_DIFF((x), (y)))
1133949bee8SArchie Cobbs 
1143949bee8SArchie Cobbs /* We store incoming fragments this way */
1153949bee8SArchie Cobbs struct ng_ppp_frag {
1163949bee8SArchie Cobbs 	int				seq;
1173949bee8SArchie Cobbs 	u_char				first;
1183949bee8SArchie Cobbs 	u_char				last;
1193949bee8SArchie Cobbs 	struct mbuf			*data;
1203949bee8SArchie Cobbs 	meta_p				meta;
1213949bee8SArchie Cobbs 	CIRCLEQ_ENTRY(ng_ppp_frag)	f_qent;
1223949bee8SArchie Cobbs };
1233949bee8SArchie Cobbs 
1243949bee8SArchie Cobbs /* We keep track of link queue status this way */
1253949bee8SArchie Cobbs struct ng_ppp_link_qstat {
1263949bee8SArchie Cobbs 	struct timeval		lastWrite;	/* time of last write */
1273949bee8SArchie Cobbs 	int			bytesInQueue;	/* bytes in the queue then */
1283949bee8SArchie Cobbs };
1293949bee8SArchie Cobbs 
1303949bee8SArchie Cobbs /* We use integer indicies to refer to the non-link hooks */
1313949bee8SArchie Cobbs static const char *const ng_ppp_hook_names[] = {
1323949bee8SArchie Cobbs 	NG_PPP_HOOK_ATALK,
1333949bee8SArchie Cobbs #define HOOK_INDEX_ATALK		0
1343949bee8SArchie Cobbs 	NG_PPP_HOOK_BYPASS,
1353949bee8SArchie Cobbs #define HOOK_INDEX_BYPASS		1
1363949bee8SArchie Cobbs 	NG_PPP_HOOK_COMPRESS,
1373949bee8SArchie Cobbs #define HOOK_INDEX_COMPRESS		2
1383949bee8SArchie Cobbs 	NG_PPP_HOOK_ENCRYPT,
1393949bee8SArchie Cobbs #define HOOK_INDEX_ENCRYPT		3
1403949bee8SArchie Cobbs 	NG_PPP_HOOK_DECOMPRESS,
1413949bee8SArchie Cobbs #define HOOK_INDEX_DECOMPRESS		4
1423949bee8SArchie Cobbs 	NG_PPP_HOOK_DECRYPT,
1433949bee8SArchie Cobbs #define HOOK_INDEX_DECRYPT		5
1443949bee8SArchie Cobbs 	NG_PPP_HOOK_INET,
1453949bee8SArchie Cobbs #define HOOK_INDEX_INET			6
1463949bee8SArchie Cobbs 	NG_PPP_HOOK_IPX,
1473949bee8SArchie Cobbs #define HOOK_INDEX_IPX			7
1483949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_COMP,
1493949bee8SArchie Cobbs #define HOOK_INDEX_VJC_COMP		8
1503949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_IP,
1513949bee8SArchie Cobbs #define HOOK_INDEX_VJC_IP		9
1523949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_UNCOMP,
1533949bee8SArchie Cobbs #define HOOK_INDEX_VJC_UNCOMP		10
1543949bee8SArchie Cobbs 	NG_PPP_HOOK_VJC_VJIP,
1553949bee8SArchie Cobbs #define HOOK_INDEX_VJC_VJIP		11
1563949bee8SArchie Cobbs 	NULL
1573949bee8SArchie Cobbs #define HOOK_INDEX_MAX			12
1583949bee8SArchie Cobbs };
1593949bee8SArchie Cobbs 
1603949bee8SArchie Cobbs /* We store index numbers in the hook private pointer. The HOOK_INDEX()
1613949bee8SArchie Cobbs    for a hook is either the index (above) for normal hooks, or the ones
1623949bee8SArchie Cobbs    complement of the link number for link hooks. */
1633949bee8SArchie Cobbs #define HOOK_INDEX(hook)	(*((int16_t *) &(hook)->private))
1644cf49a43SJulian Elischer 
1654cf49a43SJulian Elischer /* Node private data */
16662838faeSArchie Cobbs struct ng_ppp_private {
1673949bee8SArchie Cobbs 	struct ng_ppp_node_config	conf;
1683949bee8SArchie Cobbs 	struct ng_ppp_link_stat		bundleStats;
1693949bee8SArchie Cobbs 	struct ng_ppp_link_stat		linkStats[NG_PPP_MAX_LINKS];
1703949bee8SArchie Cobbs 	hook_p				links[NG_PPP_MAX_LINKS];
1713949bee8SArchie Cobbs 	hook_p				hooks[HOOK_INDEX_MAX];
1723949bee8SArchie Cobbs 	u_char				vjCompHooked;
1733949bee8SArchie Cobbs 	u_char				allLinksEqual;
1743949bee8SArchie Cobbs 	u_short				activeLinks[NG_PPP_MAX_LINKS];
1753949bee8SArchie Cobbs 	u_int				numActiveLinks;
1763949bee8SArchie Cobbs 	u_int				lastLink;	/* for round robin */
1773949bee8SArchie Cobbs 	struct ng_ppp_link_qstat	qstat[NG_PPP_MAX_LINKS];
1783949bee8SArchie Cobbs 	CIRCLEQ_HEAD(ng_ppp_fraglist, ng_ppp_frag)
1793949bee8SArchie Cobbs 					frags;		/* incoming fragments */
1803949bee8SArchie Cobbs 	int				mpSeqOut;	/* next out MP seq # */
1814cf49a43SJulian Elischer };
18262838faeSArchie Cobbs typedef struct ng_ppp_private *priv_p;
1834cf49a43SJulian Elischer 
1844cf49a43SJulian Elischer /* Netgraph node methods */
18574f5c6aaSJulian Elischer static ng_constructor_t	ng_ppp_constructor;
18674f5c6aaSJulian Elischer static ng_rcvmsg_t	ng_ppp_rcvmsg;
18774f5c6aaSJulian Elischer static ng_shutdown_t	ng_ppp_rmnode;
18874f5c6aaSJulian Elischer static ng_newhook_t	ng_ppp_newhook;
18974f5c6aaSJulian Elischer static ng_rcvdata_t	ng_ppp_rcvdata;
19074f5c6aaSJulian Elischer static ng_disconnect_t	ng_ppp_disconnect;
1914cf49a43SJulian Elischer 
1923949bee8SArchie Cobbs /* Helper functions */
193e149c4e2SArchie Cobbs static int	ng_ppp_input(node_p node, int bypass,
194e149c4e2SArchie Cobbs 			int linkNum, struct mbuf *m, meta_p meta);
195d690a6e7SArchie Cobbs static int	ng_ppp_output(node_p node, int bypass, int proto,
196e149c4e2SArchie Cobbs 			int linkNum, struct mbuf *m, meta_p meta);
197e149c4e2SArchie Cobbs static int	ng_ppp_mp_input(node_p node, int linkNum,
198e149c4e2SArchie Cobbs 			struct mbuf *m, meta_p meta);
1993949bee8SArchie Cobbs static int	ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta);
2003949bee8SArchie Cobbs static void	ng_ppp_mp_strategy(node_p node, int len, int *distrib);
2013949bee8SArchie Cobbs static int	ng_ppp_intcmp(const void *v1, const void *v2);
2023949bee8SArchie Cobbs static struct	mbuf *ng_ppp_addproto(struct mbuf *m, int proto, int compOK);
203d690a6e7SArchie Cobbs static struct	mbuf *ng_ppp_prepend(struct mbuf *m, const void *buf, int len);
2043949bee8SArchie Cobbs static int	ng_ppp_config_valid(node_p node,
2053949bee8SArchie Cobbs 			const struct ng_ppp_node_config *newConf);
2063949bee8SArchie Cobbs static void	ng_ppp_update(node_p node, int newConf);
2073949bee8SArchie Cobbs static void	ng_ppp_free_frags(node_p node);
2084cf49a43SJulian Elischer 
209f8307e12SArchie Cobbs /* Parse type for struct ng_ppp_link_config */
210f8307e12SArchie Cobbs static const struct ng_parse_struct_info
211f8307e12SArchie Cobbs 	ng_ppp_link_type_info = NG_PPP_LINK_TYPE_INFO;
212f8307e12SArchie Cobbs static const struct ng_parse_type ng_ppp_link_type = {
213f8307e12SArchie Cobbs 	&ng_parse_struct_type,
214f8307e12SArchie Cobbs 	&ng_ppp_link_type_info,
215f8307e12SArchie Cobbs };
216f8307e12SArchie Cobbs 
217f8307e12SArchie Cobbs /* Parse type for struct ng_ppp_node_config */
218f8307e12SArchie Cobbs struct ng_parse_fixedarray_info ng_ppp_array_info = {
219f8307e12SArchie Cobbs 	&ng_ppp_link_type,
220f8307e12SArchie Cobbs 	NG_PPP_MAX_LINKS
221f8307e12SArchie Cobbs };
222f8307e12SArchie Cobbs static const struct ng_parse_type ng_ppp_link_array_type = {
223f8307e12SArchie Cobbs 	&ng_parse_fixedarray_type,
224f8307e12SArchie Cobbs 	&ng_ppp_array_info,
225f8307e12SArchie Cobbs };
226f8307e12SArchie Cobbs static const struct ng_parse_struct_info ng_ppp_config_type_info
227f8307e12SArchie Cobbs 	= NG_PPP_CONFIG_TYPE_INFO(&ng_ppp_link_array_type);
228f8307e12SArchie Cobbs static const struct ng_parse_type ng_ppp_config_type = {
229f8307e12SArchie Cobbs 	&ng_parse_struct_type,
230f8307e12SArchie Cobbs 	&ng_ppp_config_type_info
231f8307e12SArchie Cobbs };
232f8307e12SArchie Cobbs 
233f8307e12SArchie Cobbs /* Parse type for struct ng_ppp_link_stat */
234f8307e12SArchie Cobbs static const struct ng_parse_struct_info
235f8307e12SArchie Cobbs 	ng_ppp_stats_type_info = NG_PPP_STATS_TYPE_INFO;
236f8307e12SArchie Cobbs static const struct ng_parse_type ng_ppp_stats_type = {
237f8307e12SArchie Cobbs 	&ng_parse_struct_type,
238f8307e12SArchie Cobbs 	&ng_ppp_stats_type_info
239f8307e12SArchie Cobbs };
240f8307e12SArchie Cobbs 
241f8307e12SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
242f8307e12SArchie Cobbs static const struct ng_cmdlist ng_ppp_cmds[] = {
243f8307e12SArchie Cobbs 	{
244f8307e12SArchie Cobbs 	  NGM_PPP_COOKIE,
245f8307e12SArchie Cobbs 	  NGM_PPP_SET_CONFIG,
246f8307e12SArchie Cobbs 	  "setconfig",
247f8307e12SArchie Cobbs 	  &ng_ppp_config_type,
248f8307e12SArchie Cobbs 	  NULL
249f8307e12SArchie Cobbs 	},
250f8307e12SArchie Cobbs 	{
251f8307e12SArchie Cobbs 	  NGM_PPP_COOKIE,
252f8307e12SArchie Cobbs 	  NGM_PPP_GET_CONFIG,
253f8307e12SArchie Cobbs 	  "getconfig",
254f8307e12SArchie Cobbs 	  NULL,
255f8307e12SArchie Cobbs 	  &ng_ppp_config_type
256f8307e12SArchie Cobbs 	},
257f8307e12SArchie Cobbs 	{
258f8307e12SArchie Cobbs 	  NGM_PPP_COOKIE,
259f8307e12SArchie Cobbs 	  NGM_PPP_GET_LINK_STATS,
260f8307e12SArchie Cobbs 	  "getstats",
261f8307e12SArchie Cobbs 	  &ng_parse_int16_type,
262f8307e12SArchie Cobbs 	  &ng_ppp_stats_type
263f8307e12SArchie Cobbs 	},
264f8307e12SArchie Cobbs 	{
265f8307e12SArchie Cobbs 	  NGM_PPP_COOKIE,
266f8307e12SArchie Cobbs 	  NGM_PPP_CLR_LINK_STATS,
267f8307e12SArchie Cobbs 	  "clrstats",
268f8307e12SArchie Cobbs 	  &ng_parse_int16_type,
269f8307e12SArchie Cobbs 	  NULL
270f8307e12SArchie Cobbs 	},
271f8307e12SArchie Cobbs 	{
272f8307e12SArchie Cobbs 	  NGM_PPP_COOKIE,
273f8307e12SArchie Cobbs 	  NGM_PPP_GETCLR_LINK_STATS,
274f8307e12SArchie Cobbs 	  "getclrstats",
275f8307e12SArchie Cobbs 	  &ng_parse_int16_type,
276f8307e12SArchie Cobbs 	  &ng_ppp_stats_type
277f8307e12SArchie Cobbs 	},
278f8307e12SArchie Cobbs 	{ 0 }
279f8307e12SArchie Cobbs };
280f8307e12SArchie Cobbs 
2814cf49a43SJulian Elischer /* Node type descriptor */
2823949bee8SArchie Cobbs static struct ng_type ng_ppp_typestruct = {
2834cf49a43SJulian Elischer 	NG_VERSION,
2844cf49a43SJulian Elischer 	NG_PPP_NODE_TYPE,
2854cf49a43SJulian Elischer 	NULL,
2864cf49a43SJulian Elischer 	ng_ppp_constructor,
2874cf49a43SJulian Elischer 	ng_ppp_rcvmsg,
2884cf49a43SJulian Elischer 	ng_ppp_rmnode,
2894cf49a43SJulian Elischer 	ng_ppp_newhook,
2904cf49a43SJulian Elischer 	NULL,
2914cf49a43SJulian Elischer 	NULL,
2924cf49a43SJulian Elischer 	ng_ppp_rcvdata,
2934cf49a43SJulian Elischer 	ng_ppp_rcvdata,
294f8307e12SArchie Cobbs 	ng_ppp_disconnect,
295f8307e12SArchie Cobbs 	ng_ppp_cmds
2964cf49a43SJulian Elischer };
2973949bee8SArchie Cobbs NETGRAPH_INIT(ppp, &ng_ppp_typestruct);
2984cf49a43SJulian Elischer 
2993949bee8SArchie Cobbs static int	*compareLatencies;		/* hack for ng_ppp_intcmp() */
3004cf49a43SJulian Elischer 
301d690a6e7SArchie Cobbs /* Address and control field header */
302d690a6e7SArchie Cobbs static const u_char	ng_ppp_acf[2] = { 0xff, 0x03 };
303d690a6e7SArchie Cobbs 
3044cf49a43SJulian Elischer #define ERROUT(x)	do { error = (x); goto done; } while (0)
3054cf49a43SJulian Elischer 
3064cf49a43SJulian Elischer /************************************************************************
3074cf49a43SJulian Elischer 			NETGRAPH NODE STUFF
3084cf49a43SJulian Elischer  ************************************************************************/
3094cf49a43SJulian Elischer 
3104cf49a43SJulian Elischer /*
3113949bee8SArchie Cobbs  * Node type constructor
3124cf49a43SJulian Elischer  */
3134cf49a43SJulian Elischer static int
3144cf49a43SJulian Elischer ng_ppp_constructor(node_p *nodep)
3154cf49a43SJulian Elischer {
3164cf49a43SJulian Elischer 	priv_p priv;
3174cf49a43SJulian Elischer 	int error;
3184cf49a43SJulian Elischer 
3194cf49a43SJulian Elischer 	/* Allocate private structure */
3204cf49a43SJulian Elischer 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK);
3214cf49a43SJulian Elischer 	if (priv == NULL)
3224cf49a43SJulian Elischer 		return (ENOMEM);
3234cf49a43SJulian Elischer 	bzero(priv, sizeof(*priv));
3244cf49a43SJulian Elischer 
3254cf49a43SJulian Elischer 	/* Call generic node constructor */
3263949bee8SArchie Cobbs 	if ((error = ng_make_node_common(&ng_ppp_typestruct, nodep))) {
3274cf49a43SJulian Elischer 		FREE(priv, M_NETGRAPH);
3284cf49a43SJulian Elischer 		return (error);
3294cf49a43SJulian Elischer 	}
3304cf49a43SJulian Elischer 	(*nodep)->private = priv;
3314cf49a43SJulian Elischer 
3323949bee8SArchie Cobbs 	/* Initialize state */
3333949bee8SArchie Cobbs 	CIRCLEQ_INIT(&priv->frags);
3343949bee8SArchie Cobbs 
3354cf49a43SJulian Elischer 	/* Done */
3364cf49a43SJulian Elischer 	return (0);
3374cf49a43SJulian Elischer }
3384cf49a43SJulian Elischer 
3394cf49a43SJulian Elischer /*
3404cf49a43SJulian Elischer  * Give our OK for a hook to be added
3414cf49a43SJulian Elischer  */
3424cf49a43SJulian Elischer static int
3434cf49a43SJulian Elischer ng_ppp_newhook(node_p node, hook_p hook, const char *name)
3444cf49a43SJulian Elischer {
3454cf49a43SJulian Elischer 	const priv_p priv = node->private;
3463949bee8SArchie Cobbs 	int linkNum = -1;
3473949bee8SArchie Cobbs 	hook_p *hookPtr = NULL;
3483949bee8SArchie Cobbs 	int hookIndex = -1;
3494cf49a43SJulian Elischer 
3503949bee8SArchie Cobbs 	/* Figure out which hook it is */
3513949bee8SArchie Cobbs 	if (strncmp(name, NG_PPP_HOOK_LINK_PREFIX,	/* a link hook? */
3523949bee8SArchie Cobbs 	    strlen(NG_PPP_HOOK_LINK_PREFIX)) == 0) {
35325792ef3SArchie Cobbs 		const char *cp;
35425792ef3SArchie Cobbs 		char *eptr;
3553949bee8SArchie Cobbs 
3562b70adcbSArchie Cobbs 		cp = name + strlen(NG_PPP_HOOK_LINK_PREFIX);
3572b70adcbSArchie Cobbs 		if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
3584cf49a43SJulian Elischer 			return (EINVAL);
3592b70adcbSArchie Cobbs 		linkNum = (int)strtoul(cp, &eptr, 10);
3602b70adcbSArchie Cobbs 		if (*eptr != '\0' || linkNum < 0 || linkNum >= NG_PPP_MAX_LINKS)
3613949bee8SArchie Cobbs 			return (EINVAL);
3623949bee8SArchie Cobbs 		hookPtr = &priv->links[linkNum];
3633949bee8SArchie Cobbs 		hookIndex = ~linkNum;
3643949bee8SArchie Cobbs 	} else {				/* must be a non-link hook */
3653949bee8SArchie Cobbs 		int i;
3664cf49a43SJulian Elischer 
3673949bee8SArchie Cobbs 		for (i = 0; ng_ppp_hook_names[i] != NULL; i++) {
3683949bee8SArchie Cobbs 			if (strcmp(name, ng_ppp_hook_names[i]) == 0) {
3693949bee8SArchie Cobbs 				hookPtr = &priv->hooks[i];
3703949bee8SArchie Cobbs 				hookIndex = i;
3713949bee8SArchie Cobbs 				break;
3723949bee8SArchie Cobbs 			}
3733949bee8SArchie Cobbs 		}
3743949bee8SArchie Cobbs 		if (ng_ppp_hook_names[i] == NULL)
3753949bee8SArchie Cobbs 			return (EINVAL);	/* no such hook */
3763949bee8SArchie Cobbs 	}
3773949bee8SArchie Cobbs 
3783949bee8SArchie Cobbs 	/* See if hook is already connected */
3793949bee8SArchie Cobbs 	if (*hookPtr != NULL)
3804cf49a43SJulian Elischer 		return (EISCONN);
3814cf49a43SJulian Elischer 
3823949bee8SArchie Cobbs 	/* Disallow more than one link unless multilink is enabled */
3833949bee8SArchie Cobbs 	if (linkNum != -1 && priv->conf.links[linkNum].enableLink
3843949bee8SArchie Cobbs 	    && !priv->conf.enableMultilink && priv->numActiveLinks >= 1)
3853949bee8SArchie Cobbs 		return (ENODEV);
3864cf49a43SJulian Elischer 
3874cf49a43SJulian Elischer 	/* OK */
3883949bee8SArchie Cobbs 	*hookPtr = hook;
3893949bee8SArchie Cobbs 	HOOK_INDEX(hook) = hookIndex;
3903949bee8SArchie Cobbs 	ng_ppp_update(node, 0);
3914cf49a43SJulian Elischer 	return (0);
3924cf49a43SJulian Elischer }
3934cf49a43SJulian Elischer 
3944cf49a43SJulian Elischer /*
3954cf49a43SJulian Elischer  * Receive a control message
3964cf49a43SJulian Elischer  */
3974cf49a43SJulian Elischer static int
3984cf49a43SJulian Elischer ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg,
3994cf49a43SJulian Elischer 	      const char *raddr, struct ng_mesg **rptr)
4004cf49a43SJulian Elischer {
4014cf49a43SJulian Elischer 	const priv_p priv = node->private;
4024cf49a43SJulian Elischer 	struct ng_mesg *resp = NULL;
4034cf49a43SJulian Elischer 	int error = 0;
4044cf49a43SJulian Elischer 
4054cf49a43SJulian Elischer 	switch (msg->header.typecookie) {
4064cf49a43SJulian Elischer 	case NGM_PPP_COOKIE:
4074cf49a43SJulian Elischer 		switch (msg->header.cmd) {
4083949bee8SArchie Cobbs 		case NGM_PPP_SET_CONFIG:
4093949bee8SArchie Cobbs 		    {
4103949bee8SArchie Cobbs 			struct ng_ppp_node_config *const newConf =
4113949bee8SArchie Cobbs 			    (struct ng_ppp_node_config *) msg->data;
4123949bee8SArchie Cobbs 
4133949bee8SArchie Cobbs 			/* Check for invalid or illegal config */
4143949bee8SArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
4154cf49a43SJulian Elischer 				ERROUT(EINVAL);
4163949bee8SArchie Cobbs 			if (!ng_ppp_config_valid(node, newConf))
4173949bee8SArchie Cobbs 				ERROUT(EINVAL);
4183949bee8SArchie Cobbs 			priv->conf = *newConf;
4193949bee8SArchie Cobbs 			ng_ppp_update(node, 1);
4204cf49a43SJulian Elischer 			break;
4213949bee8SArchie Cobbs 		    }
4223949bee8SArchie Cobbs 		case NGM_PPP_GET_CONFIG:
4233949bee8SArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT);
4244cf49a43SJulian Elischer 			if (resp == NULL)
4254cf49a43SJulian Elischer 				ERROUT(ENOMEM);
4263949bee8SArchie Cobbs 			bcopy(&priv->conf, resp->data, sizeof(priv->conf));
4274cf49a43SJulian Elischer 			break;
4283949bee8SArchie Cobbs 		case NGM_PPP_GET_LINK_STATS:
4293949bee8SArchie Cobbs 		case NGM_PPP_CLR_LINK_STATS:
430e149c4e2SArchie Cobbs 		case NGM_PPP_GETCLR_LINK_STATS:
4313949bee8SArchie Cobbs 		    {
4323949bee8SArchie Cobbs 			struct ng_ppp_link_stat *stats;
4333949bee8SArchie Cobbs 			u_int16_t linkNum;
4343949bee8SArchie Cobbs 
4353949bee8SArchie Cobbs 			if (msg->header.arglen != sizeof(u_int16_t))
4363949bee8SArchie Cobbs 				ERROUT(EINVAL);
4373949bee8SArchie Cobbs 			linkNum = *((u_int16_t *) msg->data);
4383949bee8SArchie Cobbs 			if (linkNum >= NG_PPP_MAX_LINKS
4393949bee8SArchie Cobbs 			    && linkNum != NG_PPP_BUNDLE_LINKNUM)
4403949bee8SArchie Cobbs 				ERROUT(EINVAL);
4413949bee8SArchie Cobbs 			stats = (linkNum == NG_PPP_BUNDLE_LINKNUM) ?
4423949bee8SArchie Cobbs 				&priv->bundleStats : &priv->linkStats[linkNum];
443e149c4e2SArchie Cobbs 			if (msg->header.cmd != NGM_PPP_CLR_LINK_STATS) {
4443949bee8SArchie Cobbs 				NG_MKRESPONSE(resp, msg,
4453949bee8SArchie Cobbs 				    sizeof(struct ng_ppp_link_stat), M_NOWAIT);
4463949bee8SArchie Cobbs 				if (resp == NULL)
4473949bee8SArchie Cobbs 					ERROUT(ENOMEM);
4483949bee8SArchie Cobbs 				bcopy(stats, resp->data, sizeof(*stats));
449e149c4e2SArchie Cobbs 			}
450e149c4e2SArchie Cobbs 			if (msg->header.cmd != NGM_PPP_GET_LINK_STATS)
4513949bee8SArchie Cobbs 				bzero(stats, sizeof(*stats));
4524cf49a43SJulian Elischer 			break;
4533949bee8SArchie Cobbs 		    }
4544cf49a43SJulian Elischer 		default:
4554cf49a43SJulian Elischer 			error = EINVAL;
4564cf49a43SJulian Elischer 			break;
4574cf49a43SJulian Elischer 		}
4584cf49a43SJulian Elischer 		break;
4593949bee8SArchie Cobbs 	case NGM_VJC_COOKIE:
4603949bee8SArchie Cobbs 	   {
4613949bee8SArchie Cobbs 	       char path[NG_PATHLEN + 1];
4623949bee8SArchie Cobbs 	       node_p origNode;
4633949bee8SArchie Cobbs 
4643949bee8SArchie Cobbs 	       if ((error = ng_path2node(node, raddr, &origNode, NULL)) != 0)
4653949bee8SArchie Cobbs 		       ERROUT(error);
4663949bee8SArchie Cobbs 	       snprintf(path, sizeof(path), "[%lx]:%s",
4673949bee8SArchie Cobbs 		   (long) node, NG_PPP_HOOK_VJC_IP);
4683949bee8SArchie Cobbs 	       return ng_send_msg(origNode, msg, path, rptr);
4693949bee8SArchie Cobbs 	       break;
4703949bee8SArchie Cobbs 	   }
4714cf49a43SJulian Elischer 	default:
4724cf49a43SJulian Elischer 		error = EINVAL;
4734cf49a43SJulian Elischer 		break;
4744cf49a43SJulian Elischer 	}
4754cf49a43SJulian Elischer 	if (rptr)
4764cf49a43SJulian Elischer 		*rptr = resp;
4774cf49a43SJulian Elischer 	else if (resp)
4784cf49a43SJulian Elischer 		FREE(resp, M_NETGRAPH);
4794cf49a43SJulian Elischer 
4804cf49a43SJulian Elischer done:
4814cf49a43SJulian Elischer 	FREE(msg, M_NETGRAPH);
4824cf49a43SJulian Elischer 	return (error);
4834cf49a43SJulian Elischer }
4844cf49a43SJulian Elischer 
4854cf49a43SJulian Elischer /*
4864cf49a43SJulian Elischer  * Receive data on a hook
4874cf49a43SJulian Elischer  */
4884cf49a43SJulian Elischer static int
4894cf49a43SJulian Elischer ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
4904cf49a43SJulian Elischer {
4914cf49a43SJulian Elischer 	const node_p node = hook->node;
4924cf49a43SJulian Elischer 	const priv_p priv = node->private;
4933949bee8SArchie Cobbs 	const int index = HOOK_INDEX(hook);
4943949bee8SArchie Cobbs 	u_int16_t linkNum = NG_PPP_BUNDLE_LINKNUM;
4953949bee8SArchie Cobbs 	hook_p outHook = NULL;
4963949bee8SArchie Cobbs 	int proto = 0, error;
4974cf49a43SJulian Elischer 
4983949bee8SArchie Cobbs 	/* Did it come from a link hook? */
4993949bee8SArchie Cobbs 	if (index < 0) {
5004cf49a43SJulian Elischer 
501e149c4e2SArchie Cobbs 		/* Convert index into a link number */
5023949bee8SArchie Cobbs 		linkNum = (u_int16_t)~index;
5033949bee8SArchie Cobbs 		KASSERT(linkNum < NG_PPP_MAX_LINKS,
5043949bee8SArchie Cobbs 		    ("%s: bogus index 0x%x", __FUNCTION__, index));
5053949bee8SArchie Cobbs 
5063949bee8SArchie Cobbs 		/* Stats */
5073949bee8SArchie Cobbs 		priv->linkStats[linkNum].recvFrames++;
5083949bee8SArchie Cobbs 		priv->linkStats[linkNum].recvOctets += m->m_pkthdr.len;
5093949bee8SArchie Cobbs 
510d690a6e7SArchie Cobbs 		/* Strip address and control fields, if present */
511d690a6e7SArchie Cobbs 		if (m->m_pkthdr.len >= 2) {
512d690a6e7SArchie Cobbs 			if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) {
513d690a6e7SArchie Cobbs 				NG_FREE_DATA(m, meta);
514d690a6e7SArchie Cobbs 				return (ENOBUFS);
515d690a6e7SArchie Cobbs 			}
516d690a6e7SArchie Cobbs 			if (bcmp(mtod(m, u_char *), &ng_ppp_acf, 2) == 0)
517d690a6e7SArchie Cobbs 				m_adj(m, 2);
518d690a6e7SArchie Cobbs 		}
519d690a6e7SArchie Cobbs 
520e149c4e2SArchie Cobbs 		/* Dispatch incoming frame (if not enabled, to bypass) */
521e149c4e2SArchie Cobbs 		return ng_ppp_input(node,
522e149c4e2SArchie Cobbs 		    !priv->conf.links[linkNum].enableLink, linkNum, m, meta);
5233949bee8SArchie Cobbs 	}
5243949bee8SArchie Cobbs 
5253949bee8SArchie Cobbs 	/* Get protocol & check if data allowed from this hook */
5263949bee8SArchie Cobbs 	switch (index) {
5273949bee8SArchie Cobbs 
528e149c4e2SArchie Cobbs 	/* Outgoing data */
5293949bee8SArchie Cobbs 	case HOOK_INDEX_ATALK:
5303949bee8SArchie Cobbs 		if (!priv->conf.enableAtalk) {
5313949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5323949bee8SArchie Cobbs 			return (ENXIO);
5333949bee8SArchie Cobbs 		}
5343949bee8SArchie Cobbs 		proto = PROT_APPLETALK;
5353949bee8SArchie Cobbs 		break;
5363949bee8SArchie Cobbs 	case HOOK_INDEX_IPX:
5373949bee8SArchie Cobbs 		if (!priv->conf.enableIPX) {
5383949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5393949bee8SArchie Cobbs 			return (ENXIO);
5403949bee8SArchie Cobbs 		}
5413949bee8SArchie Cobbs 		proto = PROT_IPX;
5423949bee8SArchie Cobbs 		break;
5433949bee8SArchie Cobbs 	case HOOK_INDEX_INET:
5443949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_VJIP:
5453949bee8SArchie Cobbs 		if (!priv->conf.enableIP) {
5463949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5473949bee8SArchie Cobbs 			return (ENXIO);
5483949bee8SArchie Cobbs 		}
5493949bee8SArchie Cobbs 		proto = PROT_IP;
5503949bee8SArchie Cobbs 		break;
5513949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_COMP:
5523949bee8SArchie Cobbs 		if (!priv->conf.enableVJCompression) {
5533949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5543949bee8SArchie Cobbs 			return (ENXIO);
5553949bee8SArchie Cobbs 		}
5563949bee8SArchie Cobbs 		proto = PROT_VJCOMP;
5573949bee8SArchie Cobbs 		break;
5583949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_UNCOMP:
5593949bee8SArchie Cobbs 		if (!priv->conf.enableVJCompression) {
5603949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5613949bee8SArchie Cobbs 			return (ENXIO);
5623949bee8SArchie Cobbs 		}
5633949bee8SArchie Cobbs 		proto = PROT_VJUNCOMP;
5643949bee8SArchie Cobbs 		break;
5653949bee8SArchie Cobbs 	case HOOK_INDEX_COMPRESS:
5663949bee8SArchie Cobbs 		if (!priv->conf.enableCompression) {
5673949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5683949bee8SArchie Cobbs 			return (ENXIO);
5693949bee8SArchie Cobbs 		}
5703949bee8SArchie Cobbs 		proto = PROT_COMPD;
5713949bee8SArchie Cobbs 		break;
5723949bee8SArchie Cobbs 	case HOOK_INDEX_ENCRYPT:
5733949bee8SArchie Cobbs 		if (!priv->conf.enableEncryption) {
5743949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
5753949bee8SArchie Cobbs 			return (ENXIO);
5763949bee8SArchie Cobbs 		}
5773949bee8SArchie Cobbs 		proto = PROT_CRYPTD;
5783949bee8SArchie Cobbs 		break;
5793949bee8SArchie Cobbs 	case HOOK_INDEX_BYPASS:
5803949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 4) {
5813949bee8SArchie Cobbs 			NG_FREE_META(meta);
5823949bee8SArchie Cobbs 			return (EINVAL);
5833949bee8SArchie Cobbs 		}
5843949bee8SArchie Cobbs 		if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
5853949bee8SArchie Cobbs 			NG_FREE_META(meta);
5863949bee8SArchie Cobbs 			return (ENOBUFS);
5873949bee8SArchie Cobbs 		}
5883949bee8SArchie Cobbs 		linkNum = ntohs(mtod(m, u_int16_t *)[0]);
5893949bee8SArchie Cobbs 		proto = ntohs(mtod(m, u_int16_t *)[1]);
5903949bee8SArchie Cobbs 		m_adj(m, 4);
5913949bee8SArchie Cobbs 		if (linkNum >= NG_PPP_MAX_LINKS
5923949bee8SArchie Cobbs 		    && linkNum != NG_PPP_BUNDLE_LINKNUM)
5933949bee8SArchie Cobbs 			return (EINVAL);
5943949bee8SArchie Cobbs 		break;
5953949bee8SArchie Cobbs 
596e149c4e2SArchie Cobbs 	/* Incoming data */
5973949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_IP:
598e149c4e2SArchie Cobbs 		if (!priv->conf.enableIP || !priv->conf.enableVJDecompression) {
5993949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
6003949bee8SArchie Cobbs 			return (ENXIO);
6013949bee8SArchie Cobbs 		}
6023949bee8SArchie Cobbs 		break;
6033949bee8SArchie Cobbs 	case HOOK_INDEX_DECOMPRESS:
6043949bee8SArchie Cobbs 		if (!priv->conf.enableDecompression) {
6053949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
6063949bee8SArchie Cobbs 			return (ENXIO);
6073949bee8SArchie Cobbs 		}
6083949bee8SArchie Cobbs 		break;
6093949bee8SArchie Cobbs 	case HOOK_INDEX_DECRYPT:
6103949bee8SArchie Cobbs 		if (!priv->conf.enableDecryption) {
6113949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
6123949bee8SArchie Cobbs 			return (ENXIO);
6133949bee8SArchie Cobbs 		}
6143949bee8SArchie Cobbs 		break;
6153949bee8SArchie Cobbs 	default:
616e149c4e2SArchie Cobbs 		panic("%s: bogus index 0x%x", __FUNCTION__, index);
6173949bee8SArchie Cobbs 	}
6183949bee8SArchie Cobbs 
6193949bee8SArchie Cobbs 	/* Now figure out what to do with the frame */
6203949bee8SArchie Cobbs 	switch (index) {
621e149c4e2SArchie Cobbs 
622e149c4e2SArchie Cobbs 	/* Outgoing data */
6233949bee8SArchie Cobbs 	case HOOK_INDEX_INET:
6243949bee8SArchie Cobbs 		if (priv->conf.enableVJCompression && priv->vjCompHooked) {
6253949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_IP];
6263949bee8SArchie Cobbs 			break;
6273949bee8SArchie Cobbs 		}
6283949bee8SArchie Cobbs 		/* FALLTHROUGH */
6293949bee8SArchie Cobbs 	case HOOK_INDEX_ATALK:
6303949bee8SArchie Cobbs 	case HOOK_INDEX_IPX:
6313949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_COMP:
6323949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_UNCOMP:
6333949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_VJIP:
6343949bee8SArchie Cobbs 		if (priv->conf.enableCompression
6353949bee8SArchie Cobbs 		    && priv->hooks[HOOK_INDEX_COMPRESS] != NULL) {
6363949bee8SArchie Cobbs 			if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) {
6373949bee8SArchie Cobbs 				NG_FREE_META(meta);
6383949bee8SArchie Cobbs 				return (ENOBUFS);
6393949bee8SArchie Cobbs 			}
6403949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_COMPRESS];
6413949bee8SArchie Cobbs 			break;
6423949bee8SArchie Cobbs 		}
6433949bee8SArchie Cobbs 		/* FALLTHROUGH */
6443949bee8SArchie Cobbs 	case HOOK_INDEX_COMPRESS:
6453949bee8SArchie Cobbs 		if (priv->conf.enableEncryption
6463949bee8SArchie Cobbs 		    && priv->hooks[HOOK_INDEX_ENCRYPT] != NULL) {
6473949bee8SArchie Cobbs 			if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) {
6483949bee8SArchie Cobbs 				NG_FREE_META(meta);
6493949bee8SArchie Cobbs 				return (ENOBUFS);
6503949bee8SArchie Cobbs 			}
6513949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_ENCRYPT];
6523949bee8SArchie Cobbs 			break;
6533949bee8SArchie Cobbs 		}
6543949bee8SArchie Cobbs 		/* FALLTHROUGH */
6553949bee8SArchie Cobbs 	case HOOK_INDEX_ENCRYPT:
6560e11d0f3SArchie Cobbs 		return ng_ppp_output(node, 0,
657d690a6e7SArchie Cobbs 		    proto, NG_PPP_BUNDLE_LINKNUM, m, meta);
6583949bee8SArchie Cobbs 
6590e11d0f3SArchie Cobbs 	case HOOK_INDEX_BYPASS:
6600e11d0f3SArchie Cobbs 		return ng_ppp_output(node, 1, proto, linkNum, m, meta);
6610e11d0f3SArchie Cobbs 
6623949bee8SArchie Cobbs 	/* Incoming data */
6633949bee8SArchie Cobbs 	case HOOK_INDEX_DECRYPT:
6643949bee8SArchie Cobbs 	case HOOK_INDEX_DECOMPRESS:
665e149c4e2SArchie Cobbs 		return ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta);
666e149c4e2SArchie Cobbs 
6673949bee8SArchie Cobbs 	case HOOK_INDEX_VJC_IP:
668e149c4e2SArchie Cobbs 		outHook = priv->hooks[HOOK_INDEX_INET];
669e149c4e2SArchie Cobbs 		break;
6703949bee8SArchie Cobbs 	}
6713949bee8SArchie Cobbs 
6723949bee8SArchie Cobbs 	/* Send packet out hook */
6733949bee8SArchie Cobbs 	NG_SEND_DATA(error, outHook, m, meta);
6744cf49a43SJulian Elischer 	return (error);
6754cf49a43SJulian Elischer }
6764cf49a43SJulian Elischer 
6774cf49a43SJulian Elischer /*
6784cf49a43SJulian Elischer  * Destroy node
6794cf49a43SJulian Elischer  */
6804cf49a43SJulian Elischer static int
6814cf49a43SJulian Elischer ng_ppp_rmnode(node_p node)
6824cf49a43SJulian Elischer {
6834cf49a43SJulian Elischer 	const priv_p priv = node->private;
6844cf49a43SJulian Elischer 
6854cf49a43SJulian Elischer 	/* Take down netgraph node */
6864cf49a43SJulian Elischer 	node->flags |= NG_INVALID;
6874cf49a43SJulian Elischer 	ng_cutlinks(node);
6884cf49a43SJulian Elischer 	ng_unname(node);
689e149c4e2SArchie Cobbs 	ng_ppp_free_frags(node);
6904cf49a43SJulian Elischer 	bzero(priv, sizeof(*priv));
6914cf49a43SJulian Elischer 	FREE(priv, M_NETGRAPH);
6924cf49a43SJulian Elischer 	node->private = NULL;
6934cf49a43SJulian Elischer 	ng_unref(node);		/* let the node escape */
6944cf49a43SJulian Elischer 	return (0);
6954cf49a43SJulian Elischer }
6964cf49a43SJulian Elischer 
6974cf49a43SJulian Elischer /*
6984cf49a43SJulian Elischer  * Hook disconnection
6994cf49a43SJulian Elischer  */
7004cf49a43SJulian Elischer static int
7014cf49a43SJulian Elischer ng_ppp_disconnect(hook_p hook)
7024cf49a43SJulian Elischer {
70362838faeSArchie Cobbs 	const node_p node = hook->node;
70462838faeSArchie Cobbs 	const priv_p priv = node->private;
70562838faeSArchie Cobbs 	const int index = HOOK_INDEX(hook);
70662838faeSArchie Cobbs 
70762838faeSArchie Cobbs 	/* Zero out hook pointer */
70862838faeSArchie Cobbs 	if (index < 0)
70962838faeSArchie Cobbs 		priv->links[~index] = NULL;
71062838faeSArchie Cobbs 	else
71162838faeSArchie Cobbs 		priv->hooks[index] = NULL;
71262838faeSArchie Cobbs 
71362838faeSArchie Cobbs 	/* Update derived info (or go away if no hooks left) */
71462838faeSArchie Cobbs 	if (node->numhooks > 0)
71562838faeSArchie Cobbs 		ng_ppp_update(node, 0);
71662838faeSArchie Cobbs 	else
7174cf49a43SJulian Elischer 		ng_rmnode(hook->node);
7184cf49a43SJulian Elischer 	return (0);
7194cf49a43SJulian Elischer }
7204cf49a43SJulian Elischer 
7214cf49a43SJulian Elischer /************************************************************************
7224cf49a43SJulian Elischer 			HELPER STUFF
7234cf49a43SJulian Elischer  ************************************************************************/
7244cf49a43SJulian Elischer 
7254cf49a43SJulian Elischer /*
7263949bee8SArchie Cobbs  * Handle an incoming frame.  Extract the PPP protocol number
7273949bee8SArchie Cobbs  * and dispatch accordingly.
7284cf49a43SJulian Elischer  */
7294cf49a43SJulian Elischer static int
730e149c4e2SArchie Cobbs ng_ppp_input(node_p node, int bypass, int linkNum, struct mbuf *m, meta_p meta)
7314cf49a43SJulian Elischer {
7323949bee8SArchie Cobbs 	const priv_p priv = node->private;
7333949bee8SArchie Cobbs 	hook_p outHook = NULL;
7343949bee8SArchie Cobbs 	int proto, error;
7354cf49a43SJulian Elischer 
7363949bee8SArchie Cobbs 	/* Extract protocol number */
7373949bee8SArchie Cobbs 	for (proto = 0; !PROT_VALID(proto) && m->m_pkthdr.len > 0; ) {
7383949bee8SArchie Cobbs 		if (m->m_len < 1 && (m = m_pullup(m, 1)) == NULL) {
7393949bee8SArchie Cobbs 			NG_FREE_META(meta);
7403949bee8SArchie Cobbs 			return (ENOBUFS);
7414cf49a43SJulian Elischer 		}
7423949bee8SArchie Cobbs 		proto = (proto << 8) + *mtod(m, u_char *);
7433949bee8SArchie Cobbs 		m_adj(m, 1);
7443949bee8SArchie Cobbs 	}
7453949bee8SArchie Cobbs 	if (!PROT_VALID(proto)) {
7463949bee8SArchie Cobbs 		if (linkNum == NG_PPP_BUNDLE_LINKNUM)
7473949bee8SArchie Cobbs 			priv->bundleStats.badProtos++;
7483949bee8SArchie Cobbs 		else
7493949bee8SArchie Cobbs 			priv->linkStats[linkNum].badProtos++;
7503949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
7513949bee8SArchie Cobbs 		return (EINVAL);
7523949bee8SArchie Cobbs 	}
7533949bee8SArchie Cobbs 
754e149c4e2SArchie Cobbs 	/* Bypass frame? */
755e149c4e2SArchie Cobbs 	if (bypass)
756e149c4e2SArchie Cobbs 		goto bypass;
757e149c4e2SArchie Cobbs 
7583949bee8SArchie Cobbs 	/* Check protocol */
7593949bee8SArchie Cobbs 	switch (proto) {
7603949bee8SArchie Cobbs 	case PROT_COMPD:
7613949bee8SArchie Cobbs 		if (priv->conf.enableDecompression)
7623949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_DECOMPRESS];
7633949bee8SArchie Cobbs 		break;
7643949bee8SArchie Cobbs 	case PROT_CRYPTD:
7653949bee8SArchie Cobbs 		if (priv->conf.enableDecryption)
7663949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_DECRYPT];
7673949bee8SArchie Cobbs 		break;
7683949bee8SArchie Cobbs 	case PROT_VJCOMP:
7693949bee8SArchie Cobbs 		if (priv->conf.enableVJDecompression && priv->vjCompHooked)
7703949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_COMP];
7713949bee8SArchie Cobbs 		break;
7723949bee8SArchie Cobbs 	case PROT_VJUNCOMP:
7733949bee8SArchie Cobbs 		if (priv->conf.enableVJDecompression && priv->vjCompHooked)
7743949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_VJC_UNCOMP];
7753949bee8SArchie Cobbs 		break;
7763949bee8SArchie Cobbs 	case PROT_MP:
7770e11d0f3SArchie Cobbs 		if (priv->conf.enableMultilink)
7783949bee8SArchie Cobbs 			return ng_ppp_mp_input(node, linkNum, m, meta);
7793949bee8SArchie Cobbs 		break;
7803949bee8SArchie Cobbs 	case PROT_APPLETALK:
7813949bee8SArchie Cobbs 		if (priv->conf.enableAtalk)
7823949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_ATALK];
7833949bee8SArchie Cobbs 		break;
7843949bee8SArchie Cobbs 	case PROT_IPX:
7853949bee8SArchie Cobbs 		if (priv->conf.enableIPX)
7863949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_IPX];
7873949bee8SArchie Cobbs 		break;
7883949bee8SArchie Cobbs 	case PROT_IP:
7893949bee8SArchie Cobbs 		if (priv->conf.enableIP)
7903949bee8SArchie Cobbs 			outHook = priv->hooks[HOOK_INDEX_INET];
7913949bee8SArchie Cobbs 		break;
7923949bee8SArchie Cobbs 	}
7933949bee8SArchie Cobbs 
7943949bee8SArchie Cobbs 	/* For unknown/inactive protocols, forward out the bypass hook */
795e149c4e2SArchie Cobbs bypass:
7963949bee8SArchie Cobbs 	if (outHook == NULL) {
797d690a6e7SArchie Cobbs 		u_int16_t hdr[2];
798d690a6e7SArchie Cobbs 
799d690a6e7SArchie Cobbs 		hdr[0] = htons(linkNum);
800d690a6e7SArchie Cobbs 		hdr[1] = htons((u_int16_t)proto);
801d690a6e7SArchie Cobbs 		if ((m = ng_ppp_prepend(m, &hdr, 4)) == NULL)
8023949bee8SArchie Cobbs 			return (ENOBUFS);
8033949bee8SArchie Cobbs 		outHook = priv->hooks[HOOK_INDEX_BYPASS];
8043949bee8SArchie Cobbs 	}
8053949bee8SArchie Cobbs 
8063949bee8SArchie Cobbs 	/* Forward frame */
8073949bee8SArchie Cobbs 	NG_SEND_DATA(error, outHook, m, meta);
8083949bee8SArchie Cobbs 	return (error);
8094cf49a43SJulian Elischer }
8104cf49a43SJulian Elischer 
8114cf49a43SJulian Elischer /*
8123949bee8SArchie Cobbs  * Deliver a frame out a link, either a real one or NG_PPP_BUNDLE_LINKNUM
8130e11d0f3SArchie Cobbs  * If the link is not enabled then ENXIO is returned, unless "bypass" is != 0.
8144cf49a43SJulian Elischer  */
8153949bee8SArchie Cobbs static int
816d690a6e7SArchie Cobbs ng_ppp_output(node_p node, int bypass,
817d690a6e7SArchie Cobbs 	int proto, int linkNum, struct mbuf *m, meta_p meta)
8184cf49a43SJulian Elischer {
8193949bee8SArchie Cobbs 	const priv_p priv = node->private;
820e149c4e2SArchie Cobbs 	int len, error;
8214cf49a43SJulian Elischer 
822d690a6e7SArchie Cobbs 	/* If not doing MP, map bundle virtual link to (the only) link */
823d690a6e7SArchie Cobbs 	if (linkNum == NG_PPP_BUNDLE_LINKNUM && !priv->conf.enableMultilink)
8243949bee8SArchie Cobbs 		linkNum = priv->activeLinks[0];
8253949bee8SArchie Cobbs 
826d690a6e7SArchie Cobbs 	/* Check link status (if real) */
8270e11d0f3SArchie Cobbs 	if (linkNum != NG_PPP_BUNDLE_LINKNUM) {
8280e11d0f3SArchie Cobbs 		if (!bypass && !priv->conf.links[linkNum].enableLink)
8293949bee8SArchie Cobbs 			return (ENXIO);
8303949bee8SArchie Cobbs 		if (priv->links[linkNum] == NULL) {
8313949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
8323949bee8SArchie Cobbs 			return (ENETDOWN);
8333949bee8SArchie Cobbs 		}
8340e11d0f3SArchie Cobbs 	}
8353949bee8SArchie Cobbs 
836d690a6e7SArchie Cobbs 	/* Prepend protocol number, possibly compressed */
837d690a6e7SArchie Cobbs 	if ((m = ng_ppp_addproto(m, proto,
838d690a6e7SArchie Cobbs 	    linkNum == NG_PPP_BUNDLE_LINKNUM
839d690a6e7SArchie Cobbs 	      || priv->conf.links[linkNum].enableProtoComp)) == NULL) {
840d690a6e7SArchie Cobbs 		NG_FREE_META(meta);
841d690a6e7SArchie Cobbs 		return (ENOBUFS);
842d690a6e7SArchie Cobbs 	}
843d690a6e7SArchie Cobbs 
844d690a6e7SArchie Cobbs 	/* Special handling for the MP virtual link */
845d690a6e7SArchie Cobbs 	if (linkNum == NG_PPP_BUNDLE_LINKNUM)
846d690a6e7SArchie Cobbs 		return ng_ppp_mp_output(node, m, meta);
847d690a6e7SArchie Cobbs 
848d690a6e7SArchie Cobbs 	/* Prepend address and control field (unless compressed) */
849d690a6e7SArchie Cobbs 	if (proto == PROT_LCP || !priv->conf.links[linkNum].enableACFComp) {
850d690a6e7SArchie Cobbs 		if ((m = ng_ppp_prepend(m, &ng_ppp_acf, 2)) == NULL) {
851d690a6e7SArchie Cobbs 			NG_FREE_META(meta);
852d690a6e7SArchie Cobbs 			return (ENOBUFS);
853d690a6e7SArchie Cobbs 		}
854d690a6e7SArchie Cobbs 	}
855d690a6e7SArchie Cobbs 
8563949bee8SArchie Cobbs 	/* Deliver frame */
857e149c4e2SArchie Cobbs 	len = m->m_pkthdr.len;
8583949bee8SArchie Cobbs 	NG_SEND_DATA(error, priv->links[linkNum], m, meta);
859fb1fc8abSArchie Cobbs 
860fb1fc8abSArchie Cobbs 	/* Update stats and 'bytes in queue' counter */
861fb1fc8abSArchie Cobbs 	if (error == 0) {
862fb1fc8abSArchie Cobbs 		priv->linkStats[linkNum].xmitFrames++;
863e149c4e2SArchie Cobbs 		priv->linkStats[linkNum].xmitOctets += len;
864e149c4e2SArchie Cobbs 		priv->qstat[linkNum].bytesInQueue += len;
865fb1fc8abSArchie Cobbs 		microtime(&priv->qstat[linkNum].lastWrite);
866fb1fc8abSArchie Cobbs 	}
8673949bee8SArchie Cobbs 	return error;
8683949bee8SArchie Cobbs }
8693949bee8SArchie Cobbs 
8703949bee8SArchie Cobbs /*
8713949bee8SArchie Cobbs  * Handle an incoming multi-link fragment
8723949bee8SArchie Cobbs  */
8733949bee8SArchie Cobbs static int
8743949bee8SArchie Cobbs ng_ppp_mp_input(node_p node, int linkNum, struct mbuf *m, meta_p meta)
8753949bee8SArchie Cobbs {
8763949bee8SArchie Cobbs 	const priv_p priv = node->private;
8773949bee8SArchie Cobbs 	struct ng_ppp_frag frag0, *frag = &frag0;
8783949bee8SArchie Cobbs 	struct ng_ppp_frag *qent, *qnext;
8793949bee8SArchie Cobbs 	struct ng_ppp_frag *first = NULL, *last = NULL;
8803949bee8SArchie Cobbs 	int diff, highSeq, nextSeq;
8813949bee8SArchie Cobbs 	struct mbuf *tail;
8823949bee8SArchie Cobbs 
8833949bee8SArchie Cobbs 	/* Extract fragment information from MP header */
8843949bee8SArchie Cobbs 	if (priv->conf.recvShortSeq) {
8853949bee8SArchie Cobbs 		u_int16_t shdr;
8863949bee8SArchie Cobbs 
8873949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 2) {
8883949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
8893949bee8SArchie Cobbs 			return (EINVAL);
8903949bee8SArchie Cobbs 		}
8913949bee8SArchie Cobbs 		if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) {
8923949bee8SArchie Cobbs 			NG_FREE_META(meta);
8933949bee8SArchie Cobbs 			return (ENOBUFS);
8943949bee8SArchie Cobbs 		}
8953949bee8SArchie Cobbs 		shdr = ntohs(*mtod(m, u_int16_t *));
8963949bee8SArchie Cobbs 		frag->seq = shdr & MP_SHORT_SEQ_MASK;
8973949bee8SArchie Cobbs 		frag->first = (shdr & MP_SHORT_FIRST_FLAG) != 0;
8983949bee8SArchie Cobbs 		frag->last = (shdr & MP_SHORT_LAST_FLAG) != 0;
8993949bee8SArchie Cobbs 		highSeq = CIRCLEQ_EMPTY(&priv->frags) ?
9003949bee8SArchie Cobbs 		    frag->seq : CIRCLEQ_FIRST(&priv->frags)->seq;
9013949bee8SArchie Cobbs 		diff = MP_SHORT_SEQ_DIFF(frag->seq, highSeq);
9023949bee8SArchie Cobbs 		m_adj(m, 2);
9033949bee8SArchie Cobbs 	} else {
9043949bee8SArchie Cobbs 		u_int32_t lhdr;
9053949bee8SArchie Cobbs 
9063949bee8SArchie Cobbs 		if (m->m_pkthdr.len < 4) {
9073949bee8SArchie Cobbs 			NG_FREE_DATA(m, meta);
9083949bee8SArchie Cobbs 			return (EINVAL);
9093949bee8SArchie Cobbs 		}
9103949bee8SArchie Cobbs 		if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
9113949bee8SArchie Cobbs 			NG_FREE_META(meta);
9123949bee8SArchie Cobbs 			return (ENOBUFS);
9133949bee8SArchie Cobbs 		}
9143949bee8SArchie Cobbs 		lhdr = ntohl(*mtod(m, u_int32_t *));
9153949bee8SArchie Cobbs 		frag->seq = lhdr & MP_LONG_SEQ_MASK;
9163949bee8SArchie Cobbs 		frag->first = (lhdr & MP_LONG_FIRST_FLAG) != 0;
9173949bee8SArchie Cobbs 		frag->last = (lhdr & MP_LONG_LAST_FLAG) != 0;
9183949bee8SArchie Cobbs 		highSeq = CIRCLEQ_EMPTY(&priv->frags) ?
9193949bee8SArchie Cobbs 		    frag->seq : CIRCLEQ_FIRST(&priv->frags)->seq;
9203949bee8SArchie Cobbs 		diff = MP_LONG_SEQ_DIFF(frag->seq, highSeq);
9213949bee8SArchie Cobbs 		m_adj(m, 4);
9223949bee8SArchie Cobbs 	}
9233949bee8SArchie Cobbs 	frag->data = m;
9243949bee8SArchie Cobbs 	frag->meta = meta;
9253949bee8SArchie Cobbs 
9263949bee8SArchie Cobbs 	/* If the sequence number makes a large jump, empty the queue */
9273949bee8SArchie Cobbs 	if (diff <= -MP_INSANE_SEQ_JUMP || diff >= MP_INSANE_SEQ_JUMP)
9283949bee8SArchie Cobbs 		ng_ppp_free_frags(node);
9293949bee8SArchie Cobbs 
9303949bee8SArchie Cobbs 	/* Optimization: handle a frame that's all in one fragment */
9313949bee8SArchie Cobbs 	if (frag->first && frag->last)
932e149c4e2SArchie Cobbs 		return ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta);
9333949bee8SArchie Cobbs 
9343949bee8SArchie Cobbs 	/* Allocate a new frag struct for the queue */
9353949bee8SArchie Cobbs 	MALLOC(frag, struct ng_ppp_frag *, sizeof(*frag), M_NETGRAPH, M_NOWAIT);
9363949bee8SArchie Cobbs 	if (frag == NULL) {
9373949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
9383949bee8SArchie Cobbs 		return (ENOMEM);
9393949bee8SArchie Cobbs 	}
9403949bee8SArchie Cobbs 	*frag = frag0;
9413949bee8SArchie Cobbs 	meta = NULL;
9423949bee8SArchie Cobbs 	m = NULL;
9433949bee8SArchie Cobbs 
9443949bee8SArchie Cobbs 	/* Add fragment to queue, which is reverse sorted by sequence number */
9453949bee8SArchie Cobbs 	CIRCLEQ_FOREACH(qent, &priv->frags, f_qent) {
946d690a6e7SArchie Cobbs 		diff = MP_SEQ_DIFF(priv, frag->seq, qent->seq);
9473949bee8SArchie Cobbs 		if (diff > 0) {
9483949bee8SArchie Cobbs 			CIRCLEQ_INSERT_BEFORE(&priv->frags, qent, frag, f_qent);
9493949bee8SArchie Cobbs 			break;
9503949bee8SArchie Cobbs 		} else if (diff == 0) {	     /* should never happen! */
9513949bee8SArchie Cobbs 			log(LOG_ERR, "%s: rec'd dup MP fragment\n", node->name);
9523949bee8SArchie Cobbs 			if (linkNum == NG_PPP_BUNDLE_LINKNUM)
9533949bee8SArchie Cobbs 				priv->linkStats[linkNum].dupFragments++;
9543949bee8SArchie Cobbs 			else
9553949bee8SArchie Cobbs 				priv->bundleStats.dupFragments++;
9563949bee8SArchie Cobbs 			NG_FREE_DATA(frag->data, frag->meta);
9573949bee8SArchie Cobbs 			FREE(frag, M_NETGRAPH);
9583949bee8SArchie Cobbs 			return (EINVAL);
9593949bee8SArchie Cobbs 		}
9603949bee8SArchie Cobbs 	}
9613949bee8SArchie Cobbs 	if (qent == NULL)
9623949bee8SArchie Cobbs 		CIRCLEQ_INSERT_TAIL(&priv->frags, frag, f_qent);
9633949bee8SArchie Cobbs 
9643949bee8SArchie Cobbs 	/* Find the first fragment in the possibly newly completed frame */
9653949bee8SArchie Cobbs 	for (nextSeq = frag->seq, qent = frag;
9663949bee8SArchie Cobbs 	    qent != (void *) &priv->frags;
9673949bee8SArchie Cobbs 	    qent = CIRCLEQ_PREV(qent, f_qent)) {
9683949bee8SArchie Cobbs 		if (qent->seq != nextSeq)
9693949bee8SArchie Cobbs 			goto pruneQueue;
9703949bee8SArchie Cobbs 		if (qent->first) {
9713949bee8SArchie Cobbs 			first = qent;
9723949bee8SArchie Cobbs 			break;
9733949bee8SArchie Cobbs 		}
974d690a6e7SArchie Cobbs 		nextSeq = (nextSeq - 1) & MP_SEQ_MASK(priv);
9753949bee8SArchie Cobbs 	}
9763949bee8SArchie Cobbs 
9773949bee8SArchie Cobbs 	/* Find the last fragment in the possibly newly completed frame */
9783949bee8SArchie Cobbs 	for (nextSeq = frag->seq, qent = frag;
9793949bee8SArchie Cobbs 	    qent != (void *) &priv->frags;
9803949bee8SArchie Cobbs 	    qent = CIRCLEQ_NEXT(qent, f_qent)) {
9813949bee8SArchie Cobbs 		if (qent->seq != nextSeq)
9823949bee8SArchie Cobbs 			goto pruneQueue;
9833949bee8SArchie Cobbs 		if (qent->last) {
9843949bee8SArchie Cobbs 			last = qent;
9853949bee8SArchie Cobbs 			break;
9863949bee8SArchie Cobbs 		}
987d690a6e7SArchie Cobbs 		nextSeq = (nextSeq + 1) & MP_SEQ_MASK(priv);
9883949bee8SArchie Cobbs 	}
9893949bee8SArchie Cobbs 
9903949bee8SArchie Cobbs 	/* We have a complete frame, extract it from the queue */
9913949bee8SArchie Cobbs 	for (tail = NULL, qent = first; qent != NULL; qent = qnext) {
9923949bee8SArchie Cobbs 		qnext = CIRCLEQ_PREV(qent, f_qent);
9933949bee8SArchie Cobbs 		CIRCLEQ_REMOVE(&priv->frags, qent, f_qent);
9943949bee8SArchie Cobbs 		if (tail == NULL) {
9953949bee8SArchie Cobbs 			tail = m = qent->data;
9963949bee8SArchie Cobbs 			meta = qent->meta;	/* inherit first frag's meta */
9973949bee8SArchie Cobbs 		} else {
9983949bee8SArchie Cobbs 			m->m_pkthdr.len += qent->data->m_pkthdr.len;
9993949bee8SArchie Cobbs 			tail->m_next = qent->data;
1000d690a6e7SArchie Cobbs 			NG_FREE_META(qent->meta); /* drop other frags' metas */
10013949bee8SArchie Cobbs 		}
10023949bee8SArchie Cobbs 		while (tail->m_next != NULL)
10033949bee8SArchie Cobbs 			tail = tail->m_next;
10043949bee8SArchie Cobbs 		if (qent == last)
10053949bee8SArchie Cobbs 			qnext = NULL;
10063949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
10073949bee8SArchie Cobbs 	}
10083949bee8SArchie Cobbs 
10093949bee8SArchie Cobbs pruneQueue:
10103949bee8SArchie Cobbs 	/* Prune out stale entries in the queue */
10113949bee8SArchie Cobbs 	for (qent = CIRCLEQ_LAST(&priv->frags);
10123949bee8SArchie Cobbs 	    qent != (void *) &priv->frags; qent = qnext) {
1013d690a6e7SArchie Cobbs 		if (MP_SEQ_DIFF(priv, highSeq, qent->seq) <= MP_MAX_SEQ_LINGER)
10143949bee8SArchie Cobbs 			break;
10153949bee8SArchie Cobbs 		qnext = CIRCLEQ_PREV(qent, f_qent);
10163949bee8SArchie Cobbs 		CIRCLEQ_REMOVE(&priv->frags, qent, f_qent);
10173949bee8SArchie Cobbs 		NG_FREE_DATA(qent->data, qent->meta);
10183949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
10193949bee8SArchie Cobbs 	}
10203949bee8SArchie Cobbs 
10213949bee8SArchie Cobbs 	/* Deliver newly completed frame, if any */
1022e149c4e2SArchie Cobbs 	return m ? ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta) : 0;
10233949bee8SArchie Cobbs }
10243949bee8SArchie Cobbs 
10253949bee8SArchie Cobbs /*
10263949bee8SArchie Cobbs  * Deliver a frame out on the bundle, i.e., figure out how to fragment
10273949bee8SArchie Cobbs  * the frame across the individual PPP links and do so.
10283949bee8SArchie Cobbs  */
10293949bee8SArchie Cobbs static int
10303949bee8SArchie Cobbs ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta)
10313949bee8SArchie Cobbs {
10323949bee8SArchie Cobbs 	const priv_p priv = node->private;
10333949bee8SArchie Cobbs 	int distrib[NG_PPP_MAX_LINKS];
10343949bee8SArchie Cobbs 	int firstFragment;
10353949bee8SArchie Cobbs 	int activeLinkNum;
10363949bee8SArchie Cobbs 
10373949bee8SArchie Cobbs 	/* At least one link must be active */
10383949bee8SArchie Cobbs 	if (priv->numActiveLinks == 0) {
10393949bee8SArchie Cobbs 		NG_FREE_DATA(m, meta);
10403949bee8SArchie Cobbs 		return (ENETDOWN);
10413949bee8SArchie Cobbs 	}
10423949bee8SArchie Cobbs 
10433949bee8SArchie Cobbs 	/* Round-robin strategy */
10443949bee8SArchie Cobbs 	if (priv->conf.enableRoundRobin || m->m_pkthdr.len < MP_MIN_FRAG_LEN) {
10453949bee8SArchie Cobbs 		activeLinkNum = priv->lastLink++ % priv->numActiveLinks;
10463949bee8SArchie Cobbs 		bzero(&distrib, priv->numActiveLinks * sizeof(distrib[0]));
10473949bee8SArchie Cobbs 		distrib[activeLinkNum] = m->m_pkthdr.len;
10483949bee8SArchie Cobbs 		goto deliver;
10493949bee8SArchie Cobbs 	}
10503949bee8SArchie Cobbs 
10513949bee8SArchie Cobbs 	/* Strategy when all links are equivalent (optimize the common case) */
10523949bee8SArchie Cobbs 	if (priv->allLinksEqual) {
10533949bee8SArchie Cobbs 		const int fraction = m->m_pkthdr.len / priv->numActiveLinks;
10543949bee8SArchie Cobbs 		int i, remain;
10553949bee8SArchie Cobbs 
10563949bee8SArchie Cobbs 		for (i = 0; i < priv->numActiveLinks; i++)
10573949bee8SArchie Cobbs 			distrib[priv->lastLink++ % priv->numActiveLinks]
10583949bee8SArchie Cobbs 			    = fraction;
10593949bee8SArchie Cobbs 		remain = m->m_pkthdr.len - (fraction * priv->numActiveLinks);
10603949bee8SArchie Cobbs 		while (remain > 0) {
10613949bee8SArchie Cobbs 			distrib[priv->lastLink++ % priv->numActiveLinks]++;
10623949bee8SArchie Cobbs 			remain--;
10633949bee8SArchie Cobbs 		}
10643949bee8SArchie Cobbs 		goto deliver;
10653949bee8SArchie Cobbs 	}
10663949bee8SArchie Cobbs 
10673949bee8SArchie Cobbs 	/* Strategy when all links are not equivalent */
10683949bee8SArchie Cobbs 	ng_ppp_mp_strategy(node, m->m_pkthdr.len, distrib);
10693949bee8SArchie Cobbs 
10703949bee8SArchie Cobbs deliver:
10713949bee8SArchie Cobbs 	/* Update stats */
10723949bee8SArchie Cobbs 	priv->bundleStats.xmitFrames++;
10733949bee8SArchie Cobbs 	priv->bundleStats.xmitOctets += m->m_pkthdr.len;
10743949bee8SArchie Cobbs 
10753949bee8SArchie Cobbs 	/* Send alloted portions of frame out on the link(s) */
10763949bee8SArchie Cobbs 	for (firstFragment = 1, activeLinkNum = priv->numActiveLinks - 1;
10773949bee8SArchie Cobbs 	    activeLinkNum >= 0; activeLinkNum--) {
10783949bee8SArchie Cobbs 		const int linkNum = priv->activeLinks[activeLinkNum];
10793949bee8SArchie Cobbs 
10803949bee8SArchie Cobbs 		/* Deliver fragment(s) out the next link */
10813949bee8SArchie Cobbs 		for ( ; distrib[activeLinkNum] > 0; firstFragment = 0) {
10823949bee8SArchie Cobbs 			int len, lastFragment, error;
10833949bee8SArchie Cobbs 			struct mbuf *m2;
10843949bee8SArchie Cobbs 			meta_p meta2;
10853949bee8SArchie Cobbs 
10863949bee8SArchie Cobbs 			/* Calculate fragment length; don't exceed link MTU */
10873949bee8SArchie Cobbs 			len = distrib[activeLinkNum];
10883949bee8SArchie Cobbs 			if (len > priv->conf.links[linkNum].mru)
10893949bee8SArchie Cobbs 				len = priv->conf.links[linkNum].mru;
10903949bee8SArchie Cobbs 			distrib[activeLinkNum] -= len;
10913949bee8SArchie Cobbs 			lastFragment = (len == m->m_pkthdr.len);
10923949bee8SArchie Cobbs 
10933949bee8SArchie Cobbs 			/* Split off next fragment as "m2" */
10943949bee8SArchie Cobbs 			m2 = m;
10953949bee8SArchie Cobbs 			if (!lastFragment) {
10963949bee8SArchie Cobbs 				struct mbuf *n = m_split(m, len, M_NOWAIT);
10973949bee8SArchie Cobbs 
10983949bee8SArchie Cobbs 				if (n == NULL) {
10993949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
11003949bee8SArchie Cobbs 					return (ENOMEM);
11013949bee8SArchie Cobbs 				}
11023949bee8SArchie Cobbs 				m = n;
11033949bee8SArchie Cobbs 			}
11043949bee8SArchie Cobbs 
11053949bee8SArchie Cobbs 			/* Prepend MP header */
11063949bee8SArchie Cobbs 			if (priv->conf.xmitShortSeq) {
11073949bee8SArchie Cobbs 				u_int16_t shdr;
11083949bee8SArchie Cobbs 
11093949bee8SArchie Cobbs 				shdr = priv->mpSeqOut;
11103949bee8SArchie Cobbs 				priv->mpSeqOut =
11113949bee8SArchie Cobbs 				    (priv->mpSeqOut + 1) % MP_SHORT_SEQ_MASK;
11123949bee8SArchie Cobbs 				if (firstFragment)
11133949bee8SArchie Cobbs 					shdr |= MP_SHORT_FIRST_FLAG;
11143949bee8SArchie Cobbs 				if (lastFragment)
11153949bee8SArchie Cobbs 					shdr |= MP_SHORT_LAST_FLAG;
1116d690a6e7SArchie Cobbs 				shdr = htons(shdr);
1117d690a6e7SArchie Cobbs 				m2 = ng_ppp_prepend(m2, &shdr, 2);
11183949bee8SArchie Cobbs 			} else {
11193949bee8SArchie Cobbs 				u_int32_t lhdr;
11203949bee8SArchie Cobbs 
11213949bee8SArchie Cobbs 				lhdr = priv->mpSeqOut;
11223949bee8SArchie Cobbs 				priv->mpSeqOut =
11233949bee8SArchie Cobbs 				    (priv->mpSeqOut + 1) % MP_LONG_SEQ_MASK;
11243949bee8SArchie Cobbs 				if (firstFragment)
11253949bee8SArchie Cobbs 					lhdr |= MP_LONG_FIRST_FLAG;
11263949bee8SArchie Cobbs 				if (lastFragment)
11273949bee8SArchie Cobbs 					lhdr |= MP_LONG_LAST_FLAG;
1128d690a6e7SArchie Cobbs 				lhdr = htonl(lhdr);
1129d690a6e7SArchie Cobbs 				m2 = ng_ppp_prepend(m2, &lhdr, 4);
11303949bee8SArchie Cobbs 			}
11313949bee8SArchie Cobbs 			if (m2 == NULL) {
11323949bee8SArchie Cobbs 				if (!lastFragment)
11333949bee8SArchie Cobbs 					m_freem(m);
11343949bee8SArchie Cobbs 				NG_FREE_META(meta);
11353949bee8SArchie Cobbs 				return (ENOBUFS);
11363949bee8SArchie Cobbs 			}
11373949bee8SArchie Cobbs 
11383949bee8SArchie Cobbs 			/* Copy the meta information, if any */
11393949bee8SArchie Cobbs 			if (meta != NULL && !lastFragment) {
11403949bee8SArchie Cobbs 				MALLOC(meta2, meta_p,
11413949bee8SArchie Cobbs 				    meta->used_len, M_NETGRAPH, M_NOWAIT);
11423949bee8SArchie Cobbs 				if (meta2 == NULL) {
11433949bee8SArchie Cobbs 					m_freem(m2);
11443949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
11453949bee8SArchie Cobbs 					return (ENOMEM);
11463949bee8SArchie Cobbs 				}
11473949bee8SArchie Cobbs 				meta2->allocated_len = meta->used_len;
11483949bee8SArchie Cobbs 				bcopy(meta, meta2, meta->used_len);
11493949bee8SArchie Cobbs 			} else
11503949bee8SArchie Cobbs 				meta2 = meta;
11513949bee8SArchie Cobbs 
11523949bee8SArchie Cobbs 			/* Send fragment */
115362838faeSArchie Cobbs 			error = ng_ppp_output(node, 0,
115462838faeSArchie Cobbs 			    PROT_MP, linkNum, m2, meta2);
11553949bee8SArchie Cobbs 			if (error != 0) {
11563949bee8SArchie Cobbs 				if (!lastFragment)
11573949bee8SArchie Cobbs 					NG_FREE_DATA(m, meta);
11583949bee8SArchie Cobbs 				return (error);
11593949bee8SArchie Cobbs 			}
11603949bee8SArchie Cobbs 		}
11613949bee8SArchie Cobbs 	}
11623949bee8SArchie Cobbs 
11633949bee8SArchie Cobbs 	/* Done */
11643949bee8SArchie Cobbs 	return (0);
11653949bee8SArchie Cobbs }
11663949bee8SArchie Cobbs 
11673949bee8SArchie Cobbs /*
11683949bee8SArchie Cobbs  * Computing the optimal fragmentation
11693949bee8SArchie Cobbs  * -----------------------------------
11703949bee8SArchie Cobbs  *
11713949bee8SArchie Cobbs  * This routine tries to compute the optimal fragmentation pattern based
11723949bee8SArchie Cobbs  * on each link's latency, bandwidth, and calculated additional latency.
11733949bee8SArchie Cobbs  * The latter quantity is the additional latency caused by previously
11743949bee8SArchie Cobbs  * written data that has not been transmitted yet.
11753949bee8SArchie Cobbs  *
11763949bee8SArchie Cobbs  * This algorithm is only useful when not all of the links have the
11773949bee8SArchie Cobbs  * same latency and bandwidth values.
11783949bee8SArchie Cobbs  *
11793949bee8SArchie Cobbs  * The essential idea is to make the last bit of each fragment of the
11803949bee8SArchie Cobbs  * frame arrive at the opposite end at the exact same time. This greedy
11813949bee8SArchie Cobbs  * algorithm is optimal, in that no other scheduling could result in any
11823949bee8SArchie Cobbs  * packet arriving any sooner unless packets are delivered out of order.
11833949bee8SArchie Cobbs  *
11843949bee8SArchie Cobbs  * Suppose link i has bandwidth b_i (in tens of bytes per milisecond) and
11853949bee8SArchie Cobbs  * latency l_i (in miliseconds). Consider the function function f_i(t)
11863949bee8SArchie Cobbs  * which is equal to the number of bytes that will have arrived at
11873949bee8SArchie Cobbs  * the peer after t miliseconds if we start writing continuously at
11883949bee8SArchie Cobbs  * time t = 0. Then f_i(t) = b_i * (t - l_i) = ((b_i * t) - (l_i * b_i).
11893949bee8SArchie Cobbs  * That is, f_i(t) is a line with slope b_i and y-intersect -(l_i * b_i).
11903949bee8SArchie Cobbs  * Note that the y-intersect is always <= zero because latency can't be
11913949bee8SArchie Cobbs  * negative.  Note also that really the function is f_i(t) except when
11923949bee8SArchie Cobbs  * f_i(t) is negative, in which case the function is zero.  To take
11933949bee8SArchie Cobbs  * care of this, let Q_i(t) = { if (f_i(t) > 0) return 1; else return 0; }.
11943949bee8SArchie Cobbs  * So the actual number of bytes that will have arrived at the peer after
11953949bee8SArchie Cobbs  * t miliseconds is f_i(t) * Q_i(t).
11963949bee8SArchie Cobbs  *
11973949bee8SArchie Cobbs  * At any given time, each link has some additional latency a_i >= 0
11983949bee8SArchie Cobbs  * due to previously written fragment(s) which are still in the queue.
11993949bee8SArchie Cobbs  * This value is easily computed from the time since last transmission,
12003949bee8SArchie Cobbs  * the previous latency value, the number of bytes written, and the
12013949bee8SArchie Cobbs  * link's bandwidth.
12023949bee8SArchie Cobbs  *
12033949bee8SArchie Cobbs  * Assume that l_i includes any a_i already, and that the links are
12043949bee8SArchie Cobbs  * sorted by latency, so that l_i <= l_{i+1}.
12053949bee8SArchie Cobbs  *
12063949bee8SArchie Cobbs  * Let N be the total number of bytes in the current frame we are sending.
12073949bee8SArchie Cobbs  *
12083949bee8SArchie Cobbs  * Suppose we were to start writing bytes at time t = 0 on all links
12093949bee8SArchie Cobbs  * simultaneously, which is the most we can possibly do.  Then let
12103949bee8SArchie Cobbs  * F(t) be equal to the total number of bytes received by the peer
12113949bee8SArchie Cobbs  * after t miliseconds. Then F(t) = Sum_i (f_i(t) * Q_i(t)).
12123949bee8SArchie Cobbs  *
12133949bee8SArchie Cobbs  * Our goal is simply this: fragment the frame across the links such
12143949bee8SArchie Cobbs  * that the peer is able to reconstruct the completed frame as soon as
12153949bee8SArchie Cobbs  * possible, i.e., at the least possible value of t. Call this value t_0.
12163949bee8SArchie Cobbs  *
12173949bee8SArchie Cobbs  * Then it follows that F(t_0) = N. Our strategy is first to find the value
12183949bee8SArchie Cobbs  * of t_0, and then deduce how many bytes to write to each link.
12193949bee8SArchie Cobbs  *
12203949bee8SArchie Cobbs  * Rewriting F(t_0):
12213949bee8SArchie Cobbs  *
12223949bee8SArchie Cobbs  *   t_0 = ( N + Sum_i ( l_i * b_i * Q_i(t_0) ) ) / Sum_i ( b_i * Q_i(t_0) )
12233949bee8SArchie Cobbs  *
12243949bee8SArchie Cobbs  * Now, we note that Q_i(t) is constant for l_i <= t <= l_{i+1}. t_0 will
12253949bee8SArchie Cobbs  * lie in one of these ranges.  To find it, we just need to find the i such
12263949bee8SArchie Cobbs  * that F(l_i) <= N <= F(l_{i+1}).  Then we compute all the constant values
12273949bee8SArchie Cobbs  * for Q_i() in this range, plug in the remaining values, solving for t_0.
12283949bee8SArchie Cobbs  *
12293949bee8SArchie Cobbs  * Once t_0 is known, then the number of bytes to send on link i is
12303949bee8SArchie Cobbs  * just f_i(t_0) * Q_i(t_0).
12313949bee8SArchie Cobbs  *
12323949bee8SArchie Cobbs  * In other words, we start allocating bytes to the links one at a time.
12333949bee8SArchie Cobbs  * We keep adding links until the frame is completely sent.  Some links
12343949bee8SArchie Cobbs  * may not get any bytes because their latency is too high.
12353949bee8SArchie Cobbs  *
12363949bee8SArchie Cobbs  * Is all this work really worth the trouble?  Depends on the situation.
12373949bee8SArchie Cobbs  * The bigger the ratio of computer speed to link speed, and the more
12383949bee8SArchie Cobbs  * important total bundle latency is (e.g., for interactive response time),
12393949bee8SArchie Cobbs  * the more it's worth it.  There is however the cost of calling this
12403949bee8SArchie Cobbs  * function for every frame.  The running time is O(n^2) where n is the
12413949bee8SArchie Cobbs  * number of links that receive a non-zero number of bytes.
12423949bee8SArchie Cobbs  *
12433949bee8SArchie Cobbs  * Since latency is measured in miliseconds, the "resolution" of this
12443949bee8SArchie Cobbs  * algorithm is one milisecond.
12453949bee8SArchie Cobbs  *
12463949bee8SArchie Cobbs  * To avoid this algorithm altogether, configure all links to have the
12473949bee8SArchie Cobbs  * same latency and bandwidth.
12483949bee8SArchie Cobbs  */
12493949bee8SArchie Cobbs static void
12503949bee8SArchie Cobbs ng_ppp_mp_strategy(node_p node, int len, int *distrib)
12513949bee8SArchie Cobbs {
12523949bee8SArchie Cobbs 	const priv_p priv = node->private;
12533949bee8SArchie Cobbs 	int latency[NG_PPP_MAX_LINKS];
12543949bee8SArchie Cobbs 	int sortByLatency[NG_PPP_MAX_LINKS];
12553949bee8SArchie Cobbs 	int activeLinkNum, linkNum;
12563949bee8SArchie Cobbs 	int t0, total, topSum, botSum;
12573949bee8SArchie Cobbs 	struct timeval now;
12583949bee8SArchie Cobbs 	int i, numFragments;
12593949bee8SArchie Cobbs 
12603949bee8SArchie Cobbs 	/* If only one link, this gets real easy */
12613949bee8SArchie Cobbs 	if (priv->numActiveLinks == 1) {
12623949bee8SArchie Cobbs 		distrib[0] = len;
12633949bee8SArchie Cobbs 		return;
12643949bee8SArchie Cobbs 	}
12653949bee8SArchie Cobbs 
12663949bee8SArchie Cobbs 	/* Get current time */
1267fb1fc8abSArchie Cobbs 	microtime(&now);
12683949bee8SArchie Cobbs 
12693949bee8SArchie Cobbs 	/* Compute latencies for each link at this point in time */
12703949bee8SArchie Cobbs 	for (activeLinkNum = 0;
12713949bee8SArchie Cobbs 	    activeLinkNum < priv->numActiveLinks; activeLinkNum++) {
12723949bee8SArchie Cobbs 		struct timeval diff;
12733949bee8SArchie Cobbs 		int xmitBytes;
12743949bee8SArchie Cobbs 
12753949bee8SArchie Cobbs 		/* Start with base latency value */
12763949bee8SArchie Cobbs 		linkNum = priv->activeLinks[activeLinkNum];
12773949bee8SArchie Cobbs 		latency[activeLinkNum] = priv->conf.links[linkNum].latency;
12783949bee8SArchie Cobbs 		sortByLatency[activeLinkNum] = activeLinkNum;	/* see below */
12793949bee8SArchie Cobbs 
12803949bee8SArchie Cobbs 		/* Any additional latency? */
12813949bee8SArchie Cobbs 		if (priv->qstat[activeLinkNum].bytesInQueue == 0)
12823949bee8SArchie Cobbs 			continue;
12833949bee8SArchie Cobbs 
12843949bee8SArchie Cobbs 		/* Compute time delta since last write */
12853949bee8SArchie Cobbs 		diff = now;
12863949bee8SArchie Cobbs 		timevalsub(&diff, &priv->qstat[activeLinkNum].lastWrite);
12873949bee8SArchie Cobbs 		if (now.tv_sec < 0 || diff.tv_sec >= 10) {	/* sanity */
12883949bee8SArchie Cobbs 			priv->qstat[activeLinkNum].bytesInQueue = 0;
12893949bee8SArchie Cobbs 			continue;
12903949bee8SArchie Cobbs 		}
12913949bee8SArchie Cobbs 
12923949bee8SArchie Cobbs 		/* How many bytes could have transmitted since last write? */
12933949bee8SArchie Cobbs 		xmitBytes = priv->conf.links[linkNum].bandwidth * diff.tv_sec
12943949bee8SArchie Cobbs 		    + (priv->conf.links[linkNum].bandwidth
12953949bee8SArchie Cobbs 			* (diff.tv_usec / 1000)) / 100;
12963949bee8SArchie Cobbs 		priv->qstat[activeLinkNum].bytesInQueue -= xmitBytes;
12973949bee8SArchie Cobbs 		if (priv->qstat[activeLinkNum].bytesInQueue < 0)
12983949bee8SArchie Cobbs 			priv->qstat[activeLinkNum].bytesInQueue = 0;
12993949bee8SArchie Cobbs 		else
13003949bee8SArchie Cobbs 			latency[activeLinkNum] +=
13013949bee8SArchie Cobbs 			    (100 * priv->qstat[activeLinkNum].bytesInQueue)
13023949bee8SArchie Cobbs 				/ priv->conf.links[linkNum].bandwidth;
13033949bee8SArchie Cobbs 	}
13043949bee8SArchie Cobbs 
13053949bee8SArchie Cobbs 	/* Sort links by latency */
13063949bee8SArchie Cobbs 	compareLatencies = latency;
13073949bee8SArchie Cobbs 	qsort(sortByLatency,
13083949bee8SArchie Cobbs 	    priv->numActiveLinks, sizeof(*sortByLatency), ng_ppp_intcmp);
13093949bee8SArchie Cobbs 	compareLatencies = NULL;
13103949bee8SArchie Cobbs 
13113949bee8SArchie Cobbs 	/* Find the interval we need (add links in sortByLatency[] order) */
13123949bee8SArchie Cobbs 	for (numFragments = 1;
13133949bee8SArchie Cobbs 	    numFragments < priv->numActiveLinks; numFragments++) {
13143949bee8SArchie Cobbs 		for (total = i = 0; i < numFragments; i++) {
13153949bee8SArchie Cobbs 			int flowTime;
13163949bee8SArchie Cobbs 
13173949bee8SArchie Cobbs 			flowTime = latency[sortByLatency[numFragments]]
13183949bee8SArchie Cobbs 			    - latency[sortByLatency[i]];
13193949bee8SArchie Cobbs 			total += ((flowTime * priv->conf.links[
13203949bee8SArchie Cobbs 			    priv->activeLinks[sortByLatency[i]]].bandwidth)
13213949bee8SArchie Cobbs 			    	+ 99) / 100;
13223949bee8SArchie Cobbs 		}
13233949bee8SArchie Cobbs 		if (total >= len)
13243949bee8SArchie Cobbs 			break;
13253949bee8SArchie Cobbs 	}
13263949bee8SArchie Cobbs 
13273949bee8SArchie Cobbs 	/* Solve for t_0 in that interval */
13283949bee8SArchie Cobbs 	for (topSum = botSum = i = 0; i < numFragments; i++) {
13293949bee8SArchie Cobbs 		int bw = priv->conf.links[
13303949bee8SArchie Cobbs 		    priv->activeLinks[sortByLatency[i]]].bandwidth;
13313949bee8SArchie Cobbs 
13323949bee8SArchie Cobbs 		topSum += latency[sortByLatency[i]] * bw;	/* / 100 */
13333949bee8SArchie Cobbs 		botSum += bw;					/* / 100 */
13343949bee8SArchie Cobbs 	}
13353949bee8SArchie Cobbs 	t0 = ((len * 100) + topSum + botSum / 2) / botSum;
13363949bee8SArchie Cobbs 
13373949bee8SArchie Cobbs 	/* Compute f_i(t_0) all i */
13383949bee8SArchie Cobbs 	bzero(distrib, priv->numActiveLinks * sizeof(*distrib));
13393949bee8SArchie Cobbs 	for (total = i = 0; i < numFragments; i++) {
13403949bee8SArchie Cobbs 		int bw = priv->conf.links[
13413949bee8SArchie Cobbs 		    priv->activeLinks[sortByLatency[i]]].bandwidth;
13423949bee8SArchie Cobbs 
13433949bee8SArchie Cobbs 		distrib[sortByLatency[i]] =
13443949bee8SArchie Cobbs 		    (bw * (t0 - latency[sortByLatency[i]]) + 50) / 100;
13453949bee8SArchie Cobbs 		total += distrib[sortByLatency[i]];
13463949bee8SArchie Cobbs 	}
13473949bee8SArchie Cobbs 
1348fb1fc8abSArchie Cobbs 	/* Deal with any rounding error */
1349fb1fc8abSArchie Cobbs 	if (total < len) {
13503949bee8SArchie Cobbs 		int fast = 0;
13513949bee8SArchie Cobbs 
1352fb1fc8abSArchie Cobbs 		/* Find the fastest link */
13533949bee8SArchie Cobbs 		for (i = 1; i < numFragments; i++) {
13543949bee8SArchie Cobbs 			if (priv->conf.links[
13553949bee8SArchie Cobbs 			      priv->activeLinks[sortByLatency[i]]].bandwidth >
13563949bee8SArchie Cobbs 			    priv->conf.links[
13573949bee8SArchie Cobbs 			      priv->activeLinks[sortByLatency[fast]]].bandwidth)
13583949bee8SArchie Cobbs 				fast = i;
13593949bee8SArchie Cobbs 		}
13603949bee8SArchie Cobbs 		distrib[sortByLatency[fast]] += len - total;
1361fb1fc8abSArchie Cobbs 	} else while (total > len) {
1362fb1fc8abSArchie Cobbs 		int delta, slow = 0;
13633949bee8SArchie Cobbs 
1364fb1fc8abSArchie Cobbs 		/* Find the slowest link that still has bytes to remove */
1365fb1fc8abSArchie Cobbs 		for (i = 1; i < numFragments; i++) {
1366fb1fc8abSArchie Cobbs 			if (distrib[sortByLatency[slow]] == 0
1367fb1fc8abSArchie Cobbs 			  || (distrib[sortByLatency[i]] > 0
1368fb1fc8abSArchie Cobbs 			    && priv->conf.links[priv->activeLinks[
1369fb1fc8abSArchie Cobbs 					sortByLatency[i]]].bandwidth <
1370fb1fc8abSArchie Cobbs 			      priv->conf.links[priv->activeLinks[
1371fb1fc8abSArchie Cobbs 					sortByLatency[slow]]].bandwidth))
1372fb1fc8abSArchie Cobbs 				slow = i;
1373fb1fc8abSArchie Cobbs 		}
1374fb1fc8abSArchie Cobbs 		delta = total - len;
1375fb1fc8abSArchie Cobbs 		if (delta > distrib[sortByLatency[slow]])
1376fb1fc8abSArchie Cobbs 			delta = distrib[sortByLatency[slow]];
1377fb1fc8abSArchie Cobbs 		distrib[sortByLatency[slow]] -= delta;
1378fb1fc8abSArchie Cobbs 		total -= delta;
13793949bee8SArchie Cobbs 	}
13803949bee8SArchie Cobbs }
13813949bee8SArchie Cobbs 
13823949bee8SArchie Cobbs /*
13833949bee8SArchie Cobbs  * Compare two integers
13843949bee8SArchie Cobbs  */
13853949bee8SArchie Cobbs static int
13863949bee8SArchie Cobbs ng_ppp_intcmp(const void *v1, const void *v2)
13873949bee8SArchie Cobbs {
13883949bee8SArchie Cobbs 	const int index1 = *((const int *) v1);
13893949bee8SArchie Cobbs 	const int index2 = *((const int *) v2);
13903949bee8SArchie Cobbs 
13913949bee8SArchie Cobbs 	return compareLatencies[index1] - compareLatencies[index2];
13923949bee8SArchie Cobbs }
13933949bee8SArchie Cobbs 
13943949bee8SArchie Cobbs /*
13953949bee8SArchie Cobbs  * Prepend a possibly compressed PPP protocol number in front of a frame
13963949bee8SArchie Cobbs  */
13973949bee8SArchie Cobbs static struct mbuf *
13983949bee8SArchie Cobbs ng_ppp_addproto(struct mbuf *m, int proto, int compOK)
13993949bee8SArchie Cobbs {
1400d690a6e7SArchie Cobbs 	if (compOK && PROT_COMPRESSABLE(proto)) {
1401d690a6e7SArchie Cobbs 		u_char pbyte = (u_char)proto;
14023949bee8SArchie Cobbs 
1403d690a6e7SArchie Cobbs 		return ng_ppp_prepend(m, &pbyte, 1);
1404d690a6e7SArchie Cobbs 	} else {
1405d690a6e7SArchie Cobbs 		u_int16_t pword = htons((u_int16_t)proto);
1406d690a6e7SArchie Cobbs 
1407d690a6e7SArchie Cobbs 		return ng_ppp_prepend(m, &pword, 2);
1408d690a6e7SArchie Cobbs 	}
1409d690a6e7SArchie Cobbs }
1410d690a6e7SArchie Cobbs 
1411d690a6e7SArchie Cobbs /*
1412d690a6e7SArchie Cobbs  * Prepend some bytes to an mbuf
1413d690a6e7SArchie Cobbs  */
1414d690a6e7SArchie Cobbs static struct mbuf *
1415d690a6e7SArchie Cobbs ng_ppp_prepend(struct mbuf *m, const void *buf, int len)
1416d690a6e7SArchie Cobbs {
1417d690a6e7SArchie Cobbs 	M_PREPEND(m, len, M_NOWAIT);
1418d690a6e7SArchie Cobbs 	if (m == NULL || (m->m_len < len && (m = m_pullup(m, len)) == NULL))
14194cf49a43SJulian Elischer 		return (NULL);
1420d690a6e7SArchie Cobbs 	bcopy(buf, mtod(m, u_char *), len);
14213949bee8SArchie Cobbs 	return (m);
14223949bee8SArchie Cobbs }
14233949bee8SArchie Cobbs 
14243949bee8SArchie Cobbs /*
14253949bee8SArchie Cobbs  * Update private information that is derived from other private information
14263949bee8SArchie Cobbs  */
14273949bee8SArchie Cobbs static void
14283949bee8SArchie Cobbs ng_ppp_update(node_p node, int newConf)
14293949bee8SArchie Cobbs {
14303949bee8SArchie Cobbs 	const priv_p priv = node->private;
14313949bee8SArchie Cobbs 	int i;
14323949bee8SArchie Cobbs 
14333949bee8SArchie Cobbs 	/* Update active status for VJ Compression */
14343949bee8SArchie Cobbs 	priv->vjCompHooked = priv->hooks[HOOK_INDEX_VJC_IP] != NULL
14353949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_COMP] != NULL
14363949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_UNCOMP] != NULL
14373949bee8SArchie Cobbs 	    && priv->hooks[HOOK_INDEX_VJC_VJIP] != NULL;
14383949bee8SArchie Cobbs 
14393949bee8SArchie Cobbs 	/* Increase latency for each link an amount equal to one MP header */
14403949bee8SArchie Cobbs 	if (newConf) {
14413949bee8SArchie Cobbs 		for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
14423949bee8SArchie Cobbs 			int hdrBytes;
14433949bee8SArchie Cobbs 
1444d690a6e7SArchie Cobbs 			hdrBytes = (priv->conf.links[i].enableACFComp ? 0 : 2)
1445d690a6e7SArchie Cobbs 			    + (priv->conf.links[i].enableProtoComp ? 1 : 2)
14463949bee8SArchie Cobbs 			    + (priv->conf.xmitShortSeq ? 2 : 4);
14473949bee8SArchie Cobbs 			priv->conf.links[i].latency +=
14483949bee8SArchie Cobbs 			    ((hdrBytes * priv->conf.links[i].bandwidth) + 50)
14493949bee8SArchie Cobbs 				/ 100;
14503949bee8SArchie Cobbs 		}
14513949bee8SArchie Cobbs 	}
14523949bee8SArchie Cobbs 
14533949bee8SArchie Cobbs 	/* Update list of active links */
14543949bee8SArchie Cobbs 	bzero(&priv->activeLinks, sizeof(priv->activeLinks));
14553949bee8SArchie Cobbs 	priv->numActiveLinks = 0;
14563949bee8SArchie Cobbs 	priv->allLinksEqual = 1;
14573949bee8SArchie Cobbs 	for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
14580e11d0f3SArchie Cobbs 		struct ng_ppp_link_config *const lc = &priv->conf.links[i];
14590e11d0f3SArchie Cobbs 
14600e11d0f3SArchie Cobbs 		if (lc->enableLink && priv->links[i] != NULL) {
14613949bee8SArchie Cobbs 			priv->activeLinks[priv->numActiveLinks++] = i;
14620e11d0f3SArchie Cobbs 			if (lc->latency !=
14630e11d0f3SArchie Cobbs 			      priv->conf.links[priv->activeLinks[0]].latency
14640e11d0f3SArchie Cobbs 			  || lc->bandwidth !=
14650e11d0f3SArchie Cobbs 			      priv->conf.links[priv->activeLinks[0]].bandwidth)
14663949bee8SArchie Cobbs 				priv->allLinksEqual = 0;
14673949bee8SArchie Cobbs 		}
14683949bee8SArchie Cobbs 	}
14693949bee8SArchie Cobbs 
14700e11d0f3SArchie Cobbs 	/* Reset MP state if multi-link is no longer active */
14713949bee8SArchie Cobbs 	if (!priv->conf.enableMultilink || priv->numActiveLinks == 0) {
14723949bee8SArchie Cobbs 		ng_ppp_free_frags(node);
14733949bee8SArchie Cobbs 		priv->mpSeqOut = MP_INITIAL_SEQ;
14743949bee8SArchie Cobbs 		bzero(&priv->qstat, sizeof(priv->qstat));
14753949bee8SArchie Cobbs 	}
14763949bee8SArchie Cobbs }
14773949bee8SArchie Cobbs 
14783949bee8SArchie Cobbs /*
14793949bee8SArchie Cobbs  * Determine if a new configuration would represent a valid change
14803949bee8SArchie Cobbs  * from the current configuration and link activity status.
14813949bee8SArchie Cobbs  */
14823949bee8SArchie Cobbs static int
14833949bee8SArchie Cobbs ng_ppp_config_valid(node_p node, const struct ng_ppp_node_config *newConf)
14843949bee8SArchie Cobbs {
14853949bee8SArchie Cobbs 	const priv_p priv = node->private;
14863949bee8SArchie Cobbs 	int i, newNumLinksActive;
14873949bee8SArchie Cobbs 
14883949bee8SArchie Cobbs 	/* Check per-link config and count how many links would be active */
14893949bee8SArchie Cobbs 	for (newNumLinksActive = i = 0; i < NG_PPP_MAX_LINKS; i++) {
1490e149c4e2SArchie Cobbs 		if (newConf->links[i].enableLink && priv->links[i] != NULL)
1491e149c4e2SArchie Cobbs 			newNumLinksActive++;
1492e149c4e2SArchie Cobbs 		if (!newConf->links[i].enableLink)
1493e149c4e2SArchie Cobbs 			continue;
14943949bee8SArchie Cobbs 		if (newConf->links[i].mru < MP_MIN_LINK_MRU)
14953949bee8SArchie Cobbs 			return (0);
14963949bee8SArchie Cobbs 		if (newConf->links[i].bandwidth == 0)
14973949bee8SArchie Cobbs 			return (0);
14983949bee8SArchie Cobbs 		if (newConf->links[i].bandwidth > NG_PPP_MAX_BANDWIDTH)
14993949bee8SArchie Cobbs 			return (0);
15003949bee8SArchie Cobbs 		if (newConf->links[i].latency > NG_PPP_MAX_LATENCY)
15013949bee8SArchie Cobbs 			return (0);
15023949bee8SArchie Cobbs 	}
15033949bee8SArchie Cobbs 
15043949bee8SArchie Cobbs 	/* Check bundle parameters */
1505e149c4e2SArchie Cobbs 	if (newConf->enableMultilink && newConf->mrru < MP_MIN_MRRU)
15063949bee8SArchie Cobbs 		return (0);
15073949bee8SArchie Cobbs 
15083949bee8SArchie Cobbs 	/* Disallow changes to multi-link configuration while MP is active */
15093949bee8SArchie Cobbs 	if (priv->numActiveLinks > 0 && newNumLinksActive > 0) {
15103949bee8SArchie Cobbs 		if (!priv->conf.enableMultilink != !newConf->enableMultilink
15113949bee8SArchie Cobbs 		    || !priv->conf.xmitShortSeq != !newConf->xmitShortSeq
15123949bee8SArchie Cobbs 		    || !priv->conf.recvShortSeq != !newConf->recvShortSeq)
15133949bee8SArchie Cobbs 			return (0);
15143949bee8SArchie Cobbs 	}
15153949bee8SArchie Cobbs 
1516e149c4e2SArchie Cobbs 	/* At most one link can be active unless multi-link is enabled */
1517e149c4e2SArchie Cobbs 	if (!newConf->enableMultilink && newNumLinksActive > 1)
1518e149c4e2SArchie Cobbs 		return (0);
1519e149c4e2SArchie Cobbs 
1520e149c4e2SArchie Cobbs 	/* Configuration change would be valid */
15213949bee8SArchie Cobbs 	return (1);
15223949bee8SArchie Cobbs }
15233949bee8SArchie Cobbs 
15243949bee8SArchie Cobbs /*
15253949bee8SArchie Cobbs  * Free all entries in the fragment queue
15263949bee8SArchie Cobbs  */
15273949bee8SArchie Cobbs static void
15283949bee8SArchie Cobbs ng_ppp_free_frags(node_p node)
15293949bee8SArchie Cobbs {
15303949bee8SArchie Cobbs 	const priv_p priv = node->private;
15313949bee8SArchie Cobbs 	struct ng_ppp_frag *qent, *next;
15323949bee8SArchie Cobbs 
15333949bee8SArchie Cobbs 	for (qent = CIRCLEQ_FIRST(&priv->frags);
15343949bee8SArchie Cobbs 	    qent != (void *) &priv->frags; qent = next) {
15353949bee8SArchie Cobbs 		next = CIRCLEQ_NEXT(qent, f_qent);
15363949bee8SArchie Cobbs 		NG_FREE_DATA(qent->data, qent->meta);
15373949bee8SArchie Cobbs 		FREE(qent, M_NETGRAPH);
15383949bee8SArchie Cobbs 	}
15393949bee8SArchie Cobbs 	CIRCLEQ_INIT(&priv->frags);
15404cf49a43SJulian Elischer }
15414cf49a43SJulian Elischer 
1542