1139823Simp/*-
259882Sarchie * Copyright (c) 1996-2000 Whistle Communications, Inc.
352419Sjulian * All rights reserved.
4166093Sglebius *
552419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
652419Sjulian * redistribution of this software, in source or object code forms, with or
752419Sjulian * without modifications are expressly permitted by Whistle Communications;
852419Sjulian * provided, however, that:
952419Sjulian * 1. Any and all reproductions of the source or object code must include the
1052419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1152419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1252419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1352419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1452419Sjulian *    such appears in the above copyright notice or in the software.
15166093Sglebius *
1652419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
1752419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
1852419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
1952419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2052419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2152419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2252419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2352419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2452419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2552419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2652419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
2752419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
2852419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
2952419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3052419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3152419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3252419Sjulian * OF SUCH DAMAGE.
3352419Sjulian *
34166093Sglebius * Copyright (c) 2007 Alexander Motin <mav@alkar.net>
35166093Sglebius * All rights reserved.
3652419Sjulian *
37166093Sglebius * Redistribution and use in source and binary forms, with or without
38166093Sglebius * modification, are permitted provided that the following conditions
39166093Sglebius * are met:
40166093Sglebius * 1. Redistributions of source code must retain the above copyright
41166093Sglebius *    notice unmodified, this list of conditions, and the following
42166093Sglebius *    disclaimer.
43166093Sglebius * 2. Redistributions in binary form must reproduce the above copyright
44166093Sglebius *    notice, this list of conditions and the following disclaimer in the
45166093Sglebius *    documentation and/or other materials provided with the distribution.
46166093Sglebius *
47166093Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
48166093Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49166093Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50166093Sglebius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
51166093Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52166093Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53166093Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54166093Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55166093Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56166093Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57166093Sglebius * SUCH DAMAGE.
58166093Sglebius *
59166093Sglebius * Authors: Archie Cobbs <archie@freebsd.org>, Alexander Motin <mav@alkar.net>
60166093Sglebius *
6152419Sjulian * $FreeBSD$
6252752Sjulian * $Whistle: ng_ppp.c,v 1.24 1999/11/01 09:24:52 julian Exp $
6352419Sjulian */
6452419Sjulian
6552419Sjulian/*
66166093Sglebius * PPP node type data-flow.
67166093Sglebius *
68166093Sglebius *       hook      xmit        layer         recv      hook
69166093Sglebius *              ------------------------------------
70166093Sglebius *       inet ->                                    -> inet
71166093Sglebius *       ipv6 ->                                    -> ipv6
72166093Sglebius *        ipx ->               proto                -> ipx
73166093Sglebius *      atalk ->                                    -> atalk
74166093Sglebius *     bypass ->                                    -> bypass
75166093Sglebius *              -hcomp_xmit()----------proto_recv()-
76166093Sglebius *     vjc_ip <-                                    <- vjc_ip
77166093Sglebius *   vjc_comp ->         header compression         -> vjc_comp
78166093Sglebius * vjc_uncomp ->                                    -> vjc_uncomp
79166234Sglebius *   vjc_vjip ->
80166093Sglebius *              -comp_xmit()-----------hcomp_recv()-
81166093Sglebius *   compress <-            compression             <- decompress
82166093Sglebius *   compress ->                                    -> decompress
83166093Sglebius *              -crypt_xmit()-----------comp_recv()-
84166093Sglebius *    encrypt <-             encryption             <- decrypt
85166093Sglebius *    encrypt ->                                    -> decrypt
86166093Sglebius *              -ml_xmit()-------------crypt_recv()-
87166093Sglebius *                           multilink
88166093Sglebius *              -link_xmit()--------------ml_recv()-
89166093Sglebius *      linkX <-               link                 <- linkX
90166093Sglebius *
9152419Sjulian */
9252419Sjulian
9352419Sjulian#include <sys/param.h>
9452419Sjulian#include <sys/systm.h>
9552419Sjulian#include <sys/kernel.h>
96114216Skan#include <sys/limits.h>
9759882Sarchie#include <sys/time.h>
9852419Sjulian#include <sys/mbuf.h>
9952419Sjulian#include <sys/malloc.h>
100206021Smav#include <sys/endian.h>
10152419Sjulian#include <sys/errno.h>
10252843Sphk#include <sys/ctype.h>
10352419Sjulian
10452419Sjulian#include <netgraph/ng_message.h>
10552419Sjulian#include <netgraph/netgraph.h>
10653913Sarchie#include <netgraph/ng_parse.h>
10752419Sjulian#include <netgraph/ng_ppp.h>
10852639Sarchie#include <netgraph/ng_vjc.h>
10952419Sjulian
11070870Sjulian#ifdef NG_SEPARATE_MALLOC
111227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_PPP, "netgraph_ppp", "netgraph ppp node");
11270870Sjulian#else
11370870Sjulian#define M_NETGRAPH_PPP M_NETGRAPH
11470870Sjulian#endif
11570870Sjulian
11652419Sjulian#define PROT_VALID(p)		(((p) & 0x0101) == 0x0001)
11752816Sarchie#define PROT_COMPRESSABLE(p)	(((p) & 0xff00) == 0x0000)
11852419Sjulian
11952639Sarchie/* Some PPP protocol numbers we're interested in */
120166093Sglebius#define PROT_ATALK		0x0029
12152639Sarchie#define PROT_COMPD		0x00fd
12252639Sarchie#define PROT_CRYPTD		0x0053
12352639Sarchie#define PROT_IP			0x0021
12459882Sarchie#define PROT_IPV6		0x0057
12552816Sarchie#define PROT_IPX		0x002b
12653075Sarchie#define PROT_LCP		0xc021
12752639Sarchie#define PROT_MP			0x003d
12852639Sarchie#define PROT_VJCOMP		0x002d
12952639Sarchie#define PROT_VJUNCOMP		0x002f
13052419Sjulian
13152639Sarchie/* Multilink PPP definitions */
13252639Sarchie#define MP_INITIAL_SEQ		0		/* per RFC 1990 */
13352639Sarchie#define MP_MIN_LINK_MRU		32
13452639Sarchie
13552639Sarchie#define MP_SHORT_SEQ_MASK	0x00000fff	/* short seq # mask */
13652639Sarchie#define MP_SHORT_SEQ_HIBIT	0x00000800	/* short seq # high bit */
13752639Sarchie#define MP_SHORT_FIRST_FLAG	0x00008000	/* first fragment in frame */
13852639Sarchie#define MP_SHORT_LAST_FLAG	0x00004000	/* last fragment in frame */
13952639Sarchie
14052639Sarchie#define MP_LONG_SEQ_MASK	0x00ffffff	/* long seq # mask */
14152639Sarchie#define MP_LONG_SEQ_HIBIT	0x00800000	/* long seq # high bit */
14252639Sarchie#define MP_LONG_FIRST_FLAG	0x80000000	/* first fragment in frame */
14352639Sarchie#define MP_LONG_LAST_FLAG	0x40000000	/* last fragment in frame */
14452639Sarchie
14566775Sarchie#define MP_NOSEQ		0x7fffffff	/* impossible sequence number */
14659882Sarchie
14752639Sarchie/* Sign extension of MP sequence numbers */
14866775Sarchie#define MP_SHORT_EXTEND(s)	(((s) & MP_SHORT_SEQ_HIBIT) ?		\
14966775Sarchie				    ((s) | ~MP_SHORT_SEQ_MASK)		\
15066775Sarchie				    : ((s) & MP_SHORT_SEQ_MASK))
15166775Sarchie#define MP_LONG_EXTEND(s)	(((s) & MP_LONG_SEQ_HIBIT) ?		\
15266775Sarchie				    ((s) | ~MP_LONG_SEQ_MASK)		\
15366775Sarchie				    : ((s) & MP_LONG_SEQ_MASK))
15452639Sarchie
15566775Sarchie/* Comparision of MP sequence numbers. Note: all sequence numbers
15666775Sarchie   except priv->xseq are stored with the sign bit extended. */
15766775Sarchie#define MP_SHORT_SEQ_DIFF(x,y)	MP_SHORT_EXTEND((x) - (y))
15866775Sarchie#define MP_LONG_SEQ_DIFF(x,y)	MP_LONG_EXTEND((x) - (y))
15952639Sarchie
16066775Sarchie#define MP_RECV_SEQ_DIFF(priv,x,y)					\
16166775Sarchie				((priv)->conf.recvShortSeq ?		\
16266775Sarchie				    MP_SHORT_SEQ_DIFF((x), (y)) :	\
16352639Sarchie				    MP_LONG_SEQ_DIFF((x), (y)))
16452639Sarchie
16566775Sarchie/* Increment receive sequence number */
16690594Sarchie#define MP_NEXT_RECV_SEQ(priv,seq)					\
16790594Sarchie				((priv)->conf.recvShortSeq ?		\
16890594Sarchie				    MP_SHORT_EXTEND((seq) + 1) :	\
16990594Sarchie				    MP_LONG_EXTEND((seq) + 1))
17059882Sarchie
171166093Sglebius/* Don't fragment transmitted packets to parts smaller than this */
172166093Sglebius#define MP_MIN_FRAG_LEN		32
17359882Sarchie
17459882Sarchie/* Maximum fragment reasssembly queue length */
17559882Sarchie#define MP_MAX_QUEUE_LEN	128
17659882Sarchie
17759882Sarchie/* Fragment queue scanner period */
17859882Sarchie#define MP_FRAGTIMER_INTERVAL	(hz/2)
17959882Sarchie
180168895Smav/* Average link overhead. XXX: Should be given by user-level */
181168895Smav#define MP_AVERAGE_LINK_OVERHEAD	16
182168895Smav
183166093Sglebius/* Keep this equal to ng_ppp_hook_names lower! */
184166093Sglebius#define HOOK_INDEX_MAX		13
185166093Sglebius
18652639Sarchie/* We store incoming fragments this way */
18752639Sarchiestruct ng_ppp_frag {
18859882Sarchie	int				seq;		/* fragment seq# */
189166093Sglebius	uint8_t				first;		/* First in packet? */
190166093Sglebius	uint8_t				last;		/* Last in packet? */
19159882Sarchie	struct timeval			timestamp;	/* time of reception */
19259882Sarchie	struct mbuf			*data;		/* Fragment data */
19368761Smckusick	TAILQ_ENTRY(ng_ppp_frag)	f_qent;		/* Fragment queue */
19452639Sarchie};
19552639Sarchie
19659882Sarchie/* Per-link private information */
19759882Sarchiestruct ng_ppp_link {
19859882Sarchie	struct ng_ppp_link_conf	conf;		/* link configuration */
199171688Smav	struct ng_ppp_link_stat64	stats;	/* link stats */
20059882Sarchie	hook_p			hook;		/* connection to link data */
20166775Sarchie	int32_t			seq;		/* highest rec'd seq# - MSEQ */
202166093Sglebius	uint32_t		latency;	/* calculated link latency */
203166093Sglebius	struct timeval		lastWrite;	/* time of last write for MP */
204166093Sglebius	int			bytesInQueue;	/* bytes in the output queue for MP */
20559882Sarchie};
20659882Sarchie
20759882Sarchie/* Total per-node private information */
20853406Sarchiestruct ng_ppp_private {
20959882Sarchie	struct ng_ppp_bund_conf	conf;			/* bundle config */
210171688Smav	struct ng_ppp_link_stat64	bundleStats;	/* bundle stats */
21159882Sarchie	struct ng_ppp_link	links[NG_PPP_MAX_LINKS];/* per-link info */
21266775Sarchie	int32_t			xseq;			/* next out MP seq # */
21366775Sarchie	int32_t			mseq;			/* min links[i].seq */
214166093Sglebius	uint16_t		activeLinks[NG_PPP_MAX_LINKS];	/* indicies */
215166093Sglebius	uint16_t		numActiveLinks;		/* how many links up */
216166093Sglebius	uint16_t		lastLink;		/* for round robin */
217166093Sglebius	uint8_t			vjCompHooked;		/* VJ comp hooked up? */
218166093Sglebius	uint8_t			allLinksEqual;		/* all xmit the same? */
21959882Sarchie	hook_p			hooks[HOOK_INDEX_MAX];	/* non-link hooks */
220175696Smav	struct ng_ppp_frag	fragsmem[MP_MAX_QUEUE_LEN]; /* fragments storage */
22168761Smckusick	TAILQ_HEAD(ng_ppp_fraglist, ng_ppp_frag)	/* fragment queue */
22259882Sarchie				frags;
223175696Smav	TAILQ_HEAD(ng_ppp_fragfreelist, ng_ppp_frag)	/* free fragment queue */
224175696Smav				fragsfree;
225138479Sglebius	struct callout		fragTimer;		/* fraq queue check */
226171681Smav	struct mtx		rmtx;			/* recv mutex */
227171681Smav	struct mtx		xmtx;			/* xmit mutex */
22852419Sjulian};
22953406Sarchietypedef struct ng_ppp_private *priv_p;
23052419Sjulian
23152419Sjulian/* Netgraph node methods */
23252752Sjulianstatic ng_constructor_t	ng_ppp_constructor;
23352752Sjulianstatic ng_rcvmsg_t	ng_ppp_rcvmsg;
23470700Sjulianstatic ng_shutdown_t	ng_ppp_shutdown;
23552752Sjulianstatic ng_newhook_t	ng_ppp_newhook;
23652752Sjulianstatic ng_rcvdata_t	ng_ppp_rcvdata;
23752752Sjulianstatic ng_disconnect_t	ng_ppp_disconnect;
23852419Sjulian
239166093Sglebiusstatic ng_rcvdata_t	ng_ppp_rcvdata_inet;
240166093Sglebiusstatic ng_rcvdata_t	ng_ppp_rcvdata_ipv6;
241166093Sglebiusstatic ng_rcvdata_t	ng_ppp_rcvdata_ipx;
242166093Sglebiusstatic ng_rcvdata_t	ng_ppp_rcvdata_atalk;
243166093Sglebiusstatic ng_rcvdata_t	ng_ppp_rcvdata_bypass;
244166093Sglebius
245166093Sglebiusstatic ng_rcvdata_t	ng_ppp_rcvdata_vjc_ip;
246166093Sglebiusstatic ng_rcvdata_t	ng_ppp_rcvdata_vjc_comp;
247166093Sglebiusstatic ng_rcvdata_t	ng_ppp_rcvdata_vjc_uncomp;
248166093Sglebiusstatic ng_rcvdata_t	ng_ppp_rcvdata_vjc_vjip;
249166093Sglebius
250166093Sglebiusstatic ng_rcvdata_t	ng_ppp_rcvdata_compress;
251166093Sglebiusstatic ng_rcvdata_t	ng_ppp_rcvdata_decompress;
252166093Sglebius
253166093Sglebiusstatic ng_rcvdata_t	ng_ppp_rcvdata_encrypt;
254166093Sglebiusstatic ng_rcvdata_t	ng_ppp_rcvdata_decrypt;
255166093Sglebius
256166093Sglebius/* We use integer indicies to refer to the non-link hooks. */
257166093Sglebiusstatic const struct {
258166093Sglebius	char *const name;
259166093Sglebius	ng_rcvdata_t *fn;
260166093Sglebius} ng_ppp_hook_names[] = {
261166093Sglebius#define HOOK_INDEX_ATALK	0
262166093Sglebius	{ NG_PPP_HOOK_ATALK,	ng_ppp_rcvdata_atalk },
263166093Sglebius#define HOOK_INDEX_BYPASS	1
264166093Sglebius	{ NG_PPP_HOOK_BYPASS,	ng_ppp_rcvdata_bypass },
265166093Sglebius#define HOOK_INDEX_COMPRESS	2
266166093Sglebius	{ NG_PPP_HOOK_COMPRESS,	ng_ppp_rcvdata_compress },
267166093Sglebius#define HOOK_INDEX_ENCRYPT	3
268166093Sglebius	{ NG_PPP_HOOK_ENCRYPT,	ng_ppp_rcvdata_encrypt },
269166093Sglebius#define HOOK_INDEX_DECOMPRESS	4
270166093Sglebius	{ NG_PPP_HOOK_DECOMPRESS, ng_ppp_rcvdata_decompress },
271166093Sglebius#define HOOK_INDEX_DECRYPT	5
272166093Sglebius	{ NG_PPP_HOOK_DECRYPT,	ng_ppp_rcvdata_decrypt },
273166093Sglebius#define HOOK_INDEX_INET		6
274166093Sglebius	{ NG_PPP_HOOK_INET,	ng_ppp_rcvdata_inet },
275166093Sglebius#define HOOK_INDEX_IPX		7
276166093Sglebius	{ NG_PPP_HOOK_IPX,	ng_ppp_rcvdata_ipx },
277166093Sglebius#define HOOK_INDEX_VJC_COMP	8
278166093Sglebius	{ NG_PPP_HOOK_VJC_COMP,	ng_ppp_rcvdata_vjc_comp },
279166093Sglebius#define HOOK_INDEX_VJC_IP	9
280166093Sglebius	{ NG_PPP_HOOK_VJC_IP,	ng_ppp_rcvdata_vjc_ip },
281166093Sglebius#define HOOK_INDEX_VJC_UNCOMP	10
282166093Sglebius	{ NG_PPP_HOOK_VJC_UNCOMP, ng_ppp_rcvdata_vjc_uncomp },
283166093Sglebius#define HOOK_INDEX_VJC_VJIP	11
284166093Sglebius	{ NG_PPP_HOOK_VJC_VJIP,	ng_ppp_rcvdata_vjc_vjip },
285166093Sglebius#define HOOK_INDEX_IPV6		12
286166093Sglebius	{ NG_PPP_HOOK_IPV6,	ng_ppp_rcvdata_ipv6 },
287166093Sglebius	{ NULL, NULL }
288166093Sglebius};
289166093Sglebius
29052639Sarchie/* Helper functions */
291166093Sglebiusstatic int	ng_ppp_proto_recv(node_p node, item_p item, uint16_t proto,
292166093Sglebius		    uint16_t linkNum);
293166093Sglebiusstatic int	ng_ppp_hcomp_xmit(node_p node, item_p item, uint16_t proto);
294166093Sglebiusstatic int	ng_ppp_hcomp_recv(node_p node, item_p item, uint16_t proto,
295166093Sglebius		    uint16_t linkNum);
296166093Sglebiusstatic int	ng_ppp_comp_xmit(node_p node, item_p item, uint16_t proto);
297166093Sglebiusstatic int	ng_ppp_comp_recv(node_p node, item_p item, uint16_t proto,
298166093Sglebius		    uint16_t linkNum);
299166093Sglebiusstatic int	ng_ppp_crypt_xmit(node_p node, item_p item, uint16_t proto);
300166093Sglebiusstatic int	ng_ppp_crypt_recv(node_p node, item_p item, uint16_t proto,
301166093Sglebius		    uint16_t linkNum);
302166093Sglebiusstatic int	ng_ppp_mp_xmit(node_p node, item_p item, uint16_t proto);
303166093Sglebiusstatic int	ng_ppp_mp_recv(node_p node, item_p item, uint16_t proto,
304166093Sglebius		    uint16_t linkNum);
305166093Sglebiusstatic int	ng_ppp_link_xmit(node_p node, item_p item, uint16_t proto,
306171681Smav		    uint16_t linkNum, int plen);
307166093Sglebius
308166234Sglebiusstatic int	ng_ppp_bypass(node_p node, item_p item, uint16_t proto,
309166234Sglebius		    uint16_t linkNum);
310166234Sglebius
311168896Smavstatic void	ng_ppp_bump_mseq(node_p node, int32_t new_mseq);
312168896Smavstatic int	ng_ppp_frag_drop(node_p node);
31359882Sarchiestatic int	ng_ppp_check_packet(node_p node);
314131155Sjulianstatic void	ng_ppp_get_packet(node_p node, struct mbuf **mp);
315175698Smavstatic int	ng_ppp_frag_process(node_p node, item_p oitem);
31659882Sarchiestatic int	ng_ppp_frag_trim(node_p node);
317138479Sglebiusstatic void	ng_ppp_frag_timeout(node_p node, hook_p hook, void *arg1,
318166093Sglebius		    int arg2);
31959882Sarchiestatic void	ng_ppp_frag_checkstale(node_p node);
32059882Sarchiestatic void	ng_ppp_frag_reset(node_p node);
32152639Sarchiestatic void	ng_ppp_mp_strategy(node_p node, int len, int *distrib);
322132229Sglebiusstatic int	ng_ppp_intcmp(void *latency, const void *v1, const void *v2);
323166093Sglebiusstatic struct mbuf *ng_ppp_addproto(struct mbuf *m, uint16_t proto, int compOK);
324166093Sglebiusstatic struct mbuf *ng_ppp_cutproto(struct mbuf *m, uint16_t *proto);
325166093Sglebiusstatic struct mbuf *ng_ppp_prepend(struct mbuf *m, const void *buf, int len);
32652639Sarchiestatic int	ng_ppp_config_valid(node_p node,
327166093Sglebius		    const struct ng_ppp_node_conf *newConf);
32852639Sarchiestatic void	ng_ppp_update(node_p node, int newConf);
32959882Sarchiestatic void	ng_ppp_start_frag_timer(node_p node);
33059882Sarchiestatic void	ng_ppp_stop_frag_timer(node_p node);
33152419Sjulian
33266775Sarchie/* Parse type for struct ng_ppp_mp_state_type */
33366775Sarchiestatic const struct ng_parse_fixedarray_info ng_ppp_rseq_array_info = {
33466775Sarchie	&ng_parse_hint32_type,
33566775Sarchie	NG_PPP_MAX_LINKS
33666775Sarchie};
33766775Sarchiestatic const struct ng_parse_type ng_ppp_rseq_array_type = {
33866775Sarchie	&ng_parse_fixedarray_type,
33966775Sarchie	&ng_ppp_rseq_array_info,
34066775Sarchie};
34197685Sarchiestatic const struct ng_parse_struct_field ng_ppp_mp_state_type_fields[]
34266775Sarchie	= NG_PPP_MP_STATE_TYPE_INFO(&ng_ppp_rseq_array_type);
34366775Sarchiestatic const struct ng_parse_type ng_ppp_mp_state_type = {
34466775Sarchie	&ng_parse_struct_type,
34597685Sarchie	&ng_ppp_mp_state_type_fields
34666775Sarchie};
34766775Sarchie
34859882Sarchie/* Parse type for struct ng_ppp_link_conf */
34997685Sarchiestatic const struct ng_parse_struct_field ng_ppp_link_type_fields[]
35097685Sarchie	= NG_PPP_LINK_TYPE_INFO;
35153913Sarchiestatic const struct ng_parse_type ng_ppp_link_type = {
35253913Sarchie	&ng_parse_struct_type,
35397685Sarchie	&ng_ppp_link_type_fields
35453913Sarchie};
35553913Sarchie
35659882Sarchie/* Parse type for struct ng_ppp_bund_conf */
35797685Sarchiestatic const struct ng_parse_struct_field ng_ppp_bund_type_fields[]
35897685Sarchie	= NG_PPP_BUND_TYPE_INFO;
35959882Sarchiestatic const struct ng_parse_type ng_ppp_bund_type = {
36059882Sarchie	&ng_parse_struct_type,
36197685Sarchie	&ng_ppp_bund_type_fields
36259882Sarchie};
36359882Sarchie
36459882Sarchie/* Parse type for struct ng_ppp_node_conf */
36566775Sarchiestatic const struct ng_parse_fixedarray_info ng_ppp_array_info = {
36653913Sarchie	&ng_ppp_link_type,
36753913Sarchie	NG_PPP_MAX_LINKS
36853913Sarchie};
36953913Sarchiestatic const struct ng_parse_type ng_ppp_link_array_type = {
37053913Sarchie	&ng_parse_fixedarray_type,
37153913Sarchie	&ng_ppp_array_info,
37253913Sarchie};
37397685Sarchiestatic const struct ng_parse_struct_field ng_ppp_conf_type_fields[]
37459882Sarchie	= NG_PPP_CONFIG_TYPE_INFO(&ng_ppp_bund_type, &ng_ppp_link_array_type);
37559882Sarchiestatic const struct ng_parse_type ng_ppp_conf_type = {
37653913Sarchie	&ng_parse_struct_type,
37797685Sarchie	&ng_ppp_conf_type_fields
37853913Sarchie};
37953913Sarchie
38053913Sarchie/* Parse type for struct ng_ppp_link_stat */
38197685Sarchiestatic const struct ng_parse_struct_field ng_ppp_stats_type_fields[]
38297685Sarchie	= NG_PPP_STATS_TYPE_INFO;
38353913Sarchiestatic const struct ng_parse_type ng_ppp_stats_type = {
38453913Sarchie	&ng_parse_struct_type,
38597685Sarchie	&ng_ppp_stats_type_fields
38653913Sarchie};
38753913Sarchie
388171688Smav/* Parse type for struct ng_ppp_link_stat64 */
389171688Smavstatic const struct ng_parse_struct_field ng_ppp_stats64_type_fields[]
390171688Smav	= NG_PPP_STATS64_TYPE_INFO;
391171688Smavstatic const struct ng_parse_type ng_ppp_stats64_type = {
392171688Smav	&ng_parse_struct_type,
393171688Smav	&ng_ppp_stats64_type_fields
394171688Smav};
395171688Smav
39653913Sarchie/* List of commands and how to convert arguments to/from ASCII */
39753913Sarchiestatic const struct ng_cmdlist ng_ppp_cmds[] = {
39853913Sarchie	{
39953913Sarchie	  NGM_PPP_COOKIE,
40053913Sarchie	  NGM_PPP_SET_CONFIG,
40153913Sarchie	  "setconfig",
40259882Sarchie	  &ng_ppp_conf_type,
40353913Sarchie	  NULL
40453913Sarchie	},
40553913Sarchie	{
40653913Sarchie	  NGM_PPP_COOKIE,
40753913Sarchie	  NGM_PPP_GET_CONFIG,
40853913Sarchie	  "getconfig",
40953913Sarchie	  NULL,
41059882Sarchie	  &ng_ppp_conf_type
41153913Sarchie	},
41253913Sarchie	{
41353913Sarchie	  NGM_PPP_COOKIE,
41466775Sarchie	  NGM_PPP_GET_MP_STATE,
41566775Sarchie	  "getmpstate",
41666775Sarchie	  NULL,
41766775Sarchie	  &ng_ppp_mp_state_type
41866775Sarchie	},
41966775Sarchie	{
42066775Sarchie	  NGM_PPP_COOKIE,
42153913Sarchie	  NGM_PPP_GET_LINK_STATS,
42253913Sarchie	  "getstats",
42353913Sarchie	  &ng_parse_int16_type,
42453913Sarchie	  &ng_ppp_stats_type
42553913Sarchie	},
42653913Sarchie	{
42753913Sarchie	  NGM_PPP_COOKIE,
42853913Sarchie	  NGM_PPP_CLR_LINK_STATS,
42953913Sarchie	  "clrstats",
43053913Sarchie	  &ng_parse_int16_type,
43153913Sarchie	  NULL
43253913Sarchie	},
43353913Sarchie	{
43453913Sarchie	  NGM_PPP_COOKIE,
43553913Sarchie	  NGM_PPP_GETCLR_LINK_STATS,
43653913Sarchie	  "getclrstats",
43753913Sarchie	  &ng_parse_int16_type,
43853913Sarchie	  &ng_ppp_stats_type
43953913Sarchie	},
440171688Smav	{
441171688Smav	  NGM_PPP_COOKIE,
442171688Smav	  NGM_PPP_GET_LINK_STATS64,
443171688Smav	  "getstats64",
444171688Smav	  &ng_parse_int16_type,
445171688Smav	  &ng_ppp_stats64_type
446171688Smav	},
447171688Smav	{
448171688Smav	  NGM_PPP_COOKIE,
449171688Smav	  NGM_PPP_GETCLR_LINK_STATS64,
450171688Smav	  "getclrstats64",
451171688Smav	  &ng_parse_int16_type,
452171688Smav	  &ng_ppp_stats64_type
453171688Smav	},
45453913Sarchie	{ 0 }
45553913Sarchie};
45653913Sarchie
45752419Sjulian/* Node type descriptor */
45852639Sarchiestatic struct ng_type ng_ppp_typestruct = {
459129823Sjulian	.version =	NG_ABI_VERSION,
460129823Sjulian	.name =		NG_PPP_NODE_TYPE,
461129823Sjulian	.constructor =	ng_ppp_constructor,
462129823Sjulian	.rcvmsg =	ng_ppp_rcvmsg,
463129823Sjulian	.shutdown =	ng_ppp_shutdown,
464129823Sjulian	.newhook =	ng_ppp_newhook,
465129823Sjulian	.rcvdata =	ng_ppp_rcvdata,
466129823Sjulian	.disconnect =	ng_ppp_disconnect,
467129823Sjulian	.cmdlist =	ng_ppp_cmds,
46852419Sjulian};
46952639SarchieNETGRAPH_INIT(ppp, &ng_ppp_typestruct);
47052419Sjulian
47153075Sarchie/* Address and control field header */
472166093Sglebiusstatic const uint8_t ng_ppp_acf[2] = { 0xff, 0x03 };
47353075Sarchie
47459882Sarchie/* Maximum time we'll let a complete incoming packet sit in the queue */
47559882Sarchiestatic const struct timeval ng_ppp_max_staleness = { 2, 0 };	/* 2 seconds */
47659882Sarchie
47752419Sjulian#define ERROUT(x)	do { error = (x); goto done; } while (0)
47852419Sjulian
47952419Sjulian/************************************************************************
48052419Sjulian			NETGRAPH NODE STUFF
48152419Sjulian ************************************************************************/
48252419Sjulian
48352419Sjulian/*
48452639Sarchie * Node type constructor
48552419Sjulian */
48652419Sjulianstatic int
48770700Sjulianng_ppp_constructor(node_p node)
48852419Sjulian{
48952419Sjulian	priv_p priv;
49070700Sjulian	int i;
49152419Sjulian
49252419Sjulian	/* Allocate private structure */
493220768Sglebius	priv = malloc(sizeof(*priv), M_NETGRAPH_PPP, M_WAITOK | M_ZERO);
49452419Sjulian
49570784Sjulian	NG_NODE_SET_PRIVATE(node, priv);
49652419Sjulian
49752639Sarchie	/* Initialize state */
49868761Smckusick	TAILQ_INIT(&priv->frags);
499175696Smav	TAILQ_INIT(&priv->fragsfree);
500175696Smav	for (i = 0; i < MP_MAX_QUEUE_LEN; i++)
501175696Smav		TAILQ_INSERT_TAIL(&priv->fragsfree, &priv->fragsmem[i], f_qent);
50259882Sarchie	for (i = 0; i < NG_PPP_MAX_LINKS; i++)
50359882Sarchie		priv->links[i].seq = MP_NOSEQ;
504138479Sglebius	ng_callout_init(&priv->fragTimer);
50552639Sarchie
506171681Smav	mtx_init(&priv->rmtx, "ng_ppp_recv", NULL, MTX_DEF);
507171681Smav	mtx_init(&priv->xmtx, "ng_ppp_xmit", NULL, MTX_DEF);
508171681Smav
50952419Sjulian	/* Done */
51052419Sjulian	return (0);
51152419Sjulian}
51252419Sjulian
51352419Sjulian/*
51452419Sjulian * Give our OK for a hook to be added
51552419Sjulian */
51652419Sjulianstatic int
51752419Sjulianng_ppp_newhook(node_p node, hook_p hook, const char *name)
51852419Sjulian{
51970784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
520166093Sglebius	hook_p *hookPtr = NULL;
52152639Sarchie	int linkNum = -1;
52252639Sarchie	int hookIndex = -1;
52352419Sjulian
52452639Sarchie	/* Figure out which hook it is */
52552639Sarchie	if (strncmp(name, NG_PPP_HOOK_LINK_PREFIX,	/* a link hook? */
52652639Sarchie	    strlen(NG_PPP_HOOK_LINK_PREFIX)) == 0) {
52753648Sarchie		const char *cp;
52853648Sarchie		char *eptr;
52952419Sjulian
53052816Sarchie		cp = name + strlen(NG_PPP_HOOK_LINK_PREFIX);
53152816Sarchie		if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
53252639Sarchie			return (EINVAL);
53352816Sarchie		linkNum = (int)strtoul(cp, &eptr, 10);
53452816Sarchie		if (*eptr != '\0' || linkNum < 0 || linkNum >= NG_PPP_MAX_LINKS)
53552816Sarchie			return (EINVAL);
53659882Sarchie		hookPtr = &priv->links[linkNum].hook;
53752639Sarchie		hookIndex = ~linkNum;
538166093Sglebius
539166093Sglebius		/* See if hook is already connected. */
540166093Sglebius		if (*hookPtr != NULL)
541166093Sglebius			return (EISCONN);
542166093Sglebius
543166093Sglebius		/* Disallow more than one link unless multilink is enabled. */
544166093Sglebius		if (priv->links[linkNum].conf.enableLink &&
545166093Sglebius		    !priv->conf.enableMultilink && priv->numActiveLinks >= 1)
546166093Sglebius			return (ENODEV);
547166093Sglebius
54852639Sarchie	} else {				/* must be a non-link hook */
54952639Sarchie		int i;
55052639Sarchie
551166093Sglebius		for (i = 0; ng_ppp_hook_names[i].name != NULL; i++) {
552166093Sglebius			if (strcmp(name, ng_ppp_hook_names[i].name) == 0) {
55352639Sarchie				hookPtr = &priv->hooks[i];
55452639Sarchie				hookIndex = i;
55552639Sarchie				break;
55652639Sarchie			}
55752639Sarchie		}
558166093Sglebius		if (ng_ppp_hook_names[i].name == NULL)
55952639Sarchie			return (EINVAL);	/* no such hook */
56052639Sarchie
561166093Sglebius		/* See if hook is already connected */
562166093Sglebius		if (*hookPtr != NULL)
563166093Sglebius			return (EISCONN);
56452419Sjulian
565166093Sglebius		/* Every non-linkX hook have it's own function. */
566166093Sglebius		NG_HOOK_SET_RCVDATA(hook, ng_ppp_hook_names[i].fn);
567166093Sglebius	}
56852419Sjulian
56952419Sjulian	/* OK */
57052639Sarchie	*hookPtr = hook;
571106665Sjhb	NG_HOOK_SET_PRIVATE(hook, (void *)(intptr_t)hookIndex);
57252639Sarchie	ng_ppp_update(node, 0);
57352419Sjulian	return (0);
57452419Sjulian}
57552419Sjulian
57652419Sjulian/*
57752419Sjulian * Receive a control message
57852419Sjulian */
57952419Sjulianstatic int
58070700Sjulianng_ppp_rcvmsg(node_p node, item_p item, hook_p lasthook)
58152419Sjulian{
58270784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
58352419Sjulian	struct ng_mesg *resp = NULL;
58452419Sjulian	int error = 0;
58570700Sjulian	struct ng_mesg *msg;
58652419Sjulian
58770700Sjulian	NGI_GET_MSG(item, msg);
58852419Sjulian	switch (msg->header.typecookie) {
58952419Sjulian	case NGM_PPP_COOKIE:
59052419Sjulian		switch (msg->header.cmd) {
59152639Sarchie		case NGM_PPP_SET_CONFIG:
59252639Sarchie		    {
59359882Sarchie			struct ng_ppp_node_conf *const conf =
59459882Sarchie			    (struct ng_ppp_node_conf *)msg->data;
59559882Sarchie			int i;
59652639Sarchie
59752639Sarchie			/* Check for invalid or illegal config */
59859882Sarchie			if (msg->header.arglen != sizeof(*conf))
59952419Sjulian				ERROUT(EINVAL);
60059882Sarchie			if (!ng_ppp_config_valid(node, conf))
60152639Sarchie				ERROUT(EINVAL);
60259882Sarchie
60359882Sarchie			/* Copy config */
60459882Sarchie			priv->conf = conf->bund;
60559882Sarchie			for (i = 0; i < NG_PPP_MAX_LINKS; i++)
60659882Sarchie				priv->links[i].conf = conf->links[i];
60752639Sarchie			ng_ppp_update(node, 1);
60852419Sjulian			break;
60952639Sarchie		    }
61052639Sarchie		case NGM_PPP_GET_CONFIG:
61159882Sarchie		    {
61259882Sarchie			struct ng_ppp_node_conf *conf;
61359882Sarchie			int i;
61459882Sarchie
61559882Sarchie			NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT);
61652419Sjulian			if (resp == NULL)
61752419Sjulian				ERROUT(ENOMEM);
61859882Sarchie			conf = (struct ng_ppp_node_conf *)resp->data;
61959882Sarchie			conf->bund = priv->conf;
62059882Sarchie			for (i = 0; i < NG_PPP_MAX_LINKS; i++)
62159882Sarchie				conf->links[i] = priv->links[i].conf;
62252419Sjulian			break;
62359882Sarchie		    }
62466775Sarchie		case NGM_PPP_GET_MP_STATE:
62566775Sarchie		    {
62666775Sarchie			struct ng_ppp_mp_state *info;
62766775Sarchie			int i;
62866775Sarchie
62966775Sarchie			NG_MKRESPONSE(resp, msg, sizeof(*info), M_NOWAIT);
63066775Sarchie			if (resp == NULL)
63166775Sarchie				ERROUT(ENOMEM);
63266775Sarchie			info = (struct ng_ppp_mp_state *)resp->data;
63366775Sarchie			bzero(info, sizeof(*info));
63466775Sarchie			for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
63566775Sarchie				if (priv->links[i].seq != MP_NOSEQ)
63666775Sarchie					info->rseq[i] = priv->links[i].seq;
63766775Sarchie			}
63866775Sarchie			info->mseq = priv->mseq;
63966775Sarchie			info->xseq = priv->xseq;
64066775Sarchie			break;
64166775Sarchie		    }
64252639Sarchie		case NGM_PPP_GET_LINK_STATS:
64352639Sarchie		case NGM_PPP_CLR_LINK_STATS:
64452912Sarchie		case NGM_PPP_GETCLR_LINK_STATS:
645171688Smav		case NGM_PPP_GET_LINK_STATS64:
646171688Smav		case NGM_PPP_GETCLR_LINK_STATS64:
64752639Sarchie		    {
648171688Smav			struct ng_ppp_link_stat64 *stats;
649166093Sglebius			uint16_t linkNum;
65052639Sarchie
651171688Smav			/* Process request. */
652166093Sglebius			if (msg->header.arglen != sizeof(uint16_t))
65352639Sarchie				ERROUT(EINVAL);
654166093Sglebius			linkNum = *((uint16_t *) msg->data);
65552639Sarchie			if (linkNum >= NG_PPP_MAX_LINKS
65652639Sarchie			    && linkNum != NG_PPP_BUNDLE_LINKNUM)
65752639Sarchie				ERROUT(EINVAL);
65852639Sarchie			stats = (linkNum == NG_PPP_BUNDLE_LINKNUM) ?
65959882Sarchie			    &priv->bundleStats : &priv->links[linkNum].stats;
660171688Smav
661171688Smav			/* Make 64bit reply. */
662171688Smav			if (msg->header.cmd == NGM_PPP_GET_LINK_STATS64 ||
663171688Smav			    msg->header.cmd == NGM_PPP_GETCLR_LINK_STATS64) {
66452639Sarchie				NG_MKRESPONSE(resp, msg,
665171688Smav				    sizeof(struct ng_ppp_link_stat64), M_NOWAIT);
666171688Smav				if (resp == NULL)
667171688Smav					ERROUT(ENOMEM);
668171688Smav				bcopy(stats, resp->data, sizeof(*stats));
669171688Smav			} else
670171688Smav			/* Make 32bit reply. */
671171688Smav			if (msg->header.cmd == NGM_PPP_GET_LINK_STATS ||
672171688Smav			    msg->header.cmd == NGM_PPP_GETCLR_LINK_STATS) {
673171688Smav				struct ng_ppp_link_stat *rs;
674171688Smav				NG_MKRESPONSE(resp, msg,
67552639Sarchie				    sizeof(struct ng_ppp_link_stat), M_NOWAIT);
67652639Sarchie				if (resp == NULL)
67752639Sarchie					ERROUT(ENOMEM);
678171688Smav				rs = (struct ng_ppp_link_stat *)resp->data;
679171688Smav				/* Truncate 64->32 bits. */
680171688Smav				rs->xmitFrames = stats->xmitFrames;
681171688Smav				rs->xmitOctets = stats->xmitOctets;
682171688Smav				rs->recvFrames = stats->recvFrames;
683171688Smav				rs->recvOctets = stats->recvOctets;
684171688Smav				rs->badProtos = stats->badProtos;
685171688Smav				rs->runts = stats->runts;
686171688Smav				rs->dupFragments = stats->dupFragments;
687171688Smav				rs->dropFragments = stats->dropFragments;
68852912Sarchie			}
689171688Smav			/* Clear stats. */
690171688Smav			if (msg->header.cmd != NGM_PPP_GET_LINK_STATS &&
691171688Smav			    msg->header.cmd != NGM_PPP_GET_LINK_STATS64)
69252639Sarchie				bzero(stats, sizeof(*stats));
69352419Sjulian			break;
69452639Sarchie		    }
69552419Sjulian		default:
69652419Sjulian			error = EINVAL;
69752419Sjulian			break;
69852419Sjulian		}
69952419Sjulian		break;
70052639Sarchie	case NGM_VJC_COOKIE:
70159882Sarchie	    {
70270700Sjulian		/*
703166093Sglebius		 * Forward it to the vjc node. leave the
70470700Sjulian		 * old return address alone.
70570784Sjulian		 * If we have no hook, let NG_RESPOND_MSG
70670784Sjulian		 * clean up any remaining resources.
70770784Sjulian		 * Because we have no resp, the item will be freed
70870784Sjulian		 * along with anything it references. Don't
70970784Sjulian		 * let msg be freed twice.
71070700Sjulian		 */
71170700Sjulian		NGI_MSG(item) = msg;	/* put it back in the item */
71270784Sjulian		msg = NULL;
713166093Sglebius		if ((lasthook = priv->hooks[HOOK_INDEX_VJC_IP])) {
71470784Sjulian			NG_FWD_ITEM_HOOK(error, item, lasthook);
71570700Sjulian		}
71670700Sjulian		return (error);
71759882Sarchie	    }
71852419Sjulian	default:
71952419Sjulian		error = EINVAL;
72052419Sjulian		break;
72152419Sjulian	}
72252419Sjuliandone:
72370700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
72470700Sjulian	NG_FREE_MSG(msg);
72552419Sjulian	return (error);
72652419Sjulian}
72752419Sjulian
72852419Sjulian/*
729166093Sglebius * Destroy node
73052419Sjulian */
73152419Sjulianstatic int
732166093Sglebiusng_ppp_shutdown(node_p node)
73352419Sjulian{
734166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
735166093Sglebius
736166093Sglebius	/* Stop fragment queue timer */
737166093Sglebius	ng_ppp_stop_frag_timer(node);
738166093Sglebius
739166093Sglebius	/* Take down netgraph node */
740166093Sglebius	ng_ppp_frag_reset(node);
741171681Smav	mtx_destroy(&priv->rmtx);
742171681Smav	mtx_destroy(&priv->xmtx);
743166093Sglebius	bzero(priv, sizeof(*priv));
744184205Sdes	free(priv, M_NETGRAPH_PPP);
745166093Sglebius	NG_NODE_SET_PRIVATE(node, NULL);
746166093Sglebius	NG_NODE_UNREF(node);		/* let the node escape */
747166093Sglebius	return (0);
748166093Sglebius}
749166093Sglebius
750166093Sglebius/*
751166093Sglebius * Hook disconnection
752166093Sglebius */
753166093Sglebiusstatic int
754166093Sglebiusng_ppp_disconnect(hook_p hook)
755166093Sglebius{
75670784Sjulian	const node_p node = NG_HOOK_NODE(hook);
75770784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
758106665Sjhb	const int index = (intptr_t)NG_HOOK_PRIVATE(hook);
75952419Sjulian
760166093Sglebius	/* Zero out hook pointer */
761166093Sglebius	if (index < 0)
762166093Sglebius		priv->links[~index].hook = NULL;
763166093Sglebius	else
764166093Sglebius		priv->hooks[index] = NULL;
76552419Sjulian
766166093Sglebius	/* Update derived info (or go away if no hooks left). */
767166093Sglebius	if (NG_NODE_NUMHOOKS(node) > 0)
768166093Sglebius		ng_ppp_update(node, 0);
769166093Sglebius	else if (NG_NODE_IS_VALID(node))
770166093Sglebius		ng_rmnode_self(node);
77152419Sjulian
772166093Sglebius	return (0);
773166093Sglebius}
77452419Sjulian
775166093Sglebius/*
776166093Sglebius * Proto layer
777166093Sglebius */
77853075Sarchie
779166093Sglebius/*
780166093Sglebius * Receive data on a hook inet.
781166093Sglebius */
782166093Sglebiusstatic int
783166093Sglebiusng_ppp_rcvdata_inet(hook_p hook, item_p item)
784166093Sglebius{
785166093Sglebius	const node_p node = NG_HOOK_NODE(hook);
786166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
787166093Sglebius
788166093Sglebius	if (!priv->conf.enableIP) {
789166093Sglebius		NG_FREE_ITEM(item);
790166093Sglebius		return (ENXIO);
79152639Sarchie	}
792166093Sglebius	return (ng_ppp_hcomp_xmit(NG_HOOK_NODE(hook), item, PROT_IP));
793166093Sglebius}
79452419Sjulian
795166093Sglebius/*
796166093Sglebius * Receive data on a hook ipv6.
797166093Sglebius */
798166093Sglebiusstatic int
799166093Sglebiusng_ppp_rcvdata_ipv6(hook_p hook, item_p item)
800166093Sglebius{
801166093Sglebius	const node_p node = NG_HOOK_NODE(hook);
802166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
80352639Sarchie
804166093Sglebius	if (!priv->conf.enableIPv6) {
805166093Sglebius		NG_FREE_ITEM(item);
806166093Sglebius		return (ENXIO);
807166093Sglebius	}
808166093Sglebius	return (ng_ppp_hcomp_xmit(NG_HOOK_NODE(hook), item, PROT_IPV6));
809166093Sglebius}
810166093Sglebius
811166093Sglebius/*
812166093Sglebius * Receive data on a hook atalk.
813166093Sglebius */
814166093Sglebiusstatic int
815166093Sglebiusng_ppp_rcvdata_atalk(hook_p hook, item_p item)
816166093Sglebius{
817166093Sglebius	const node_p node = NG_HOOK_NODE(hook);
818166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
819166093Sglebius
820166093Sglebius	if (!priv->conf.enableAtalk) {
821166093Sglebius		NG_FREE_ITEM(item);
822166093Sglebius		return (ENXIO);
823166093Sglebius	}
824166093Sglebius	return (ng_ppp_hcomp_xmit(NG_HOOK_NODE(hook), item, PROT_ATALK));
825166093Sglebius}
826166093Sglebius
827166093Sglebius/*
828166093Sglebius * Receive data on a hook ipx
829166093Sglebius */
830166093Sglebiusstatic int
831166093Sglebiusng_ppp_rcvdata_ipx(hook_p hook, item_p item)
832166093Sglebius{
833166093Sglebius	const node_p node = NG_HOOK_NODE(hook);
834166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
835166093Sglebius
836166093Sglebius	if (!priv->conf.enableIPX) {
837166093Sglebius		NG_FREE_ITEM(item);
838166093Sglebius		return (ENXIO);
839166093Sglebius	}
840166093Sglebius	return (ng_ppp_hcomp_xmit(NG_HOOK_NODE(hook), item, PROT_IPX));
841166093Sglebius}
842166093Sglebius
843166093Sglebius/*
844166093Sglebius * Receive data on a hook bypass
845166093Sglebius */
846166093Sglebiusstatic int
847166093Sglebiusng_ppp_rcvdata_bypass(hook_p hook, item_p item)
848166093Sglebius{
849166093Sglebius	uint16_t linkNum;
850166093Sglebius	uint16_t proto;
851166093Sglebius	struct mbuf *m;
852166093Sglebius
853166093Sglebius	NGI_GET_M(item, m);
854166093Sglebius	if (m->m_pkthdr.len < 4) {
855166093Sglebius		NG_FREE_ITEM(item);
856166093Sglebius		return (EINVAL);
857166093Sglebius	}
858166093Sglebius	if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
859166093Sglebius		NG_FREE_ITEM(item);
860166093Sglebius		return (ENOBUFS);
861166093Sglebius	}
862206021Smav	linkNum = be16dec(mtod(m, uint8_t *));
863206021Smav	proto = be16dec(mtod(m, uint8_t *) + 2);
864166093Sglebius	m_adj(m, 4);
865166093Sglebius	NGI_M(item) = m;
866166093Sglebius
867166093Sglebius	if (linkNum == NG_PPP_BUNDLE_LINKNUM)
868166093Sglebius		return (ng_ppp_hcomp_xmit(NG_HOOK_NODE(hook), item, proto));
869166093Sglebius	else
870166093Sglebius		return (ng_ppp_link_xmit(NG_HOOK_NODE(hook), item, proto,
871171681Smav		    linkNum, 0));
872166093Sglebius}
873166093Sglebius
874166093Sglebiusstatic int
875166234Sglebiusng_ppp_bypass(node_p node, item_p item, uint16_t proto, uint16_t linkNum)
876166234Sglebius{
877166234Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
878166234Sglebius	uint16_t hdr[2];
879166234Sglebius	struct mbuf *m;
880166234Sglebius	int error;
881166234Sglebius
882166234Sglebius	if (priv->hooks[HOOK_INDEX_BYPASS] == NULL) {
883166234Sglebius	    NG_FREE_ITEM(item);
884166234Sglebius	    return (ENXIO);
885166234Sglebius	}
886166234Sglebius
887166234Sglebius	/* Add 4-byte bypass header. */
888166234Sglebius	hdr[0] = htons(linkNum);
889166234Sglebius	hdr[1] = htons(proto);
890166234Sglebius
891166234Sglebius	NGI_GET_M(item, m);
892166234Sglebius	if ((m = ng_ppp_prepend(m, &hdr, 4)) == NULL) {
893166234Sglebius		NG_FREE_ITEM(item);
894166234Sglebius		return (ENOBUFS);
895166234Sglebius	}
896166234Sglebius	NGI_M(item) = m;
897166234Sglebius
898166234Sglebius	/* Send packet out hook. */
899166234Sglebius	NG_FWD_ITEM_HOOK(error, item, priv->hooks[HOOK_INDEX_BYPASS]);
900166234Sglebius	return (error);
901166234Sglebius}
902166234Sglebius
903166234Sglebiusstatic int
904166093Sglebiusng_ppp_proto_recv(node_p node, item_p item, uint16_t proto, uint16_t linkNum)
905166093Sglebius{
906166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
907166093Sglebius	hook_p outHook = NULL;
908166093Sglebius	int error;
909206000Smav#ifdef ALIGNED_POINTER
910206000Smav	struct mbuf *m, *n;
911166093Sglebius
912206000Smav	NGI_GET_M(item, m);
913206000Smav	if (!ALIGNED_POINTER(mtod(m, caddr_t), uint32_t)) {
914206000Smav		n = m_defrag(m, M_NOWAIT);
915206000Smav		if (n == NULL) {
916206000Smav			m_freem(m);
917206000Smav			NG_FREE_ITEM(item);
918206000Smav			return (ENOBUFS);
919206000Smav		}
920206000Smav		m = n;
921206000Smav	}
922206000Smav	NGI_M(item) = m;
923206000Smav#endif /* ALIGNED_POINTER */
924166093Sglebius	switch (proto) {
925166093Sglebius	    case PROT_IP:
926166093Sglebius		if (priv->conf.enableIP)
927166093Sglebius		    outHook = priv->hooks[HOOK_INDEX_INET];
92852419Sjulian		break;
929166093Sglebius	    case PROT_IPV6:
930166093Sglebius		if (priv->conf.enableIPv6)
931166093Sglebius		    outHook = priv->hooks[HOOK_INDEX_IPV6];
93252639Sarchie		break;
933166093Sglebius	    case PROT_ATALK:
934166093Sglebius		if (priv->conf.enableAtalk)
935166093Sglebius		    outHook = priv->hooks[HOOK_INDEX_ATALK];
93659882Sarchie		break;
937166093Sglebius	    case PROT_IPX:
938166093Sglebius		if (priv->conf.enableIPX)
939166093Sglebius		    outHook = priv->hooks[HOOK_INDEX_IPX];
94052639Sarchie		break;
941166093Sglebius	}
942165580Sglebius
943166234Sglebius	if (outHook == NULL)
944166234Sglebius		return (ng_ppp_bypass(node, item, proto, linkNum));
945165580Sglebius
946166234Sglebius	/* Send packet out hook. */
947166234Sglebius	NG_FWD_ITEM_HOOK(error, item, outHook);
948166093Sglebius	return (error);
949166093Sglebius}
95052419Sjulian
951166093Sglebius/*
952166093Sglebius * Header compression layer
953166093Sglebius */
95452912Sarchie
955166093Sglebiusstatic int
956166093Sglebiusng_ppp_hcomp_xmit(node_p node, item_p item, uint16_t proto)
957166093Sglebius{
958166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
95952639Sarchie
960166093Sglebius	if (proto == PROT_IP &&
961166093Sglebius	    priv->conf.enableVJCompression &&
962166093Sglebius	    priv->vjCompHooked) {
963166093Sglebius		int error;
96453088Sarchie
965166093Sglebius		/* Send packet out hook. */
966166093Sglebius		NG_FWD_ITEM_HOOK(error, item, priv->hooks[HOOK_INDEX_VJC_IP]);
967166093Sglebius		return (error);
96852419Sjulian	}
96952419Sjulian
970166093Sglebius	return (ng_ppp_comp_xmit(node, item, proto));
97152419Sjulian}
97252419Sjulian
97352419Sjulian/*
974166093Sglebius * Receive data on a hook vjc_comp.
97552419Sjulian */
97652419Sjulianstatic int
977166093Sglebiusng_ppp_rcvdata_vjc_comp(hook_p hook, item_p item)
97852419Sjulian{
979166093Sglebius	const node_p node = NG_HOOK_NODE(hook);
98070784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
98152419Sjulian
982166093Sglebius	if (!priv->conf.enableVJCompression) {
983166093Sglebius		NG_FREE_ITEM(item);
984166093Sglebius		return (ENXIO);
985166093Sglebius	}
986166093Sglebius	return (ng_ppp_comp_xmit(node, item, PROT_VJCOMP));
987166093Sglebius}
98859882Sarchie
989166093Sglebius/*
990166093Sglebius * Receive data on a hook vjc_uncomp.
991166093Sglebius */
992166093Sglebiusstatic int
993166093Sglebiusng_ppp_rcvdata_vjc_uncomp(hook_p hook, item_p item)
994166093Sglebius{
995166093Sglebius	const node_p node = NG_HOOK_NODE(hook);
996166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
997166093Sglebius
998166093Sglebius	if (!priv->conf.enableVJCompression) {
999166093Sglebius		NG_FREE_ITEM(item);
1000166093Sglebius		return (ENXIO);
1001166093Sglebius	}
1002166093Sglebius	return (ng_ppp_comp_xmit(node, item, PROT_VJUNCOMP));
100352419Sjulian}
100452419Sjulian
100552419Sjulian/*
1006166093Sglebius * Receive data on a hook vjc_vjip.
100752419Sjulian */
100852419Sjulianstatic int
1009166093Sglebiusng_ppp_rcvdata_vjc_vjip(hook_p hook, item_p item)
101052419Sjulian{
101170784Sjulian	const node_p node = NG_HOOK_NODE(hook);
101270784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
101353406Sarchie
1014166093Sglebius	if (!priv->conf.enableVJCompression) {
1015166093Sglebius		NG_FREE_ITEM(item);
1016166093Sglebius		return (ENXIO);
1017166093Sglebius	}
1018166093Sglebius	return (ng_ppp_comp_xmit(node, item, PROT_IP));
1019166093Sglebius}
102053406Sarchie
1021166093Sglebiusstatic int
1022166093Sglebiusng_ppp_hcomp_recv(node_p node, item_p item, uint16_t proto, uint16_t linkNum)
1023166093Sglebius{
1024166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
1025166093Sglebius
1026166093Sglebius	if (priv->conf.enableVJDecompression && priv->vjCompHooked) {
1027166093Sglebius		hook_p outHook = NULL;
1028166093Sglebius
1029166093Sglebius		switch (proto) {
1030166093Sglebius		    case PROT_VJCOMP:
1031166093Sglebius			outHook = priv->hooks[HOOK_INDEX_VJC_COMP];
1032166093Sglebius			break;
1033166093Sglebius		    case PROT_VJUNCOMP:
1034166093Sglebius			outHook = priv->hooks[HOOK_INDEX_VJC_UNCOMP];
1035166093Sglebius			break;
103670700Sjulian		}
1037166093Sglebius
1038166093Sglebius		if (outHook) {
1039166234Sglebius			int error;
1040166234Sglebius
1041166093Sglebius			/* Send packet out hook. */
1042166093Sglebius			NG_FWD_ITEM_HOOK(error, item, outHook);
1043166093Sglebius			return (error);
1044166093Sglebius		}
104570700Sjulian	}
1046166093Sglebius
1047166093Sglebius	return (ng_ppp_proto_recv(node, item, proto, linkNum));
104852419Sjulian}
104952419Sjulian
1050166093Sglebius/*
1051166093Sglebius * Receive data on a hook vjc_ip.
1052166093Sglebius */
1053166093Sglebiusstatic int
1054166093Sglebiusng_ppp_rcvdata_vjc_ip(hook_p hook, item_p item)
1055166093Sglebius{
1056166093Sglebius	const node_p node = NG_HOOK_NODE(hook);
1057166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
105852419Sjulian
1059172186Smav	if (!priv->conf.enableVJDecompression) {
1060166093Sglebius		NG_FREE_ITEM(item);
1061166093Sglebius		return (ENXIO);
1062166093Sglebius	}
1063166093Sglebius	return (ng_ppp_proto_recv(node, item, PROT_IP, NG_PPP_BUNDLE_LINKNUM));
1064166093Sglebius}
1065166093Sglebius
106652419Sjulian/*
1067166093Sglebius * Compression layer
106852419Sjulian */
1069166093Sglebius
107052419Sjulianstatic int
1071166093Sglebiusng_ppp_comp_xmit(node_p node, item_p item, uint16_t proto)
107252419Sjulian{
107370784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
107452419Sjulian
1075166093Sglebius	if (priv->conf.enableCompression &&
1076166093Sglebius	    proto < 0x4000 &&
1077166093Sglebius	    proto != PROT_COMPD &&
1078166093Sglebius	    proto != PROT_CRYPTD &&
1079166093Sglebius	    priv->hooks[HOOK_INDEX_COMPRESS] != NULL) {
1080166093Sglebius	        struct mbuf *m;
1081166093Sglebius		int error;
108270700Sjulian
1083166093Sglebius	        NGI_GET_M(item, m);
1084166093Sglebius		if ((m = ng_ppp_addproto(m, proto, 0)) == NULL) {
108570700Sjulian			NG_FREE_ITEM(item);
108652639Sarchie			return (ENOBUFS);
108752639Sarchie		}
1088166093Sglebius		NGI_M(item) = m;
1089166093Sglebius
1090166093Sglebius		/* Send packet out hook. */
1091166093Sglebius		NG_FWD_ITEM_HOOK(error, item, priv->hooks[HOOK_INDEX_COMPRESS]);
1092166093Sglebius		return (error);
109352639Sarchie	}
1094166093Sglebius
1095166093Sglebius	return (ng_ppp_crypt_xmit(node, item, proto));
1096166093Sglebius}
1097166093Sglebius
1098166093Sglebius/*
1099166093Sglebius * Receive data on a hook compress.
1100166093Sglebius */
1101166093Sglebiusstatic int
1102166093Sglebiusng_ppp_rcvdata_compress(hook_p hook, item_p item)
1103166093Sglebius{
1104166093Sglebius	const node_p node = NG_HOOK_NODE(hook);
1105166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
1106166093Sglebius	uint16_t proto;
1107166093Sglebius
1108166093Sglebius	switch (priv->conf.enableCompression) {
1109166093Sglebius	    case NG_PPP_COMPRESS_NONE:
111070700Sjulian		NG_FREE_ITEM(item);
1111166093Sglebius		return (ENXIO);
1112166093Sglebius	    case NG_PPP_COMPRESS_FULL:
1113166093Sglebius		{
1114166093Sglebius			struct mbuf *m;
1115166093Sglebius
1116166093Sglebius			NGI_GET_M(item, m);
1117166093Sglebius			if ((m = ng_ppp_cutproto(m, &proto)) == NULL) {
1118166093Sglebius				NG_FREE_ITEM(item);
1119166093Sglebius				return (EIO);
1120166093Sglebius			}
1121166093Sglebius			NGI_M(item) = m;
1122166093Sglebius			if (!PROT_VALID(proto)) {
1123166093Sglebius				NG_FREE_ITEM(item);
1124166093Sglebius				return (EIO);
1125166093Sglebius			}
1126166093Sglebius		}
1127166093Sglebius		break;
1128166093Sglebius	    default:
1129166093Sglebius		proto = PROT_COMPD;
1130166093Sglebius		break;
113152639Sarchie	}
1132166093Sglebius	return (ng_ppp_crypt_xmit(node, item, proto));
1133166093Sglebius}
113452419Sjulian
1135166093Sglebiusstatic int
1136166093Sglebiusng_ppp_comp_recv(node_p node, item_p item, uint16_t proto, uint16_t linkNum)
1137166093Sglebius{
1138166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
113952912Sarchie
1140166093Sglebius	if (proto < 0x4000 &&
1141166093Sglebius	    ((proto == PROT_COMPD && priv->conf.enableDecompression) ||
1142166093Sglebius	    priv->conf.enableDecompression == NG_PPP_DECOMPRESS_FULL) &&
1143166093Sglebius	    priv->hooks[HOOK_INDEX_DECOMPRESS] != NULL) {
1144166093Sglebius		int error;
1145166093Sglebius
1146166093Sglebius		if (priv->conf.enableDecompression == NG_PPP_DECOMPRESS_FULL) {
1147166093Sglebius			struct mbuf *m;
1148166093Sglebius			NGI_GET_M(item, m);
1149165580Sglebius			if ((m = ng_ppp_addproto(m, proto, 0)) == NULL) {
1150165580Sglebius				NG_FREE_ITEM(item);
1151166093Sglebius				return (EIO);
1152165580Sglebius			}
1153166093Sglebius			NGI_M(item) = m;
115498063Sjulian		}
1155165580Sglebius
1156166093Sglebius		/* Send packet out hook. */
1157166093Sglebius		NG_FWD_ITEM_HOOK(error, item,
1158166093Sglebius		    priv->hooks[HOOK_INDEX_DECOMPRESS]);
1159166093Sglebius		return (error);
1160166234Sglebius	} else if (proto == PROT_COMPD) {
1161166234Sglebius		/* Disabled protos MUST be silently discarded, but
1162166234Sglebius		 * unsupported MUST not. Let user-level decide this. */
1163166234Sglebius		return (ng_ppp_bypass(node, item, proto, linkNum));
116452639Sarchie	}
116552639Sarchie
1166166093Sglebius	return (ng_ppp_hcomp_recv(node, item, proto, linkNum));
1167166093Sglebius}
116853075Sarchie
1169166093Sglebius/*
1170166093Sglebius * Receive data on a hook decompress.
1171166093Sglebius */
1172166093Sglebiusstatic int
1173166093Sglebiusng_ppp_rcvdata_decompress(hook_p hook, item_p item)
1174166093Sglebius{
1175166093Sglebius	const node_p node = NG_HOOK_NODE(hook);
1176166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
1177166093Sglebius	uint16_t proto;
1178166093Sglebius	struct mbuf *m;
1179166093Sglebius
1180166093Sglebius	if (!priv->conf.enableDecompression) {
1181166093Sglebius		NG_FREE_ITEM(item);
1182166093Sglebius		return (ENXIO);
1183166093Sglebius	}
1184166093Sglebius	NGI_GET_M(item, m);
1185166093Sglebius	if ((m = ng_ppp_cutproto(m, &proto)) == NULL) {
1186166093Sglebius	        NG_FREE_ITEM(item);
1187166093Sglebius	        return (EIO);
1188166093Sglebius	}
1189166093Sglebius	NGI_M(item) = m;
1190166093Sglebius	if (!PROT_VALID(proto)) {
1191166093Sglebius		priv->bundleStats.badProtos++;
1192166093Sglebius		NG_FREE_ITEM(item);
1193166093Sglebius		return (EIO);
1194166093Sglebius	}
1195166093Sglebius	return (ng_ppp_hcomp_recv(node, item, proto, NG_PPP_BUNDLE_LINKNUM));
1196166093Sglebius}
1197166093Sglebius
1198166093Sglebius/*
1199166093Sglebius * Encryption layer
1200166093Sglebius */
1201166093Sglebius
1202166093Sglebiusstatic int
1203166093Sglebiusng_ppp_crypt_xmit(node_p node, item_p item, uint16_t proto)
1204166093Sglebius{
1205166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
1206166093Sglebius
1207166093Sglebius	if (priv->conf.enableEncryption &&
1208166093Sglebius	    proto < 0x4000 &&
1209166093Sglebius	    proto != PROT_CRYPTD &&
1210166093Sglebius	    priv->hooks[HOOK_INDEX_ENCRYPT] != NULL) {
1211166093Sglebius		struct mbuf *m;
1212166093Sglebius		int error;
1213166093Sglebius
1214166093Sglebius	        NGI_GET_M(item, m);
1215166093Sglebius		if ((m = ng_ppp_addproto(m, proto, 0)) == NULL) {
121670700Sjulian			NG_FREE_ITEM(item);
121752639Sarchie			return (ENOBUFS);
121855481Sarchie		}
1219166093Sglebius		NGI_M(item) = m;
1220166093Sglebius
1221166093Sglebius		/* Send packet out hook. */
1222166093Sglebius		NG_FWD_ITEM_HOOK(error, item, priv->hooks[HOOK_INDEX_ENCRYPT]);
1223166093Sglebius		return (error);
122452639Sarchie	}
122552639Sarchie
1226166093Sglebius	return (ng_ppp_mp_xmit(node, item, proto));
122752639Sarchie}
122852639Sarchie
122952639Sarchie/*
1230166093Sglebius * Receive data on a hook encrypt.
123152639Sarchie */
123252639Sarchiestatic int
1233166093Sglebiusng_ppp_rcvdata_encrypt(hook_p hook, item_p item)
123452639Sarchie{
1235166093Sglebius	const node_p node = NG_HOOK_NODE(hook);
123670784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
1237166093Sglebius
1238166093Sglebius	if (!priv->conf.enableEncryption) {
1239166093Sglebius		NG_FREE_ITEM(item);
1240166093Sglebius		return (ENXIO);
1241166093Sglebius	}
1242166093Sglebius	return (ng_ppp_mp_xmit(node, item, PROT_CRYPTD));
1243166093Sglebius}
1244166093Sglebius
1245166093Sglebiusstatic int
1246166093Sglebiusng_ppp_crypt_recv(node_p node, item_p item, uint16_t proto, uint16_t linkNum)
1247166093Sglebius{
1248166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
1249166093Sglebius
1250166234Sglebius	if (proto == PROT_CRYPTD) {
1251166234Sglebius		if (priv->conf.enableDecryption &&
1252166234Sglebius		    priv->hooks[HOOK_INDEX_DECRYPT] != NULL) {
1253166234Sglebius			int error;
1254166093Sglebius
1255166234Sglebius			/* Send packet out hook. */
1256166234Sglebius			NG_FWD_ITEM_HOOK(error, item,
1257166234Sglebius			    priv->hooks[HOOK_INDEX_DECRYPT]);
1258166234Sglebius			return (error);
1259166234Sglebius		} else {
1260166234Sglebius			/* Disabled protos MUST be silently discarded, but
1261166234Sglebius			 * unsupported MUST not. Let user-level decide this. */
1262166234Sglebius			return (ng_ppp_bypass(node, item, proto, linkNum));
1263166234Sglebius		}
1264166093Sglebius	}
1265166093Sglebius
1266166093Sglebius	return (ng_ppp_comp_recv(node, item, proto, linkNum));
1267166093Sglebius}
1268166093Sglebius
1269166093Sglebius/*
1270166093Sglebius * Receive data on a hook decrypt.
1271166093Sglebius */
1272166093Sglebiusstatic int
1273166093Sglebiusng_ppp_rcvdata_decrypt(hook_p hook, item_p item)
1274166093Sglebius{
1275166093Sglebius	const node_p node = NG_HOOK_NODE(hook);
1276166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
1277166093Sglebius	uint16_t proto;
127870700Sjulian	struct mbuf *m;
127952639Sarchie
1280166093Sglebius	if (!priv->conf.enableDecryption) {
1281166093Sglebius		NG_FREE_ITEM(item);
1282166093Sglebius		return (ENXIO);
1283166093Sglebius	}
128492298Sarchie	NGI_GET_M(item, m);
1285166093Sglebius	if ((m = ng_ppp_cutproto(m, &proto)) == NULL) {
1286166093Sglebius	        NG_FREE_ITEM(item);
1287166093Sglebius	        return (EIO);
1288166093Sglebius	}
1289166093Sglebius	NGI_M(item) = m;
1290166093Sglebius	if (!PROT_VALID(proto)) {
1291166093Sglebius		priv->bundleStats.badProtos++;
1292166093Sglebius		NG_FREE_ITEM(item);
1293166093Sglebius		return (EIO);
1294166093Sglebius	}
1295166093Sglebius	return (ng_ppp_comp_recv(node, item, proto, NG_PPP_BUNDLE_LINKNUM));
1296166093Sglebius}
129792298Sarchie
1298166093Sglebius/*
1299166093Sglebius * Link layer
1300166093Sglebius */
130152639Sarchie
1302166093Sglebiusstatic int
1303171681Smavng_ppp_link_xmit(node_p node, item_p item, uint16_t proto, uint16_t linkNum, int plen)
1304166093Sglebius{
1305166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
1306166093Sglebius	struct ng_ppp_link *link;
1307166093Sglebius	int len, error;
1308166093Sglebius	struct mbuf *m;
1309166093Sglebius	uint16_t mru;
131059882Sarchie
1311166093Sglebius	/* Check if link correct. */
1312166093Sglebius	if (linkNum >= NG_PPP_MAX_LINKS) {
1313171681Smav		ERROUT(ENETDOWN);
131452639Sarchie	}
131552639Sarchie
1316166093Sglebius	/* Get link pointer (optimization). */
1317166093Sglebius	link = &priv->links[linkNum];
1318166093Sglebius
1319166093Sglebius	/* Check link status (if real). */
1320166093Sglebius	if (link->hook == NULL) {
1321171681Smav		ERROUT(ENETDOWN);
1322166093Sglebius	}
1323166093Sglebius
1324166093Sglebius	/* Extract mbuf. */
1325166093Sglebius	NGI_GET_M(item, m);
1326166093Sglebius
1327166093Sglebius	/* Check peer's MRU for this link. */
1328166093Sglebius	mru = link->conf.mru;
132992298Sarchie	if (mru != 0 && m->m_pkthdr.len > mru) {
133092298Sarchie		NG_FREE_M(m);
1331171681Smav		ERROUT(EMSGSIZE);
133292298Sarchie	}
133392298Sarchie
1334166093Sglebius	/* Prepend protocol number, possibly compressed. */
1335166093Sglebius	if ((m = ng_ppp_addproto(m, proto, link->conf.enableProtoComp)) ==
1336166093Sglebius	    NULL) {
1337171681Smav		ERROUT(ENOBUFS);
133853075Sarchie	}
133953075Sarchie
1340166093Sglebius	/* Prepend address and control field (unless compressed). */
134159882Sarchie	if (proto == PROT_LCP || !link->conf.enableACFComp) {
1342171681Smav		if ((m = ng_ppp_prepend(m, &ng_ppp_acf, 2)) == NULL)
1343171681Smav			ERROUT(ENOBUFS);
134453075Sarchie	}
134553075Sarchie
1346166093Sglebius	/* Deliver frame. */
134752912Sarchie	len = m->m_pkthdr.len;
1348166093Sglebius	NG_FWD_NEW_DATA(error, item, link->hook, m);
134952766Sarchie
1350171681Smav	mtx_lock(&priv->xmtx);
1351171681Smav
1352171681Smav	/* Update link stats. */
1353171681Smav	link->stats.xmitFrames++;
1354171681Smav	link->stats.xmitOctets += len;
1355171681Smav
1356171681Smav	/* Update bundle stats. */
1357171681Smav	if (plen > 0) {
1358171681Smav	    priv->bundleStats.xmitFrames++;
1359171681Smav	    priv->bundleStats.xmitOctets += plen;
1360171681Smav	}
1361171681Smav
1362171681Smav	/* Update 'bytes in queue' counter. */
136352766Sarchie	if (error == 0) {
1364166093Sglebius		/* bytesInQueue and lastWrite required only for mp_strategy. */
1365170283Smav		if (priv->conf.enableMultilink && !priv->allLinksEqual &&
1366170283Smav		    !priv->conf.enableRoundRobin) {
1367170283Smav			/* If queue was empty, then mark this time. */
1368170283Smav			if (link->bytesInQueue == 0)
1369170283Smav				getmicrouptime(&link->lastWrite);
1370170283Smav			link->bytesInQueue += len + MP_AVERAGE_LINK_OVERHEAD;
1371170283Smav			/* Limit max queue length to 50 pkts. BW can be defined
1372170283Smav		    	   incorrectly and link may not signal overload. */
1373170283Smav			if (link->bytesInQueue > 50 * 1600)
1374170283Smav				link->bytesInQueue = 50 * 1600;
1375166093Sglebius		}
137652766Sarchie	}
1377171681Smav	mtx_unlock(&priv->xmtx);
1378166093Sglebius	return (error);
1379171681Smav
1380171681Smavdone:
1381171681Smav	NG_FREE_ITEM(item);
1382171681Smav	return (error);
138352639Sarchie}
138452639Sarchie
138552639Sarchie/*
1386166093Sglebius * Receive data on a hook linkX.
1387166093Sglebius */
1388166093Sglebiusstatic int
1389166093Sglebiusng_ppp_rcvdata(hook_p hook, item_p item)
1390166093Sglebius{
1391166093Sglebius	const node_p node = NG_HOOK_NODE(hook);
1392166093Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
1393166093Sglebius	const int index = (intptr_t)NG_HOOK_PRIVATE(hook);
1394166093Sglebius	const uint16_t linkNum = (uint16_t)~index;
1395166093Sglebius	struct ng_ppp_link * const link = &priv->links[linkNum];
1396166093Sglebius	uint16_t proto;
1397166093Sglebius	struct mbuf *m;
1398171681Smav	int error = 0;
1399166093Sglebius
1400166099Smjacob	KASSERT(linkNum < NG_PPP_MAX_LINKS,
1401166093Sglebius	    ("%s: bogus index 0x%x", __func__, index));
1402166093Sglebius
1403166093Sglebius	NGI_GET_M(item, m);
1404166093Sglebius
1405171681Smav	mtx_lock(&priv->rmtx);
1406171681Smav
1407166093Sglebius	/* Stats */
1408166093Sglebius	link->stats.recvFrames++;
1409166093Sglebius	link->stats.recvOctets += m->m_pkthdr.len;
1410166093Sglebius
1411166093Sglebius	/* Strip address and control fields, if present. */
1412171681Smav	if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL)
1413171681Smav		ERROUT(ENOBUFS);
1414176057Smav	if (mtod(m, uint8_t *)[0] == 0xff &&
1415176057Smav	    mtod(m, uint8_t *)[1] == 0x03)
1416166093Sglebius		m_adj(m, 2);
1417166093Sglebius
1418171681Smav	/* Get protocol number */
1419171681Smav	if ((m = ng_ppp_cutproto(m, &proto)) == NULL)
1420171681Smav		ERROUT(ENOBUFS);
1421166093Sglebius	NGI_M(item) = m; 	/* Put changed m back into item. */
1422166093Sglebius
1423166093Sglebius	if (!PROT_VALID(proto)) {
1424166093Sglebius		link->stats.badProtos++;
1425171681Smav		ERROUT(EIO);
1426166093Sglebius	}
1427166093Sglebius
1428166234Sglebius	/* LCP packets must go directly to bypass. */
1429171681Smav	if (proto >= 0xB000) {
1430171681Smav		mtx_unlock(&priv->rmtx);
1431166234Sglebius		return (ng_ppp_bypass(node, item, proto, linkNum));
1432171681Smav	}
1433166234Sglebius
1434171681Smav	/* Other packets are denied on a disabled link. */
1435171681Smav	if (!link->conf.enableLink)
1436171681Smav		ERROUT(ENXIO);
1437166234Sglebius
1438171681Smav	/* Proceed to multilink layer. Mutex will be unlocked inside. */
1439171681Smav	error = ng_ppp_mp_recv(node, item, proto, linkNum);
1440171681Smav	mtx_assert(&priv->rmtx, MA_NOTOWNED);
1441171681Smav	return (error);
1442171681Smav
1443171681Smavdone:
1444171681Smav	mtx_unlock(&priv->rmtx);
1445171681Smav	NG_FREE_ITEM(item);
1446171681Smav	return (error);
1447166093Sglebius}
1448166093Sglebius
1449166093Sglebius/*
1450166093Sglebius * Multilink layer
1451166093Sglebius */
1452166093Sglebius
1453166093Sglebius/*
145452639Sarchie * Handle an incoming multi-link fragment
145559882Sarchie *
145659882Sarchie * The fragment reassembly algorithm is somewhat complex. This is mainly
145759882Sarchie * because we are required not to reorder the reconstructed packets, yet
145859882Sarchie * fragments are only guaranteed to arrive in order on a per-link basis.
145959882Sarchie * In other words, when we have a complete packet ready, but the previous
146059882Sarchie * packet is still incomplete, we have to decide between delivering the
146159882Sarchie * complete packet and throwing away the incomplete one, or waiting to
146259882Sarchie * see if the remainder of the incomplete one arrives, at which time we
146359882Sarchie * can deliver both packets, in order.
146459882Sarchie *
146559882Sarchie * This problem is exacerbated by "sequence number slew", which is when
146659882Sarchie * the sequence numbers coming in from different links are far apart from
146759882Sarchie * each other. In particular, certain unnamed equipment (*cough* Ascend)
146859882Sarchie * has been seen to generate sequence number slew of up to 10 on an ISDN
146959882Sarchie * 2B-channel MP link. There is nothing invalid about sequence number slew
147059882Sarchie * but it makes the reasssembly process have to work harder.
147159882Sarchie *
147259882Sarchie * However, the peer is required to transmit fragments in order on each
147359882Sarchie * link. That means if we define MSEQ as the minimum over all links of
147459882Sarchie * the highest sequence number received on that link, then we can always
147559882Sarchie * give up any hope of receiving a fragment with sequence number < MSEQ in
147659882Sarchie * the future (all of this using 'wraparound' sequence number space).
147759882Sarchie * Therefore we can always immediately throw away incomplete packets
147859882Sarchie * missing fragments with sequence numbers < MSEQ.
147959882Sarchie *
148059882Sarchie * Here is an overview of our algorithm:
148159882Sarchie *
148259882Sarchie *    o Received fragments are inserted into a queue, for which we
148359882Sarchie *	maintain these invariants between calls to this function:
148459882Sarchie *
148559882Sarchie *	- Fragments are ordered in the queue by sequence number
148659882Sarchie *	- If a complete packet is at the head of the queue, then
148759882Sarchie *	  the first fragment in the packet has seq# > MSEQ + 1
148859882Sarchie *	  (otherwise, we could deliver it immediately)
148959882Sarchie *	- If any fragments have seq# < MSEQ, then they are necessarily
149059882Sarchie *	  part of a packet whose missing seq#'s are all > MSEQ (otherwise,
149159882Sarchie *	  we can throw them away because they'll never be completed)
149259882Sarchie *	- The queue contains at most MP_MAX_QUEUE_LEN fragments
149359882Sarchie *
149459882Sarchie *    o We have a periodic timer that checks the queue for the first
149559882Sarchie *	complete packet that has been sitting in the queue "too long".
149659882Sarchie *	When one is detected, all previous (incomplete) fragments are
149759882Sarchie *	discarded, their missing fragments are declared lost and MSEQ
149859882Sarchie *	is increased.
149959882Sarchie *
150059882Sarchie *    o If we recieve a fragment with seq# < MSEQ, we throw it away
150159882Sarchie *	because we've already delcared it lost.
150259882Sarchie *
150359882Sarchie * This assumes linkNum != NG_PPP_BUNDLE_LINKNUM.
150452639Sarchie */
150552639Sarchiestatic int
1506166093Sglebiusng_ppp_mp_recv(node_p node, item_p item, uint16_t proto, uint16_t linkNum)
150752639Sarchie{
150870784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
150959882Sarchie	struct ng_ppp_link *const link = &priv->links[linkNum];
1510175696Smav	struct ng_ppp_frag *frag;
151159882Sarchie	struct ng_ppp_frag *qent;
151259882Sarchie	int i, diff, inserted;
151370700Sjulian	struct mbuf *m;
1514171681Smav	int	error = 0;
151552639Sarchie
1516171681Smav	if ((!priv->conf.enableMultilink) || proto != PROT_MP) {
1517171681Smav		/* Stats */
1518171681Smav		priv->bundleStats.recvFrames++;
1519171681Smav		priv->bundleStats.recvOctets += NGI_M(item)->m_pkthdr.len;
1520171681Smav
1521171681Smav		mtx_unlock(&priv->rmtx);
1522166093Sglebius		return (ng_ppp_crypt_recv(node, item, proto, linkNum));
1523171681Smav	}
1524166093Sglebius
152570700Sjulian	NGI_GET_M(item, m);
152661143Sarchie
1527175696Smav	/* Get a new frag struct from the free queue */
1528175696Smav	if ((frag = TAILQ_FIRST(&priv->fragsfree)) == NULL) {
1529175696Smav		printf("No free fragments headers in ng_ppp!\n");
1530175696Smav		NG_FREE_M(m);
1531175696Smav		goto process;
1532175696Smav	}
1533175696Smav
153452639Sarchie	/* Extract fragment information from MP header */
153552639Sarchie	if (priv->conf.recvShortSeq) {
1536166093Sglebius		uint16_t shdr;
153752639Sarchie
153852639Sarchie		if (m->m_pkthdr.len < 2) {
153959882Sarchie			link->stats.runts++;
154070700Sjulian			NG_FREE_M(m);
1541171681Smav			ERROUT(EINVAL);
154252639Sarchie		}
1543131155Sjulian		if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL)
1544171681Smav			ERROUT(ENOBUFS);
1545131155Sjulian
1546206021Smav		shdr = be16dec(mtod(m, void *));
154766775Sarchie		frag->seq = MP_SHORT_EXTEND(shdr);
154852639Sarchie		frag->first = (shdr & MP_SHORT_FIRST_FLAG) != 0;
154952639Sarchie		frag->last = (shdr & MP_SHORT_LAST_FLAG) != 0;
155059882Sarchie		diff = MP_SHORT_SEQ_DIFF(frag->seq, priv->mseq);
155152639Sarchie		m_adj(m, 2);
155252639Sarchie	} else {
1553166093Sglebius		uint32_t lhdr;
155452639Sarchie
155552639Sarchie		if (m->m_pkthdr.len < 4) {
155659882Sarchie			link->stats.runts++;
155770700Sjulian			NG_FREE_M(m);
1558171681Smav			ERROUT(EINVAL);
155952639Sarchie		}
1560131155Sjulian		if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL)
1561171681Smav			ERROUT(ENOBUFS);
1562131155Sjulian
1563206021Smav		lhdr = be32dec(mtod(m, void *));
156466775Sarchie		frag->seq = MP_LONG_EXTEND(lhdr);
156552639Sarchie		frag->first = (lhdr & MP_LONG_FIRST_FLAG) != 0;
156652639Sarchie		frag->last = (lhdr & MP_LONG_LAST_FLAG) != 0;
156759882Sarchie		diff = MP_LONG_SEQ_DIFF(frag->seq, priv->mseq);
156852639Sarchie		m_adj(m, 4);
156952639Sarchie	}
157052639Sarchie	frag->data = m;
157159882Sarchie	getmicrouptime(&frag->timestamp);
157252639Sarchie
157359882Sarchie	/* If sequence number is < MSEQ, we've already declared this
157459882Sarchie	   fragment as lost, so we have no choice now but to drop it */
157559882Sarchie	if (diff < 0) {
157659882Sarchie		link->stats.dropFragments++;
157770700Sjulian		NG_FREE_M(m);
1578171681Smav		ERROUT(0);
157959882Sarchie	}
158052639Sarchie
158159882Sarchie	/* Update highest received sequence number on this link and MSEQ */
158259882Sarchie	priv->mseq = link->seq = frag->seq;
158359882Sarchie	for (i = 0; i < priv->numActiveLinks; i++) {
158459882Sarchie		struct ng_ppp_link *const alink =
158559882Sarchie		    &priv->links[priv->activeLinks[i]];
158652639Sarchie
158766775Sarchie		if (MP_RECV_SEQ_DIFF(priv, alink->seq, priv->mseq) < 0)
158859882Sarchie			priv->mseq = alink->seq;
158959882Sarchie	}
159059882Sarchie
1591175696Smav	/* Remove frag struct from free queue. */
1592175696Smav	TAILQ_REMOVE(&priv->fragsfree, frag, f_qent);
159352639Sarchie
159459882Sarchie	/* Add fragment to queue, which is sorted by sequence number */
159554755Sarchie	inserted = 0;
159668761Smckusick	TAILQ_FOREACH_REVERSE(qent, &priv->frags, ng_ppp_fraglist, f_qent) {
159766775Sarchie		diff = MP_RECV_SEQ_DIFF(priv, frag->seq, qent->seq);
159852639Sarchie		if (diff > 0) {
159968761Smckusick			TAILQ_INSERT_AFTER(&priv->frags, qent, frag, f_qent);
160054755Sarchie			inserted = 1;
160152639Sarchie			break;
160252639Sarchie		} else if (diff == 0) {	     /* should never happen! */
160359882Sarchie			link->stats.dupFragments++;
160470700Sjulian			NG_FREE_M(frag->data);
1605175696Smav			TAILQ_INSERT_HEAD(&priv->fragsfree, frag, f_qent);
1606171681Smav			ERROUT(EINVAL);
160752639Sarchie		}
160852639Sarchie	}
160954755Sarchie	if (!inserted)
161068761Smckusick		TAILQ_INSERT_HEAD(&priv->frags, frag, f_qent);
161152639Sarchie
1612171681Smavprocess:
161359882Sarchie	/* Process the queue */
1614171681Smav	/* NOTE: rmtx will be unlocked for sending time! */
1615175698Smav	error = ng_ppp_frag_process(node, item);
1616175698Smav	mtx_unlock(&priv->rmtx);
1617175698Smav	return (error);
1618171681Smav
1619171681Smavdone:
1620171681Smav	mtx_unlock(&priv->rmtx);
1621175698Smav	NG_FREE_ITEM(item);
1622171681Smav	return (error);
162359882Sarchie}
162454755Sarchie
1625166093Sglebius/************************************************************************
1626166093Sglebius			HELPER STUFF
1627166093Sglebius ************************************************************************/
1628166093Sglebius
162959882Sarchie/*
1630168896Smav * If new mseq > current then set it and update all active links
1631168896Smav */
1632168896Smavstatic void
1633168896Smavng_ppp_bump_mseq(node_p node, int32_t new_mseq)
1634168896Smav{
1635168896Smav	const priv_p priv = NG_NODE_PRIVATE(node);
1636168896Smav	int i;
1637168896Smav
1638168896Smav	if (MP_RECV_SEQ_DIFF(priv, priv->mseq, new_mseq) < 0) {
1639168896Smav		priv->mseq = new_mseq;
1640168896Smav		for (i = 0; i < priv->numActiveLinks; i++) {
1641168896Smav			struct ng_ppp_link *const alink =
1642168896Smav			    &priv->links[priv->activeLinks[i]];
1643168896Smav
1644168896Smav			if (MP_RECV_SEQ_DIFF(priv,
1645168896Smav			    alink->seq, new_mseq) < 0)
1646168896Smav				alink->seq = new_mseq;
1647168896Smav		}
1648168896Smav	}
1649168896Smav}
1650168896Smav
1651168896Smav/*
165259882Sarchie * Examine our list of fragments, and determine if there is a
165359882Sarchie * complete and deliverable packet at the head of the list.
165459882Sarchie * Return 1 if so, zero otherwise.
165559882Sarchie */
165659882Sarchiestatic int
165759882Sarchieng_ppp_check_packet(node_p node)
165859882Sarchie{
165970784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
166059882Sarchie	struct ng_ppp_frag *qent, *qnext;
166159882Sarchie
166259882Sarchie	/* Check for empty queue */
166368761Smckusick	if (TAILQ_EMPTY(&priv->frags))
166459882Sarchie		return (0);
166559882Sarchie
166659882Sarchie	/* Check first fragment is the start of a deliverable packet */
166768761Smckusick	qent = TAILQ_FIRST(&priv->frags);
166866775Sarchie	if (!qent->first || MP_RECV_SEQ_DIFF(priv, qent->seq, priv->mseq) > 1)
166959882Sarchie		return (0);
167059882Sarchie
167159882Sarchie	/* Check that all the fragments are there */
167259882Sarchie	while (!qent->last) {
167368761Smckusick		qnext = TAILQ_NEXT(qent, f_qent);
167468761Smckusick		if (qnext == NULL)	/* end of queue */
167559882Sarchie			return (0);
167666775Sarchie		if (qnext->seq != MP_NEXT_RECV_SEQ(priv, qent->seq))
167759882Sarchie			return (0);
167859882Sarchie		qent = qnext;
167952639Sarchie	}
168052639Sarchie
168159882Sarchie	/* Got one */
168259882Sarchie	return (1);
168359882Sarchie}
168459882Sarchie
168559882Sarchie/*
168659882Sarchie * Pull a completed packet off the head of the incoming fragment queue.
168759882Sarchie * This assumes there is a completed packet there to pull off.
168859882Sarchie */
168959882Sarchiestatic void
1690131155Sjulianng_ppp_get_packet(node_p node, struct mbuf **mp)
169159882Sarchie{
169270784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
169359882Sarchie	struct ng_ppp_frag *qent, *qnext;
169459882Sarchie	struct mbuf *m = NULL, *tail;
169559882Sarchie
169668761Smckusick	qent = TAILQ_FIRST(&priv->frags);
169768761Smckusick	KASSERT(!TAILQ_EMPTY(&priv->frags) && qent->first,
169887599Sobrien	    ("%s: no packet", __func__));
169959882Sarchie	for (tail = NULL; qent != NULL; qent = qnext) {
170068761Smckusick		qnext = TAILQ_NEXT(qent, f_qent);
170168761Smckusick		KASSERT(!TAILQ_EMPTY(&priv->frags),
170287599Sobrien		    ("%s: empty q", __func__));
170368761Smckusick		TAILQ_REMOVE(&priv->frags, qent, f_qent);
1704131155Sjulian		if (tail == NULL)
170552639Sarchie			tail = m = qent->data;
1706131155Sjulian		else {
170752639Sarchie			m->m_pkthdr.len += qent->data->m_pkthdr.len;
170852639Sarchie			tail->m_next = qent->data;
170952639Sarchie		}
171052639Sarchie		while (tail->m_next != NULL)
171152639Sarchie			tail = tail->m_next;
1712168896Smav		if (qent->last) {
171352639Sarchie			qnext = NULL;
1714168896Smav			/* Bump MSEQ if necessary */
1715168896Smav			ng_ppp_bump_mseq(node, qent->seq);
1716168896Smav		}
1717175696Smav		TAILQ_INSERT_HEAD(&priv->fragsfree, qent, f_qent);
171852639Sarchie	}
171959882Sarchie	*mp = m;
172059882Sarchie}
172152639Sarchie
172259882Sarchie/*
172359882Sarchie * Trim fragments from the queue whose packets can never be completed.
172459882Sarchie * This assumes a complete packet is NOT at the beginning of the queue.
172559882Sarchie * Returns 1 if fragments were removed, zero otherwise.
172659882Sarchie */
172759882Sarchiestatic int
172859882Sarchieng_ppp_frag_trim(node_p node)
172959882Sarchie{
173070784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
173159882Sarchie	struct ng_ppp_frag *qent, *qnext = NULL;
173259882Sarchie	int removed = 0;
173359882Sarchie
173459882Sarchie	/* Scan for "dead" fragments and remove them */
173559882Sarchie	while (1) {
173659882Sarchie		int dead = 0;
173759882Sarchie
173859882Sarchie		/* If queue is empty, we're done */
173968761Smckusick		if (TAILQ_EMPTY(&priv->frags))
174052639Sarchie			break;
174159882Sarchie
174259882Sarchie		/* Determine whether first fragment can ever be completed */
174368761Smckusick		TAILQ_FOREACH(qent, &priv->frags, f_qent) {
174466775Sarchie			if (MP_RECV_SEQ_DIFF(priv, qent->seq, priv->mseq) >= 0)
174559882Sarchie				break;
174668761Smckusick			qnext = TAILQ_NEXT(qent, f_qent);
174768761Smckusick			KASSERT(qnext != NULL,
174887599Sobrien			    ("%s: last frag < MSEQ?", __func__));
174966775Sarchie			if (qnext->seq != MP_NEXT_RECV_SEQ(priv, qent->seq)
175059882Sarchie			    || qent->last || qnext->first) {
175159882Sarchie				dead = 1;
175259882Sarchie				break;
175359882Sarchie			}
175459882Sarchie		}
175559882Sarchie		if (!dead)
175659882Sarchie			break;
175759882Sarchie
175859882Sarchie		/* Remove fragment and all others in the same packet */
175968761Smckusick		while ((qent = TAILQ_FIRST(&priv->frags)) != qnext) {
176068761Smckusick			KASSERT(!TAILQ_EMPTY(&priv->frags),
176187599Sobrien			    ("%s: empty q", __func__));
176259882Sarchie			priv->bundleStats.dropFragments++;
176368761Smckusick			TAILQ_REMOVE(&priv->frags, qent, f_qent);
176470700Sjulian			NG_FREE_M(qent->data);
1765175696Smav			TAILQ_INSERT_HEAD(&priv->fragsfree, qent, f_qent);
176659882Sarchie			removed = 1;
176759882Sarchie		}
176859882Sarchie	}
176959882Sarchie	return (removed);
177059882Sarchie}
177159882Sarchie
177259882Sarchie/*
1773168896Smav * Drop fragments on queue overflow.
1774168896Smav * Returns 1 if fragments were removed, zero otherwise.
177559882Sarchie */
177659882Sarchiestatic int
1777168896Smavng_ppp_frag_drop(node_p node)
177859882Sarchie{
177970784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
178059882Sarchie
178159882Sarchie	/* Check queue length */
1782175696Smav	if (TAILQ_EMPTY(&priv->fragsfree)) {
178359882Sarchie		struct ng_ppp_frag *qent;
178459882Sarchie
178559882Sarchie		/* Get oldest fragment */
178668761Smckusick		KASSERT(!TAILQ_EMPTY(&priv->frags),
178787599Sobrien		    ("%s: empty q", __func__));
178868761Smckusick		qent = TAILQ_FIRST(&priv->frags);
178959882Sarchie
179059882Sarchie		/* Bump MSEQ if necessary */
1791168896Smav		ng_ppp_bump_mseq(node, qent->seq);
179259882Sarchie
179359882Sarchie		/* Drop it */
179459882Sarchie		priv->bundleStats.dropFragments++;
179568761Smckusick		TAILQ_REMOVE(&priv->frags, qent, f_qent);
179670700Sjulian		NG_FREE_M(qent->data);
1797175696Smav		TAILQ_INSERT_HEAD(&priv->fragsfree, qent, f_qent);
179859882Sarchie
1799168896Smav		return (1);
180052639Sarchie	}
1801168896Smav	return (0);
1802168896Smav}
180352639Sarchie
1804168896Smav/*
1805168896Smav * Run the queue, restoring the queue invariants
1806168896Smav */
1807168896Smavstatic int
1808175698Smavng_ppp_frag_process(node_p node, item_p oitem)
1809168896Smav{
1810168896Smav	const priv_p priv = NG_NODE_PRIVATE(node);
1811168896Smav	struct mbuf *m;
1812168896Smav	item_p item;
1813168896Smav	uint16_t proto;
1814168896Smav
1815168896Smav	do {
1816168896Smav		/* Deliver any deliverable packets */
1817168896Smav		while (ng_ppp_check_packet(node)) {
1818168896Smav			ng_ppp_get_packet(node, &m);
1819168896Smav			if ((m = ng_ppp_cutproto(m, &proto)) == NULL)
1820168896Smav				continue;
1821168896Smav			if (!PROT_VALID(proto)) {
1822168896Smav				priv->bundleStats.badProtos++;
1823168896Smav				NG_FREE_M(m);
1824168896Smav				continue;
1825168896Smav			}
1826175698Smav			if (oitem) { /* If original item present - reuse it. */
1827175698Smav				item = oitem;
1828175698Smav				oitem = NULL;
1829175698Smav				NGI_M(item) = m;
1830175698Smav			} else {
1831175698Smav				item = ng_package_data(m, NG_NOFLAGS);
1832175698Smav			}
1833175698Smav			if (item != NULL) {
1834171681Smav				/* Stats */
1835171681Smav				priv->bundleStats.recvFrames++;
1836171681Smav				priv->bundleStats.recvOctets +=
1837171681Smav				    NGI_M(item)->m_pkthdr.len;
1838171681Smav
1839171681Smav				/* Drop mutex for the sending time.
1840171681Smav				 * Priv may change, but we are ready!
1841171681Smav				 */
1842171681Smav				mtx_unlock(&priv->rmtx);
1843168896Smav				ng_ppp_crypt_recv(node, item, proto,
1844168896Smav					NG_PPP_BUNDLE_LINKNUM);
1845171681Smav				mtx_lock(&priv->rmtx);
1846171681Smav			}
1847168896Smav		}
1848168896Smav	  /* Delete dead fragments and try again */
1849168896Smav	} while (ng_ppp_frag_trim(node) || ng_ppp_frag_drop(node));
1850175698Smav
1851175698Smav	/* If we haven't reused original item - free it. */
1852175698Smav	if (oitem) NG_FREE_ITEM(oitem);
1853168896Smav
185459882Sarchie	/* Done */
185559882Sarchie	return (0);
185652639Sarchie}
185752639Sarchie
185852639Sarchie/*
185959882Sarchie * Check for 'stale' completed packets that need to be delivered
186059882Sarchie *
186159882Sarchie * If a link goes down or has a temporary failure, MSEQ can get
186259882Sarchie * "stuck", because no new incoming fragments appear on that link.
186359882Sarchie * This can cause completed packets to never get delivered if
186459882Sarchie * their sequence numbers are all > MSEQ + 1.
186559882Sarchie *
186659882Sarchie * This routine checks how long all of the completed packets have
186759882Sarchie * been sitting in the queue, and if too long, removes fragments
186859882Sarchie * from the queue and increments MSEQ to allow them to be delivered.
186959882Sarchie */
187059882Sarchiestatic void
187159882Sarchieng_ppp_frag_checkstale(node_p node)
187259882Sarchie{
187370784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
187459882Sarchie	struct ng_ppp_frag *qent, *beg, *end;
187559882Sarchie	struct timeval now, age;
187659882Sarchie	struct mbuf *m;
1877168896Smav	int seq;
187870700Sjulian	item_p item;
1879111934Sarchie	int endseq;
1880166093Sglebius	uint16_t proto;
188159882Sarchie
188259882Sarchie	now.tv_sec = 0;			/* uninitialized state */
188359882Sarchie	while (1) {
188459882Sarchie
188559882Sarchie		/* If queue is empty, we're done */
188668761Smckusick		if (TAILQ_EMPTY(&priv->frags))
188759882Sarchie			break;
188859882Sarchie
188959882Sarchie		/* Find the first complete packet in the queue */
189059882Sarchie		beg = end = NULL;
189168761Smckusick		seq = TAILQ_FIRST(&priv->frags)->seq;
189268761Smckusick		TAILQ_FOREACH(qent, &priv->frags, f_qent) {
189359882Sarchie			if (qent->first)
189459882Sarchie				beg = qent;
189559882Sarchie			else if (qent->seq != seq)
189659882Sarchie				beg = NULL;
189759882Sarchie			if (beg != NULL && qent->last) {
189859882Sarchie				end = qent;
189959882Sarchie				break;
190059882Sarchie			}
190166775Sarchie			seq = MP_NEXT_RECV_SEQ(priv, seq);
190259882Sarchie		}
190359882Sarchie
190459882Sarchie		/* If none found, exit */
190559882Sarchie		if (end == NULL)
190659882Sarchie			break;
190759882Sarchie
190859882Sarchie		/* Get current time (we assume we've been up for >= 1 second) */
190959882Sarchie		if (now.tv_sec == 0)
191059882Sarchie			getmicrouptime(&now);
191159882Sarchie
191259882Sarchie		/* Check if packet has been queued too long */
191359882Sarchie		age = now;
191459882Sarchie		timevalsub(&age, &beg->timestamp);
191559882Sarchie		if (timevalcmp(&age, &ng_ppp_max_staleness, < ))
191659882Sarchie			break;
191759882Sarchie
191859882Sarchie		/* Throw away junk fragments in front of the completed packet */
191968761Smckusick		while ((qent = TAILQ_FIRST(&priv->frags)) != beg) {
192068761Smckusick			KASSERT(!TAILQ_EMPTY(&priv->frags),
192187599Sobrien			    ("%s: empty q", __func__));
192259882Sarchie			priv->bundleStats.dropFragments++;
192368761Smckusick			TAILQ_REMOVE(&priv->frags, qent, f_qent);
192470700Sjulian			NG_FREE_M(qent->data);
1925175696Smav			TAILQ_INSERT_HEAD(&priv->fragsfree, qent, f_qent);
192659882Sarchie		}
192759882Sarchie
192859882Sarchie		/* Extract completed packet */
1929111934Sarchie		endseq = end->seq;
1930131155Sjulian		ng_ppp_get_packet(node, &m);
193159882Sarchie
1932166093Sglebius		if ((m = ng_ppp_cutproto(m, &proto)) == NULL)
1933166093Sglebius			continue;
1934166093Sglebius		if (!PROT_VALID(proto)) {
1935166093Sglebius			priv->bundleStats.badProtos++;
1936166093Sglebius			NG_FREE_M(m);
1937166093Sglebius			continue;
1938166093Sglebius		}
1939166093Sglebius
194059882Sarchie		/* Deliver packet */
1941171681Smav		if ((item = ng_package_data(m, NG_NOFLAGS)) != NULL) {
1942171681Smav			/* Stats */
1943171681Smav			priv->bundleStats.recvFrames++;
1944171681Smav			priv->bundleStats.recvOctets += NGI_M(item)->m_pkthdr.len;
1945171681Smav
1946166093Sglebius			ng_ppp_crypt_recv(node, item, proto,
1947166093Sglebius				NG_PPP_BUNDLE_LINKNUM);
1948171681Smav		}
194959882Sarchie	}
195059882Sarchie}
195159882Sarchie
195259882Sarchie/*
195359882Sarchie * Periodically call ng_ppp_frag_checkstale()
195459882Sarchie */
195559882Sarchiestatic void
1956138479Sglebiusng_ppp_frag_timeout(node_p node, hook_p hook, void *arg1, int arg2)
195759882Sarchie{
1958138479Sglebius	/* XXX: is this needed? */
1959138479Sglebius	if (NG_NODE_NOT_VALID(node))
196059882Sarchie		return;
196159882Sarchie
1962138479Sglebius	/* Scan the fragment queue */
1963138479Sglebius	ng_ppp_frag_checkstale(node);
196459882Sarchie
196559882Sarchie	/* Start timer again */
196659882Sarchie	ng_ppp_start_frag_timer(node);
196759882Sarchie}
196859882Sarchie
196959882Sarchie/*
197052639Sarchie * Deliver a frame out on the bundle, i.e., figure out how to fragment
197152639Sarchie * the frame across the individual PPP links and do so.
197252639Sarchie */
197352639Sarchiestatic int
1974166093Sglebiusng_ppp_mp_xmit(node_p node, item_p item, uint16_t proto)
197552639Sarchie{
197670784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
197792298Sarchie	const int hdr_len = priv->conf.xmitShortSeq ? 2 : 4;
197852639Sarchie	int distrib[NG_PPP_MAX_LINKS];
197952639Sarchie	int firstFragment;
198052639Sarchie	int activeLinkNum;
1981166093Sglebius	struct mbuf *m;
1982175594Smav	int	plen;
1983171681Smav	int	frags;
1984171681Smav	int32_t	seq;
198552639Sarchie
198652639Sarchie	/* At least one link must be active */
198752639Sarchie	if (priv->numActiveLinks == 0) {
1988166093Sglebius		NG_FREE_ITEM(item);
198952639Sarchie		return (ENETDOWN);
199052639Sarchie	}
1991171681Smav
1992171681Smav	/* Save length for later stats. */
1993175594Smav	plen = NGI_M(item)->m_pkthdr.len;
199452639Sarchie
1995171681Smav	if (!priv->conf.enableMultilink) {
1996166093Sglebius		return (ng_ppp_link_xmit(node, item, proto,
1997175594Smav		    priv->activeLinks[0], plen));
1998171681Smav	}
1999166093Sglebius
2000187387Smav	/* Check peer's MRRU for this bundle. */
2001187387Smav	if (plen > priv->conf.mrru) {
2002187387Smav		NG_FREE_ITEM(item);
2003187387Smav		return (EMSGSIZE);
2004187387Smav	}
2005187387Smav
2006166093Sglebius	/* Extract mbuf. */
2007166093Sglebius	NGI_GET_M(item, m);
2008166093Sglebius
2009166093Sglebius	/* Prepend protocol number, possibly compressed. */
2010175697Smav	if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) {
2011175697Smav		NG_FREE_ITEM(item);
2012166093Sglebius		return (ENOBUFS);
2013175697Smav	}
2014166093Sglebius
2015168897Smav	/* Clear distribution plan */
2016168897Smav	bzero(&distrib, priv->numActiveLinks * sizeof(distrib[0]));
2017168897Smav
2018171681Smav	mtx_lock(&priv->xmtx);
2019171681Smav
202052639Sarchie	/* Round-robin strategy */
2021168897Smav	if (priv->conf.enableRoundRobin) {
202252639Sarchie		activeLinkNum = priv->lastLink++ % priv->numActiveLinks;
202352639Sarchie		distrib[activeLinkNum] = m->m_pkthdr.len;
202452639Sarchie		goto deliver;
202552639Sarchie	}
202652639Sarchie
202752639Sarchie	/* Strategy when all links are equivalent (optimize the common case) */
202852639Sarchie	if (priv->allLinksEqual) {
2029168897Smav		int	numFrags, fraction, remain;
2030168897Smav		int	i;
2031168897Smav
2032168897Smav		/* Calculate optimal fragment count */
2033168897Smav		numFrags = priv->numActiveLinks;
2034168897Smav		if (numFrags > m->m_pkthdr.len / MP_MIN_FRAG_LEN)
2035168897Smav		    numFrags = m->m_pkthdr.len / MP_MIN_FRAG_LEN;
2036168897Smav		if (numFrags == 0)
2037168897Smav		    numFrags = 1;
203852639Sarchie
2039168897Smav		fraction = m->m_pkthdr.len / numFrags;
2040168897Smav		remain = m->m_pkthdr.len - (fraction * numFrags);
2041168897Smav
2042168897Smav		/* Assign distribution */
2043168897Smav		for (i = 0; i < numFrags; i++) {
204452639Sarchie			distrib[priv->lastLink++ % priv->numActiveLinks]
2045168897Smav			    = fraction + (((remain--) > 0)?1:0);
204652639Sarchie		}
204752639Sarchie		goto deliver;
204852639Sarchie	}
204952639Sarchie
205052639Sarchie	/* Strategy when all links are not equivalent */
205152639Sarchie	ng_ppp_mp_strategy(node, m->m_pkthdr.len, distrib);
205252639Sarchie
205352639Sarchiedeliver:
2054171681Smav	/* Estimate fragments count */
2055171681Smav	frags = 0;
2056171681Smav	for (activeLinkNum = priv->numActiveLinks - 1;
2057171681Smav	    activeLinkNum >= 0; activeLinkNum--) {
2058171681Smav		const uint16_t linkNum = priv->activeLinks[activeLinkNum];
2059171681Smav		struct ng_ppp_link *const link = &priv->links[linkNum];
2060171681Smav
2061171681Smav		frags += (distrib[activeLinkNum] + link->conf.mru - hdr_len - 1) /
2062171681Smav		    (link->conf.mru - hdr_len);
2063171681Smav	}
2064171681Smav
2065171681Smav	/* Get out initial sequence number */
2066171681Smav	seq = priv->xseq;
2067171681Smav
2068171681Smav	/* Update next sequence number */
2069171681Smav	if (priv->conf.xmitShortSeq) {
2070171681Smav	    priv->xseq = (seq + frags) & MP_SHORT_SEQ_MASK;
2071171681Smav	} else {
2072171681Smav	    priv->xseq = (seq + frags) & MP_LONG_SEQ_MASK;
2073171681Smav	}
2074171681Smav
2075171681Smav	mtx_unlock(&priv->xmtx);
2076171681Smav
207752639Sarchie	/* Send alloted portions of frame out on the link(s) */
207852639Sarchie	for (firstFragment = 1, activeLinkNum = priv->numActiveLinks - 1;
207952639Sarchie	    activeLinkNum >= 0; activeLinkNum--) {
2080166093Sglebius		const uint16_t linkNum = priv->activeLinks[activeLinkNum];
208159882Sarchie		struct ng_ppp_link *const link = &priv->links[linkNum];
208252639Sarchie
208352639Sarchie		/* Deliver fragment(s) out the next link */
208452639Sarchie		for ( ; distrib[activeLinkNum] > 0; firstFragment = 0) {
208552639Sarchie			int len, lastFragment, error;
208652639Sarchie			struct mbuf *m2;
208752639Sarchie
208852639Sarchie			/* Calculate fragment length; don't exceed link MTU */
208952639Sarchie			len = distrib[activeLinkNum];
209092298Sarchie			if (len > link->conf.mru - hdr_len)
209192298Sarchie				len = link->conf.mru - hdr_len;
209252639Sarchie			distrib[activeLinkNum] -= len;
209352639Sarchie			lastFragment = (len == m->m_pkthdr.len);
209452639Sarchie
209552639Sarchie			/* Split off next fragment as "m2" */
209652639Sarchie			m2 = m;
209752639Sarchie			if (!lastFragment) {
2098243882Sglebius				struct mbuf *n = m_split(m, len, M_NOWAIT);
209952639Sarchie
210052639Sarchie				if (n == NULL) {
210170700Sjulian					NG_FREE_M(m);
2102175697Smav					if (firstFragment)
2103175697Smav						NG_FREE_ITEM(item);
210452639Sarchie					return (ENOMEM);
210552639Sarchie				}
2106243882Sglebius				m_tag_copy_chain(n, m, M_NOWAIT);
210752639Sarchie				m = n;
210852639Sarchie			}
210952639Sarchie
211052639Sarchie			/* Prepend MP header */
211152639Sarchie			if (priv->conf.xmitShortSeq) {
2112166093Sglebius				uint16_t shdr;
211352639Sarchie
2114171681Smav				shdr = seq;
2115171681Smav				seq = (seq + 1) & MP_SHORT_SEQ_MASK;
211652639Sarchie				if (firstFragment)
211752639Sarchie					shdr |= MP_SHORT_FIRST_FLAG;
211852639Sarchie				if (lastFragment)
211952639Sarchie					shdr |= MP_SHORT_LAST_FLAG;
212053075Sarchie				shdr = htons(shdr);
212153075Sarchie				m2 = ng_ppp_prepend(m2, &shdr, 2);
212252639Sarchie			} else {
2123166093Sglebius				uint32_t lhdr;
212452639Sarchie
2125171681Smav				lhdr = seq;
2126171681Smav				seq = (seq + 1) & MP_LONG_SEQ_MASK;
212752639Sarchie				if (firstFragment)
212852639Sarchie					lhdr |= MP_LONG_FIRST_FLAG;
212952639Sarchie				if (lastFragment)
213052639Sarchie					lhdr |= MP_LONG_LAST_FLAG;
213153075Sarchie				lhdr = htonl(lhdr);
213253075Sarchie				m2 = ng_ppp_prepend(m2, &lhdr, 4);
213352639Sarchie			}
213452639Sarchie			if (m2 == NULL) {
213552639Sarchie				if (!lastFragment)
213652639Sarchie					m_freem(m);
2137175697Smav				if (firstFragment)
2138175697Smav					NG_FREE_ITEM(item);
213952639Sarchie				return (ENOBUFS);
214052639Sarchie			}
214152639Sarchie
214252639Sarchie			/* Send fragment */
2143175697Smav			if (firstFragment) {
2144175697Smav				NGI_M(item) = m2; /* Reuse original item. */
2145175697Smav			} else {
2146175697Smav				item = ng_package_data(m2, NG_NOFLAGS);
2147175697Smav			}
2148175697Smav			if (item != NULL) {
2149166093Sglebius				error = ng_ppp_link_xmit(node, item, PROT_MP,
2150175594Smav					    linkNum, (firstFragment?plen:0));
2151146302Sglebius				if (error != 0) {
2152146302Sglebius					if (!lastFragment)
2153146302Sglebius						NG_FREE_M(m);
2154146302Sglebius					return (error);
2155146302Sglebius				}
215652639Sarchie			}
215752639Sarchie		}
215852639Sarchie	}
215952639Sarchie
216052639Sarchie	/* Done */
216152639Sarchie	return (0);
216252639Sarchie}
216352639Sarchie
216452639Sarchie/*
216552639Sarchie * Computing the optimal fragmentation
216652639Sarchie * -----------------------------------
216752639Sarchie *
216852639Sarchie * This routine tries to compute the optimal fragmentation pattern based
216952639Sarchie * on each link's latency, bandwidth, and calculated additional latency.
217052639Sarchie * The latter quantity is the additional latency caused by previously
217152639Sarchie * written data that has not been transmitted yet.
217252639Sarchie *
217352639Sarchie * This algorithm is only useful when not all of the links have the
217452639Sarchie * same latency and bandwidth values.
217552639Sarchie *
217652639Sarchie * The essential idea is to make the last bit of each fragment of the
217752639Sarchie * frame arrive at the opposite end at the exact same time. This greedy
217852639Sarchie * algorithm is optimal, in that no other scheduling could result in any
217952639Sarchie * packet arriving any sooner unless packets are delivered out of order.
218052639Sarchie *
218152639Sarchie * Suppose link i has bandwidth b_i (in tens of bytes per milisecond) and
218252639Sarchie * latency l_i (in miliseconds). Consider the function function f_i(t)
218352639Sarchie * which is equal to the number of bytes that will have arrived at
218452639Sarchie * the peer after t miliseconds if we start writing continuously at
218552639Sarchie * time t = 0. Then f_i(t) = b_i * (t - l_i) = ((b_i * t) - (l_i * b_i).
218652639Sarchie * That is, f_i(t) is a line with slope b_i and y-intersect -(l_i * b_i).
218752639Sarchie * Note that the y-intersect is always <= zero because latency can't be
218852639Sarchie * negative.  Note also that really the function is f_i(t) except when
218952639Sarchie * f_i(t) is negative, in which case the function is zero.  To take
219052639Sarchie * care of this, let Q_i(t) = { if (f_i(t) > 0) return 1; else return 0; }.
219152639Sarchie * So the actual number of bytes that will have arrived at the peer after
219252639Sarchie * t miliseconds is f_i(t) * Q_i(t).
219352639Sarchie *
219452639Sarchie * At any given time, each link has some additional latency a_i >= 0
219552639Sarchie * due to previously written fragment(s) which are still in the queue.
219652639Sarchie * This value is easily computed from the time since last transmission,
219752639Sarchie * the previous latency value, the number of bytes written, and the
219852639Sarchie * link's bandwidth.
219952639Sarchie *
220052639Sarchie * Assume that l_i includes any a_i already, and that the links are
220152639Sarchie * sorted by latency, so that l_i <= l_{i+1}.
220252639Sarchie *
220352639Sarchie * Let N be the total number of bytes in the current frame we are sending.
220452639Sarchie *
220552639Sarchie * Suppose we were to start writing bytes at time t = 0 on all links
220652639Sarchie * simultaneously, which is the most we can possibly do.  Then let
220752639Sarchie * F(t) be equal to the total number of bytes received by the peer
220852639Sarchie * after t miliseconds. Then F(t) = Sum_i (f_i(t) * Q_i(t)).
220952639Sarchie *
221052639Sarchie * Our goal is simply this: fragment the frame across the links such
221152639Sarchie * that the peer is able to reconstruct the completed frame as soon as
221252639Sarchie * possible, i.e., at the least possible value of t. Call this value t_0.
221352639Sarchie *
221452639Sarchie * Then it follows that F(t_0) = N. Our strategy is first to find the value
221552639Sarchie * of t_0, and then deduce how many bytes to write to each link.
221652639Sarchie *
221752639Sarchie * Rewriting F(t_0):
221852639Sarchie *
221952639Sarchie *   t_0 = ( N + Sum_i ( l_i * b_i * Q_i(t_0) ) ) / Sum_i ( b_i * Q_i(t_0) )
222052639Sarchie *
222152639Sarchie * Now, we note that Q_i(t) is constant for l_i <= t <= l_{i+1}. t_0 will
222252639Sarchie * lie in one of these ranges.  To find it, we just need to find the i such
222352639Sarchie * that F(l_i) <= N <= F(l_{i+1}).  Then we compute all the constant values
222452639Sarchie * for Q_i() in this range, plug in the remaining values, solving for t_0.
222552639Sarchie *
222652639Sarchie * Once t_0 is known, then the number of bytes to send on link i is
222752639Sarchie * just f_i(t_0) * Q_i(t_0).
222852639Sarchie *
222952639Sarchie * In other words, we start allocating bytes to the links one at a time.
223052639Sarchie * We keep adding links until the frame is completely sent.  Some links
223152639Sarchie * may not get any bytes because their latency is too high.
223252639Sarchie *
223352639Sarchie * Is all this work really worth the trouble?  Depends on the situation.
223452639Sarchie * The bigger the ratio of computer speed to link speed, and the more
223552639Sarchie * important total bundle latency is (e.g., for interactive response time),
223652639Sarchie * the more it's worth it.  There is however the cost of calling this
223752639Sarchie * function for every frame.  The running time is O(n^2) where n is the
223852639Sarchie * number of links that receive a non-zero number of bytes.
223952639Sarchie *
224052639Sarchie * Since latency is measured in miliseconds, the "resolution" of this
224152639Sarchie * algorithm is one milisecond.
224252639Sarchie *
224352639Sarchie * To avoid this algorithm altogether, configure all links to have the
224452639Sarchie * same latency and bandwidth.
224552639Sarchie */
224652639Sarchiestatic void
224752639Sarchieng_ppp_mp_strategy(node_p node, int len, int *distrib)
224852639Sarchie{
224970784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
225052639Sarchie	int latency[NG_PPP_MAX_LINKS];
225152639Sarchie	int sortByLatency[NG_PPP_MAX_LINKS];
225259882Sarchie	int activeLinkNum;
225352639Sarchie	int t0, total, topSum, botSum;
225452639Sarchie	struct timeval now;
225552639Sarchie	int i, numFragments;
225652639Sarchie
225752639Sarchie	/* If only one link, this gets real easy */
225852639Sarchie	if (priv->numActiveLinks == 1) {
225952639Sarchie		distrib[0] = len;
226052639Sarchie		return;
226152639Sarchie	}
226252639Sarchie
226352639Sarchie	/* Get current time */
226459882Sarchie	getmicrouptime(&now);
226552639Sarchie
226652639Sarchie	/* Compute latencies for each link at this point in time */
226752639Sarchie	for (activeLinkNum = 0;
226852639Sarchie	    activeLinkNum < priv->numActiveLinks; activeLinkNum++) {
226959882Sarchie		struct ng_ppp_link *alink;
227052639Sarchie		struct timeval diff;
227152639Sarchie		int xmitBytes;
227252639Sarchie
227352639Sarchie		/* Start with base latency value */
227459882Sarchie		alink = &priv->links[priv->activeLinks[activeLinkNum]];
2275133055Sbz		latency[activeLinkNum] = alink->latency;
227652639Sarchie		sortByLatency[activeLinkNum] = activeLinkNum;	/* see below */
227752639Sarchie
227852639Sarchie		/* Any additional latency? */
227959882Sarchie		if (alink->bytesInQueue == 0)
228052639Sarchie			continue;
228152639Sarchie
228252639Sarchie		/* Compute time delta since last write */
228352639Sarchie		diff = now;
228459882Sarchie		timevalsub(&diff, &alink->lastWrite);
2285168895Smav
2286168895Smav		/* alink->bytesInQueue will be changed, mark change time. */
2287168895Smav		alink->lastWrite = now;
2288168895Smav
228952639Sarchie		if (now.tv_sec < 0 || diff.tv_sec >= 10) {	/* sanity */
229059882Sarchie			alink->bytesInQueue = 0;
229152639Sarchie			continue;
229252639Sarchie		}
229352639Sarchie
229452639Sarchie		/* How many bytes could have transmitted since last write? */
2295168895Smav		xmitBytes = (alink->conf.bandwidth * 10 * diff.tv_sec)
229659882Sarchie		    + (alink->conf.bandwidth * (diff.tv_usec / 1000)) / 100;
229759882Sarchie		alink->bytesInQueue -= xmitBytes;
229859882Sarchie		if (alink->bytesInQueue < 0)
229959882Sarchie			alink->bytesInQueue = 0;
230052419Sjulian		else
230152639Sarchie			latency[activeLinkNum] +=
230259882Sarchie			    (100 * alink->bytesInQueue) / alink->conf.bandwidth;
230352419Sjulian	}
230452639Sarchie
230559882Sarchie	/* Sort active links by latency */
2306132229Sglebius	qsort_r(sortByLatency,
2307132229Sglebius	    priv->numActiveLinks, sizeof(*sortByLatency), latency, ng_ppp_intcmp);
230852639Sarchie
230952639Sarchie	/* Find the interval we need (add links in sortByLatency[] order) */
231052639Sarchie	for (numFragments = 1;
231152639Sarchie	    numFragments < priv->numActiveLinks; numFragments++) {
231252639Sarchie		for (total = i = 0; i < numFragments; i++) {
231352639Sarchie			int flowTime;
231452639Sarchie
231552639Sarchie			flowTime = latency[sortByLatency[numFragments]]
231652639Sarchie			    - latency[sortByLatency[i]];
231759882Sarchie			total += ((flowTime * priv->links[
231859882Sarchie			    priv->activeLinks[sortByLatency[i]]].conf.bandwidth)
231952639Sarchie			    	+ 99) / 100;
232052639Sarchie		}
232152639Sarchie		if (total >= len)
232252639Sarchie			break;
232352639Sarchie	}
232452639Sarchie
232552639Sarchie	/* Solve for t_0 in that interval */
232652639Sarchie	for (topSum = botSum = i = 0; i < numFragments; i++) {
232759882Sarchie		int bw = priv->links[
232859882Sarchie		    priv->activeLinks[sortByLatency[i]]].conf.bandwidth;
232952639Sarchie
233052639Sarchie		topSum += latency[sortByLatency[i]] * bw;	/* / 100 */
233152639Sarchie		botSum += bw;					/* / 100 */
233252639Sarchie	}
233352639Sarchie	t0 = ((len * 100) + topSum + botSum / 2) / botSum;
233452639Sarchie
233552639Sarchie	/* Compute f_i(t_0) all i */
233652639Sarchie	for (total = i = 0; i < numFragments; i++) {
233759882Sarchie		int bw = priv->links[
233859882Sarchie		    priv->activeLinks[sortByLatency[i]]].conf.bandwidth;
233952639Sarchie
234052639Sarchie		distrib[sortByLatency[i]] =
234152639Sarchie		    (bw * (t0 - latency[sortByLatency[i]]) + 50) / 100;
234252639Sarchie		total += distrib[sortByLatency[i]];
234352639Sarchie	}
234452639Sarchie
234552766Sarchie	/* Deal with any rounding error */
234652766Sarchie	if (total < len) {
234759882Sarchie		struct ng_ppp_link *fastLink =
234859882Sarchie		    &priv->links[priv->activeLinks[sortByLatency[0]]];
234952639Sarchie		int fast = 0;
235052639Sarchie
235152766Sarchie		/* Find the fastest link */
235252639Sarchie		for (i = 1; i < numFragments; i++) {
235359882Sarchie			struct ng_ppp_link *const link =
235459882Sarchie			    &priv->links[priv->activeLinks[sortByLatency[i]]];
235559882Sarchie
235659882Sarchie			if (link->conf.bandwidth > fastLink->conf.bandwidth) {
235752639Sarchie				fast = i;
235859882Sarchie				fastLink = link;
235959882Sarchie			}
236052639Sarchie		}
236152639Sarchie		distrib[sortByLatency[fast]] += len - total;
236252766Sarchie	} else while (total > len) {
236359882Sarchie		struct ng_ppp_link *slowLink =
236459882Sarchie		    &priv->links[priv->activeLinks[sortByLatency[0]]];
236552766Sarchie		int delta, slow = 0;
236652639Sarchie
236752766Sarchie		/* Find the slowest link that still has bytes to remove */
236852766Sarchie		for (i = 1; i < numFragments; i++) {
236959882Sarchie			struct ng_ppp_link *const link =
237059882Sarchie			    &priv->links[priv->activeLinks[sortByLatency[i]]];
237159882Sarchie
237252766Sarchie			if (distrib[sortByLatency[slow]] == 0
237352766Sarchie			  || (distrib[sortByLatency[i]] > 0
237459882Sarchie			    && link->conf.bandwidth <
237559882Sarchie			      slowLink->conf.bandwidth)) {
237652766Sarchie				slow = i;
237759882Sarchie				slowLink = link;
237859882Sarchie			}
237952766Sarchie		}
238052766Sarchie		delta = total - len;
238152766Sarchie		if (delta > distrib[sortByLatency[slow]])
238252766Sarchie			delta = distrib[sortByLatency[slow]];
238352766Sarchie		distrib[sortByLatency[slow]] -= delta;
238452766Sarchie		total -= delta;
238552639Sarchie	}
238652419Sjulian}
238752419Sjulian
238852419Sjulian/*
238952639Sarchie * Compare two integers
239052419Sjulian */
239152639Sarchiestatic int
2392132229Sglebiusng_ppp_intcmp(void *latency, const void *v1, const void *v2)
239352419Sjulian{
239452639Sarchie	const int index1 = *((const int *) v1);
239552639Sarchie	const int index2 = *((const int *) v2);
239652419Sjulian
2397132229Sglebius	return ((int *)latency)[index1] - ((int *)latency)[index2];
239852639Sarchie}
239952639Sarchie
240052639Sarchie/*
240152639Sarchie * Prepend a possibly compressed PPP protocol number in front of a frame
240252639Sarchie */
240352639Sarchiestatic struct mbuf *
2404166093Sglebiusng_ppp_addproto(struct mbuf *m, uint16_t proto, int compOK)
240552639Sarchie{
240653075Sarchie	if (compOK && PROT_COMPRESSABLE(proto)) {
2407166093Sglebius		uint8_t pbyte = (uint8_t)proto;
240852639Sarchie
240953075Sarchie		return ng_ppp_prepend(m, &pbyte, 1);
241053075Sarchie	} else {
2411166093Sglebius		uint16_t pword = htons((uint16_t)proto);
241253075Sarchie
241353075Sarchie		return ng_ppp_prepend(m, &pword, 2);
241453075Sarchie	}
241553075Sarchie}
241653075Sarchie
241753075Sarchie/*
2418166093Sglebius * Cut a possibly compressed PPP protocol number from the front of a frame.
241953075Sarchie */
242053075Sarchiestatic struct mbuf *
2421166093Sglebiusng_ppp_cutproto(struct mbuf *m, uint16_t *proto)
2422166093Sglebius{
2423166093Sglebius
2424166093Sglebius	*proto = 0;
2425166093Sglebius	if (m->m_len < 1 && (m = m_pullup(m, 1)) == NULL)
2426166093Sglebius		return (NULL);
2427166093Sglebius
2428166093Sglebius	*proto = *mtod(m, uint8_t *);
2429166093Sglebius	m_adj(m, 1);
2430166093Sglebius
2431166093Sglebius	if (!PROT_VALID(*proto)) {
2432166093Sglebius		if (m->m_len < 1 && (m = m_pullup(m, 1)) == NULL)
2433166093Sglebius			return (NULL);
2434166093Sglebius
2435166093Sglebius		*proto = (*proto << 8) + *mtod(m, uint8_t *);
2436166093Sglebius		m_adj(m, 1);
2437166093Sglebius	}
2438166093Sglebius
2439166093Sglebius	return (m);
2440166093Sglebius}
2441166093Sglebius
2442166093Sglebius/*
2443166093Sglebius * Prepend some bytes to an mbuf.
2444166093Sglebius */
2445166093Sglebiusstatic struct mbuf *
244653075Sarchieng_ppp_prepend(struct mbuf *m, const void *buf, int len)
244753075Sarchie{
2448243882Sglebius	M_PREPEND(m, len, M_NOWAIT);
244953075Sarchie	if (m == NULL || (m->m_len < len && (m = m_pullup(m, len)) == NULL))
245052639Sarchie		return (NULL);
2451166093Sglebius	bcopy(buf, mtod(m, uint8_t *), len);
245252639Sarchie	return (m);
245352639Sarchie}
245452639Sarchie
245552639Sarchie/*
245652639Sarchie * Update private information that is derived from other private information
245752639Sarchie */
245852639Sarchiestatic void
245952639Sarchieng_ppp_update(node_p node, int newConf)
246052639Sarchie{
246170784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
246252639Sarchie	int i;
246352639Sarchie
246452639Sarchie	/* Update active status for VJ Compression */
246552639Sarchie	priv->vjCompHooked = priv->hooks[HOOK_INDEX_VJC_IP] != NULL
246652639Sarchie	    && priv->hooks[HOOK_INDEX_VJC_COMP] != NULL
246752639Sarchie	    && priv->hooks[HOOK_INDEX_VJC_UNCOMP] != NULL
246852639Sarchie	    && priv->hooks[HOOK_INDEX_VJC_VJIP] != NULL;
246952639Sarchie
247052639Sarchie	/* Increase latency for each link an amount equal to one MP header */
247152639Sarchie	if (newConf) {
247252639Sarchie		for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
247352639Sarchie			int hdrBytes;
247452639Sarchie
2475168895Smav			if (priv->links[i].conf.bandwidth == 0)
2476168895Smav			    continue;
2477168895Smav
2478168895Smav			hdrBytes = MP_AVERAGE_LINK_OVERHEAD
2479168895Smav			    + (priv->links[i].conf.enableACFComp ? 0 : 2)
248059882Sarchie			    + (priv->links[i].conf.enableProtoComp ? 1 : 2)
248152639Sarchie			    + (priv->conf.xmitShortSeq ? 2 : 4);
2482133055Sbz			priv->links[i].latency =
2483133055Sbz			    priv->links[i].conf.latency +
2484168895Smav			    (hdrBytes / priv->links[i].conf.bandwidth + 50) / 100;
248552639Sarchie		}
248652419Sjulian	}
248752639Sarchie
248852639Sarchie	/* Update list of active links */
248952639Sarchie	bzero(&priv->activeLinks, sizeof(priv->activeLinks));
249052639Sarchie	priv->numActiveLinks = 0;
249152639Sarchie	priv->allLinksEqual = 1;
249252639Sarchie	for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
249359882Sarchie		struct ng_ppp_link *const link = &priv->links[i];
249453088Sarchie
249559882Sarchie		/* Is link active? */
249659882Sarchie		if (link->conf.enableLink && link->hook != NULL) {
249759882Sarchie			struct ng_ppp_link *link0;
249859882Sarchie
249959882Sarchie			/* Add link to list of active links */
250052639Sarchie			priv->activeLinks[priv->numActiveLinks++] = i;
250159882Sarchie			link0 = &priv->links[priv->activeLinks[0]];
250259882Sarchie
250359882Sarchie			/* Determine if all links are still equal */
2504133055Sbz			if (link->latency != link0->latency
250559882Sarchie			  || link->conf.bandwidth != link0->conf.bandwidth)
250652639Sarchie				priv->allLinksEqual = 0;
250759882Sarchie
250859882Sarchie			/* Initialize rec'd sequence number */
250959882Sarchie			if (link->seq == MP_NOSEQ) {
251059882Sarchie				link->seq = (link == link0) ?
251159882Sarchie				    MP_INITIAL_SEQ : link0->seq;
251259882Sarchie			}
251359882Sarchie		} else
251459882Sarchie			link->seq = MP_NOSEQ;
251552639Sarchie	}
251652639Sarchie
251759882Sarchie	/* Update MP state as multi-link is active or not */
251859882Sarchie	if (priv->conf.enableMultilink && priv->numActiveLinks > 0)
251959882Sarchie		ng_ppp_start_frag_timer(node);
252059882Sarchie	else {
252159882Sarchie		ng_ppp_stop_frag_timer(node);
252259882Sarchie		ng_ppp_frag_reset(node);
252359882Sarchie		priv->xseq = MP_INITIAL_SEQ;
252459882Sarchie		priv->mseq = MP_INITIAL_SEQ;
252559882Sarchie		for (i = 0; i < NG_PPP_MAX_LINKS; i++) {
252659882Sarchie			struct ng_ppp_link *const link = &priv->links[i];
252759882Sarchie
252859882Sarchie			bzero(&link->lastWrite, sizeof(link->lastWrite));
252959882Sarchie			link->bytesInQueue = 0;
253059882Sarchie			link->seq = MP_NOSEQ;
253159882Sarchie		}
253252639Sarchie	}
253352419Sjulian}
253452419Sjulian
253552639Sarchie/*
253652639Sarchie * Determine if a new configuration would represent a valid change
253752639Sarchie * from the current configuration and link activity status.
253852639Sarchie */
253952639Sarchiestatic int
254059882Sarchieng_ppp_config_valid(node_p node, const struct ng_ppp_node_conf *newConf)
254152639Sarchie{
254270784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
254352639Sarchie	int i, newNumLinksActive;
254452639Sarchie
254552639Sarchie	/* Check per-link config and count how many links would be active */
254652639Sarchie	for (newNumLinksActive = i = 0; i < NG_PPP_MAX_LINKS; i++) {
254759882Sarchie		if (newConf->links[i].enableLink && priv->links[i].hook != NULL)
254852912Sarchie			newNumLinksActive++;
254952912Sarchie		if (!newConf->links[i].enableLink)
255052912Sarchie			continue;
255152639Sarchie		if (newConf->links[i].mru < MP_MIN_LINK_MRU)
255252639Sarchie			return (0);
255352639Sarchie		if (newConf->links[i].bandwidth == 0)
255452639Sarchie			return (0);
255552639Sarchie		if (newConf->links[i].bandwidth > NG_PPP_MAX_BANDWIDTH)
255652639Sarchie			return (0);
255752639Sarchie		if (newConf->links[i].latency > NG_PPP_MAX_LATENCY)
255852639Sarchie			return (0);
255952639Sarchie	}
256052639Sarchie
256152639Sarchie	/* Disallow changes to multi-link configuration while MP is active */
256252639Sarchie	if (priv->numActiveLinks > 0 && newNumLinksActive > 0) {
256359882Sarchie		if (!priv->conf.enableMultilink
256459882Sarchie				!= !newConf->bund.enableMultilink
256559882Sarchie		    || !priv->conf.xmitShortSeq != !newConf->bund.xmitShortSeq
256659882Sarchie		    || !priv->conf.recvShortSeq != !newConf->bund.recvShortSeq)
256752639Sarchie			return (0);
256852639Sarchie	}
256952639Sarchie
257052912Sarchie	/* At most one link can be active unless multi-link is enabled */
257159882Sarchie	if (!newConf->bund.enableMultilink && newNumLinksActive > 1)
257252912Sarchie		return (0);
257352912Sarchie
257452912Sarchie	/* Configuration change would be valid */
257552639Sarchie	return (1);
257652639Sarchie}
257752639Sarchie
257852639Sarchie/*
257952639Sarchie * Free all entries in the fragment queue
258052639Sarchie */
258152639Sarchiestatic void
258259882Sarchieng_ppp_frag_reset(node_p node)
258352639Sarchie{
258470784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
258554755Sarchie	struct ng_ppp_frag *qent, *qnext;
258652639Sarchie
258768761Smckusick	for (qent = TAILQ_FIRST(&priv->frags); qent; qent = qnext) {
258868761Smckusick		qnext = TAILQ_NEXT(qent, f_qent);
258970700Sjulian		NG_FREE_M(qent->data);
2590175696Smav		TAILQ_INSERT_HEAD(&priv->fragsfree, qent, f_qent);
259152639Sarchie	}
259268761Smckusick	TAILQ_INIT(&priv->frags);
259352639Sarchie}
259452639Sarchie
259559882Sarchie/*
259659882Sarchie * Start fragment queue timer
259759882Sarchie */
259859882Sarchiestatic void
259959882Sarchieng_ppp_start_frag_timer(node_p node)
260059882Sarchie{
260170784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
260259882Sarchie
2603140068Sglebius	if (!(callout_pending(&priv->fragTimer)))
2604138479Sglebius		ng_callout(&priv->fragTimer, node, NULL, MP_FRAGTIMER_INTERVAL,
2605138479Sglebius		    ng_ppp_frag_timeout, NULL, 0);
260659882Sarchie}
260759882Sarchie
260859882Sarchie/*
260959882Sarchie * Stop fragment queue timer
261059882Sarchie */
261159882Sarchiestatic void
261259882Sarchieng_ppp_stop_frag_timer(node_p node)
261359882Sarchie{
261470784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
261559882Sarchie
2616140068Sglebius	if (callout_pending(&priv->fragTimer))
2617138479Sglebius		ng_uncallout(&priv->fragTimer, node);
261859882Sarchie}
2619