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