xref: /freebsd/usr.sbin/ppp/mp.c (revision 086760227f4a61242f39a1773af00d9299b6ad34)
13b0f8d2eSBrian Somers /*-
23b0f8d2eSBrian Somers  * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
33b0f8d2eSBrian Somers  * All rights reserved.
43b0f8d2eSBrian Somers  *
53b0f8d2eSBrian Somers  * Redistribution and use in source and binary forms, with or without
63b0f8d2eSBrian Somers  * modification, are permitted provided that the following conditions
73b0f8d2eSBrian Somers  * are met:
83b0f8d2eSBrian Somers  * 1. Redistributions of source code must retain the above copyright
93b0f8d2eSBrian Somers  *    notice, this list of conditions and the following disclaimer.
103b0f8d2eSBrian Somers  * 2. Redistributions in binary form must reproduce the above copyright
113b0f8d2eSBrian Somers  *    notice, this list of conditions and the following disclaimer in the
123b0f8d2eSBrian Somers  *    documentation and/or other materials provided with the distribution.
133b0f8d2eSBrian Somers  *
143b0f8d2eSBrian Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
153b0f8d2eSBrian Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
163b0f8d2eSBrian Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
173b0f8d2eSBrian Somers  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
183b0f8d2eSBrian Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
193b0f8d2eSBrian Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
203b0f8d2eSBrian Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
213b0f8d2eSBrian Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
223b0f8d2eSBrian Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
233b0f8d2eSBrian Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
243b0f8d2eSBrian Somers  * SUCH DAMAGE.
253b0f8d2eSBrian Somers  *
2608676022SBrian Somers  *	$Id: mp.c,v 1.1.2.10 1998/04/23 21:50:11 brian Exp $
273b0f8d2eSBrian Somers  */
283b0f8d2eSBrian Somers 
293b0f8d2eSBrian Somers #include <sys/types.h>
303b0f8d2eSBrian Somers #include <netinet/in.h>
313b0f8d2eSBrian Somers #include <netinet/in_systm.h>
323b0f8d2eSBrian Somers #include <netinet/ip.h>
3349052c95SBrian Somers #include <arpa/inet.h>
3408676022SBrian Somers #include <net/if_dl.h>
3508676022SBrian Somers #include <sys/socket.h>
363b0f8d2eSBrian Somers 
3708676022SBrian Somers #include <errno.h>
383b0f8d2eSBrian Somers #include <stdlib.h>
3949052c95SBrian Somers #include <stdio.h>
403b0f8d2eSBrian Somers #include <string.h>
413b0f8d2eSBrian Somers #include <termios.h>
4208676022SBrian Somers #include <unistd.h>
433b0f8d2eSBrian Somers 
443b0f8d2eSBrian Somers #include "command.h"
453b0f8d2eSBrian Somers #include "mbuf.h"
463b0f8d2eSBrian Somers #include "log.h"
473b0f8d2eSBrian Somers #include "defs.h"
483b0f8d2eSBrian Somers #include "timer.h"
493b0f8d2eSBrian Somers #include "fsm.h"
503b0f8d2eSBrian Somers #include "iplist.h"
513b0f8d2eSBrian Somers #include "throughput.h"
523b0f8d2eSBrian Somers #include "slcompress.h"
533b0f8d2eSBrian Somers #include "ipcp.h"
543b0f8d2eSBrian Somers #include "auth.h"
553b0f8d2eSBrian Somers #include "lcp.h"
563b0f8d2eSBrian Somers #include "lqr.h"
573b0f8d2eSBrian Somers #include "hdlc.h"
583b0f8d2eSBrian Somers #include "async.h"
593b0f8d2eSBrian Somers #include "ccp.h"
603b0f8d2eSBrian Somers #include "link.h"
613b0f8d2eSBrian Somers #include "descriptor.h"
623b0f8d2eSBrian Somers #include "physical.h"
633b0f8d2eSBrian Somers #include "chat.h"
643b0f8d2eSBrian Somers #include "lcpproto.h"
653b0f8d2eSBrian Somers #include "filter.h"
663b0f8d2eSBrian Somers #include "mp.h"
673b0f8d2eSBrian Somers #include "chap.h"
683b0f8d2eSBrian Somers #include "datalink.h"
693b0f8d2eSBrian Somers #include "bundle.h"
703b0f8d2eSBrian Somers #include "ip.h"
7149052c95SBrian Somers #include "prompt.h"
7208676022SBrian Somers #include "id.h"
7308676022SBrian Somers #include "arp.h"
743b0f8d2eSBrian Somers 
753b0f8d2eSBrian Somers static u_int32_t
763b0f8d2eSBrian Somers inc_seq(struct mp *mp, u_int32_t seq)
773b0f8d2eSBrian Somers {
783b0f8d2eSBrian Somers   seq++;
7949052c95SBrian Somers   if (mp->peer_is12bit) {
803b0f8d2eSBrian Somers     if (seq & 0xfffff000)
813b0f8d2eSBrian Somers       seq = 0;
823b0f8d2eSBrian Somers   } else if (seq & 0xff000000)
833b0f8d2eSBrian Somers     seq = 0;
843b0f8d2eSBrian Somers   return seq;
853b0f8d2eSBrian Somers }
863b0f8d2eSBrian Somers 
873b0f8d2eSBrian Somers static int
883b0f8d2eSBrian Somers mp_ReadHeader(struct mp *mp, struct mbuf *m, struct mp_header *header)
893b0f8d2eSBrian Somers {
9049052c95SBrian Somers   if (mp->local_is12bit) {
91ce828a6eSBrian Somers     header->seq = ntohs(*(u_int16_t *)MBUF_CTOP(m));
923b0f8d2eSBrian Somers     if (header->seq & 0x3000) {
933b0f8d2eSBrian Somers       LogPrintf(LogWARN, "Oops - MP header without required zero bits\n");
943b0f8d2eSBrian Somers       return 0;
953b0f8d2eSBrian Somers     }
96ce828a6eSBrian Somers     header->begin = header->seq & 0x8000 ? 1 : 0;
97ce828a6eSBrian Somers     header->end = header->seq & 0x4000 ? 1 : 0;
983b0f8d2eSBrian Somers     header->seq &= 0x0fff;
993b0f8d2eSBrian Somers     return 2;
1003b0f8d2eSBrian Somers   } else {
101ce828a6eSBrian Somers     header->seq = ntohl(*(u_int32_t *)MBUF_CTOP(m));
1023b0f8d2eSBrian Somers     if (header->seq & 0x3f000000) {
1033b0f8d2eSBrian Somers       LogPrintf(LogWARN, "Oops - MP header without required zero bits\n");
1043b0f8d2eSBrian Somers       return 0;
1053b0f8d2eSBrian Somers     }
106ce828a6eSBrian Somers     header->begin = header->seq & 0x80000000 ? 1 : 0;
107ce828a6eSBrian Somers     header->end = header->seq & 0x40000000 ? 1 : 0;
1083b0f8d2eSBrian Somers     header->seq &= 0x00ffffff;
1093b0f8d2eSBrian Somers     return 4;
1103b0f8d2eSBrian Somers   }
1113b0f8d2eSBrian Somers }
1123b0f8d2eSBrian Somers 
1133b0f8d2eSBrian Somers static void
1143b0f8d2eSBrian Somers mp_LayerStart(void *v, struct fsm *fp)
1153b0f8d2eSBrian Somers {
11649052c95SBrian Somers   /* The given FSM (ccp) is about to start up ! */
1173b0f8d2eSBrian Somers }
1183b0f8d2eSBrian Somers 
1193b0f8d2eSBrian Somers static void
1203b0f8d2eSBrian Somers mp_LayerUp(void *v, struct fsm *fp)
1213b0f8d2eSBrian Somers {
12249052c95SBrian Somers   /* The given fsm (ccp) is now up */
1233b0f8d2eSBrian Somers }
1243b0f8d2eSBrian Somers 
1253b0f8d2eSBrian Somers static void
1263b0f8d2eSBrian Somers mp_LayerDown(void *v, struct fsm *fp)
1273b0f8d2eSBrian Somers {
12849052c95SBrian Somers   /* The given FSM (ccp) has been told to come down */
1293b0f8d2eSBrian Somers }
1303b0f8d2eSBrian Somers 
1313b0f8d2eSBrian Somers static void
1323b0f8d2eSBrian Somers mp_LayerFinish(void *v, struct fsm *fp)
1333b0f8d2eSBrian Somers {
13449052c95SBrian Somers   /* The given fsm (ccp) is now down */
1353b0f8d2eSBrian Somers }
1363b0f8d2eSBrian Somers 
1373b0f8d2eSBrian Somers void
1383b0f8d2eSBrian Somers mp_Init(struct mp *mp, struct bundle *bundle)
1393b0f8d2eSBrian Somers {
14049052c95SBrian Somers   mp->peer_is12bit = mp->local_is12bit = 0;
14149052c95SBrian Somers   mp->peer_mrru = mp->local_mrru = 0;
14249052c95SBrian Somers   mp->peer_enddisc.class = 0;
14349052c95SBrian Somers   *mp->peer_enddisc.address = '\0';
14449052c95SBrian Somers   mp->peer_enddisc.len = 0;
1453b0f8d2eSBrian Somers   mp->seq.out = 0;
1463b0f8d2eSBrian Somers   mp->seq.min_in = 0;
1473b0f8d2eSBrian Somers   mp->seq.next_in = 0;
1483b0f8d2eSBrian Somers   mp->inbufs = NULL;
1493b0f8d2eSBrian Somers   mp->bundle = bundle;
1503b0f8d2eSBrian Somers 
1513b0f8d2eSBrian Somers   mp->link.type = MP_LINK;
1523b0f8d2eSBrian Somers   mp->link.name = "mp";
1533b0f8d2eSBrian Somers   mp->link.len = sizeof *mp;
1543b0f8d2eSBrian Somers   throughput_init(&mp->link.throughput);
1553b0f8d2eSBrian Somers   memset(mp->link.Queue, '\0', sizeof mp->link.Queue);
1563b0f8d2eSBrian Somers   memset(mp->link.proto_in, '\0', sizeof mp->link.proto_in);
1573b0f8d2eSBrian Somers   memset(mp->link.proto_out, '\0', sizeof mp->link.proto_out);
1583b0f8d2eSBrian Somers 
1593b0f8d2eSBrian Somers   mp->fsmp.LayerStart = mp_LayerStart;
1603b0f8d2eSBrian Somers   mp->fsmp.LayerUp = mp_LayerUp;
1613b0f8d2eSBrian Somers   mp->fsmp.LayerDown = mp_LayerDown;
1623b0f8d2eSBrian Somers   mp->fsmp.LayerFinish = mp_LayerFinish;
1633b0f8d2eSBrian Somers   mp->fsmp.object = mp;
1643b0f8d2eSBrian Somers 
16549052c95SBrian Somers   mp->cfg.mrru = 0;
16649052c95SBrian Somers   mp->cfg.shortseq = NEG_ENABLED|NEG_ACCEPTED;
16749052c95SBrian Somers   mp->cfg.enddisc.class = 0;
16849052c95SBrian Somers   *mp->cfg.enddisc.address = '\0';
16949052c95SBrian Somers   mp->cfg.enddisc.len = 0;
17049052c95SBrian Somers 
1713b0f8d2eSBrian Somers   lcp_Init(&mp->link.lcp, mp->bundle, &mp->link, NULL);
1723b0f8d2eSBrian Somers   ccp_Init(&mp->link.ccp, mp->bundle, &mp->link, &mp->fsmp);
17349052c95SBrian Somers }
17449052c95SBrian Somers 
17549052c95SBrian Somers int
17649052c95SBrian Somers mp_Up(struct mp *mp, u_short local_mrru, u_short peer_mrru,
17749052c95SBrian Somers            int local_shortseq, int peer_shortseq)
17849052c95SBrian Somers {
17949052c95SBrian Somers   if (mp->active) {
18049052c95SBrian Somers     /* We're adding a link - do a last validation on our parameters */
18149052c95SBrian Somers     if (mp->local_mrru != local_mrru ||
18249052c95SBrian Somers         mp->peer_mrru != peer_mrru ||
18349052c95SBrian Somers         mp->local_is12bit != local_shortseq ||
18449052c95SBrian Somers         mp->peer_is12bit != peer_shortseq) {
18549052c95SBrian Somers       LogPrintf(LogPHASE, "Invalid MRRU/SHORTSEQ MP parameters !\n");
18649052c95SBrian Somers       return 0;
18749052c95SBrian Somers     }
18849052c95SBrian Somers   } else {
18949052c95SBrian Somers     /* First link in multilink mode */
19049052c95SBrian Somers 
19149052c95SBrian Somers     mp->local_mrru = local_mrru;
19249052c95SBrian Somers     mp->peer_mrru = peer_mrru;
19349052c95SBrian Somers     mp->local_is12bit = local_shortseq;
19449052c95SBrian Somers     mp->peer_is12bit = peer_shortseq;
19549052c95SBrian Somers 
196673903ecSBrian Somers     throughput_init(&mp->link.throughput);
197673903ecSBrian Somers     memset(mp->link.Queue, '\0', sizeof mp->link.Queue);
198673903ecSBrian Somers     memset(mp->link.proto_in, '\0', sizeof mp->link.proto_in);
199673903ecSBrian Somers     memset(mp->link.proto_out, '\0', sizeof mp->link.proto_out);
200673903ecSBrian Somers 
201673903ecSBrian Somers     mp->seq.out = 0;
202673903ecSBrian Somers     mp->seq.min_in = 0;
203673903ecSBrian Somers     mp->seq.next_in = 0;
204673903ecSBrian Somers 
20549052c95SBrian Somers     /* Re-point our IPCP layer at our MP link */
206ce828a6eSBrian Somers     ipcp_SetLink(&mp->bundle->ncp.ipcp, &mp->link);
2073b0f8d2eSBrian Somers 
2083b0f8d2eSBrian Somers     /* Our lcp's already up 'cos of the NULL parent */
2093b0f8d2eSBrian Somers     FsmUp(&mp->link.ccp.fsm);
2103b0f8d2eSBrian Somers     FsmOpen(&mp->link.ccp.fsm);
2113b0f8d2eSBrian Somers 
2123b0f8d2eSBrian Somers     mp->active = 1;
21349052c95SBrian Somers   }
2143b0f8d2eSBrian Somers 
21549052c95SBrian Somers   return 1;
2163b0f8d2eSBrian Somers }
2173b0f8d2eSBrian Somers 
2183b0f8d2eSBrian Somers void
219673903ecSBrian Somers mp_Down(struct mp *mp)
220673903ecSBrian Somers {
221673903ecSBrian Somers   if (mp->active) {
222673903ecSBrian Somers     struct mbuf *next;
223673903ecSBrian Somers 
224673903ecSBrian Somers     /* CCP goes down with a bank */
225673903ecSBrian Somers     FsmDown(&mp->link.ccp.fsm);
226673903ecSBrian Somers     FsmClose(&mp->link.ccp.fsm);
227673903ecSBrian Somers 
228673903ecSBrian Somers     /* Received fragments go in the bit-bucket */
229673903ecSBrian Somers     while (mp->inbufs) {
230673903ecSBrian Somers       next = mp->inbufs->pnext;
231673903ecSBrian Somers       pfree(mp->inbufs);
232673903ecSBrian Somers       mp->inbufs = next;
233673903ecSBrian Somers     }
234673903ecSBrian Somers 
235673903ecSBrian Somers     mp->active = 0;
236673903ecSBrian Somers   }
237673903ecSBrian Somers }
238673903ecSBrian Somers 
239673903ecSBrian Somers void
2403b0f8d2eSBrian Somers mp_linkInit(struct mp_link *mplink)
2413b0f8d2eSBrian Somers {
2423b0f8d2eSBrian Somers   mplink->seq = 0;
2433b0f8d2eSBrian Somers   mplink->weight = 1500;
2443b0f8d2eSBrian Somers }
2453b0f8d2eSBrian Somers 
2463b0f8d2eSBrian Somers void
2473b0f8d2eSBrian Somers mp_Input(struct mp *mp, struct mbuf *m, struct physical *p)
2483b0f8d2eSBrian Somers {
2493b0f8d2eSBrian Somers   struct mp_header mh, h;
2503b0f8d2eSBrian Somers   struct mbuf *q, *last;
2513b0f8d2eSBrian Somers   int32_t seq;
2523b0f8d2eSBrian Somers 
253cdbbb6b5SBrian Somers   if (mp_ReadHeader(mp, m, &mh) == 0) {
2543b0f8d2eSBrian Somers     pfree(m);
2553b0f8d2eSBrian Somers     return;
2563b0f8d2eSBrian Somers   }
2573b0f8d2eSBrian Somers 
2583b0f8d2eSBrian Somers   seq = p->dl->mp.seq;
2593b0f8d2eSBrian Somers   p->dl->mp.seq = mh.seq;
2603b0f8d2eSBrian Somers   if (mp->seq.min_in == seq) {
2613b0f8d2eSBrian Somers     /*
2623b0f8d2eSBrian Somers      * We've received new data on the link that has our min (oldest) seq.
2633b0f8d2eSBrian Somers      * Figure out which link now has the smallest (oldest) seq.
2643b0f8d2eSBrian Somers      */
2653b0f8d2eSBrian Somers     struct datalink *dl;
2663b0f8d2eSBrian Somers 
2673b0f8d2eSBrian Somers     mp->seq.min_in = p->dl->mp.seq;
2683b0f8d2eSBrian Somers     for (dl = mp->bundle->links; dl; dl = dl->next)
2693b0f8d2eSBrian Somers       if (mp->seq.min_in > dl->mp.seq)
2703b0f8d2eSBrian Somers         mp->seq.min_in = dl->mp.seq;
2713b0f8d2eSBrian Somers   }
2723b0f8d2eSBrian Somers 
2733b0f8d2eSBrian Somers   /*
2743b0f8d2eSBrian Somers    * Now process as many of our fragments as we can, adding our new
2753b0f8d2eSBrian Somers    * fragment in as we go, and ordering with the oldest at the top of
2763b0f8d2eSBrian Somers    * the queue.
2773b0f8d2eSBrian Somers    */
2783b0f8d2eSBrian Somers 
2793b0f8d2eSBrian Somers   if (!mp->inbufs) {
2803b0f8d2eSBrian Somers     mp->inbufs = m;
2813b0f8d2eSBrian Somers     m = NULL;
2823b0f8d2eSBrian Somers   }
2833b0f8d2eSBrian Somers 
2843b0f8d2eSBrian Somers   last = NULL;
2853b0f8d2eSBrian Somers   seq = mp->seq.next_in;
2863b0f8d2eSBrian Somers   q = mp->inbufs;
2873b0f8d2eSBrian Somers   while (q) {
2883b0f8d2eSBrian Somers     mp_ReadHeader(mp, q, &h);
2893b0f8d2eSBrian Somers     if (m && h.seq > mh.seq) {
2903b0f8d2eSBrian Somers       /* Our received fragment fits in before this one, so link it in */
2913b0f8d2eSBrian Somers       if (last)
2923b0f8d2eSBrian Somers         last->pnext = m;
2933b0f8d2eSBrian Somers       else
2943b0f8d2eSBrian Somers         mp->inbufs = m;
2953b0f8d2eSBrian Somers       m->pnext = q;
2963b0f8d2eSBrian Somers       q = m;
2973b0f8d2eSBrian Somers       h = mh;
2983b0f8d2eSBrian Somers       m = NULL;
2993b0f8d2eSBrian Somers     }
3003b0f8d2eSBrian Somers 
3013b0f8d2eSBrian Somers     if (h.seq != seq) {
3023b0f8d2eSBrian Somers       /* we're missing something :-( */
3033b0f8d2eSBrian Somers       if (mp->seq.min_in > seq) {
3043b0f8d2eSBrian Somers         /* we're never gonna get it */
3053b0f8d2eSBrian Somers         struct mbuf *next;
3063b0f8d2eSBrian Somers 
3073b0f8d2eSBrian Somers         /* Zap all older fragments */
3083b0f8d2eSBrian Somers         while (mp->inbufs != q) {
309ce828a6eSBrian Somers           LogPrintf(LogDEBUG, "Drop frag\n");
3103b0f8d2eSBrian Somers           next = mp->inbufs->pnext;
3113b0f8d2eSBrian Somers           pfree(mp->inbufs);
3123b0f8d2eSBrian Somers           mp->inbufs = next;
3133b0f8d2eSBrian Somers         }
3143b0f8d2eSBrian Somers 
3153b0f8d2eSBrian Somers         /*
3163b0f8d2eSBrian Somers          * Zap everything until the next `end' fragment OR just before
3173b0f8d2eSBrian Somers          * the next `begin' fragment OR 'till seq.min_in - whichever
3183b0f8d2eSBrian Somers          * comes first.
3193b0f8d2eSBrian Somers          */
3203b0f8d2eSBrian Somers         do {
3213b0f8d2eSBrian Somers           mp_ReadHeader(mp, mp->inbufs, &h);
3223b0f8d2eSBrian Somers           if (h.begin) {
323ce828a6eSBrian Somers             /* We might be able to process this ! */
3243b0f8d2eSBrian Somers             h.seq--;  /* We're gonna look for fragment with h.seq+1 */
3253b0f8d2eSBrian Somers             break;
3263b0f8d2eSBrian Somers           }
3273b0f8d2eSBrian Somers           next = mp->inbufs->pnext;
328ce828a6eSBrian Somers           LogPrintf(LogDEBUG, "Drop frag %u\n", h.seq);
3293b0f8d2eSBrian Somers           pfree(mp->inbufs);
3303b0f8d2eSBrian Somers           mp->inbufs = next;
331ce828a6eSBrian Somers         } while (mp->inbufs && (h.seq >= mp->seq.min_in || h.end));
3323b0f8d2eSBrian Somers 
3333b0f8d2eSBrian Somers         /*
3343b0f8d2eSBrian Somers          * Continue processing things from here.
3353b0f8d2eSBrian Somers          * This deals with the possibility that we received a fragment
3363b0f8d2eSBrian Somers          * on the slowest link that invalidates some of our data (because
3373b0f8d2eSBrian Somers          * of the hole at `q'), but where there are subsequent `whole'
3383b0f8d2eSBrian Somers          * packets that have already been received.
3393b0f8d2eSBrian Somers          */
3403b0f8d2eSBrian Somers 
3413b0f8d2eSBrian Somers         mp->seq.next_in = seq = h.seq + 1;
3423b0f8d2eSBrian Somers         last = NULL;
3433b0f8d2eSBrian Somers         q = mp->inbufs;
3443b0f8d2eSBrian Somers       } else
3453b0f8d2eSBrian Somers         /* we may still receive the missing fragment */
3463b0f8d2eSBrian Somers         break;
3473b0f8d2eSBrian Somers     } else if (h.end) {
3483b0f8d2eSBrian Somers       /* We've got something, reassemble */
3493b0f8d2eSBrian Somers       struct mbuf **frag = &q;
3503b0f8d2eSBrian Somers       int len;
351ce828a6eSBrian Somers       u_long first = -1;
3523b0f8d2eSBrian Somers 
3533b0f8d2eSBrian Somers       do {
3543b0f8d2eSBrian Somers         *frag = mp->inbufs;
3553b0f8d2eSBrian Somers         mp->inbufs = mp->inbufs->pnext;
3563b0f8d2eSBrian Somers         len = mp_ReadHeader(mp, *frag, &h);
357ce828a6eSBrian Somers         if (first == -1)
358ce828a6eSBrian Somers           first = h.seq;
3593b0f8d2eSBrian Somers         (*frag)->offset += len;
3603b0f8d2eSBrian Somers         (*frag)->cnt -= len;
3613b0f8d2eSBrian Somers         (*frag)->pnext = NULL;
3623b0f8d2eSBrian Somers         if (frag == &q && !h.begin) {
3633b0f8d2eSBrian Somers           LogPrintf(LogWARN, "Oops - MP frag %lu should have a begin flag\n",
3643b0f8d2eSBrian Somers                     (u_long)h.seq);
3653b0f8d2eSBrian Somers           pfree(q);
3663b0f8d2eSBrian Somers           q = NULL;
3673b0f8d2eSBrian Somers         } else if (frag != &q && h.begin) {
3683b0f8d2eSBrian Somers           LogPrintf(LogWARN, "Oops - MP frag %lu should have an end flag\n",
3693b0f8d2eSBrian Somers                     (u_long)h.seq - 1);
3703b0f8d2eSBrian Somers           /*
3713b0f8d2eSBrian Somers            * Stuff our fragment back at the front of the queue and zap
3723b0f8d2eSBrian Somers            * our half-assembed packet.
3733b0f8d2eSBrian Somers            */
3743b0f8d2eSBrian Somers           (*frag)->pnext = mp->inbufs;
3753b0f8d2eSBrian Somers           mp->inbufs = *frag;
3763b0f8d2eSBrian Somers           *frag = NULL;
3773b0f8d2eSBrian Somers           pfree(q);
3783b0f8d2eSBrian Somers           q = NULL;
3793b0f8d2eSBrian Somers           frag = &q;
3803b0f8d2eSBrian Somers           h.end = 0;	/* just in case it's a whole packet */
3813b0f8d2eSBrian Somers         } else
3823b0f8d2eSBrian Somers           do
3833b0f8d2eSBrian Somers             frag = &(*frag)->next;
384ce828a6eSBrian Somers           while (*frag != NULL);
3853b0f8d2eSBrian Somers       } while (!h.end);
3863b0f8d2eSBrian Somers 
3873b0f8d2eSBrian Somers       if (q) {
388673903ecSBrian Somers         u_short proto;
389673903ecSBrian Somers         u_char ch;
390673903ecSBrian Somers 
3913b0f8d2eSBrian Somers         q = mbread(q, &ch, 1);
392673903ecSBrian Somers         proto = ch;
393673903ecSBrian Somers         if (!(proto & 1)) {
394673903ecSBrian Somers           q = mbread(q, &ch, 1);
395673903ecSBrian Somers           proto <<= 8;
3963b0f8d2eSBrian Somers           proto += ch;
397673903ecSBrian Somers         }
398673903ecSBrian Somers         if (LogIsKept(LogDEBUG))
399673903ecSBrian Somers           LogPrintf(LogDEBUG, "MP: Reassembled frags %ld-%lu, length %d\n",
400673903ecSBrian Somers                     first, (u_long)h.seq, plength(q));
4013b0f8d2eSBrian Somers         hdlc_DecodePacket(mp->bundle, proto, q, &mp->link);
4023b0f8d2eSBrian Somers       }
4033b0f8d2eSBrian Somers 
4043b0f8d2eSBrian Somers       mp->seq.next_in = seq = h.seq + 1;
4053b0f8d2eSBrian Somers       last = NULL;
4063b0f8d2eSBrian Somers       q = mp->inbufs;
4073b0f8d2eSBrian Somers     } else {
4083b0f8d2eSBrian Somers       /* Look for the next fragment */
4093b0f8d2eSBrian Somers       seq++;
4103b0f8d2eSBrian Somers       last = q;
4113b0f8d2eSBrian Somers       q = q->pnext;
4123b0f8d2eSBrian Somers     }
4133b0f8d2eSBrian Somers   }
4143b0f8d2eSBrian Somers 
4153b0f8d2eSBrian Somers   if (m) {
4163b0f8d2eSBrian Somers     /* We still have to find a home for our new fragment */
4173b0f8d2eSBrian Somers     last = NULL;
4183b0f8d2eSBrian Somers     for (q = mp->inbufs; q; last = q, q = q->pnext) {
4193b0f8d2eSBrian Somers       mp_ReadHeader(mp, q, &h);
4203b0f8d2eSBrian Somers       if (h.seq > mh.seq) {
4213b0f8d2eSBrian Somers         /* Our received fragment fits in before this one, so link it in */
4223b0f8d2eSBrian Somers         if (last)
4233b0f8d2eSBrian Somers           last->pnext = m;
4243b0f8d2eSBrian Somers         else
4253b0f8d2eSBrian Somers           mp->inbufs = m;
4263b0f8d2eSBrian Somers         m->pnext = q;
4273b0f8d2eSBrian Somers         break;
4283b0f8d2eSBrian Somers       }
4293b0f8d2eSBrian Somers     }
4303b0f8d2eSBrian Somers   }
4313b0f8d2eSBrian Somers }
4323b0f8d2eSBrian Somers 
4333b0f8d2eSBrian Somers static void
4343b0f8d2eSBrian Somers mp_Output(struct mp *mp, struct link *l, struct mbuf *m, int begin, int end)
4353b0f8d2eSBrian Somers {
4363b0f8d2eSBrian Somers   struct mbuf *mo;
4373b0f8d2eSBrian Somers 
438ce828a6eSBrian Somers   /* Stuff an MP header on the front of our packet and send it */
4393b0f8d2eSBrian Somers   mo = mballoc(4, MB_MP);
4403b0f8d2eSBrian Somers   mo->next = m;
44149052c95SBrian Somers   if (mp->peer_is12bit) {
442ce828a6eSBrian Somers     u_int16_t *seq16;
443ce828a6eSBrian Somers 
444ce828a6eSBrian Somers     seq16 = (u_int16_t *)MBUF_CTOP(mo);
445ce828a6eSBrian Somers     *seq16 = htons((begin << 15) | (end << 14) | (u_int16_t)mp->seq.out);
4463b0f8d2eSBrian Somers     mo->cnt = 2;
4473b0f8d2eSBrian Somers   } else {
448ce828a6eSBrian Somers     u_int32_t *seq32;
449ce828a6eSBrian Somers 
450ce828a6eSBrian Somers     seq32 = (u_int32_t *)MBUF_CTOP(mo);
451ce828a6eSBrian Somers     *seq32 = htonl((begin << 31) | (end << 30) | (u_int32_t)mp->seq.out);
4523b0f8d2eSBrian Somers     mo->cnt = 4;
4533b0f8d2eSBrian Somers   }
454673903ecSBrian Somers   if (LogIsKept(LogDEBUG))
455673903ecSBrian Somers     LogPrintf(LogDEBUG, "MP[frag %d]: Send %d bytes on %s\n",
456ce828a6eSBrian Somers               mp->seq.out, plength(mo), l->name);
4573b0f8d2eSBrian Somers   mp->seq.out = inc_seq(mp, mp->seq.out);
4583b0f8d2eSBrian Somers 
4593b0f8d2eSBrian Somers   HdlcOutput(l, PRI_NORMAL, PROTO_MP, mo);
4603b0f8d2eSBrian Somers }
4613b0f8d2eSBrian Somers 
4623b0f8d2eSBrian Somers int
4633b0f8d2eSBrian Somers mp_FillQueues(struct bundle *bundle)
4643b0f8d2eSBrian Somers {
4653b0f8d2eSBrian Somers   struct mp *mp = &bundle->ncp.mp;
4663b0f8d2eSBrian Somers   struct datalink *dl;
4673b0f8d2eSBrian Somers   int total, add, len, begin, end, looped;
4683b0f8d2eSBrian Somers   struct mbuf *m, *mo;
4693b0f8d2eSBrian Somers 
4703b0f8d2eSBrian Somers   /*
4713b0f8d2eSBrian Somers    * XXX:  This routine is fairly simplistic.  It should re-order the
4723b0f8d2eSBrian Somers    *       links based on the amount of data less than the links weight
4733b0f8d2eSBrian Somers    *       that was queued.  That way we'd ``prefer'' the least used
4743b0f8d2eSBrian Somers    *       links the next time 'round.
4753b0f8d2eSBrian Somers    */
4763b0f8d2eSBrian Somers 
4773b0f8d2eSBrian Somers   total = 0;
4783b0f8d2eSBrian Somers   for (dl = bundle->links; dl; dl = dl->next) {
4793b0f8d2eSBrian Somers     if (dl->physical->out)
4803b0f8d2eSBrian Somers       /* this link has suffered a short write.  Let it continue */
4813b0f8d2eSBrian Somers       continue;
4823b0f8d2eSBrian Somers     add = link_QueueLen(&dl->physical->link);
4833b0f8d2eSBrian Somers     total += add;
4843b0f8d2eSBrian Somers     if (add)
4853b0f8d2eSBrian Somers       /* this link has got stuff already queued.  Let it continue */
4863b0f8d2eSBrian Somers       continue;
4873b0f8d2eSBrian Somers     if (!link_QueueLen(&mp->link) && !IpFlushPacket(&mp->link, bundle))
4883b0f8d2eSBrian Somers       /* Nothing else to send */
4893b0f8d2eSBrian Somers       break;
4903b0f8d2eSBrian Somers 
4913b0f8d2eSBrian Somers     m = link_Dequeue(&mp->link);
4923b0f8d2eSBrian Somers     len = plength(m);
4933b0f8d2eSBrian Somers     add += len;
4943b0f8d2eSBrian Somers     begin = 1;
4953b0f8d2eSBrian Somers     end = 0;
4963b0f8d2eSBrian Somers     looped = 0;
4973b0f8d2eSBrian Somers 
4983b0f8d2eSBrian Somers     for (; !end; dl = dl->next) {
4993b0f8d2eSBrian Somers       if (dl == NULL) {
5003b0f8d2eSBrian Somers         /* Keep going 'till we get rid of the whole of `m' */
5013b0f8d2eSBrian Somers         looped = 1;
5023b0f8d2eSBrian Somers         dl = bundle->links;
5033b0f8d2eSBrian Somers       }
5043b0f8d2eSBrian Somers       if (len <= dl->mp.weight + LINK_MINWEIGHT) {
5053b0f8d2eSBrian Somers         mo = m;
5063b0f8d2eSBrian Somers         end = 1;
5073b0f8d2eSBrian Somers       } else {
5083b0f8d2eSBrian Somers         mo = mballoc(dl->mp.weight, MB_MP);
5093b0f8d2eSBrian Somers         mo->cnt = dl->mp.weight;
5103b0f8d2eSBrian Somers         len -= mo->cnt;
5113b0f8d2eSBrian Somers         m = mbread(m, MBUF_CTOP(mo), mo->cnt);
5123b0f8d2eSBrian Somers       }
5133b0f8d2eSBrian Somers       mp_Output(mp, &dl->physical->link, mo, begin, end);
5143b0f8d2eSBrian Somers       begin = 0;
5153b0f8d2eSBrian Somers     }
516ce828a6eSBrian Somers     if (!dl || looped)
5173b0f8d2eSBrian Somers       break;
5183b0f8d2eSBrian Somers   }
5193b0f8d2eSBrian Somers 
5203b0f8d2eSBrian Somers   return total;
5213b0f8d2eSBrian Somers }
5223b0f8d2eSBrian Somers 
5233b0f8d2eSBrian Somers int
5243b0f8d2eSBrian Somers mp_SetDatalinkWeight(struct cmdargs const *arg)
5253b0f8d2eSBrian Somers {
5263b0f8d2eSBrian Somers   int val;
5273b0f8d2eSBrian Somers 
52825092092SBrian Somers   if (arg->argc != arg->argn+1)
5293b0f8d2eSBrian Somers     return -1;
5303b0f8d2eSBrian Somers 
53125092092SBrian Somers   val = atoi(arg->argv[arg->argn]);
5323b0f8d2eSBrian Somers   if (val < LINK_MINWEIGHT) {
5333b0f8d2eSBrian Somers     LogPrintf(LogWARN, "Link weights must not be less than %d\n",
5343b0f8d2eSBrian Somers               LINK_MINWEIGHT);
5353b0f8d2eSBrian Somers     return 1;
5363b0f8d2eSBrian Somers   }
5373b0f8d2eSBrian Somers   arg->cx->mp.weight = val;
5383b0f8d2eSBrian Somers   return 0;
5393b0f8d2eSBrian Somers }
54049052c95SBrian Somers 
54149052c95SBrian Somers int
54249052c95SBrian Somers mp_ShowStatus(struct cmdargs const *arg)
54349052c95SBrian Somers {
54449052c95SBrian Somers   struct mp *mp = &arg->bundle->ncp.mp;
54549052c95SBrian Somers 
54649052c95SBrian Somers   prompt_Printf(arg->prompt, "Multilink is %sactive\n", mp->active ? "" : "in");
54749052c95SBrian Somers 
54849052c95SBrian Somers   prompt_Printf(arg->prompt, "\nMy Side:\n");
54949052c95SBrian Somers   if (mp->active) {
55049052c95SBrian Somers     prompt_Printf(arg->prompt, " MRRU:      %u\n", mp->local_mrru);
55149052c95SBrian Somers     prompt_Printf(arg->prompt, " Short Seq: %s\n",
55249052c95SBrian Somers                   mp->local_is12bit ? "on" : "off");
55349052c95SBrian Somers   }
55449052c95SBrian Somers   prompt_Printf(arg->prompt, " End Disc:  %s\n",
55549052c95SBrian Somers                 mp_Enddisc(mp->cfg.enddisc.class, mp->cfg.enddisc.address,
55649052c95SBrian Somers                            mp->cfg.enddisc.len));
55749052c95SBrian Somers 
55849052c95SBrian Somers   prompt_Printf(arg->prompt, "\nHis Side:\n");
55949052c95SBrian Somers   if (mp->active) {
56049052c95SBrian Somers     prompt_Printf(arg->prompt, " Next SEQ:  %u\n", mp->seq.out);
56149052c95SBrian Somers     prompt_Printf(arg->prompt, " MRRU:      %u\n", mp->peer_mrru);
56249052c95SBrian Somers     prompt_Printf(arg->prompt, " Short Seq: %s\n",
56349052c95SBrian Somers                   mp->peer_is12bit ? "on" : "off");
56449052c95SBrian Somers   }
56549052c95SBrian Somers   prompt_Printf(arg->prompt, " End Disc:  %s\n",
56649052c95SBrian Somers                 mp_Enddisc(mp->peer_enddisc.class, mp->peer_enddisc.address,
56749052c95SBrian Somers                            mp->peer_enddisc.len));
56849052c95SBrian Somers 
56949052c95SBrian Somers   prompt_Printf(arg->prompt, "\nDefaults:\n");
57049052c95SBrian Somers 
57149052c95SBrian Somers   prompt_Printf(arg->prompt, " MRRU:      ");
57249052c95SBrian Somers   if (mp->cfg.mrru)
57349052c95SBrian Somers     prompt_Printf(arg->prompt, "%d (multilink enabled)\n", mp->cfg.mrru);
57449052c95SBrian Somers   else
57549052c95SBrian Somers     prompt_Printf(arg->prompt, "disabled\n");
57649052c95SBrian Somers   prompt_Printf(arg->prompt, " Short Seq: %s\n",
57749052c95SBrian Somers                   command_ShowNegval(mp->cfg.shortseq));
57849052c95SBrian Somers 
57949052c95SBrian Somers   return 0;
58049052c95SBrian Somers }
58149052c95SBrian Somers 
58249052c95SBrian Somers const char *
58349052c95SBrian Somers mp_Enddisc(u_char c, const char *address, int len)
58449052c95SBrian Somers {
58549052c95SBrian Somers   static char result[100];
58649052c95SBrian Somers   int f, header;
58749052c95SBrian Somers 
58849052c95SBrian Somers   switch (c) {
58908676022SBrian Somers     case ENDDISC_NULL:
59049052c95SBrian Somers       sprintf(result, "Null Class");
59149052c95SBrian Somers       break;
59249052c95SBrian Somers 
59308676022SBrian Somers     case ENDDISC_LOCAL:
59449052c95SBrian Somers       snprintf(result, sizeof result, "Local Addr: %.*s", len, address);
59549052c95SBrian Somers       break;
59649052c95SBrian Somers 
59708676022SBrian Somers     case ENDDISC_IP:
59849052c95SBrian Somers       if (len == 4)
59949052c95SBrian Somers         snprintf(result, sizeof result, "IP %s",
60049052c95SBrian Somers                  inet_ntoa(*(const struct in_addr *)address));
60149052c95SBrian Somers       else
60249052c95SBrian Somers         sprintf(result, "IP[%d] ???", len);
60349052c95SBrian Somers       break;
60449052c95SBrian Somers 
60508676022SBrian Somers     case ENDDISC_MAC:
60649052c95SBrian Somers       if (len == 6) {
60749052c95SBrian Somers         const u_char *m = (const u_char *)address;
60849052c95SBrian Somers         snprintf(result, sizeof result, "MAC %02x:%02x:%02x:%02x:%02x:%02x",
60949052c95SBrian Somers                  m[0], m[1], m[2], m[3], m[4], m[5]);
61049052c95SBrian Somers       } else
61149052c95SBrian Somers         sprintf(result, "MAC[%d] ???", len);
61249052c95SBrian Somers       break;
61349052c95SBrian Somers 
61408676022SBrian Somers     case ENDDISC_MAGIC:
61549052c95SBrian Somers       sprintf(result, "Magic: 0x");
61649052c95SBrian Somers       header = strlen(result);
61749052c95SBrian Somers       if (len > sizeof result - header - 1)
61849052c95SBrian Somers         len = sizeof result - header - 1;
61949052c95SBrian Somers       for (f = 0; f < len; f++)
62049052c95SBrian Somers         sprintf(result + header + 2 * f, "%02x", address[f]);
62149052c95SBrian Somers       break;
62249052c95SBrian Somers 
62308676022SBrian Somers     case ENDDISC_PSN:
62449052c95SBrian Somers       snprintf(result, sizeof result, "PSN: %.*s", len, address);
62549052c95SBrian Somers       break;
62649052c95SBrian Somers 
62749052c95SBrian Somers      default:
62849052c95SBrian Somers       sprintf(result, "%d: ", (int)c);
62949052c95SBrian Somers       header = strlen(result);
63049052c95SBrian Somers       if (len > sizeof result - header - 1)
63149052c95SBrian Somers         len = sizeof result - header - 1;
63249052c95SBrian Somers       for (f = 0; f < len; f++)
63349052c95SBrian Somers         sprintf(result + header + 2 * f, "%02x", address[f]);
63449052c95SBrian Somers       break;
63549052c95SBrian Somers   }
63649052c95SBrian Somers   return result;
63749052c95SBrian Somers }
63849052c95SBrian Somers 
63949052c95SBrian Somers int
64049052c95SBrian Somers mp_SetEnddisc(struct cmdargs const *arg)
64149052c95SBrian Somers {
64249052c95SBrian Somers   struct mp *mp = &arg->bundle->ncp.mp;
64349052c95SBrian Somers 
64449052c95SBrian Somers   if (bundle_Phase(arg->bundle) != PHASE_DEAD) {
64549052c95SBrian Somers     LogPrintf(LogWARN, "set enddisc: Only available at phase DEAD\n");
64649052c95SBrian Somers     return 1;
64749052c95SBrian Somers   }
64849052c95SBrian Somers 
64949052c95SBrian Somers   if (arg->argc == arg->argn) {
65049052c95SBrian Somers     mp->cfg.enddisc.class = 0;
65149052c95SBrian Somers     *mp->cfg.enddisc.address = '\0';
65249052c95SBrian Somers     mp->cfg.enddisc.len = 0;
65349052c95SBrian Somers   } else if (arg->argc > arg->argn)
65408676022SBrian Somers     if (!strcasecmp(arg->argv[arg->argn], "label")) {
65508676022SBrian Somers       mp->cfg.enddisc.class = ENDDISC_LOCAL;
65608676022SBrian Somers       strcpy(mp->cfg.enddisc.address, arg->bundle->cfg.label);
65708676022SBrian Somers       mp->cfg.enddisc.len = strlen(mp->cfg.enddisc.address);
65808676022SBrian Somers     } else if (!strcasecmp(arg->argv[arg->argn], "ip")) {
65949052c95SBrian Somers       memcpy(mp->cfg.enddisc.address,
66008676022SBrian Somers              &arg->bundle->ncp.ipcp.cfg.my_range.ipaddr.s_addr,
66149052c95SBrian Somers              sizeof arg->bundle->ncp.ipcp.my_ip.s_addr);
66208676022SBrian Somers       mp->cfg.enddisc.class = ENDDISC_IP;
66349052c95SBrian Somers       mp->cfg.enddisc.len = sizeof arg->bundle->ncp.ipcp.my_ip.s_addr;
66408676022SBrian Somers     } else if (!strcasecmp(arg->argv[arg->argn], "mac")) {
66508676022SBrian Somers       struct sockaddr_dl hwaddr;
66608676022SBrian Somers       int s;
66708676022SBrian Somers 
66808676022SBrian Somers       s = ID0socket(AF_INET, SOCK_DGRAM, 0);
66908676022SBrian Somers       if (s < 0) {
67008676022SBrian Somers         LogPrintf(LogERROR, "set enddisc: socket(): %s\n", strerror(errno));
67108676022SBrian Somers         return 2;
67208676022SBrian Somers       }
67308676022SBrian Somers       if (get_ether_addr(s, arg->bundle->ncp.ipcp.cfg.my_range.ipaddr,
67408676022SBrian Somers                          &hwaddr)) {
67508676022SBrian Somers         mp->cfg.enddisc.class = ENDDISC_MAC;
67608676022SBrian Somers         memcpy(mp->cfg.enddisc.address, hwaddr.sdl_data + hwaddr.sdl_nlen,
67708676022SBrian Somers                hwaddr.sdl_alen);
67808676022SBrian Somers         mp->cfg.enddisc.len = hwaddr.sdl_alen;
67908676022SBrian Somers       } else {
68008676022SBrian Somers         LogPrintf(LogWARN, "set enddisc: Can't locate MAC address for %s\n",
68108676022SBrian Somers                   inet_ntoa(arg->bundle->ncp.ipcp.cfg.my_range.ipaddr));
68208676022SBrian Somers         close(s);
68308676022SBrian Somers         return 4;
68408676022SBrian Somers       }
68508676022SBrian Somers       close(s);
68649052c95SBrian Somers     } else if (!strcasecmp(arg->argv[arg->argn], "magic")) {
68749052c95SBrian Somers       int f;
68849052c95SBrian Somers 
68949052c95SBrian Somers       randinit();
69049052c95SBrian Somers       for (f = 0; f < 20; f += sizeof(long))
69149052c95SBrian Somers         *(long *)(mp->cfg.enddisc.address + f) = random();
69208676022SBrian Somers       mp->cfg.enddisc.class = ENDDISC_MAGIC;
69349052c95SBrian Somers       mp->cfg.enddisc.len = 20;
69449052c95SBrian Somers     } else if (!strcasecmp(arg->argv[arg->argn], "psn")) {
69549052c95SBrian Somers       if (arg->argc > arg->argn+1) {
69608676022SBrian Somers         mp->cfg.enddisc.class = ENDDISC_PSN;
69749052c95SBrian Somers         strcpy(mp->cfg.enddisc.address, arg->argv[arg->argn+1]);
69849052c95SBrian Somers         mp->cfg.enddisc.len = strlen(mp->cfg.enddisc.address);
69949052c95SBrian Somers       } else {
70049052c95SBrian Somers         LogPrintf(LogWARN, "PSN endpoint requires additional data\n");
70108676022SBrian Somers         return 5;
70249052c95SBrian Somers       }
70349052c95SBrian Somers     } else {
70449052c95SBrian Somers       LogPrintf(LogWARN, "%s: Unrecognised endpoint type\n",
70549052c95SBrian Somers                 arg->argv[arg->argn]);
70608676022SBrian Somers       return 6;
70749052c95SBrian Somers     }
70849052c95SBrian Somers 
70949052c95SBrian Somers   return 0;
71049052c95SBrian Somers }
711