ng_ppp.c revision 98063
152419Sjulian
252419Sjulian/*
352419Sjulian * ng_ppp.c
452419Sjulian *
559882Sarchie * Copyright (c) 1996-2000 Whistle Communications, Inc.
652419Sjulian * All rights reserved.
752419Sjulian *
852419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
952419Sjulian * redistribution of this software, in source or object code forms, with or
1052419Sjulian * without modifications are expressly permitted by Whistle Communications;
1152419Sjulian * provided, however, that:
1252419Sjulian * 1. Any and all reproductions of the source or object code must include the
1352419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1452419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1552419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1652419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1752419Sjulian *    such appears in the above copyright notice or in the software.
1852419Sjulian *
1952419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2052419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2152419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2252419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2352419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2452419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2552419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2652419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2752419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2852419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2952419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3052419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3152419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3252419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3352419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3452419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3552419Sjulian * OF SUCH DAMAGE.
3652419Sjulian *
3766775Sarchie * Author: Archie Cobbs <archie@freebsd.org>
3852419Sjulian *
3952419Sjulian * $FreeBSD: head/sys/netgraph/ng_ppp.c 98063 2002-06-09 07:28:35Z julian $
4052752Sjulian * $Whistle: ng_ppp.c,v 1.24 1999/11/01 09:24:52 julian Exp $
4152419Sjulian */
4252419Sjulian
4352419Sjulian/*
4452639Sarchie * PPP node type.
4552419Sjulian */
4652419Sjulian
4752419Sjulian#include <sys/param.h>
4852419Sjulian#include <sys/systm.h>
4952419Sjulian#include <sys/kernel.h>
5059882Sarchie#include <sys/time.h>
5152419Sjulian#include <sys/mbuf.h>
5252419Sjulian#include <sys/malloc.h>
5352419Sjulian#include <sys/errno.h>
5452843Sphk#include <sys/ctype.h>
5552419Sjulian
5659882Sarchie#include <machine/limits.h>
5759882Sarchie
5852419Sjulian#include <netgraph/ng_message.h>
5952419Sjulian#include <netgraph/netgraph.h>
6053913Sarchie#include <netgraph/ng_parse.h>
6152419Sjulian#include <netgraph/ng_ppp.h>
6252639Sarchie#include <netgraph/ng_vjc.h>
6352419Sjulian
6470870Sjulian#ifdef NG_SEPARATE_MALLOC
6570870SjulianMALLOC_DEFINE(M_NETGRAPH_PPP, "netgraph_ppp", "netgraph ppp node");
6670870Sjulian#else
6770870Sjulian#define M_NETGRAPH_PPP M_NETGRAPH
6870870Sjulian#endif
6970870Sjulian
7052419Sjulian#define PROT_VALID(p)		(((p) & 0x0101) == 0x0001)
7152816Sarchie#define PROT_COMPRESSABLE(p)	(((p) & 0xff00) == 0x0000)
7252419Sjulian
7352639Sarchie/* Some PPP protocol numbers we're interested in */
7452639Sarchie#define PROT_APPLETALK		0x0029
7552639Sarchie#define PROT_COMPD		0x00fd
7652639Sarchie#define PROT_CRYPTD		0x0053
7752639Sarchie#define PROT_IP			0x0021
7859882Sarchie#define PROT_IPV6		0x0057
7952816Sarchie#define PROT_IPX		0x002b
8053075Sarchie#define PROT_LCP		0xc021
8152639Sarchie#define PROT_MP			0x003d
8252639Sarchie#define PROT_VJCOMP		0x002d
8352639Sarchie#define PROT_VJUNCOMP		0x002f
8452419Sjulian
8552639Sarchie/* Multilink PPP definitions */
8652639Sarchie#define MP_MIN_MRRU		1500		/* per RFC 1990 */
8752639Sarchie#define MP_INITIAL_SEQ		0		/* per RFC 1990 */
8852639Sarchie#define MP_MIN_LINK_MRU		32
8952639Sarchie
9052639Sarchie#define MP_SHORT_SEQ_MASK	0x00000fff	/* short seq # mask */
9152639Sarchie#define MP_SHORT_SEQ_HIBIT	0x00000800	/* short seq # high bit */
9252639Sarchie#define MP_SHORT_FIRST_FLAG	0x00008000	/* first fragment in frame */
9352639Sarchie#define MP_SHORT_LAST_FLAG	0x00004000	/* last fragment in frame */
9452639Sarchie
9552639Sarchie#define MP_LONG_SEQ_MASK	0x00ffffff	/* long seq # mask */
9652639Sarchie#define MP_LONG_SEQ_HIBIT	0x00800000	/* long seq # high bit */
9752639Sarchie#define MP_LONG_FIRST_FLAG	0x80000000	/* first fragment in frame */
9852639Sarchie#define MP_LONG_LAST_FLAG	0x40000000	/* last fragment in frame */
9952639Sarchie
10066775Sarchie#define MP_NOSEQ		0x7fffffff	/* impossible sequence number */
10159882Sarchie
10252639Sarchie/* Sign extension of MP sequence numbers */
10366775Sarchie#define MP_SHORT_EXTEND(s)	(((s) & MP_SHORT_SEQ_HIBIT) ?		\
10466775Sarchie				    ((s) | ~MP_SHORT_SEQ_MASK)		\
10566775Sarchie				    : ((s) & MP_SHORT_SEQ_MASK))
10666775Sarchie#define MP_LONG_EXTEND(s)	(((s) & MP_LONG_SEQ_HIBIT) ?		\
10766775Sarchie				    ((s) | ~MP_LONG_SEQ_MASK)		\
10866775Sarchie				    : ((s) & MP_LONG_SEQ_MASK))
10952639Sarchie
11066775Sarchie/* Comparision of MP sequence numbers. Note: all sequence numbers
11166775Sarchie   except priv->xseq are stored with the sign bit extended. */
11266775Sarchie#define MP_SHORT_SEQ_DIFF(x,y)	MP_SHORT_EXTEND((x) - (y))
11366775Sarchie#define MP_LONG_SEQ_DIFF(x,y)	MP_LONG_EXTEND((x) - (y))
11452639Sarchie
11566775Sarchie#define MP_RECV_SEQ_DIFF(priv,x,y)					\
11666775Sarchie				((priv)->conf.recvShortSeq ?		\
11766775Sarchie				    MP_SHORT_SEQ_DIFF((x), (y)) :	\
11852639Sarchie				    MP_LONG_SEQ_DIFF((x), (y)))
11952639Sarchie
12066775Sarchie/* Increment receive sequence number */
12190594Sarchie#define MP_NEXT_RECV_SEQ(priv,seq)					\
12290594Sarchie				((priv)->conf.recvShortSeq ?		\
12390594Sarchie				    MP_SHORT_EXTEND((seq) + 1) :	\
12490594Sarchie				    MP_LONG_EXTEND((seq) + 1))
12559882Sarchie
12659882Sarchie/* Don't fragment transmitted packets smaller than this */
12759882Sarchie#define MP_MIN_FRAG_LEN		6
12859882Sarchie
12959882Sarchie/* Maximum fragment reasssembly queue length */
13059882Sarchie#define MP_MAX_QUEUE_LEN	128
13159882Sarchie
13259882Sarchie/* Fragment queue scanner period */
13359882Sarchie#define MP_FRAGTIMER_INTERVAL	(hz/2)
13459882Sarchie
13552639Sarchie/* We store incoming fragments this way */
13652639Sarchiestruct ng_ppp_frag {
13759882Sarchie	int				seq;		/* fragment seq# */
13859882Sarchie	u_char				first;		/* First in packet? */
13959882Sarchie	u_char				last;		/* Last in packet? */
14059882Sarchie	struct timeval			timestamp;	/* time of reception */
14159882Sarchie	struct mbuf			*data;		/* Fragment data */
14259882Sarchie	meta_p				meta;		/* Fragment meta */
14368761Smckusick	TAILQ_ENTRY(ng_ppp_frag)	f_qent;		/* Fragment queue */
14452639Sarchie};
14552639Sarchie
14652639Sarchie/* We use integer indicies to refer to the non-link hooks */
14752639Sarchiestatic const char *const ng_ppp_hook_names[] = {
14852639Sarchie	NG_PPP_HOOK_ATALK,
14952639Sarchie#define HOOK_INDEX_ATALK		0
15052639Sarchie	NG_PPP_HOOK_BYPASS,
15152639Sarchie#define HOOK_INDEX_BYPASS		1
15252639Sarchie	NG_PPP_HOOK_COMPRESS,
15352639Sarchie#define HOOK_INDEX_COMPRESS		2
15452639Sarchie	NG_PPP_HOOK_ENCRYPT,
15552639Sarchie#define HOOK_INDEX_ENCRYPT		3
15652639Sarchie	NG_PPP_HOOK_DECOMPRESS,
15752639Sarchie#define HOOK_INDEX_DECOMPRESS		4
15852639Sarchie	NG_PPP_HOOK_DECRYPT,
15952639Sarchie#define HOOK_INDEX_DECRYPT		5
16052639Sarchie	NG_PPP_HOOK_INET,
16152639Sarchie#define HOOK_INDEX_INET			6
16252639Sarchie	NG_PPP_HOOK_IPX,
16352639Sarchie#define HOOK_INDEX_IPX			7
16452639Sarchie	NG_PPP_HOOK_VJC_COMP,
16552639Sarchie#define HOOK_INDEX_VJC_COMP		8
16652639Sarchie	NG_PPP_HOOK_VJC_IP,
16752639Sarchie#define HOOK_INDEX_VJC_IP		9
16852639Sarchie	NG_PPP_HOOK_VJC_UNCOMP,
16952639Sarchie#define HOOK_INDEX_VJC_UNCOMP		10
17052639Sarchie	NG_PPP_HOOK_VJC_VJIP,
17152639Sarchie#define HOOK_INDEX_VJC_VJIP		11
17259882Sarchie	NG_PPP_HOOK_IPV6,
17359882Sarchie#define HOOK_INDEX_IPV6			12
17452639Sarchie	NULL
17559882Sarchie#define HOOK_INDEX_MAX			13
17652639Sarchie};
17752639Sarchie
17852639Sarchie/* We store index numbers in the hook private pointer. The HOOK_INDEX()
17952639Sarchie   for a hook is either the index (above) for normal hooks, or the ones
18070784Sjulian   complement of the link number for link hooks.
18170784SjulianXXX Not any more.. (what a hack)
18252639Sarchie#define HOOK_INDEX(hook)	(*((int16_t *) &(hook)->private))
18370784Sjulian*/
18452639Sarchie
18559882Sarchie/* Per-link private information */
18659882Sarchiestruct ng_ppp_link {
18759882Sarchie	struct ng_ppp_link_conf	conf;		/* link configuration */
18859882Sarchie	hook_p			hook;		/* connection to link data */
18966775Sarchie	int32_t			seq;		/* highest rec'd seq# - MSEQ */
19059882Sarchie	struct timeval		lastWrite;	/* time of last write */
19159882Sarchie	int			bytesInQueue;	/* bytes in the output queue */
19259882Sarchie	struct ng_ppp_link_stat	stats;		/* Link stats */
19359882Sarchie};
19459882Sarchie
19559882Sarchie/* Total per-node private information */
19653406Sarchiestruct ng_ppp_private {
19759882Sarchie	struct ng_ppp_bund_conf	conf;			/* bundle config */
19859882Sarchie	struct ng_ppp_link_stat	bundleStats;		/* bundle stats */
19959882Sarchie	struct ng_ppp_link	links[NG_PPP_MAX_LINKS];/* per-link info */
20066775Sarchie	int32_t			xseq;			/* next out MP seq # */
20166775Sarchie	int32_t			mseq;			/* min links[i].seq */
20259882Sarchie	u_char			vjCompHooked;		/* VJ comp hooked up? */
20359882Sarchie	u_char			allLinksEqual;		/* all xmit the same? */
20459882Sarchie	u_char			timerActive;		/* frag timer active? */
20559882Sarchie	u_int			numActiveLinks;		/* how many links up */
20659882Sarchie	int			activeLinks[NG_PPP_MAX_LINKS];	/* indicies */
20759882Sarchie	u_int			lastLink;		/* for round robin */
20859882Sarchie	hook_p			hooks[HOOK_INDEX_MAX];	/* non-link hooks */
20968761Smckusick	TAILQ_HEAD(ng_ppp_fraglist, ng_ppp_frag)	/* fragment queue */
21059882Sarchie				frags;
21159882Sarchie	int			qlen;			/* fraq queue length */
21259882Sarchie	struct callout_handle	fragTimer;		/* fraq queue check */
21352419Sjulian};
21453406Sarchietypedef struct ng_ppp_private *priv_p;
21552419Sjulian
21652419Sjulian/* Netgraph node methods */
21752752Sjulianstatic ng_constructor_t	ng_ppp_constructor;
21852752Sjulianstatic ng_rcvmsg_t	ng_ppp_rcvmsg;
21970700Sjulianstatic ng_shutdown_t	ng_ppp_shutdown;
22052752Sjulianstatic ng_newhook_t	ng_ppp_newhook;
22152752Sjulianstatic ng_rcvdata_t	ng_ppp_rcvdata;
22252752Sjulianstatic ng_disconnect_t	ng_ppp_disconnect;
22352419Sjulian
22452639Sarchie/* Helper functions */
22552912Sarchiestatic int	ng_ppp_input(node_p node, int bypass,
22670700Sjulian			int linkNum, item_p item);
22753075Sarchiestatic int	ng_ppp_output(node_p node, int bypass, int proto,
22870700Sjulian			int linkNum, item_p item);
22970700Sjulianstatic int	ng_ppp_mp_input(node_p node, int linkNum, item_p item);
23059882Sarchiestatic int	ng_ppp_check_packet(node_p node);
23159882Sarchiestatic void	ng_ppp_get_packet(node_p node, struct mbuf **mp, meta_p *metap);
23259882Sarchiestatic int	ng_ppp_frag_process(node_p node);
23359882Sarchiestatic int	ng_ppp_frag_trim(node_p node);
23459882Sarchiestatic void	ng_ppp_frag_timeout(void *arg);
23559882Sarchiestatic void	ng_ppp_frag_checkstale(node_p node);
23659882Sarchiestatic void	ng_ppp_frag_reset(node_p node);
23752639Sarchiestatic int	ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta);
23852639Sarchiestatic void	ng_ppp_mp_strategy(node_p node, int len, int *distrib);
23952639Sarchiestatic int	ng_ppp_intcmp(const void *v1, const void *v2);
24052639Sarchiestatic struct	mbuf *ng_ppp_addproto(struct mbuf *m, int proto, int compOK);
24153075Sarchiestatic struct	mbuf *ng_ppp_prepend(struct mbuf *m, const void *buf, int len);
24252639Sarchiestatic int	ng_ppp_config_valid(node_p node,
24359882Sarchie			const struct ng_ppp_node_conf *newConf);
24452639Sarchiestatic void	ng_ppp_update(node_p node, int newConf);
24559882Sarchiestatic void	ng_ppp_start_frag_timer(node_p node);
24659882Sarchiestatic void	ng_ppp_stop_frag_timer(node_p node);
24752419Sjulian
24866775Sarchie/* Parse type for struct ng_ppp_mp_state_type */
24966775Sarchiestatic const struct ng_parse_fixedarray_info ng_ppp_rseq_array_info = {
25066775Sarchie	&ng_parse_hint32_type,
25166775Sarchie	NG_PPP_MAX_LINKS
25266775Sarchie};
25366775Sarchiestatic const struct ng_parse_type ng_ppp_rseq_array_type = {
25466775Sarchie	&ng_parse_fixedarray_type,
25566775Sarchie	&ng_ppp_rseq_array_info,
25666775Sarchie};
25797685Sarchiestatic const struct ng_parse_struct_field ng_ppp_mp_state_type_fields[]
25866775Sarchie	= NG_PPP_MP_STATE_TYPE_INFO(&ng_ppp_rseq_array_type);
25966775Sarchiestatic const struct ng_parse_type ng_ppp_mp_state_type = {
26066775Sarchie	&ng_parse_struct_type,
26197685Sarchie	&ng_ppp_mp_state_type_fields
26266775Sarchie};
26366775Sarchie
26459882Sarchie/* Parse type for struct ng_ppp_link_conf */
26597685Sarchiestatic const struct ng_parse_struct_field ng_ppp_link_type_fields[]
26697685Sarchie	= NG_PPP_LINK_TYPE_INFO;
26753913Sarchiestatic const struct ng_parse_type ng_ppp_link_type = {
26853913Sarchie	&ng_parse_struct_type,
26997685Sarchie	&ng_ppp_link_type_fields
27053913Sarchie};
27153913Sarchie
27259882Sarchie/* Parse type for struct ng_ppp_bund_conf */
27397685Sarchiestatic const struct ng_parse_struct_field ng_ppp_bund_type_fields[]
27497685Sarchie	= NG_PPP_BUND_TYPE_INFO;
27559882Sarchiestatic const struct ng_parse_type ng_ppp_bund_type = {
27659882Sarchie	&ng_parse_struct_type,
27797685Sarchie	&ng_ppp_bund_type_fields
27859882Sarchie};
27959882Sarchie
28059882Sarchie/* Parse type for struct ng_ppp_node_conf */
28166775Sarchiestatic const struct ng_parse_fixedarray_info ng_ppp_array_info = {
28253913Sarchie	&ng_ppp_link_type,
28353913Sarchie	NG_PPP_MAX_LINKS
28453913Sarchie};
28553913Sarchiestatic const struct ng_parse_type ng_ppp_link_array_type = {
28653913Sarchie	&ng_parse_fixedarray_type,
28753913Sarchie	&ng_ppp_array_info,
28853913Sarchie};
28997685Sarchiestatic const struct ng_parse_struct_field ng_ppp_conf_type_fields[]
29059882Sarchie	= NG_PPP_CONFIG_TYPE_INFO(&ng_ppp_bund_type, &ng_ppp_link_array_type);
29159882Sarchiestatic const struct ng_parse_type ng_ppp_conf_type = {
29253913Sarchie	&ng_parse_struct_type,
29397685Sarchie	&ng_ppp_conf_type_fields
29453913Sarchie};
29553913Sarchie
29653913Sarchie/* Parse type for struct ng_ppp_link_stat */
29797685Sarchiestatic const struct ng_parse_struct_field ng_ppp_stats_type_fields[]
29897685Sarchie	= NG_PPP_STATS_TYPE_INFO;
29953913Sarchiestatic const struct ng_parse_type ng_ppp_stats_type = {
30053913Sarchie	&ng_parse_struct_type,
30197685Sarchie	&ng_ppp_stats_type_fields
30253913Sarchie};
30353913Sarchie
30453913Sarchie/* List of commands and how to convert arguments to/from ASCII */
30553913Sarchiestatic const struct ng_cmdlist ng_ppp_cmds[] = {
30653913Sarchie	{
30753913Sarchie	  NGM_PPP_COOKIE,
30853913Sarchie	  NGM_PPP_SET_CONFIG,
30953913Sarchie	  "setconfig",
31059882Sarchie	  &ng_ppp_conf_type,
31153913Sarchie	  NULL
31253913Sarchie	},
31353913Sarchie	{
31453913Sarchie	  NGM_PPP_COOKIE,
31553913Sarchie	  NGM_PPP_GET_CONFIG,
31653913Sarchie	  "getconfig",
31753913Sarchie	  NULL,
31859882Sarchie	  &ng_ppp_conf_type
31953913Sarchie	},
32053913Sarchie	{
32153913Sarchie	  NGM_PPP_COOKIE,
32266775Sarchie	  NGM_PPP_GET_MP_STATE,
32366775Sarchie	  "getmpstate",
32466775Sarchie	  NULL,
32566775Sarchie	  &ng_ppp_mp_state_type
32666775Sarchie	},
32766775Sarchie	{
32866775Sarchie	  NGM_PPP_COOKIE,
32953913Sarchie	  NGM_PPP_GET_LINK_STATS,
33053913Sarchie	  "getstats",
33153913Sarchie	  &ng_parse_int16_type,
33253913Sarchie	  &ng_ppp_stats_type
33353913Sarchie	},
33453913Sarchie	{
33553913Sarchie	  NGM_PPP_COOKIE,
33653913Sarchie	  NGM_PPP_CLR_LINK_STATS,
33753913Sarchie	  "clrstats",
33853913Sarchie	  &ng_parse_int16_type,
33953913Sarchie	  NULL
34053913Sarchie	},
34153913Sarchie	{
34253913Sarchie	  NGM_PPP_COOKIE,
34353913Sarchie	  NGM_PPP_GETCLR_LINK_STATS,
34453913Sarchie	  "getclrstats",
34553913Sarchie	  &ng_parse_int16_type,
34653913Sarchie	  &ng_ppp_stats_type
34753913Sarchie	},
34853913Sarchie	{ 0 }
34953913Sarchie};
35053913Sarchie
35152419Sjulian/* Node type descriptor */
35252639Sarchiestatic struct ng_type ng_ppp_typestruct = {
35370159Sjulian	NG_ABI_VERSION,
35452419Sjulian	NG_PPP_NODE_TYPE,
35552419Sjulian	NULL,
35652419Sjulian	ng_ppp_constructor,
35752419Sjulian	ng_ppp_rcvmsg,
35870700Sjulian	ng_ppp_shutdown,
35952419Sjulian	ng_ppp_newhook,
36052419Sjulian	NULL,
36152419Sjulian	NULL,
36252419Sjulian	ng_ppp_rcvdata,
36353913Sarchie	ng_ppp_disconnect,
36453913Sarchie	ng_ppp_cmds
36552419Sjulian};
36652639SarchieNETGRAPH_INIT(ppp, &ng_ppp_typestruct);
36752419Sjulian
36859882Sarchiestatic int *compareLatencies;			/* hack for ng_ppp_intcmp() */
36952419Sjulian
37053075Sarchie/* Address and control field header */
37159882Sarchiestatic const u_char ng_ppp_acf[2] = { 0xff, 0x03 };
37253075Sarchie
37359882Sarchie/* Maximum time we'll let a complete incoming packet sit in the queue */
37459882Sarchiestatic const struct timeval ng_ppp_max_staleness = { 2, 0 };	/* 2 seconds */
37559882Sarchie
37652419Sjulian#define ERROUT(x)	do { error = (x); goto done; } while (0)
37752419Sjulian
37852419Sjulian/************************************************************************
37952419Sjulian			NETGRAPH NODE STUFF
38052419Sjulian ************************************************************************/
38152419Sjulian
38252419Sjulian/*
38352639Sarchie * Node type constructor
38452419Sjulian */
38552419Sjulianstatic int
38670700Sjulianng_ppp_constructor(node_p node)
38752419Sjulian{
38852419Sjulian	priv_p priv;
38970700Sjulian	int i;
39052419Sjulian
39152419Sjulian	/* Allocate private structure */
39270870Sjulian	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH_PPP, M_NOWAIT | M_ZERO);
39352419Sjulian	if (priv == NULL)
39452419Sjulian		return (ENOMEM);
39552419Sjulian
39670784Sjulian	NG_NODE_SET_PRIVATE(node, priv);
39752419Sjulian
39852639Sarchie	/* Initialize state */
39968761Smckusick	TAILQ_INIT(&priv->frags);
40059882Sarchie	for (i = 0; i < NG_PPP_MAX_LINKS; i++)
40159882Sarchie		priv->links[i].seq = MP_NOSEQ;
40259882Sarchie	callout_handle_init(&priv->fragTimer);
40352639Sarchie
40452419Sjulian	/* Done */
40552419Sjulian	return (0);
40652419Sjulian}
40752419Sjulian
40852419Sjulian/*
40952419Sjulian * Give our OK for a hook to be added
41052419Sjulian */
41152419Sjulianstatic int
41252419Sjulianng_ppp_newhook(node_p node, hook_p hook, const char *name)
41352419Sjulian{
41470784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
41552639Sarchie	int linkNum = -1;
41652639Sarchie	hook_p *hookPtr = NULL;
41752639Sarchie	int hookIndex = -1;
41852419Sjulian
41952639Sarchie	/* Figure out which hook it is */
42052639Sarchie	if (strncmp(name, NG_PPP_HOOK_LINK_PREFIX,	/* a link hook? */
42152639Sarchie	    strlen(NG_PPP_HOOK_LINK_PREFIX)) == 0) {
42253648Sarchie		const char *cp;
42353648Sarchie		char *eptr;
42452419Sjulian
42552816Sarchie		cp = name + strlen(NG_PPP_HOOK_LINK_PREFIX);
42652816Sarchie		if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
42752639Sarchie			return (EINVAL);
42852816Sarchie		linkNum = (int)strtoul(cp, &eptr, 10);
42952816Sarchie		if (*eptr != '\0' || linkNum < 0 || linkNum >= NG_PPP_MAX_LINKS)
43052816Sarchie			return (EINVAL);
43159882Sarchie		hookPtr = &priv->links[linkNum].hook;
43252639Sarchie		hookIndex = ~linkNum;
43352639Sarchie	} else {				/* must be a non-link hook */
43452639Sarchie		int i;
43552639Sarchie
43652639Sarchie		for (i = 0; ng_ppp_hook_names[i] != NULL; i++) {
43752639Sarchie			if (strcmp(name, ng_ppp_hook_names[i]) == 0) {
43852639Sarchie				hookPtr = &priv->hooks[i];
43952639Sarchie				hookIndex = i;
44052639Sarchie				break;
44152639Sarchie			}
44252639Sarchie		}
44352639Sarchie		if (ng_ppp_hook_names[i] == NULL)
44452639Sarchie			return (EINVAL);	/* no such hook */
44552639Sarchie	}
44652639Sarchie
44752639Sarchie	/* See if hook is already connected */
44852639Sarchie	if (*hookPtr != NULL)
44952419Sjulian		return (EISCONN);
45052419Sjulian
45152639Sarchie	/* Disallow more than one link unless multilink is enabled */
45259882Sarchie	if (linkNum != -1 && priv->links[linkNum].conf.enableLink
45352639Sarchie	    && !priv->conf.enableMultilink && priv->numActiveLinks >= 1)
45452639Sarchie		return (ENODEV);
45552419Sjulian
45652419Sjulian	/* OK */
45752639Sarchie	*hookPtr = hook;
45870784Sjulian	NG_HOOK_SET_PRIVATE(hook, (void *)hookIndex);
45952639Sarchie	ng_ppp_update(node, 0);
46052419Sjulian	return (0);
46152419Sjulian}
46252419Sjulian
46352419Sjulian/*
46452419Sjulian * Receive a control message
46552419Sjulian */
46652419Sjulianstatic int
46770700Sjulianng_ppp_rcvmsg(node_p node, item_p item, hook_p lasthook)
46852419Sjulian{
46970784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
47052419Sjulian	struct ng_mesg *resp = NULL;
47152419Sjulian	int error = 0;
47270700Sjulian	struct ng_mesg *msg;
47352419Sjulian
47470700Sjulian	NGI_GET_MSG(item, msg);
47552419Sjulian	switch (msg->header.typecookie) {
47652419Sjulian	case NGM_PPP_COOKIE:
47752419Sjulian		switch (msg->header.cmd) {
47852639Sarchie		case NGM_PPP_SET_CONFIG:
47952639Sarchie		    {
48059882Sarchie			struct ng_ppp_node_conf *const conf =
48159882Sarchie			    (struct ng_ppp_node_conf *)msg->data;
48259882Sarchie			int i;
48352639Sarchie
48452639Sarchie			/* Check for invalid or illegal config */
48559882Sarchie			if (msg->header.arglen != sizeof(*conf))
48652419Sjulian				ERROUT(EINVAL);
48759882Sarchie			if (!ng_ppp_config_valid(node, conf))
48852639Sarchie				ERROUT(EINVAL);
48959882Sarchie
49059882Sarchie			/* Copy config */
49159882Sarchie			priv->conf = conf->bund;
49259882Sarchie			for (i = 0; i < NG_PPP_MAX_LINKS; i++)
49359882Sarchie				priv->links[i].conf = conf->links[i];
49452639Sarchie			ng_ppp_update(node, 1);
49552419Sjulian			break;
49652639Sarchie		    }
49752639Sarchie		case NGM_PPP_GET_CONFIG:
49859882Sarchie		    {
49959882Sarchie			struct ng_ppp_node_conf *conf;
50059882Sarchie			int i;
50159882Sarchie
50259882Sarchie			NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT);
50352419Sjulian			if (resp == NULL)
50452419Sjulian				ERROUT(ENOMEM);
50559882Sarchie			conf = (struct ng_ppp_node_conf *)resp->data;
50659882Sarchie			conf->bund = priv->conf;
50759882Sarchie			for (i = 0; i < NG_PPP_MAX_LINKS; i++)
50859882Sarchie				conf->links[i] = priv->links[i].conf;
50952419Sjulian			break;
51059882Sarchie		    }
51166775Sarchie		case NGM_PPP_GET_MP_STATE:
51266775Sarchie		    {
51366775Sarchie			struct ng_ppp_mp_state *info;
51466775Sarchie			int i;
51566775Sarchie
51666775Sarchie			NG_MKRESPONSE(resp, msg, sizeof(*info), M_NOWAIT);
51766775Sarchie			if (resp == NULL)
51866775Sarchie				ERROUT(ENOMEM);
51966775Sarchie			info = (struct ng_ppp_mp_state *)resp->data;
52066775Sarchie			bzero(info, sizeof(*info));
52166775Sarchie			for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
52266775Sarchie				if (priv->links[i].seq != MP_NOSEQ)
52366775Sarchie					info->rseq[i] = priv->links[i].seq;
52466775Sarchie			}
52566775Sarchie			info->mseq = priv->mseq;
52666775Sarchie			info->xseq = priv->xseq;
52766775Sarchie			break;
52866775Sarchie		    }
52952639Sarchie		case NGM_PPP_GET_LINK_STATS:
53052639Sarchie		case NGM_PPP_CLR_LINK_STATS:
53152912Sarchie		case NGM_PPP_GETCLR_LINK_STATS:
53252639Sarchie		    {
53352639Sarchie			struct ng_ppp_link_stat *stats;
53452639Sarchie			u_int16_t linkNum;
53552639Sarchie
53652639Sarchie			if (msg->header.arglen != sizeof(u_int16_t))
53752639Sarchie				ERROUT(EINVAL);
53852639Sarchie			linkNum = *((u_int16_t *) msg->data);
53952639Sarchie			if (linkNum >= NG_PPP_MAX_LINKS
54052639Sarchie			    && linkNum != NG_PPP_BUNDLE_LINKNUM)
54152639Sarchie				ERROUT(EINVAL);
54252639Sarchie			stats = (linkNum == NG_PPP_BUNDLE_LINKNUM) ?
54359882Sarchie			    &priv->bundleStats : &priv->links[linkNum].stats;
54452912Sarchie			if (msg->header.cmd != NGM_PPP_CLR_LINK_STATS) {
54552639Sarchie				NG_MKRESPONSE(resp, msg,
54652639Sarchie				    sizeof(struct ng_ppp_link_stat), M_NOWAIT);
54752639Sarchie				if (resp == NULL)
54852639Sarchie					ERROUT(ENOMEM);
54952639Sarchie				bcopy(stats, resp->data, sizeof(*stats));
55052912Sarchie			}
55152912Sarchie			if (msg->header.cmd != NGM_PPP_GET_LINK_STATS)
55252639Sarchie				bzero(stats, sizeof(*stats));
55352419Sjulian			break;
55452639Sarchie		    }
55552419Sjulian		default:
55652419Sjulian			error = EINVAL;
55752419Sjulian			break;
55852419Sjulian		}
55952419Sjulian		break;
56052639Sarchie	case NGM_VJC_COOKIE:
56159882Sarchie	    {
56270700Sjulian		/*
56370700Sjulian		 * Forward it to the vjc node. leave the
56470700Sjulian		 * old return address alone.
56570784Sjulian		 * If we have no hook, let NG_RESPOND_MSG
56670784Sjulian		 * clean up any remaining resources.
56770784Sjulian		 * Because we have no resp, the item will be freed
56870784Sjulian		 * along with anything it references. Don't
56970784Sjulian		 * let msg be freed twice.
57070700Sjulian		 */
57170700Sjulian		NGI_MSG(item) = msg;	/* put it back in the item */
57270784Sjulian		msg = NULL;
57370784Sjulian		if ((lasthook = priv->links[HOOK_INDEX_VJC_IP].hook)) {
57470784Sjulian			NG_FWD_ITEM_HOOK(error, item, lasthook);
57570700Sjulian		}
57670700Sjulian		return (error);
57759882Sarchie	    }
57852419Sjulian	default:
57952419Sjulian		error = EINVAL;
58052419Sjulian		break;
58152419Sjulian	}
58252419Sjuliandone:
58370700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
58470700Sjulian	NG_FREE_MSG(msg);
58552419Sjulian	return (error);
58652419Sjulian}
58752419Sjulian
58852419Sjulian/*
58952419Sjulian * Receive data on a hook
59052419Sjulian */
59152419Sjulianstatic int
59270700Sjulianng_ppp_rcvdata(hook_p hook, item_p item)
59352419Sjulian{
59470784Sjulian	const node_p node = NG_HOOK_NODE(hook);
59570784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
59670784Sjulian	const int index = (int)NG_HOOK_PRIVATE(hook);
59752639Sarchie	u_int16_t linkNum = NG_PPP_BUNDLE_LINKNUM;
59852639Sarchie	hook_p outHook = NULL;
59952639Sarchie	int proto = 0, error;
60070700Sjulian	struct mbuf *m;
60152419Sjulian
60270700Sjulian	NGI_GET_M(item, m);
60352639Sarchie	/* Did it come from a link hook? */
60452639Sarchie	if (index < 0) {
60559882Sarchie		struct ng_ppp_link *link;
60652419Sjulian
60752912Sarchie		/* Convert index into a link number */
60852639Sarchie		linkNum = (u_int16_t)~index;
60952639Sarchie		KASSERT(linkNum < NG_PPP_MAX_LINKS,
61087599Sobrien		    ("%s: bogus index 0x%x", __func__, index));
61159882Sarchie		link = &priv->links[linkNum];
61252419Sjulian
61352419Sjulian		/* Stats */
61459882Sarchie		link->stats.recvFrames++;
61559882Sarchie		link->stats.recvOctets += m->m_pkthdr.len;
61652419Sjulian
61753075Sarchie		/* Strip address and control fields, if present */
61853075Sarchie		if (m->m_pkthdr.len >= 2) {
61953075Sarchie			if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) {
62070700Sjulian				NG_FREE_ITEM(item);
62153075Sarchie				return (ENOBUFS);
62253075Sarchie			}
62353075Sarchie			if (bcmp(mtod(m, u_char *), &ng_ppp_acf, 2) == 0)
62453075Sarchie				m_adj(m, 2);
62553075Sarchie		}
62653075Sarchie
62752912Sarchie		/* Dispatch incoming frame (if not enabled, to bypass) */
62870700Sjulian		NGI_M(item) = m; 	/* put changed m back in item */
62952912Sarchie		return ng_ppp_input(node,
63070700Sjulian		    !link->conf.enableLink, linkNum, item);
63152639Sarchie	}
63252419Sjulian
63352639Sarchie	/* Get protocol & check if data allowed from this hook */
63470700Sjulian	NGI_M(item) = m; 	/* put possibly changed m back in item */
63552639Sarchie	switch (index) {
63652639Sarchie
63752912Sarchie	/* Outgoing data */
63852639Sarchie	case HOOK_INDEX_ATALK:
63952639Sarchie		if (!priv->conf.enableAtalk) {
64070700Sjulian			NG_FREE_ITEM(item);
64152639Sarchie			return (ENXIO);
64252419Sjulian		}
64352639Sarchie		proto = PROT_APPLETALK;
64452419Sjulian		break;
64552639Sarchie	case HOOK_INDEX_IPX:
64652639Sarchie		if (!priv->conf.enableIPX) {
64770700Sjulian			NG_FREE_ITEM(item);
64852639Sarchie			return (ENXIO);
64952639Sarchie		}
65052639Sarchie		proto = PROT_IPX;
65152639Sarchie		break;
65259882Sarchie	case HOOK_INDEX_IPV6:
65359882Sarchie		if (!priv->conf.enableIPv6) {
65470700Sjulian			NG_FREE_ITEM(item);
65559882Sarchie			return (ENXIO);
65659882Sarchie		}
65759882Sarchie		proto = PROT_IPV6;
65859882Sarchie		break;
65952639Sarchie	case HOOK_INDEX_INET:
66052639Sarchie	case HOOK_INDEX_VJC_VJIP:
66152639Sarchie		if (!priv->conf.enableIP) {
66270700Sjulian			NG_FREE_ITEM(item);
66352639Sarchie			return (ENXIO);
66452639Sarchie		}
66552639Sarchie		proto = PROT_IP;
66652639Sarchie		break;
66752639Sarchie	case HOOK_INDEX_VJC_COMP:
66852639Sarchie		if (!priv->conf.enableVJCompression) {
66970700Sjulian			NG_FREE_ITEM(item);
67052639Sarchie			return (ENXIO);
67152639Sarchie		}
67252639Sarchie		proto = PROT_VJCOMP;
67352639Sarchie		break;
67452639Sarchie	case HOOK_INDEX_VJC_UNCOMP:
67552639Sarchie		if (!priv->conf.enableVJCompression) {
67670700Sjulian			NG_FREE_ITEM(item);
67752639Sarchie			return (ENXIO);
67852639Sarchie		}
67952639Sarchie		proto = PROT_VJUNCOMP;
68052639Sarchie		break;
68152639Sarchie	case HOOK_INDEX_COMPRESS:
68252639Sarchie		if (!priv->conf.enableCompression) {
68370700Sjulian			NG_FREE_ITEM(item);
68452639Sarchie			return (ENXIO);
68552639Sarchie		}
68652639Sarchie		proto = PROT_COMPD;
68752639Sarchie		break;
68852639Sarchie	case HOOK_INDEX_ENCRYPT:
68952639Sarchie		if (!priv->conf.enableEncryption) {
69070700Sjulian			NG_FREE_ITEM(item);
69152639Sarchie			return (ENXIO);
69252639Sarchie		}
69352639Sarchie		proto = PROT_CRYPTD;
69452639Sarchie		break;
69552639Sarchie	case HOOK_INDEX_BYPASS:
69652639Sarchie		if (m->m_pkthdr.len < 4) {
69770700Sjulian			NG_FREE_ITEM(item);
69852639Sarchie			return (EINVAL);
69952639Sarchie		}
70052639Sarchie		if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
70170700Sjulian			NGI_M(item) = NULL; /* don't free twice */
70270700Sjulian			NG_FREE_ITEM(item);
70352639Sarchie			return (ENOBUFS);
70452639Sarchie		}
70570700Sjulian		NGI_M(item) = m; /* m may have changed */
70652639Sarchie		linkNum = ntohs(mtod(m, u_int16_t *)[0]);
70752639Sarchie		proto = ntohs(mtod(m, u_int16_t *)[1]);
70852639Sarchie		m_adj(m, 4);
70952639Sarchie		if (linkNum >= NG_PPP_MAX_LINKS
71055481Sarchie		    && linkNum != NG_PPP_BUNDLE_LINKNUM) {
71170700Sjulian			NG_FREE_ITEM(item);
71252639Sarchie			return (EINVAL);
71355481Sarchie		}
71452639Sarchie		break;
71552419Sjulian
71652912Sarchie	/* Incoming data */
71752639Sarchie	case HOOK_INDEX_VJC_IP:
71852912Sarchie		if (!priv->conf.enableIP || !priv->conf.enableVJDecompression) {
71970700Sjulian			NG_FREE_ITEM(item);
72052639Sarchie			return (ENXIO);
72152639Sarchie		}
72252419Sjulian		break;
72352639Sarchie	case HOOK_INDEX_DECOMPRESS:
72452639Sarchie		if (!priv->conf.enableDecompression) {
72570700Sjulian			NG_FREE_ITEM(item);
72652639Sarchie			return (ENXIO);
72752639Sarchie		}
72852639Sarchie		break;
72952639Sarchie	case HOOK_INDEX_DECRYPT:
73052639Sarchie		if (!priv->conf.enableDecryption) {
73170700Sjulian			NG_FREE_ITEM(item);
73252639Sarchie			return (ENXIO);
73352639Sarchie		}
73452639Sarchie		break;
73552639Sarchie	default:
73687599Sobrien		panic("%s: bogus index 0x%x", __func__, index);
73752419Sjulian	}
73852419Sjulian
73952639Sarchie	/* Now figure out what to do with the frame */
74052639Sarchie	switch (index) {
74152912Sarchie
74252912Sarchie	/* Outgoing data */
74352639Sarchie	case HOOK_INDEX_INET:
74452639Sarchie		if (priv->conf.enableVJCompression && priv->vjCompHooked) {
74552639Sarchie			outHook = priv->hooks[HOOK_INDEX_VJC_IP];
74652639Sarchie			break;
74752639Sarchie		}
74852639Sarchie		/* FALLTHROUGH */
74952639Sarchie	case HOOK_INDEX_ATALK:
75059882Sarchie	case HOOK_INDEX_IPV6:
75152639Sarchie	case HOOK_INDEX_IPX:
75252639Sarchie	case HOOK_INDEX_VJC_COMP:
75352639Sarchie	case HOOK_INDEX_VJC_UNCOMP:
75452639Sarchie	case HOOK_INDEX_VJC_VJIP:
75552639Sarchie		if (priv->conf.enableCompression
75652639Sarchie		    && priv->hooks[HOOK_INDEX_COMPRESS] != NULL) {
75752639Sarchie			if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) {
75870700Sjulian				NGI_M(item) = NULL;
75970700Sjulian				NG_FREE_ITEM(item);
76052639Sarchie				return (ENOBUFS);
76152639Sarchie			}
76270700Sjulian			NGI_M(item) = m; /* m may have changed */
76352639Sarchie			outHook = priv->hooks[HOOK_INDEX_COMPRESS];
76452639Sarchie			break;
76552639Sarchie		}
76652639Sarchie		/* FALLTHROUGH */
76752639Sarchie	case HOOK_INDEX_COMPRESS:
76852639Sarchie		if (priv->conf.enableEncryption
76952639Sarchie		    && priv->hooks[HOOK_INDEX_ENCRYPT] != NULL) {
77052639Sarchie			if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) {
77170700Sjulian				NGI_M(item) = NULL;
77270700Sjulian				NG_FREE_ITEM(item);
77352639Sarchie				return (ENOBUFS);
77452639Sarchie			}
77570700Sjulian			NGI_M(item) = m; /* m may have changed */
77652639Sarchie			outHook = priv->hooks[HOOK_INDEX_ENCRYPT];
77752639Sarchie			break;
77852639Sarchie		}
77952639Sarchie		/* FALLTHROUGH */
78052639Sarchie	case HOOK_INDEX_ENCRYPT:
78170700Sjulian		return ng_ppp_output(node, 0, proto, NG_PPP_BUNDLE_LINKNUM, item);
78252639Sarchie
78353088Sarchie	case HOOK_INDEX_BYPASS:
78470700Sjulian		return ng_ppp_output(node, 1, proto, linkNum, item);
78553088Sarchie
78652639Sarchie	/* Incoming data */
78752639Sarchie	case HOOK_INDEX_DECRYPT:
78852639Sarchie	case HOOK_INDEX_DECOMPRESS:
78970700Sjulian		return ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, item);
79052912Sarchie
79152639Sarchie	case HOOK_INDEX_VJC_IP:
79252912Sarchie		outHook = priv->hooks[HOOK_INDEX_INET];
79352912Sarchie		break;
79452419Sjulian	}
79552419Sjulian
79652639Sarchie	/* Send packet out hook */
79770784Sjulian	NG_FWD_ITEM_HOOK(error, item, outHook);
79852419Sjulian	return (error);
79952419Sjulian}
80052419Sjulian
80152419Sjulian/*
80252419Sjulian * Destroy node
80352419Sjulian */
80452419Sjulianstatic int
80570700Sjulianng_ppp_shutdown(node_p node)
80652419Sjulian{
80770784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
80852419Sjulian
80959882Sarchie	/* Stop fragment queue timer */
81059882Sarchie	ng_ppp_stop_frag_timer(node);
81159882Sarchie
81252419Sjulian	/* Take down netgraph node */
81359882Sarchie	ng_ppp_frag_reset(node);
81452419Sjulian	bzero(priv, sizeof(*priv));
81570870Sjulian	FREE(priv, M_NETGRAPH_PPP);
81670784Sjulian	NG_NODE_SET_PRIVATE(node, NULL);
81770784Sjulian	NG_NODE_UNREF(node);		/* let the node escape */
81852419Sjulian	return (0);
81952419Sjulian}
82052419Sjulian
82152419Sjulian/*
82252419Sjulian * Hook disconnection
82352419Sjulian */
82452419Sjulianstatic int
82552419Sjulianng_ppp_disconnect(hook_p hook)
82652419Sjulian{
82770784Sjulian	const node_p node = NG_HOOK_NODE(hook);
82870784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
82970784Sjulian	const int index = (int)NG_HOOK_PRIVATE(hook);
83053406Sarchie
83153406Sarchie	/* Zero out hook pointer */
83253406Sarchie	if (index < 0)
83359882Sarchie		priv->links[~index].hook = NULL;
83453406Sarchie	else
83553406Sarchie		priv->hooks[index] = NULL;
83653406Sarchie
83753406Sarchie	/* Update derived info (or go away if no hooks left) */
83870784Sjulian	if (NG_NODE_NUMHOOKS(node) > 0) {
83953406Sarchie		ng_ppp_update(node, 0);
84070700Sjulian	} else {
84170784Sjulian		if (NG_NODE_IS_VALID(node)) {
84270700Sjulian			ng_rmnode_self(node);
84370700Sjulian		}
84470700Sjulian	}
84552419Sjulian	return (0);
84652419Sjulian}
84752419Sjulian
84852419Sjulian/************************************************************************
84952419Sjulian			HELPER STUFF
85052419Sjulian ************************************************************************/
85152419Sjulian
85252419Sjulian/*
85352639Sarchie * Handle an incoming frame.  Extract the PPP protocol number
85452639Sarchie * and dispatch accordingly.
85552419Sjulian */
85652419Sjulianstatic int
85770700Sjulianng_ppp_input(node_p node, int bypass, int linkNum, item_p item)
85852419Sjulian{
85970784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
86052639Sarchie	hook_p outHook = NULL;
86152639Sarchie	int proto, error;
86270700Sjulian	struct mbuf *m;
86352419Sjulian
86470700Sjulian
86570700Sjulian	NGI_GET_M(item, m);
86652639Sarchie	/* Extract protocol number */
86752639Sarchie	for (proto = 0; !PROT_VALID(proto) && m->m_pkthdr.len > 0; ) {
86852639Sarchie		if (m->m_len < 1 && (m = m_pullup(m, 1)) == NULL) {
86970700Sjulian			NG_FREE_ITEM(item);
87052639Sarchie			return (ENOBUFS);
87152639Sarchie		}
87252639Sarchie		proto = (proto << 8) + *mtod(m, u_char *);
87352639Sarchie		m_adj(m, 1);
87452639Sarchie	}
87552639Sarchie	if (!PROT_VALID(proto)) {
87652639Sarchie		if (linkNum == NG_PPP_BUNDLE_LINKNUM)
87752639Sarchie			priv->bundleStats.badProtos++;
87852639Sarchie		else
87959882Sarchie			priv->links[linkNum].stats.badProtos++;
88070700Sjulian		NG_FREE_ITEM(item);
88170700Sjulian		NG_FREE_M(m);
88252639Sarchie		return (EINVAL);
88352639Sarchie	}
88452419Sjulian
88552912Sarchie	/* Bypass frame? */
88652912Sarchie	if (bypass)
88752912Sarchie		goto bypass;
88852912Sarchie
88952639Sarchie	/* Check protocol */
89052639Sarchie	switch (proto) {
89152639Sarchie	case PROT_COMPD:
89252639Sarchie		if (priv->conf.enableDecompression)
89352639Sarchie			outHook = priv->hooks[HOOK_INDEX_DECOMPRESS];
89452639Sarchie		break;
89552639Sarchie	case PROT_CRYPTD:
89652639Sarchie		if (priv->conf.enableDecryption)
89752639Sarchie			outHook = priv->hooks[HOOK_INDEX_DECRYPT];
89852639Sarchie		break;
89952639Sarchie	case PROT_VJCOMP:
90052639Sarchie		if (priv->conf.enableVJDecompression && priv->vjCompHooked)
90152639Sarchie			outHook = priv->hooks[HOOK_INDEX_VJC_COMP];
90252639Sarchie		break;
90352639Sarchie	case PROT_VJUNCOMP:
90452639Sarchie		if (priv->conf.enableVJDecompression && priv->vjCompHooked)
90552639Sarchie			outHook = priv->hooks[HOOK_INDEX_VJC_UNCOMP];
90652639Sarchie		break;
90752639Sarchie	case PROT_MP:
90859882Sarchie		if (priv->conf.enableMultilink
90998063Sjulian		    && linkNum != NG_PPP_BUNDLE_LINKNUM) {
91098063Sjulian			NGI_M(item) = m;
91170700Sjulian			return ng_ppp_mp_input(node, linkNum, item);
91298063Sjulian		}
91352639Sarchie		break;
91452639Sarchie	case PROT_APPLETALK:
91552639Sarchie		if (priv->conf.enableAtalk)
91652639Sarchie			outHook = priv->hooks[HOOK_INDEX_ATALK];
91752639Sarchie		break;
91852639Sarchie	case PROT_IPX:
91952639Sarchie		if (priv->conf.enableIPX)
92052639Sarchie			outHook = priv->hooks[HOOK_INDEX_IPX];
92152639Sarchie		break;
92252639Sarchie	case PROT_IP:
92352639Sarchie		if (priv->conf.enableIP)
92452639Sarchie			outHook = priv->hooks[HOOK_INDEX_INET];
92552639Sarchie		break;
92659882Sarchie	case PROT_IPV6:
92759882Sarchie		if (priv->conf.enableIPv6)
92859882Sarchie			outHook = priv->hooks[HOOK_INDEX_IPV6];
92959882Sarchie		break;
93052639Sarchie	}
93152639Sarchie
93255481Sarchiebypass:
93352639Sarchie	/* For unknown/inactive protocols, forward out the bypass hook */
93452639Sarchie	if (outHook == NULL) {
93553075Sarchie		u_int16_t hdr[2];
93653075Sarchie
93753075Sarchie		hdr[0] = htons(linkNum);
93853075Sarchie		hdr[1] = htons((u_int16_t)proto);
93955481Sarchie		if ((m = ng_ppp_prepend(m, &hdr, 4)) == NULL) {
94070700Sjulian			NG_FREE_ITEM(item);
94152639Sarchie			return (ENOBUFS);
94255481Sarchie		}
94352639Sarchie		outHook = priv->hooks[HOOK_INDEX_BYPASS];
94452639Sarchie	}
94552639Sarchie
94652639Sarchie	/* Forward frame */
94770700Sjulian	NG_FWD_NEW_DATA(error, item, outHook, m);
94852639Sarchie	return (error);
94952639Sarchie}
95052639Sarchie
95152639Sarchie/*
95292298Sarchie * Deliver a frame out a link, either a real one or NG_PPP_BUNDLE_LINKNUM.
95353088Sarchie * If the link is not enabled then ENXIO is returned, unless "bypass" is != 0.
95492298Sarchie *
95592298Sarchie * If the frame is too big for the particular link, return EMSGSIZE.
95652639Sarchie */
95752639Sarchiestatic int
95853075Sarchieng_ppp_output(node_p node, int bypass,
95970700Sjulian	int proto, int linkNum, item_p item)
96052639Sarchie{
96170784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
96259882Sarchie	struct ng_ppp_link *link;
96352912Sarchie	int len, error;
96470700Sjulian	struct mbuf *m;
96592298Sarchie	u_int16_t mru;
96652639Sarchie
96792298Sarchie	/* Extract mbuf */
96892298Sarchie	NGI_GET_M(item, m);
96992298Sarchie
97053075Sarchie	/* If not doing MP, map bundle virtual link to (the only) link */
97153075Sarchie	if (linkNum == NG_PPP_BUNDLE_LINKNUM && !priv->conf.enableMultilink)
97252639Sarchie		linkNum = priv->activeLinks[0];
97352639Sarchie
97459882Sarchie	/* Get link pointer (optimization) */
97559882Sarchie	link = (linkNum != NG_PPP_BUNDLE_LINKNUM) ?
97659882Sarchie	    &priv->links[linkNum] : NULL;
97759882Sarchie
97853075Sarchie	/* Check link status (if real) */
97953088Sarchie	if (linkNum != NG_PPP_BUNDLE_LINKNUM) {
98059882Sarchie		if (!bypass && !link->conf.enableLink) {
98170700Sjulian			NG_FREE_M(m);
98270700Sjulian			NG_FREE_ITEM(item);
98353088Sarchie			return (ENXIO);
98455481Sarchie		}
98559882Sarchie		if (link->hook == NULL) {
98670700Sjulian			NG_FREE_M(m);
98770700Sjulian			NG_FREE_ITEM(item);
98853088Sarchie			return (ENETDOWN);
98953088Sarchie		}
99052639Sarchie	}
99152639Sarchie
99292298Sarchie	/* Check peer's MRU for this link */
99392298Sarchie	mru = (link != NULL) ? link->conf.mru : priv->conf.mrru;
99492298Sarchie	if (mru != 0 && m->m_pkthdr.len > mru) {
99592298Sarchie		NG_FREE_M(m);
99692298Sarchie		NG_FREE_ITEM(item);
99792298Sarchie		return (EMSGSIZE);
99892298Sarchie	}
99992298Sarchie
100053075Sarchie	/* Prepend protocol number, possibly compressed */
100153075Sarchie	if ((m = ng_ppp_addproto(m, proto,
100253075Sarchie	    linkNum == NG_PPP_BUNDLE_LINKNUM
100359882Sarchie	      || link->conf.enableProtoComp)) == NULL) {
100470700Sjulian		NG_FREE_ITEM(item);
100553075Sarchie		return (ENOBUFS);
100653075Sarchie	}
100753075Sarchie
100853075Sarchie	/* Special handling for the MP virtual link */
100970700Sjulian	if (linkNum == NG_PPP_BUNDLE_LINKNUM) {
101070700Sjulian		meta_p meta;
101170700Sjulian
101270700Sjulian		/* strip off and discard the queue item */
101370700Sjulian		NGI_GET_META(item, meta);
101470700Sjulian		NG_FREE_ITEM(item);
101553075Sarchie		return ng_ppp_mp_output(node, m, meta);
101670700Sjulian	}
101753075Sarchie
101853075Sarchie	/* Prepend address and control field (unless compressed) */
101959882Sarchie	if (proto == PROT_LCP || !link->conf.enableACFComp) {
102053075Sarchie		if ((m = ng_ppp_prepend(m, &ng_ppp_acf, 2)) == NULL) {
102170700Sjulian			NG_FREE_ITEM(item);
102253075Sarchie			return (ENOBUFS);
102353075Sarchie		}
102453075Sarchie	}
102553075Sarchie
102652639Sarchie	/* Deliver frame */
102752912Sarchie	len = m->m_pkthdr.len;
102870700Sjulian	NG_FWD_NEW_DATA(error, item,  link->hook, m);
102952766Sarchie
103052766Sarchie	/* Update stats and 'bytes in queue' counter */
103152766Sarchie	if (error == 0) {
103259882Sarchie		link->stats.xmitFrames++;
103359882Sarchie		link->stats.xmitOctets += len;
103459882Sarchie		link->bytesInQueue += len;
103559882Sarchie		getmicrouptime(&link->lastWrite);
103652766Sarchie	}
103752639Sarchie	return error;
103852639Sarchie}
103952639Sarchie
104052639Sarchie/*
104152639Sarchie * Handle an incoming multi-link fragment
104259882Sarchie *
104359882Sarchie * The fragment reassembly algorithm is somewhat complex. This is mainly
104459882Sarchie * because we are required not to reorder the reconstructed packets, yet
104559882Sarchie * fragments are only guaranteed to arrive in order on a per-link basis.
104659882Sarchie * In other words, when we have a complete packet ready, but the previous
104759882Sarchie * packet is still incomplete, we have to decide between delivering the
104859882Sarchie * complete packet and throwing away the incomplete one, or waiting to
104959882Sarchie * see if the remainder of the incomplete one arrives, at which time we
105059882Sarchie * can deliver both packets, in order.
105159882Sarchie *
105259882Sarchie * This problem is exacerbated by "sequence number slew", which is when
105359882Sarchie * the sequence numbers coming in from different links are far apart from
105459882Sarchie * each other. In particular, certain unnamed equipment (*cough* Ascend)
105559882Sarchie * has been seen to generate sequence number slew of up to 10 on an ISDN
105659882Sarchie * 2B-channel MP link. There is nothing invalid about sequence number slew
105759882Sarchie * but it makes the reasssembly process have to work harder.
105859882Sarchie *
105959882Sarchie * However, the peer is required to transmit fragments in order on each
106059882Sarchie * link. That means if we define MSEQ as the minimum over all links of
106159882Sarchie * the highest sequence number received on that link, then we can always
106259882Sarchie * give up any hope of receiving a fragment with sequence number < MSEQ in
106359882Sarchie * the future (all of this using 'wraparound' sequence number space).
106459882Sarchie * Therefore we can always immediately throw away incomplete packets
106559882Sarchie * missing fragments with sequence numbers < MSEQ.
106659882Sarchie *
106759882Sarchie * Here is an overview of our algorithm:
106859882Sarchie *
106959882Sarchie *    o Received fragments are inserted into a queue, for which we
107059882Sarchie *	maintain these invariants between calls to this function:
107159882Sarchie *
107259882Sarchie *	- Fragments are ordered in the queue by sequence number
107359882Sarchie *	- If a complete packet is at the head of the queue, then
107459882Sarchie *	  the first fragment in the packet has seq# > MSEQ + 1
107559882Sarchie *	  (otherwise, we could deliver it immediately)
107659882Sarchie *	- If any fragments have seq# < MSEQ, then they are necessarily
107759882Sarchie *	  part of a packet whose missing seq#'s are all > MSEQ (otherwise,
107859882Sarchie *	  we can throw them away because they'll never be completed)
107959882Sarchie *	- The queue contains at most MP_MAX_QUEUE_LEN fragments
108059882Sarchie *
108159882Sarchie *    o We have a periodic timer that checks the queue for the first
108259882Sarchie *	complete packet that has been sitting in the queue "too long".
108359882Sarchie *	When one is detected, all previous (incomplete) fragments are
108459882Sarchie *	discarded, their missing fragments are declared lost and MSEQ
108559882Sarchie *	is increased.
108659882Sarchie *
108759882Sarchie *    o If we recieve a fragment with seq# < MSEQ, we throw it away
108859882Sarchie *	because we've already delcared it lost.
108959882Sarchie *
109059882Sarchie * This assumes linkNum != NG_PPP_BUNDLE_LINKNUM.
109152639Sarchie */
109252639Sarchiestatic int
109370700Sjulianng_ppp_mp_input(node_p node, int linkNum, item_p item)
109452639Sarchie{
109570784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
109659882Sarchie	struct ng_ppp_link *const link = &priv->links[linkNum];
109752639Sarchie	struct ng_ppp_frag frag0, *frag = &frag0;
109859882Sarchie	struct ng_ppp_frag *qent;
109959882Sarchie	int i, diff, inserted;
110070700Sjulian	struct mbuf *m;
110170700Sjulian	meta_p meta;
110252639Sarchie
110370700Sjulian	NGI_GET_M(item, m);
110470700Sjulian	NGI_GET_META(item, meta);
110570700Sjulian	NG_FREE_ITEM(item);
110661143Sarchie	/* Stats */
110761143Sarchie	priv->bundleStats.recvFrames++;
110861143Sarchie	priv->bundleStats.recvOctets += m->m_pkthdr.len;
110961143Sarchie
111052639Sarchie	/* Extract fragment information from MP header */
111152639Sarchie	if (priv->conf.recvShortSeq) {
111252639Sarchie		u_int16_t shdr;
111352639Sarchie
111452639Sarchie		if (m->m_pkthdr.len < 2) {
111559882Sarchie			link->stats.runts++;
111670700Sjulian			NG_FREE_M(m);
111770700Sjulian			NG_FREE_META(meta);
111852639Sarchie			return (EINVAL);
111952639Sarchie		}
112052639Sarchie		if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) {
112152639Sarchie			NG_FREE_META(meta);
112252639Sarchie			return (ENOBUFS);
112352639Sarchie		}
112452639Sarchie		shdr = ntohs(*mtod(m, u_int16_t *));
112566775Sarchie		frag->seq = MP_SHORT_EXTEND(shdr);
112652639Sarchie		frag->first = (shdr & MP_SHORT_FIRST_FLAG) != 0;
112752639Sarchie		frag->last = (shdr & MP_SHORT_LAST_FLAG) != 0;
112859882Sarchie		diff = MP_SHORT_SEQ_DIFF(frag->seq, priv->mseq);
112952639Sarchie		m_adj(m, 2);
113052639Sarchie	} else {
113152639Sarchie		u_int32_t lhdr;
113252639Sarchie
113352639Sarchie		if (m->m_pkthdr.len < 4) {
113459882Sarchie			link->stats.runts++;
113570700Sjulian			NG_FREE_M(m);
113670700Sjulian			NG_FREE_META(meta);
113752639Sarchie			return (EINVAL);
113852639Sarchie		}
113952639Sarchie		if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
114052639Sarchie			NG_FREE_META(meta);
114152639Sarchie			return (ENOBUFS);
114252639Sarchie		}
114352639Sarchie		lhdr = ntohl(*mtod(m, u_int32_t *));
114466775Sarchie		frag->seq = MP_LONG_EXTEND(lhdr);
114552639Sarchie		frag->first = (lhdr & MP_LONG_FIRST_FLAG) != 0;
114652639Sarchie		frag->last = (lhdr & MP_LONG_LAST_FLAG) != 0;
114759882Sarchie		diff = MP_LONG_SEQ_DIFF(frag->seq, priv->mseq);
114852639Sarchie		m_adj(m, 4);
114952639Sarchie	}
115052639Sarchie	frag->data = m;
115152639Sarchie	frag->meta = meta;
115259882Sarchie	getmicrouptime(&frag->timestamp);
115352639Sarchie
115459882Sarchie	/* If sequence number is < MSEQ, we've already declared this
115559882Sarchie	   fragment as lost, so we have no choice now but to drop it */
115659882Sarchie	if (diff < 0) {
115759882Sarchie		link->stats.dropFragments++;
115870700Sjulian		NG_FREE_M(m);
115970700Sjulian		NG_FREE_META(meta);
116059882Sarchie		return (0);
116159882Sarchie	}
116252639Sarchie
116359882Sarchie	/* Update highest received sequence number on this link and MSEQ */
116459882Sarchie	priv->mseq = link->seq = frag->seq;
116559882Sarchie	for (i = 0; i < priv->numActiveLinks; i++) {
116659882Sarchie		struct ng_ppp_link *const alink =
116759882Sarchie		    &priv->links[priv->activeLinks[i]];
116852639Sarchie
116966775Sarchie		if (MP_RECV_SEQ_DIFF(priv, alink->seq, priv->mseq) < 0)
117059882Sarchie			priv->mseq = alink->seq;
117159882Sarchie	}
117259882Sarchie
117352639Sarchie	/* Allocate a new frag struct for the queue */
117470870Sjulian	MALLOC(frag, struct ng_ppp_frag *, sizeof(*frag), M_NETGRAPH_PPP, M_NOWAIT);
117552639Sarchie	if (frag == NULL) {
117670700Sjulian		NG_FREE_M(m);
117770700Sjulian		NG_FREE_META(meta);
117859882Sarchie		ng_ppp_frag_process(node);
117952639Sarchie		return (ENOMEM);
118052639Sarchie	}
118152639Sarchie	*frag = frag0;
118252639Sarchie
118359882Sarchie	/* Add fragment to queue, which is sorted by sequence number */
118454755Sarchie	inserted = 0;
118568761Smckusick	TAILQ_FOREACH_REVERSE(qent, &priv->frags, ng_ppp_fraglist, f_qent) {
118666775Sarchie		diff = MP_RECV_SEQ_DIFF(priv, frag->seq, qent->seq);
118752639Sarchie		if (diff > 0) {
118868761Smckusick			TAILQ_INSERT_AFTER(&priv->frags, qent, frag, f_qent);
118954755Sarchie			inserted = 1;
119052639Sarchie			break;
119152639Sarchie		} else if (diff == 0) {	     /* should never happen! */
119259882Sarchie			link->stats.dupFragments++;
119370700Sjulian			NG_FREE_M(frag->data);
119470700Sjulian			NG_FREE_META(frag->meta);
119570870Sjulian			FREE(frag, M_NETGRAPH_PPP);
119652639Sarchie			return (EINVAL);
119752639Sarchie		}
119852639Sarchie	}
119954755Sarchie	if (!inserted)
120068761Smckusick		TAILQ_INSERT_HEAD(&priv->frags, frag, f_qent);
120159882Sarchie	priv->qlen++;
120252639Sarchie
120359882Sarchie	/* Process the queue */
120459882Sarchie	return ng_ppp_frag_process(node);
120559882Sarchie}
120654755Sarchie
120759882Sarchie/*
120859882Sarchie * Examine our list of fragments, and determine if there is a
120959882Sarchie * complete and deliverable packet at the head of the list.
121059882Sarchie * Return 1 if so, zero otherwise.
121159882Sarchie */
121259882Sarchiestatic int
121359882Sarchieng_ppp_check_packet(node_p node)
121459882Sarchie{
121570784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
121659882Sarchie	struct ng_ppp_frag *qent, *qnext;
121759882Sarchie
121859882Sarchie	/* Check for empty queue */
121968761Smckusick	if (TAILQ_EMPTY(&priv->frags))
122059882Sarchie		return (0);
122159882Sarchie
122259882Sarchie	/* Check first fragment is the start of a deliverable packet */
122368761Smckusick	qent = TAILQ_FIRST(&priv->frags);
122466775Sarchie	if (!qent->first || MP_RECV_SEQ_DIFF(priv, qent->seq, priv->mseq) > 1)
122559882Sarchie		return (0);
122659882Sarchie
122759882Sarchie	/* Check that all the fragments are there */
122859882Sarchie	while (!qent->last) {
122968761Smckusick		qnext = TAILQ_NEXT(qent, f_qent);
123068761Smckusick		if (qnext == NULL)	/* end of queue */
123159882Sarchie			return (0);
123266775Sarchie		if (qnext->seq != MP_NEXT_RECV_SEQ(priv, qent->seq))
123359882Sarchie			return (0);
123459882Sarchie		qent = qnext;
123552639Sarchie	}
123652639Sarchie
123759882Sarchie	/* Got one */
123859882Sarchie	return (1);
123959882Sarchie}
124059882Sarchie
124159882Sarchie/*
124259882Sarchie * Pull a completed packet off the head of the incoming fragment queue.
124359882Sarchie * This assumes there is a completed packet there to pull off.
124459882Sarchie */
124559882Sarchiestatic void
124659882Sarchieng_ppp_get_packet(node_p node, struct mbuf **mp, meta_p *metap)
124759882Sarchie{
124870784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
124959882Sarchie	struct ng_ppp_frag *qent, *qnext;
125059882Sarchie	struct mbuf *m = NULL, *tail;
125159882Sarchie
125268761Smckusick	qent = TAILQ_FIRST(&priv->frags);
125368761Smckusick	KASSERT(!TAILQ_EMPTY(&priv->frags) && qent->first,
125487599Sobrien	    ("%s: no packet", __func__));
125559882Sarchie	for (tail = NULL; qent != NULL; qent = qnext) {
125668761Smckusick		qnext = TAILQ_NEXT(qent, f_qent);
125768761Smckusick		KASSERT(!TAILQ_EMPTY(&priv->frags),
125887599Sobrien		    ("%s: empty q", __func__));
125968761Smckusick		TAILQ_REMOVE(&priv->frags, qent, f_qent);
126052639Sarchie		if (tail == NULL) {
126152639Sarchie			tail = m = qent->data;
126259882Sarchie			*metap = qent->meta;	/* inherit first frag's meta */
126352639Sarchie		} else {
126452639Sarchie			m->m_pkthdr.len += qent->data->m_pkthdr.len;
126552639Sarchie			tail->m_next = qent->data;
126653075Sarchie			NG_FREE_META(qent->meta); /* drop other frags' metas */
126752639Sarchie		}
126852639Sarchie		while (tail->m_next != NULL)
126952639Sarchie			tail = tail->m_next;
127059882Sarchie		if (qent->last)
127152639Sarchie			qnext = NULL;
127270870Sjulian		FREE(qent, M_NETGRAPH_PPP);
127359882Sarchie		priv->qlen--;
127452639Sarchie	}
127559882Sarchie	*mp = m;
127659882Sarchie}
127752639Sarchie
127859882Sarchie/*
127959882Sarchie * Trim fragments from the queue whose packets can never be completed.
128059882Sarchie * This assumes a complete packet is NOT at the beginning of the queue.
128159882Sarchie * Returns 1 if fragments were removed, zero otherwise.
128259882Sarchie */
128359882Sarchiestatic int
128459882Sarchieng_ppp_frag_trim(node_p node)
128559882Sarchie{
128670784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
128759882Sarchie	struct ng_ppp_frag *qent, *qnext = NULL;
128859882Sarchie	int removed = 0;
128959882Sarchie
129059882Sarchie	/* Scan for "dead" fragments and remove them */
129159882Sarchie	while (1) {
129259882Sarchie		int dead = 0;
129359882Sarchie
129459882Sarchie		/* If queue is empty, we're done */
129568761Smckusick		if (TAILQ_EMPTY(&priv->frags))
129652639Sarchie			break;
129759882Sarchie
129859882Sarchie		/* Determine whether first fragment can ever be completed */
129968761Smckusick		TAILQ_FOREACH(qent, &priv->frags, f_qent) {
130066775Sarchie			if (MP_RECV_SEQ_DIFF(priv, qent->seq, priv->mseq) >= 0)
130159882Sarchie				break;
130268761Smckusick			qnext = TAILQ_NEXT(qent, f_qent);
130368761Smckusick			KASSERT(qnext != NULL,
130487599Sobrien			    ("%s: last frag < MSEQ?", __func__));
130566775Sarchie			if (qnext->seq != MP_NEXT_RECV_SEQ(priv, qent->seq)
130659882Sarchie			    || qent->last || qnext->first) {
130759882Sarchie				dead = 1;
130859882Sarchie				break;
130959882Sarchie			}
131059882Sarchie		}
131159882Sarchie		if (!dead)
131259882Sarchie			break;
131359882Sarchie
131459882Sarchie		/* Remove fragment and all others in the same packet */
131568761Smckusick		while ((qent = TAILQ_FIRST(&priv->frags)) != qnext) {
131668761Smckusick			KASSERT(!TAILQ_EMPTY(&priv->frags),
131787599Sobrien			    ("%s: empty q", __func__));
131859882Sarchie			priv->bundleStats.dropFragments++;
131968761Smckusick			TAILQ_REMOVE(&priv->frags, qent, f_qent);
132070700Sjulian			NG_FREE_M(qent->data);
132170700Sjulian			NG_FREE_META(qent->meta);
132270870Sjulian			FREE(qent, M_NETGRAPH_PPP);
132359882Sarchie			priv->qlen--;
132459882Sarchie			removed = 1;
132559882Sarchie		}
132659882Sarchie	}
132759882Sarchie	return (removed);
132859882Sarchie}
132959882Sarchie
133059882Sarchie/*
133159882Sarchie * Run the queue, restoring the queue invariants
133259882Sarchie */
133359882Sarchiestatic int
133459882Sarchieng_ppp_frag_process(node_p node)
133559882Sarchie{
133670784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
133759882Sarchie	struct mbuf *m;
133859882Sarchie	meta_p meta;
133970700Sjulian	item_p item;
134059882Sarchie
134159882Sarchie	/* Deliver any deliverable packets */
134259882Sarchie	while (ng_ppp_check_packet(node)) {
134359882Sarchie		ng_ppp_get_packet(node, &m, &meta);
134470700Sjulian		item = ng_package_data(m, meta);
134570700Sjulian		ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, item);
134659882Sarchie	}
134759882Sarchie
134859882Sarchie	/* Delete dead fragments and try again */
134959882Sarchie	if (ng_ppp_frag_trim(node)) {
135059882Sarchie		while (ng_ppp_check_packet(node)) {
135159882Sarchie			ng_ppp_get_packet(node, &m, &meta);
135270700Sjulian			item = ng_package_data(m, meta);
135370700Sjulian			ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, item);
135459882Sarchie		}
135559882Sarchie	}
135659882Sarchie
135759882Sarchie	/* Check for stale fragments while we're here */
135859882Sarchie	ng_ppp_frag_checkstale(node);
135959882Sarchie
136059882Sarchie	/* Check queue length */
136159882Sarchie	if (priv->qlen > MP_MAX_QUEUE_LEN) {
136259882Sarchie		struct ng_ppp_frag *qent;
136359882Sarchie		int i;
136459882Sarchie
136559882Sarchie		/* Get oldest fragment */
136668761Smckusick		KASSERT(!TAILQ_EMPTY(&priv->frags),
136787599Sobrien		    ("%s: empty q", __func__));
136868761Smckusick		qent = TAILQ_FIRST(&priv->frags);
136959882Sarchie
137059882Sarchie		/* Bump MSEQ if necessary */
137166775Sarchie		if (MP_RECV_SEQ_DIFF(priv, priv->mseq, qent->seq) < 0) {
137259882Sarchie			priv->mseq = qent->seq;
137359882Sarchie			for (i = 0; i < priv->numActiveLinks; i++) {
137459882Sarchie				struct ng_ppp_link *const alink =
137559882Sarchie				    &priv->links[priv->activeLinks[i]];
137659882Sarchie
137766775Sarchie				if (MP_RECV_SEQ_DIFF(priv,
137859882Sarchie				    alink->seq, priv->mseq) < 0)
137959882Sarchie					alink->seq = priv->mseq;
138059882Sarchie			}
138159882Sarchie		}
138259882Sarchie
138359882Sarchie		/* Drop it */
138459882Sarchie		priv->bundleStats.dropFragments++;
138568761Smckusick		TAILQ_REMOVE(&priv->frags, qent, f_qent);
138670700Sjulian		NG_FREE_M(qent->data);
138770700Sjulian		NG_FREE_META(qent->meta);
138870870Sjulian		FREE(qent, M_NETGRAPH_PPP);
138959882Sarchie		priv->qlen--;
139059882Sarchie
139159882Sarchie		/* Process queue again */
139259882Sarchie		return ng_ppp_frag_process(node);
139352639Sarchie	}
139452639Sarchie
139559882Sarchie	/* Done */
139659882Sarchie	return (0);
139752639Sarchie}
139852639Sarchie
139952639Sarchie/*
140059882Sarchie * Check for 'stale' completed packets that need to be delivered
140159882Sarchie *
140259882Sarchie * If a link goes down or has a temporary failure, MSEQ can get
140359882Sarchie * "stuck", because no new incoming fragments appear on that link.
140459882Sarchie * This can cause completed packets to never get delivered if
140559882Sarchie * their sequence numbers are all > MSEQ + 1.
140659882Sarchie *
140759882Sarchie * This routine checks how long all of the completed packets have
140859882Sarchie * been sitting in the queue, and if too long, removes fragments
140959882Sarchie * from the queue and increments MSEQ to allow them to be delivered.
141059882Sarchie */
141159882Sarchiestatic void
141259882Sarchieng_ppp_frag_checkstale(node_p node)
141359882Sarchie{
141470784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
141559882Sarchie	struct ng_ppp_frag *qent, *beg, *end;
141659882Sarchie	struct timeval now, age;
141759882Sarchie	struct mbuf *m;
141859882Sarchie	meta_p meta;
141959882Sarchie	int i, seq;
142070700Sjulian	item_p item;
142159882Sarchie
142259882Sarchie	now.tv_sec = 0;			/* uninitialized state */
142359882Sarchie	while (1) {
142459882Sarchie
142559882Sarchie		/* If queue is empty, we're done */
142668761Smckusick		if (TAILQ_EMPTY(&priv->frags))
142759882Sarchie			break;
142859882Sarchie
142959882Sarchie		/* Find the first complete packet in the queue */
143059882Sarchie		beg = end = NULL;
143168761Smckusick		seq = TAILQ_FIRST(&priv->frags)->seq;
143268761Smckusick		TAILQ_FOREACH(qent, &priv->frags, f_qent) {
143359882Sarchie			if (qent->first)
143459882Sarchie				beg = qent;
143559882Sarchie			else if (qent->seq != seq)
143659882Sarchie				beg = NULL;
143759882Sarchie			if (beg != NULL && qent->last) {
143859882Sarchie				end = qent;
143959882Sarchie				break;
144059882Sarchie			}
144166775Sarchie			seq = MP_NEXT_RECV_SEQ(priv, seq);
144259882Sarchie		}
144359882Sarchie
144459882Sarchie		/* If none found, exit */
144559882Sarchie		if (end == NULL)
144659882Sarchie			break;
144759882Sarchie
144859882Sarchie		/* Get current time (we assume we've been up for >= 1 second) */
144959882Sarchie		if (now.tv_sec == 0)
145059882Sarchie			getmicrouptime(&now);
145159882Sarchie
145259882Sarchie		/* Check if packet has been queued too long */
145359882Sarchie		age = now;
145459882Sarchie		timevalsub(&age, &beg->timestamp);
145559882Sarchie		if (timevalcmp(&age, &ng_ppp_max_staleness, < ))
145659882Sarchie			break;
145759882Sarchie
145859882Sarchie		/* Throw away junk fragments in front of the completed packet */
145968761Smckusick		while ((qent = TAILQ_FIRST(&priv->frags)) != beg) {
146068761Smckusick			KASSERT(!TAILQ_EMPTY(&priv->frags),
146187599Sobrien			    ("%s: empty q", __func__));
146259882Sarchie			priv->bundleStats.dropFragments++;
146368761Smckusick			TAILQ_REMOVE(&priv->frags, qent, f_qent);
146470700Sjulian			NG_FREE_M(qent->data);
146570700Sjulian			NG_FREE_META(qent->meta);
146670870Sjulian			FREE(qent, M_NETGRAPH_PPP);
146759882Sarchie			priv->qlen--;
146859882Sarchie		}
146959882Sarchie
147059882Sarchie		/* Extract completed packet */
147159882Sarchie		ng_ppp_get_packet(node, &m, &meta);
147259882Sarchie
147359882Sarchie		/* Bump MSEQ if necessary */
147466775Sarchie		if (MP_RECV_SEQ_DIFF(priv, priv->mseq, end->seq) < 0) {
147559882Sarchie			priv->mseq = end->seq;
147659882Sarchie			for (i = 0; i < priv->numActiveLinks; i++) {
147759882Sarchie				struct ng_ppp_link *const alink =
147859882Sarchie				    &priv->links[priv->activeLinks[i]];
147959882Sarchie
148066775Sarchie				if (MP_RECV_SEQ_DIFF(priv,
148159882Sarchie				    alink->seq, priv->mseq) < 0)
148259882Sarchie					alink->seq = priv->mseq;
148359882Sarchie			}
148459882Sarchie		}
148559882Sarchie
148659882Sarchie		/* Deliver packet */
148770700Sjulian		item = ng_package_data(m, meta);
148870700Sjulian		ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, item);
148959882Sarchie	}
149059882Sarchie}
149159882Sarchie
149259882Sarchie/*
149359882Sarchie * Periodically call ng_ppp_frag_checkstale()
149459882Sarchie */
149559882Sarchiestatic void
149659882Sarchieng_ppp_frag_timeout(void *arg)
149759882Sarchie{
149859882Sarchie	const node_p node = arg;
149970784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
150059882Sarchie	int s = splnet();
150159882Sarchie
150259882Sarchie	/* Handle the race where shutdown happens just before splnet() above */
150370784Sjulian	if (NG_NODE_NOT_VALID(node)) {
150470784Sjulian		NG_NODE_UNREF(node);
150559882Sarchie		splx(s);
150659882Sarchie		return;
150759882Sarchie	}
150859882Sarchie
150959882Sarchie	/* Reset timer state after timeout */
151087599Sobrien	KASSERT(priv->timerActive, ("%s: !timerActive", __func__));
151159882Sarchie	priv->timerActive = 0;
151287599Sobrien	KASSERT(node->nd_refs > 1, ("%s: nd_refs=%d", __func__, node->nd_refs));
151370784Sjulian	NG_NODE_UNREF(node);
151459882Sarchie
151559882Sarchie	/* Start timer again */
151659882Sarchie	ng_ppp_start_frag_timer(node);
151759882Sarchie
151859882Sarchie	/* Scan the fragment queue */
151959882Sarchie	ng_ppp_frag_checkstale(node);
152059882Sarchie	splx(s);
152159882Sarchie}
152259882Sarchie
152359882Sarchie/*
152452639Sarchie * Deliver a frame out on the bundle, i.e., figure out how to fragment
152552639Sarchie * the frame across the individual PPP links and do so.
152652639Sarchie */
152752639Sarchiestatic int
152852639Sarchieng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta)
152952639Sarchie{
153070784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
153192298Sarchie	const int hdr_len = priv->conf.xmitShortSeq ? 2 : 4;
153252639Sarchie	int distrib[NG_PPP_MAX_LINKS];
153352639Sarchie	int firstFragment;
153452639Sarchie	int activeLinkNum;
153570700Sjulian	item_p item;
153652639Sarchie
153752639Sarchie	/* At least one link must be active */
153852639Sarchie	if (priv->numActiveLinks == 0) {
153970700Sjulian		NG_FREE_M(m);
154070700Sjulian		NG_FREE_META(meta);
154152639Sarchie		return (ENETDOWN);
154252639Sarchie	}
154352639Sarchie
154452639Sarchie	/* Round-robin strategy */
154552639Sarchie	if (priv->conf.enableRoundRobin || m->m_pkthdr.len < MP_MIN_FRAG_LEN) {
154652639Sarchie		activeLinkNum = priv->lastLink++ % priv->numActiveLinks;
154752639Sarchie		bzero(&distrib, priv->numActiveLinks * sizeof(distrib[0]));
154852639Sarchie		distrib[activeLinkNum] = m->m_pkthdr.len;
154952639Sarchie		goto deliver;
155052639Sarchie	}
155152639Sarchie
155252639Sarchie	/* Strategy when all links are equivalent (optimize the common case) */
155352639Sarchie	if (priv->allLinksEqual) {
155452639Sarchie		const int fraction = m->m_pkthdr.len / priv->numActiveLinks;
155552639Sarchie		int i, remain;
155652639Sarchie
155752639Sarchie		for (i = 0; i < priv->numActiveLinks; i++)
155852639Sarchie			distrib[priv->lastLink++ % priv->numActiveLinks]
155952639Sarchie			    = fraction;
156052639Sarchie		remain = m->m_pkthdr.len - (fraction * priv->numActiveLinks);
156152639Sarchie		while (remain > 0) {
156252639Sarchie			distrib[priv->lastLink++ % priv->numActiveLinks]++;
156352639Sarchie			remain--;
156452639Sarchie		}
156552639Sarchie		goto deliver;
156652639Sarchie	}
156752639Sarchie
156852639Sarchie	/* Strategy when all links are not equivalent */
156952639Sarchie	ng_ppp_mp_strategy(node, m->m_pkthdr.len, distrib);
157052639Sarchie
157152639Sarchiedeliver:
157252639Sarchie	/* Update stats */
157352639Sarchie	priv->bundleStats.xmitFrames++;
157452639Sarchie	priv->bundleStats.xmitOctets += m->m_pkthdr.len;
157552639Sarchie
157652639Sarchie	/* Send alloted portions of frame out on the link(s) */
157752639Sarchie	for (firstFragment = 1, activeLinkNum = priv->numActiveLinks - 1;
157852639Sarchie	    activeLinkNum >= 0; activeLinkNum--) {
157952639Sarchie		const int linkNum = priv->activeLinks[activeLinkNum];
158059882Sarchie		struct ng_ppp_link *const link = &priv->links[linkNum];
158152639Sarchie
158252639Sarchie		/* Deliver fragment(s) out the next link */
158352639Sarchie		for ( ; distrib[activeLinkNum] > 0; firstFragment = 0) {
158452639Sarchie			int len, lastFragment, error;
158552639Sarchie			struct mbuf *m2;
158652639Sarchie			meta_p meta2;
158752639Sarchie
158852639Sarchie			/* Calculate fragment length; don't exceed link MTU */
158952639Sarchie			len = distrib[activeLinkNum];
159092298Sarchie			if (len > link->conf.mru - hdr_len)
159192298Sarchie				len = link->conf.mru - hdr_len;
159252639Sarchie			distrib[activeLinkNum] -= len;
159352639Sarchie			lastFragment = (len == m->m_pkthdr.len);
159452639Sarchie
159552639Sarchie			/* Split off next fragment as "m2" */
159652639Sarchie			m2 = m;
159752639Sarchie			if (!lastFragment) {
159852639Sarchie				struct mbuf *n = m_split(m, len, M_NOWAIT);
159952639Sarchie
160052639Sarchie				if (n == NULL) {
160170700Sjulian					NG_FREE_M(m);
160270700Sjulian					NG_FREE_META(meta);
160352639Sarchie					return (ENOMEM);
160452639Sarchie				}
160552639Sarchie				m = n;
160652639Sarchie			}
160752639Sarchie
160852639Sarchie			/* Prepend MP header */
160952639Sarchie			if (priv->conf.xmitShortSeq) {
161052639Sarchie				u_int16_t shdr;
161152639Sarchie
161259882Sarchie				shdr = priv->xseq;
161359882Sarchie				priv->xseq =
161466775Sarchie				    (priv->xseq + 1) & MP_SHORT_SEQ_MASK;
161552639Sarchie				if (firstFragment)
161652639Sarchie					shdr |= MP_SHORT_FIRST_FLAG;
161752639Sarchie				if (lastFragment)
161852639Sarchie					shdr |= MP_SHORT_LAST_FLAG;
161953075Sarchie				shdr = htons(shdr);
162053075Sarchie				m2 = ng_ppp_prepend(m2, &shdr, 2);
162152639Sarchie			} else {
162252639Sarchie				u_int32_t lhdr;
162352639Sarchie
162459882Sarchie				lhdr = priv->xseq;
162559882Sarchie				priv->xseq =
162666775Sarchie				    (priv->xseq + 1) & MP_LONG_SEQ_MASK;
162752639Sarchie				if (firstFragment)
162852639Sarchie					lhdr |= MP_LONG_FIRST_FLAG;
162952639Sarchie				if (lastFragment)
163052639Sarchie					lhdr |= MP_LONG_LAST_FLAG;
163153075Sarchie				lhdr = htonl(lhdr);
163253075Sarchie				m2 = ng_ppp_prepend(m2, &lhdr, 4);
163352639Sarchie			}
163452639Sarchie			if (m2 == NULL) {
163552639Sarchie				if (!lastFragment)
163652639Sarchie					m_freem(m);
163752639Sarchie				NG_FREE_META(meta);
163852639Sarchie				return (ENOBUFS);
163952639Sarchie			}
164052639Sarchie
164152639Sarchie			/* Copy the meta information, if any */
164259882Sarchie			meta2 = lastFragment ? meta : ng_copy_meta(meta);
164352639Sarchie
164452639Sarchie			/* Send fragment */
164570700Sjulian			item = ng_package_data(m2, meta2);
164670700Sjulian			error = ng_ppp_output(node, 0, PROT_MP, linkNum, item);
164752639Sarchie			if (error != 0) {
164870700Sjulian				if (!lastFragment) {
164970700Sjulian					NG_FREE_M(m);
165070700Sjulian					NG_FREE_META(meta);
165170700Sjulian				}
165252639Sarchie				return (error);
165352639Sarchie			}
165452639Sarchie		}
165552639Sarchie	}
165652639Sarchie
165752639Sarchie	/* Done */
165852639Sarchie	return (0);
165952639Sarchie}
166052639Sarchie
166152639Sarchie/*
166252639Sarchie * Computing the optimal fragmentation
166352639Sarchie * -----------------------------------
166452639Sarchie *
166552639Sarchie * This routine tries to compute the optimal fragmentation pattern based
166652639Sarchie * on each link's latency, bandwidth, and calculated additional latency.
166752639Sarchie * The latter quantity is the additional latency caused by previously
166852639Sarchie * written data that has not been transmitted yet.
166952639Sarchie *
167052639Sarchie * This algorithm is only useful when not all of the links have the
167152639Sarchie * same latency and bandwidth values.
167252639Sarchie *
167352639Sarchie * The essential idea is to make the last bit of each fragment of the
167452639Sarchie * frame arrive at the opposite end at the exact same time. This greedy
167552639Sarchie * algorithm is optimal, in that no other scheduling could result in any
167652639Sarchie * packet arriving any sooner unless packets are delivered out of order.
167752639Sarchie *
167852639Sarchie * Suppose link i has bandwidth b_i (in tens of bytes per milisecond) and
167952639Sarchie * latency l_i (in miliseconds). Consider the function function f_i(t)
168052639Sarchie * which is equal to the number of bytes that will have arrived at
168152639Sarchie * the peer after t miliseconds if we start writing continuously at
168252639Sarchie * time t = 0. Then f_i(t) = b_i * (t - l_i) = ((b_i * t) - (l_i * b_i).
168352639Sarchie * That is, f_i(t) is a line with slope b_i and y-intersect -(l_i * b_i).
168452639Sarchie * Note that the y-intersect is always <= zero because latency can't be
168552639Sarchie * negative.  Note also that really the function is f_i(t) except when
168652639Sarchie * f_i(t) is negative, in which case the function is zero.  To take
168752639Sarchie * care of this, let Q_i(t) = { if (f_i(t) > 0) return 1; else return 0; }.
168852639Sarchie * So the actual number of bytes that will have arrived at the peer after
168952639Sarchie * t miliseconds is f_i(t) * Q_i(t).
169052639Sarchie *
169152639Sarchie * At any given time, each link has some additional latency a_i >= 0
169252639Sarchie * due to previously written fragment(s) which are still in the queue.
169352639Sarchie * This value is easily computed from the time since last transmission,
169452639Sarchie * the previous latency value, the number of bytes written, and the
169552639Sarchie * link's bandwidth.
169652639Sarchie *
169752639Sarchie * Assume that l_i includes any a_i already, and that the links are
169852639Sarchie * sorted by latency, so that l_i <= l_{i+1}.
169952639Sarchie *
170052639Sarchie * Let N be the total number of bytes in the current frame we are sending.
170152639Sarchie *
170252639Sarchie * Suppose we were to start writing bytes at time t = 0 on all links
170352639Sarchie * simultaneously, which is the most we can possibly do.  Then let
170452639Sarchie * F(t) be equal to the total number of bytes received by the peer
170552639Sarchie * after t miliseconds. Then F(t) = Sum_i (f_i(t) * Q_i(t)).
170652639Sarchie *
170752639Sarchie * Our goal is simply this: fragment the frame across the links such
170852639Sarchie * that the peer is able to reconstruct the completed frame as soon as
170952639Sarchie * possible, i.e., at the least possible value of t. Call this value t_0.
171052639Sarchie *
171152639Sarchie * Then it follows that F(t_0) = N. Our strategy is first to find the value
171252639Sarchie * of t_0, and then deduce how many bytes to write to each link.
171352639Sarchie *
171452639Sarchie * Rewriting F(t_0):
171552639Sarchie *
171652639Sarchie *   t_0 = ( N + Sum_i ( l_i * b_i * Q_i(t_0) ) ) / Sum_i ( b_i * Q_i(t_0) )
171752639Sarchie *
171852639Sarchie * Now, we note that Q_i(t) is constant for l_i <= t <= l_{i+1}. t_0 will
171952639Sarchie * lie in one of these ranges.  To find it, we just need to find the i such
172052639Sarchie * that F(l_i) <= N <= F(l_{i+1}).  Then we compute all the constant values
172152639Sarchie * for Q_i() in this range, plug in the remaining values, solving for t_0.
172252639Sarchie *
172352639Sarchie * Once t_0 is known, then the number of bytes to send on link i is
172452639Sarchie * just f_i(t_0) * Q_i(t_0).
172552639Sarchie *
172652639Sarchie * In other words, we start allocating bytes to the links one at a time.
172752639Sarchie * We keep adding links until the frame is completely sent.  Some links
172852639Sarchie * may not get any bytes because their latency is too high.
172952639Sarchie *
173052639Sarchie * Is all this work really worth the trouble?  Depends on the situation.
173152639Sarchie * The bigger the ratio of computer speed to link speed, and the more
173252639Sarchie * important total bundle latency is (e.g., for interactive response time),
173352639Sarchie * the more it's worth it.  There is however the cost of calling this
173452639Sarchie * function for every frame.  The running time is O(n^2) where n is the
173552639Sarchie * number of links that receive a non-zero number of bytes.
173652639Sarchie *
173752639Sarchie * Since latency is measured in miliseconds, the "resolution" of this
173852639Sarchie * algorithm is one milisecond.
173952639Sarchie *
174052639Sarchie * To avoid this algorithm altogether, configure all links to have the
174152639Sarchie * same latency and bandwidth.
174252639Sarchie */
174352639Sarchiestatic void
174452639Sarchieng_ppp_mp_strategy(node_p node, int len, int *distrib)
174552639Sarchie{
174670784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
174752639Sarchie	int latency[NG_PPP_MAX_LINKS];
174852639Sarchie	int sortByLatency[NG_PPP_MAX_LINKS];
174959882Sarchie	int activeLinkNum;
175052639Sarchie	int t0, total, topSum, botSum;
175152639Sarchie	struct timeval now;
175252639Sarchie	int i, numFragments;
175352639Sarchie
175452639Sarchie	/* If only one link, this gets real easy */
175552639Sarchie	if (priv->numActiveLinks == 1) {
175652639Sarchie		distrib[0] = len;
175752639Sarchie		return;
175852639Sarchie	}
175952639Sarchie
176052639Sarchie	/* Get current time */
176159882Sarchie	getmicrouptime(&now);
176252639Sarchie
176352639Sarchie	/* Compute latencies for each link at this point in time */
176452639Sarchie	for (activeLinkNum = 0;
176552639Sarchie	    activeLinkNum < priv->numActiveLinks; activeLinkNum++) {
176659882Sarchie		struct ng_ppp_link *alink;
176752639Sarchie		struct timeval diff;
176852639Sarchie		int xmitBytes;
176952639Sarchie
177052639Sarchie		/* Start with base latency value */
177159882Sarchie		alink = &priv->links[priv->activeLinks[activeLinkNum]];
177259882Sarchie		latency[activeLinkNum] = alink->conf.latency;
177352639Sarchie		sortByLatency[activeLinkNum] = activeLinkNum;	/* see below */
177452639Sarchie
177552639Sarchie		/* Any additional latency? */
177659882Sarchie		if (alink->bytesInQueue == 0)
177752639Sarchie			continue;
177852639Sarchie
177952639Sarchie		/* Compute time delta since last write */
178052639Sarchie		diff = now;
178159882Sarchie		timevalsub(&diff, &alink->lastWrite);
178252639Sarchie		if (now.tv_sec < 0 || diff.tv_sec >= 10) {	/* sanity */
178359882Sarchie			alink->bytesInQueue = 0;
178452639Sarchie			continue;
178552639Sarchie		}
178652639Sarchie
178752639Sarchie		/* How many bytes could have transmitted since last write? */
178859882Sarchie		xmitBytes = (alink->conf.bandwidth * diff.tv_sec)
178959882Sarchie		    + (alink->conf.bandwidth * (diff.tv_usec / 1000)) / 100;
179059882Sarchie		alink->bytesInQueue -= xmitBytes;
179159882Sarchie		if (alink->bytesInQueue < 0)
179259882Sarchie			alink->bytesInQueue = 0;
179352419Sjulian		else
179452639Sarchie			latency[activeLinkNum] +=
179559882Sarchie			    (100 * alink->bytesInQueue) / alink->conf.bandwidth;
179652419Sjulian	}
179752639Sarchie
179859882Sarchie	/* Sort active links by latency */
179952639Sarchie	compareLatencies = latency;
180052639Sarchie	qsort(sortByLatency,
180152639Sarchie	    priv->numActiveLinks, sizeof(*sortByLatency), ng_ppp_intcmp);
180252639Sarchie	compareLatencies = NULL;
180352639Sarchie
180452639Sarchie	/* Find the interval we need (add links in sortByLatency[] order) */
180552639Sarchie	for (numFragments = 1;
180652639Sarchie	    numFragments < priv->numActiveLinks; numFragments++) {
180752639Sarchie		for (total = i = 0; i < numFragments; i++) {
180852639Sarchie			int flowTime;
180952639Sarchie
181052639Sarchie			flowTime = latency[sortByLatency[numFragments]]
181152639Sarchie			    - latency[sortByLatency[i]];
181259882Sarchie			total += ((flowTime * priv->links[
181359882Sarchie			    priv->activeLinks[sortByLatency[i]]].conf.bandwidth)
181452639Sarchie			    	+ 99) / 100;
181552639Sarchie		}
181652639Sarchie		if (total >= len)
181752639Sarchie			break;
181852639Sarchie	}
181952639Sarchie
182052639Sarchie	/* Solve for t_0 in that interval */
182152639Sarchie	for (topSum = botSum = i = 0; i < numFragments; i++) {
182259882Sarchie		int bw = priv->links[
182359882Sarchie		    priv->activeLinks[sortByLatency[i]]].conf.bandwidth;
182452639Sarchie
182552639Sarchie		topSum += latency[sortByLatency[i]] * bw;	/* / 100 */
182652639Sarchie		botSum += bw;					/* / 100 */
182752639Sarchie	}
182852639Sarchie	t0 = ((len * 100) + topSum + botSum / 2) / botSum;
182952639Sarchie
183052639Sarchie	/* Compute f_i(t_0) all i */
183152639Sarchie	bzero(distrib, priv->numActiveLinks * sizeof(*distrib));
183252639Sarchie	for (total = i = 0; i < numFragments; i++) {
183359882Sarchie		int bw = priv->links[
183459882Sarchie		    priv->activeLinks[sortByLatency[i]]].conf.bandwidth;
183552639Sarchie
183652639Sarchie		distrib[sortByLatency[i]] =
183752639Sarchie		    (bw * (t0 - latency[sortByLatency[i]]) + 50) / 100;
183852639Sarchie		total += distrib[sortByLatency[i]];
183952639Sarchie	}
184052639Sarchie
184152766Sarchie	/* Deal with any rounding error */
184252766Sarchie	if (total < len) {
184359882Sarchie		struct ng_ppp_link *fastLink =
184459882Sarchie		    &priv->links[priv->activeLinks[sortByLatency[0]]];
184552639Sarchie		int fast = 0;
184652639Sarchie
184752766Sarchie		/* Find the fastest link */
184852639Sarchie		for (i = 1; i < numFragments; i++) {
184959882Sarchie			struct ng_ppp_link *const link =
185059882Sarchie			    &priv->links[priv->activeLinks[sortByLatency[i]]];
185159882Sarchie
185259882Sarchie			if (link->conf.bandwidth > fastLink->conf.bandwidth) {
185352639Sarchie				fast = i;
185459882Sarchie				fastLink = link;
185559882Sarchie			}
185652639Sarchie		}
185752639Sarchie		distrib[sortByLatency[fast]] += len - total;
185852766Sarchie	} else while (total > len) {
185959882Sarchie		struct ng_ppp_link *slowLink =
186059882Sarchie		    &priv->links[priv->activeLinks[sortByLatency[0]]];
186152766Sarchie		int delta, slow = 0;
186252639Sarchie
186352766Sarchie		/* Find the slowest link that still has bytes to remove */
186452766Sarchie		for (i = 1; i < numFragments; i++) {
186559882Sarchie			struct ng_ppp_link *const link =
186659882Sarchie			    &priv->links[priv->activeLinks[sortByLatency[i]]];
186759882Sarchie
186852766Sarchie			if (distrib[sortByLatency[slow]] == 0
186952766Sarchie			  || (distrib[sortByLatency[i]] > 0
187059882Sarchie			    && link->conf.bandwidth <
187159882Sarchie			      slowLink->conf.bandwidth)) {
187252766Sarchie				slow = i;
187359882Sarchie				slowLink = link;
187459882Sarchie			}
187552766Sarchie		}
187652766Sarchie		delta = total - len;
187752766Sarchie		if (delta > distrib[sortByLatency[slow]])
187852766Sarchie			delta = distrib[sortByLatency[slow]];
187952766Sarchie		distrib[sortByLatency[slow]] -= delta;
188052766Sarchie		total -= delta;
188152639Sarchie	}
188252419Sjulian}
188352419Sjulian
188452419Sjulian/*
188552639Sarchie * Compare two integers
188652419Sjulian */
188752639Sarchiestatic int
188852639Sarchieng_ppp_intcmp(const void *v1, const void *v2)
188952419Sjulian{
189052639Sarchie	const int index1 = *((const int *) v1);
189152639Sarchie	const int index2 = *((const int *) v2);
189252419Sjulian
189352639Sarchie	return compareLatencies[index1] - compareLatencies[index2];
189452639Sarchie}
189552639Sarchie
189652639Sarchie/*
189752639Sarchie * Prepend a possibly compressed PPP protocol number in front of a frame
189852639Sarchie */
189952639Sarchiestatic struct mbuf *
190052639Sarchieng_ppp_addproto(struct mbuf *m, int proto, int compOK)
190152639Sarchie{
190253075Sarchie	if (compOK && PROT_COMPRESSABLE(proto)) {
190353075Sarchie		u_char pbyte = (u_char)proto;
190452639Sarchie
190553075Sarchie		return ng_ppp_prepend(m, &pbyte, 1);
190653075Sarchie	} else {
190753075Sarchie		u_int16_t pword = htons((u_int16_t)proto);
190853075Sarchie
190953075Sarchie		return ng_ppp_prepend(m, &pword, 2);
191053075Sarchie	}
191153075Sarchie}
191253075Sarchie
191353075Sarchie/*
191453075Sarchie * Prepend some bytes to an mbuf
191553075Sarchie */
191653075Sarchiestatic struct mbuf *
191753075Sarchieng_ppp_prepend(struct mbuf *m, const void *buf, int len)
191853075Sarchie{
191953075Sarchie	M_PREPEND(m, len, M_NOWAIT);
192053075Sarchie	if (m == NULL || (m->m_len < len && (m = m_pullup(m, len)) == NULL))
192152639Sarchie		return (NULL);
192253075Sarchie	bcopy(buf, mtod(m, u_char *), len);
192352639Sarchie	return (m);
192452639Sarchie}
192552639Sarchie
192652639Sarchie/*
192752639Sarchie * Update private information that is derived from other private information
192852639Sarchie */
192952639Sarchiestatic void
193052639Sarchieng_ppp_update(node_p node, int newConf)
193152639Sarchie{
193270784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
193352639Sarchie	int i;
193452639Sarchie
193552639Sarchie	/* Update active status for VJ Compression */
193652639Sarchie	priv->vjCompHooked = priv->hooks[HOOK_INDEX_VJC_IP] != NULL
193752639Sarchie	    && priv->hooks[HOOK_INDEX_VJC_COMP] != NULL
193852639Sarchie	    && priv->hooks[HOOK_INDEX_VJC_UNCOMP] != NULL
193952639Sarchie	    && priv->hooks[HOOK_INDEX_VJC_VJIP] != NULL;
194052639Sarchie
194152639Sarchie	/* Increase latency for each link an amount equal to one MP header */
194252639Sarchie	if (newConf) {
194352639Sarchie		for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
194452639Sarchie			int hdrBytes;
194552639Sarchie
194659882Sarchie			hdrBytes = (priv->links[i].conf.enableACFComp ? 0 : 2)
194759882Sarchie			    + (priv->links[i].conf.enableProtoComp ? 1 : 2)
194852639Sarchie			    + (priv->conf.xmitShortSeq ? 2 : 4);
194959882Sarchie			priv->links[i].conf.latency +=
195059882Sarchie			    ((hdrBytes * priv->links[i].conf.bandwidth) + 50)
195152639Sarchie				/ 100;
195252639Sarchie		}
195352419Sjulian	}
195452639Sarchie
195552639Sarchie	/* Update list of active links */
195652639Sarchie	bzero(&priv->activeLinks, sizeof(priv->activeLinks));
195752639Sarchie	priv->numActiveLinks = 0;
195852639Sarchie	priv->allLinksEqual = 1;
195952639Sarchie	for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
196059882Sarchie		struct ng_ppp_link *const link = &priv->links[i];
196153088Sarchie
196259882Sarchie		/* Is link active? */
196359882Sarchie		if (link->conf.enableLink && link->hook != NULL) {
196459882Sarchie			struct ng_ppp_link *link0;
196559882Sarchie
196659882Sarchie			/* Add link to list of active links */
196752639Sarchie			priv->activeLinks[priv->numActiveLinks++] = i;
196859882Sarchie			link0 = &priv->links[priv->activeLinks[0]];
196959882Sarchie
197059882Sarchie			/* Determine if all links are still equal */
197159882Sarchie			if (link->conf.latency != link0->conf.latency
197259882Sarchie			  || link->conf.bandwidth != link0->conf.bandwidth)
197352639Sarchie				priv->allLinksEqual = 0;
197459882Sarchie
197559882Sarchie			/* Initialize rec'd sequence number */
197659882Sarchie			if (link->seq == MP_NOSEQ) {
197759882Sarchie				link->seq = (link == link0) ?
197859882Sarchie				    MP_INITIAL_SEQ : link0->seq;
197959882Sarchie			}
198059882Sarchie		} else
198159882Sarchie			link->seq = MP_NOSEQ;
198252639Sarchie	}
198352639Sarchie
198459882Sarchie	/* Update MP state as multi-link is active or not */
198559882Sarchie	if (priv->conf.enableMultilink && priv->numActiveLinks > 0)
198659882Sarchie		ng_ppp_start_frag_timer(node);
198759882Sarchie	else {
198859882Sarchie		ng_ppp_stop_frag_timer(node);
198959882Sarchie		ng_ppp_frag_reset(node);
199059882Sarchie		priv->xseq = MP_INITIAL_SEQ;
199159882Sarchie		priv->mseq = MP_INITIAL_SEQ;
199259882Sarchie		for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
199359882Sarchie			struct ng_ppp_link *const link = &priv->links[i];
199459882Sarchie
199559882Sarchie			bzero(&link->lastWrite, sizeof(link->lastWrite));
199659882Sarchie			link->bytesInQueue = 0;
199759882Sarchie			link->seq = MP_NOSEQ;
199859882Sarchie		}
199952639Sarchie	}
200052419Sjulian}
200152419Sjulian
200252639Sarchie/*
200352639Sarchie * Determine if a new configuration would represent a valid change
200452639Sarchie * from the current configuration and link activity status.
200552639Sarchie */
200652639Sarchiestatic int
200759882Sarchieng_ppp_config_valid(node_p node, const struct ng_ppp_node_conf *newConf)
200852639Sarchie{
200970784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
201052639Sarchie	int i, newNumLinksActive;
201152639Sarchie
201252639Sarchie	/* Check per-link config and count how many links would be active */
201352639Sarchie	for (newNumLinksActive = i = 0; i < NG_PPP_MAX_LINKS; i++) {
201459882Sarchie		if (newConf->links[i].enableLink && priv->links[i].hook != NULL)
201552912Sarchie			newNumLinksActive++;
201652912Sarchie		if (!newConf->links[i].enableLink)
201752912Sarchie			continue;
201852639Sarchie		if (newConf->links[i].mru < MP_MIN_LINK_MRU)
201952639Sarchie			return (0);
202052639Sarchie		if (newConf->links[i].bandwidth == 0)
202152639Sarchie			return (0);
202252639Sarchie		if (newConf->links[i].bandwidth > NG_PPP_MAX_BANDWIDTH)
202352639Sarchie			return (0);
202452639Sarchie		if (newConf->links[i].latency > NG_PPP_MAX_LATENCY)
202552639Sarchie			return (0);
202652639Sarchie	}
202752639Sarchie
202852639Sarchie	/* Check bundle parameters */
202959882Sarchie	if (newConf->bund.enableMultilink && newConf->bund.mrru < MP_MIN_MRRU)
203052639Sarchie		return (0);
203152639Sarchie
203252639Sarchie	/* Disallow changes to multi-link configuration while MP is active */
203352639Sarchie	if (priv->numActiveLinks > 0 && newNumLinksActive > 0) {
203459882Sarchie		if (!priv->conf.enableMultilink
203559882Sarchie				!= !newConf->bund.enableMultilink
203659882Sarchie		    || !priv->conf.xmitShortSeq != !newConf->bund.xmitShortSeq
203759882Sarchie		    || !priv->conf.recvShortSeq != !newConf->bund.recvShortSeq)
203852639Sarchie			return (0);
203952639Sarchie	}
204052639Sarchie
204152912Sarchie	/* At most one link can be active unless multi-link is enabled */
204259882Sarchie	if (!newConf->bund.enableMultilink && newNumLinksActive > 1)
204352912Sarchie		return (0);
204452912Sarchie
204552912Sarchie	/* Configuration change would be valid */
204652639Sarchie	return (1);
204752639Sarchie}
204852639Sarchie
204952639Sarchie/*
205052639Sarchie * Free all entries in the fragment queue
205152639Sarchie */
205252639Sarchiestatic void
205359882Sarchieng_ppp_frag_reset(node_p node)
205452639Sarchie{
205570784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
205654755Sarchie	struct ng_ppp_frag *qent, *qnext;
205752639Sarchie
205868761Smckusick	for (qent = TAILQ_FIRST(&priv->frags); qent; qent = qnext) {
205968761Smckusick		qnext = TAILQ_NEXT(qent, f_qent);
206070700Sjulian		NG_FREE_M(qent->data);
206170700Sjulian		NG_FREE_META(qent->meta);
206270870Sjulian		FREE(qent, M_NETGRAPH_PPP);
206352639Sarchie	}
206468761Smckusick	TAILQ_INIT(&priv->frags);
206559882Sarchie	priv->qlen = 0;
206652639Sarchie}
206752639Sarchie
206859882Sarchie/*
206959882Sarchie * Start fragment queue timer
207059882Sarchie */
207159882Sarchiestatic void
207259882Sarchieng_ppp_start_frag_timer(node_p node)
207359882Sarchie{
207470784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
207559882Sarchie
207659882Sarchie	if (!priv->timerActive) {
207759882Sarchie		priv->fragTimer = timeout(ng_ppp_frag_timeout,
207859882Sarchie		    node, MP_FRAGTIMER_INTERVAL);
207959882Sarchie		priv->timerActive = 1;
208070784Sjulian		NG_NODE_REF(node);
208159882Sarchie	}
208259882Sarchie}
208359882Sarchie
208459882Sarchie/*
208559882Sarchie * Stop fragment queue timer
208659882Sarchie */
208759882Sarchiestatic void
208859882Sarchieng_ppp_stop_frag_timer(node_p node)
208959882Sarchie{
209070784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
209159882Sarchie
209259882Sarchie	if (priv->timerActive) {
209359882Sarchie		untimeout(ng_ppp_frag_timeout, node, priv->fragTimer);
209459882Sarchie		priv->timerActive = 0;
209570784Sjulian		KASSERT(node->nd_refs > 1,
209687599Sobrien		    ("%s: nd_refs=%d", __func__, node->nd_refs));
209770784Sjulian		NG_NODE_UNREF(node);
209859882Sarchie	}
209959882Sarchie}
210059882Sarchie
2101