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