136285Sbrian/*-
236285Sbrian * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
336285Sbrian * All rights reserved.
436285Sbrian *
536285Sbrian * Redistribution and use in source and binary forms, with or without
636285Sbrian * modification, are permitted provided that the following conditions
736285Sbrian * are met:
836285Sbrian * 1. Redistributions of source code must retain the above copyright
936285Sbrian *    notice, this list of conditions and the following disclaimer.
1036285Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1136285Sbrian *    notice, this list of conditions and the following disclaimer in the
1236285Sbrian *    documentation and/or other materials provided with the distribution.
1336285Sbrian *
1436285Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1536285Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1636285Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1736285Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1836285Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1936285Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2036285Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2136285Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2236285Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2336285Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2436285Sbrian * SUCH DAMAGE.
2536285Sbrian *
2650479Speter * $FreeBSD$
2736285Sbrian */
2836285Sbrian
2943313Sbrian#include <sys/param.h>
3036285Sbrian#include <netinet/in.h>
3136285Sbrian#include <netinet/in_systm.h>
3236285Sbrian#include <netinet/ip.h>
3336285Sbrian#include <arpa/inet.h>
3436285Sbrian#include <net/if_dl.h>
3536285Sbrian#include <sys/socket.h>
3636285Sbrian#include <sys/un.h>
3736285Sbrian
3836285Sbrian#include <errno.h>
3936285Sbrian#include <paths.h>
40102500Sbrian#include <stdarg.h>
41102500Sbrian#include <stdio.h>
4236285Sbrian#include <stdlib.h>
4336285Sbrian#include <string.h>
4436285Sbrian#include <sys/stat.h>
4536285Sbrian#include <termios.h>
4636285Sbrian#include <unistd.h>
4736285Sbrian
4846686Sbrian#include "layer.h"
4950059Sbrian#ifndef NONAT
5051075Sbrian#include "nat_cmd.h"
5146686Sbrian#endif
5246686Sbrian#include "vjcomp.h"
5338814Sbrian#include "ua.h"
5437009Sbrian#include "defs.h"
5536285Sbrian#include "command.h"
5636285Sbrian#include "mbuf.h"
5736285Sbrian#include "log.h"
5836285Sbrian#include "timer.h"
5936285Sbrian#include "fsm.h"
6036285Sbrian#include "iplist.h"
6136285Sbrian#include "throughput.h"
6236285Sbrian#include "slcompress.h"
6338557Sbrian#include "lqr.h"
6438557Sbrian#include "hdlc.h"
6581634Sbrian#include "ncpaddr.h"
6636285Sbrian#include "ipcp.h"
6736285Sbrian#include "auth.h"
6836285Sbrian#include "lcp.h"
6936285Sbrian#include "async.h"
7036285Sbrian#include "ccp.h"
7136285Sbrian#include "link.h"
7236285Sbrian#include "descriptor.h"
7336285Sbrian#include "physical.h"
7436285Sbrian#include "chat.h"
7546686Sbrian#include "proto.h"
7636285Sbrian#include "filter.h"
7736285Sbrian#include "mp.h"
7836285Sbrian#include "chap.h"
7938174Sbrian#include "cbcp.h"
8036285Sbrian#include "datalink.h"
8143313Sbrian#ifndef NORADIUS
8243313Sbrian#include "radius.h"
8343313Sbrian#endif
8481634Sbrian#include "ipv6cp.h"
8581634Sbrian#include "ncp.h"
8636285Sbrian#include "bundle.h"
8736285Sbrian#include "prompt.h"
8836285Sbrian#include "id.h"
8936285Sbrian#include "arp.h"
9036285Sbrian
9136285Sbrianvoid
9236285Sbrianpeerid_Init(struct peerid *peer)
9336285Sbrian{
9436285Sbrian  peer->enddisc.class = 0;
9536285Sbrian  *peer->enddisc.address = '\0';
9636285Sbrian  peer->enddisc.len = 0;
9736285Sbrian  *peer->authname = '\0';
9836285Sbrian}
9936285Sbrian
10036285Sbrianint
10136285Sbrianpeerid_Equal(const struct peerid *p1, const struct peerid *p2)
10236285Sbrian{
10336285Sbrian  return !strcmp(p1->authname, p2->authname) &&
10436285Sbrian         p1->enddisc.class == p2->enddisc.class &&
10536285Sbrian         p1->enddisc.len == p2->enddisc.len &&
10636285Sbrian         !memcmp(p1->enddisc.address, p2->enddisc.address, p1->enddisc.len);
10736285Sbrian}
10836285Sbrian
10936285Sbrianstatic u_int32_t
11036285Sbrianinc_seq(unsigned is12bit, u_int32_t seq)
11136285Sbrian{
11236285Sbrian  seq++;
11336285Sbrian  if (is12bit) {
11436285Sbrian    if (seq & 0xfffff000)
11536285Sbrian      seq = 0;
11636285Sbrian  } else if (seq & 0xff000000)
11736285Sbrian    seq = 0;
11836285Sbrian  return seq;
11936285Sbrian}
12036285Sbrian
12136285Sbrianstatic int
12236285Sbrianisbefore(unsigned is12bit, u_int32_t seq1, u_int32_t seq2)
12336285Sbrian{
12436285Sbrian  u_int32_t max = (is12bit ? 0xfff : 0xffffff) - 0x200;
12536285Sbrian
12636285Sbrian  if (seq1 > max) {
12736285Sbrian    if (seq2 < 0x200 || seq2 > seq1)
12836285Sbrian      return 1;
12936285Sbrian  } else if ((seq1 > 0x200 || seq2 <= max) && seq1 < seq2)
13036285Sbrian    return 1;
13136285Sbrian
13236285Sbrian  return 0;
13336285Sbrian}
13436285Sbrian
13536285Sbrianstatic int
13636285Sbrianmp_ReadHeader(struct mp *mp, struct mbuf *m, struct mp_header *header)
13736285Sbrian{
13836285Sbrian  if (mp->local_is12bit) {
13938814Sbrian    u_int16_t val;
14038814Sbrian
14138814Sbrian    ua_ntohs(MBUF_CTOP(m), &val);
14238814Sbrian    if (val & 0x3000) {
14336285Sbrian      log_Printf(LogWARN, "Oops - MP header without required zero bits\n");
14436285Sbrian      return 0;
14536285Sbrian    }
14638814Sbrian    header->begin = val & 0x8000 ? 1 : 0;
14738814Sbrian    header->end = val & 0x4000 ? 1 : 0;
14838814Sbrian    header->seq = val & 0x0fff;
14936285Sbrian    return 2;
15036285Sbrian  } else {
15138814Sbrian    ua_ntohl(MBUF_CTOP(m), &header->seq);
15236285Sbrian    if (header->seq & 0x3f000000) {
15336285Sbrian      log_Printf(LogWARN, "Oops - MP header without required zero bits\n");
15436285Sbrian      return 0;
15536285Sbrian    }
15636285Sbrian    header->begin = header->seq & 0x80000000 ? 1 : 0;
15736285Sbrian    header->end = header->seq & 0x40000000 ? 1 : 0;
15836285Sbrian    header->seq &= 0x00ffffff;
15936285Sbrian    return 4;
16036285Sbrian  }
16136285Sbrian}
16236285Sbrian
16336285Sbrianstatic void
164134789Sbrianmp_LayerStart(void *v __unused, struct fsm *fp __unused)
16536285Sbrian{
16636285Sbrian  /* The given FSM (ccp) is about to start up ! */
16736285Sbrian}
16836285Sbrian
16936285Sbrianstatic void
170134789Sbrianmp_LayerUp(void *v __unused, struct fsm *fp)
17136285Sbrian{
17236285Sbrian  /* The given fsm (ccp) is now up */
17379165Sbrian
17479165Sbrian  bundle_CalculateBandwidth(fp->bundle);	/* Against ccp_MTUOverhead */
17536285Sbrian}
17636285Sbrian
17736285Sbrianstatic void
178134789Sbrianmp_LayerDown(void *v __unused, struct fsm *fp __unused)
17936285Sbrian{
18036285Sbrian  /* The given FSM (ccp) has been told to come down */
18136285Sbrian}
18236285Sbrian
18336285Sbrianstatic void
184134789Sbrianmp_LayerFinish(void *v __unused, struct fsm *fp)
18536285Sbrian{
18636285Sbrian  /* The given fsm (ccp) is now down */
18736285Sbrian  if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
18836285Sbrian    fsm_Open(fp);		/* CCP goes to ST_STOPPED */
18936285Sbrian}
19036285Sbrian
19149434Sbrianstatic void
19249434Sbrianmp_UpDown(void *v)
19349434Sbrian{
19449434Sbrian  struct mp *mp = (struct mp *)v;
19549434Sbrian  int percent;
19649434Sbrian
19764670Sbrian  percent = MAX(mp->link.stats.total.in.OctetsPerSecond,
19864670Sbrian                mp->link.stats.total.out.OctetsPerSecond) * 800 /
19964670Sbrian            mp->bundle->bandwidth;
20049434Sbrian  if (percent >= mp->cfg.autoload.max) {
20149434Sbrian    log_Printf(LogDEBUG, "%d%% saturation - bring a link up ?\n", percent);
20249434Sbrian    bundle_AutoAdjust(mp->bundle, percent, AUTO_UP);
20349434Sbrian  } else if (percent <= mp->cfg.autoload.min) {
20449434Sbrian    log_Printf(LogDEBUG, "%d%% saturation - bring a link down ?\n", percent);
20549434Sbrian    bundle_AutoAdjust(mp->bundle, percent, AUTO_DOWN);
20649434Sbrian  }
20749434Sbrian}
20849434Sbrian
20936285Sbrianvoid
21049434Sbrianmp_StopAutoloadTimer(struct mp *mp)
21149434Sbrian{
21264652Sbrian  throughput_stop(&mp->link.stats.total);
21349434Sbrian}
21449434Sbrian
21549434Sbrianvoid
21649434Sbrianmp_CheckAutoloadTimer(struct mp *mp)
21749434Sbrian{
21864652Sbrian  if (mp->link.stats.total.SamplePeriod != mp->cfg.autoload.period) {
21964652Sbrian    throughput_destroy(&mp->link.stats.total);
22064652Sbrian    throughput_init(&mp->link.stats.total, mp->cfg.autoload.period);
22164652Sbrian    throughput_callback(&mp->link.stats.total, mp_UpDown, mp);
22249434Sbrian  }
22349434Sbrian
22449434Sbrian  if (bundle_WantAutoloadTimer(mp->bundle))
22564652Sbrian    throughput_start(&mp->link.stats.total, "MP throughput", 1);
22649434Sbrian  else
22749434Sbrian    mp_StopAutoloadTimer(mp);
22849434Sbrian}
22949434Sbrian
23049434Sbrianvoid
23149434Sbrianmp_RestartAutoloadTimer(struct mp *mp)
23249434Sbrian{
23364652Sbrian  if (mp->link.stats.total.SamplePeriod != mp->cfg.autoload.period)
23449434Sbrian    mp_CheckAutoloadTimer(mp);
23549434Sbrian  else
23664652Sbrian    throughput_clear(&mp->link.stats.total, THROUGHPUT_OVERALL, NULL);
23749434Sbrian}
23849434Sbrian
23949434Sbrianvoid
24036285Sbrianmp_Init(struct mp *mp, struct bundle *bundle)
24136285Sbrian{
24236285Sbrian  mp->peer_is12bit = mp->local_is12bit = 0;
24336285Sbrian  mp->peer_mrru = mp->local_mrru = 0;
24436285Sbrian
24536285Sbrian  peerid_Init(&mp->peer);
24636285Sbrian
24736285Sbrian  mp->out.seq = 0;
24836285Sbrian  mp->out.link = 0;
24981634Sbrian  mp->out.af = AF_INET;
25036285Sbrian  mp->seq.min_in = 0;
25136285Sbrian  mp->seq.next_in = 0;
25236285Sbrian  mp->inbufs = NULL;
25336285Sbrian  mp->bundle = bundle;
25436285Sbrian
25546686Sbrian  mp->link.type = LOGICAL_LINK;
25636285Sbrian  mp->link.name = "mp";
25736285Sbrian  mp->link.len = sizeof *mp;
25836285Sbrian
25949434Sbrian  mp->cfg.autoload.period = SAMPLE_PERIOD;
26049434Sbrian  mp->cfg.autoload.min = mp->cfg.autoload.max = 0;
26164652Sbrian  throughput_init(&mp->link.stats.total, mp->cfg.autoload.period);
26264652Sbrian  throughput_callback(&mp->link.stats.total, mp_UpDown, mp);
26364652Sbrian  mp->link.stats.parent = NULL;
26464652Sbrian  mp->link.stats.gather = 0;	/* Let the physical links gather stats */
26536285Sbrian  memset(mp->link.Queue, '\0', sizeof mp->link.Queue);
26636285Sbrian  memset(mp->link.proto_in, '\0', sizeof mp->link.proto_in);
26736285Sbrian  memset(mp->link.proto_out, '\0', sizeof mp->link.proto_out);
26836285Sbrian
26936285Sbrian  mp->fsmp.LayerStart = mp_LayerStart;
27036285Sbrian  mp->fsmp.LayerUp = mp_LayerUp;
27136285Sbrian  mp->fsmp.LayerDown = mp_LayerDown;
27236285Sbrian  mp->fsmp.LayerFinish = mp_LayerFinish;
27336285Sbrian  mp->fsmp.object = mp;
27436285Sbrian
27536285Sbrian  mpserver_Init(&mp->server);
27636285Sbrian
27736285Sbrian  mp->cfg.mrru = 0;
27836285Sbrian  mp->cfg.shortseq = NEG_ENABLED|NEG_ACCEPTED;
27947858Sbrian  mp->cfg.negenddisc = NEG_ENABLED|NEG_ACCEPTED;
28036285Sbrian  mp->cfg.enddisc.class = 0;
28136285Sbrian  *mp->cfg.enddisc.address = '\0';
28236285Sbrian  mp->cfg.enddisc.len = 0;
28336285Sbrian
28436285Sbrian  lcp_Init(&mp->link.lcp, mp->bundle, &mp->link, NULL);
28536285Sbrian  ccp_Init(&mp->link.ccp, mp->bundle, &mp->link, &mp->fsmp);
28646686Sbrian
28746686Sbrian  link_EmptyStack(&mp->link);
28846686Sbrian  link_Stack(&mp->link, &protolayer);
28946686Sbrian  link_Stack(&mp->link, &ccplayer);
29046686Sbrian  link_Stack(&mp->link, &vjlayer);
29150059Sbrian#ifndef NONAT
29250059Sbrian  link_Stack(&mp->link, &natlayer);
29346686Sbrian#endif
29436285Sbrian}
29536285Sbrian
29636285Sbrianint
29736285Sbrianmp_Up(struct mp *mp, struct datalink *dl)
29836285Sbrian{
29936285Sbrian  struct lcp *lcp = &dl->physical->link.lcp;
30036285Sbrian
30136285Sbrian  if (mp->active) {
30236285Sbrian    /* We're adding a link - do a last validation on our parameters */
30336285Sbrian    if (!peerid_Equal(&dl->peer, &mp->peer)) {
30436285Sbrian      log_Printf(LogPHASE, "%s: Inappropriate peer !\n", dl->name);
30560778Sbrian      log_Printf(LogPHASE, "  Attached to peer %s/%s\n", mp->peer.authname,
30660778Sbrian                 mp_Enddisc(mp->peer.enddisc.class, mp->peer.enddisc.address,
30760778Sbrian                            mp->peer.enddisc.len));
30860778Sbrian      log_Printf(LogPHASE, "  New link is peer %s/%s\n", dl->peer.authname,
30960778Sbrian                 mp_Enddisc(dl->peer.enddisc.class, dl->peer.enddisc.address,
31060778Sbrian                            dl->peer.enddisc.len));
31136285Sbrian      return MP_FAILED;
31236285Sbrian    }
31336285Sbrian    if (mp->local_mrru != lcp->want_mrru ||
31436285Sbrian        mp->peer_mrru != lcp->his_mrru ||
31536285Sbrian        mp->local_is12bit != lcp->want_shortseq ||
31636285Sbrian        mp->peer_is12bit != lcp->his_shortseq) {
31736285Sbrian      log_Printf(LogPHASE, "%s: Invalid MRRU/SHORTSEQ MP parameters !\n",
31836285Sbrian                dl->name);
31936285Sbrian      return MP_FAILED;
32036285Sbrian    }
32136285Sbrian    return MP_ADDED;
32236285Sbrian  } else {
32336285Sbrian    /* First link in multilink mode */
32436285Sbrian
32536285Sbrian    mp->local_mrru = lcp->want_mrru;
32636285Sbrian    mp->peer_mrru = lcp->his_mrru;
32736285Sbrian    mp->local_is12bit = lcp->want_shortseq;
32836285Sbrian    mp->peer_is12bit = lcp->his_shortseq;
32936285Sbrian    mp->peer = dl->peer;
33036285Sbrian
33164652Sbrian    throughput_destroy(&mp->link.stats.total);
33264652Sbrian    throughput_init(&mp->link.stats.total, mp->cfg.autoload.period);
33364652Sbrian    throughput_callback(&mp->link.stats.total, mp_UpDown, mp);
33436285Sbrian    memset(mp->link.Queue, '\0', sizeof mp->link.Queue);
33536285Sbrian    memset(mp->link.proto_in, '\0', sizeof mp->link.proto_in);
33636285Sbrian    memset(mp->link.proto_out, '\0', sizeof mp->link.proto_out);
33736285Sbrian
33864652Sbrian    /* Tell the link who it belongs to */
33964652Sbrian    dl->physical->link.stats.parent = &mp->link.stats.total;
34064652Sbrian
34136285Sbrian    mp->out.seq = 0;
34236285Sbrian    mp->out.link = 0;
34381634Sbrian    mp->out.af = AF_INET;
34436285Sbrian    mp->seq.min_in = 0;
34536285Sbrian    mp->seq.next_in = 0;
34636285Sbrian
34736285Sbrian    /*
34836285Sbrian     * Now we create our server socket.
34936285Sbrian     * If it already exists, join it.  Otherwise, create and own it
35036285Sbrian     */
35136285Sbrian    switch (mpserver_Open(&mp->server, &mp->peer)) {
35236285Sbrian    case MPSERVER_CONNECTED:
35336285Sbrian      log_Printf(LogPHASE, "mp: Transfer link on %s\n",
35436285Sbrian                mp->server.socket.sun_path);
35536285Sbrian      mp->server.send.dl = dl;		/* Defer 'till it's safe to send */
35636285Sbrian      return MP_LINKSENT;
35736285Sbrian    case MPSERVER_FAILED:
35836285Sbrian      return MP_FAILED;
35936285Sbrian    case MPSERVER_LISTENING:
36036285Sbrian      log_Printf(LogPHASE, "mp: Listening on %s\n", mp->server.socket.sun_path);
36136285Sbrian      log_Printf(LogPHASE, "    First link: %s\n", dl->name);
36236285Sbrian
36381634Sbrian      /* Re-point our NCP layers at our MP link */
36481634Sbrian      ncp_SetLink(&mp->bundle->ncp, &mp->link);
36536285Sbrian
36636285Sbrian      /* Our lcp's already up 'cos of the NULL parent */
36737320Sbrian      if (ccp_SetOpenMode(&mp->link.ccp)) {
36837320Sbrian        fsm_Up(&mp->link.ccp.fsm);
36937320Sbrian        fsm_Open(&mp->link.ccp.fsm);
37037320Sbrian      }
37136285Sbrian
37236285Sbrian      mp->active = 1;
37336285Sbrian      break;
37436285Sbrian    }
37536285Sbrian  }
37636285Sbrian
37736285Sbrian  return MP_UP;
37836285Sbrian}
37936285Sbrian
38036285Sbrianvoid
38136285Sbrianmp_Down(struct mp *mp)
38236285Sbrian{
38336285Sbrian  if (mp->active) {
38436285Sbrian    struct mbuf *next;
38536285Sbrian
38649434Sbrian    /* Stop that ! */
38749434Sbrian    mp_StopAutoloadTimer(mp);
38849434Sbrian
38936285Sbrian    /* Don't want any more of these */
39036285Sbrian    mpserver_Close(&mp->server);
39136285Sbrian
39236285Sbrian    /* CCP goes down with a bang */
39337060Sbrian    fsm2initial(&mp->link.ccp.fsm);
39436285Sbrian
39536285Sbrian    /* Received fragments go in the bit-bucket */
39636285Sbrian    while (mp->inbufs) {
39754912Sbrian      next = mp->inbufs->m_nextpkt;
39854912Sbrian      m_freem(mp->inbufs);
39936285Sbrian      mp->inbufs = next;
40036285Sbrian    }
40136285Sbrian
40236285Sbrian    peerid_Init(&mp->peer);
40336285Sbrian    mp->active = 0;
40436285Sbrian  }
40536285Sbrian}
40636285Sbrian
40736285Sbrianvoid
40836285Sbrianmp_linkInit(struct mp_link *mplink)
40936285Sbrian{
41036285Sbrian  mplink->seq = 0;
41149434Sbrian  mplink->bandwidth = 0;
41236285Sbrian}
41336285Sbrian
41446686Sbrianstatic void
41546686Sbrianmp_Assemble(struct mp *mp, struct mbuf *m, struct physical *p)
41636285Sbrian{
41736285Sbrian  struct mp_header mh, h;
41836285Sbrian  struct mbuf *q, *last;
419134789Sbrian  u_int32_t seq;
42036285Sbrian
42136312Sbrian  /*
42236312Sbrian   * When `m' and `p' are NULL, it means our oldest link has gone down.
42336312Sbrian   * We want to determine a new min, and process any intermediate stuff
42436312Sbrian   * as normal
42536312Sbrian   */
42636312Sbrian
42736312Sbrian  if (m && mp_ReadHeader(mp, m, &mh) == 0) {
42854912Sbrian    m_freem(m);
42936285Sbrian    return;
43036285Sbrian  }
43136285Sbrian
43236312Sbrian  if (p) {
43336312Sbrian    seq = p->dl->mp.seq;
43436312Sbrian    p->dl->mp.seq = mh.seq;
43536312Sbrian  } else
43636312Sbrian    seq = mp->seq.min_in;
43736312Sbrian
43836285Sbrian  if (mp->seq.min_in == seq) {
43936285Sbrian    /*
44036285Sbrian     * We've received new data on the link that has our min (oldest) seq.
44136285Sbrian     * Figure out which link now has the smallest (oldest) seq.
44236285Sbrian     */
44336285Sbrian    struct datalink *dl;
44436285Sbrian
44536312Sbrian    mp->seq.min_in = (u_int32_t)-1;
44636285Sbrian    for (dl = mp->bundle->links; dl; dl = dl->next)
44736312Sbrian      if (dl->state == DATALINK_OPEN &&
448134789Sbrian          (mp->seq.min_in == (u_int32_t)-1 ||
44936312Sbrian           isbefore(mp->local_is12bit, dl->mp.seq, mp->seq.min_in)))
45036285Sbrian        mp->seq.min_in = dl->mp.seq;
45136285Sbrian  }
45236285Sbrian
45336285Sbrian  /*
45436285Sbrian   * Now process as many of our fragments as we can, adding our new
45536285Sbrian   * fragment in as we go, and ordering with the oldest at the top of
45636285Sbrian   * the queue.
45736285Sbrian   */
45836285Sbrian
45936285Sbrian  last = NULL;
46036285Sbrian  seq = mp->seq.next_in;
46136285Sbrian  q = mp->inbufs;
46263020Sbrian  while (q || m) {
46363020Sbrian    if (!q) {
46436285Sbrian      if (last)
46554912Sbrian        last->m_nextpkt = m;
46636285Sbrian      else
46736285Sbrian        mp->inbufs = m;
46836285Sbrian      q = m;
46963020Sbrian      m = NULL;
47036285Sbrian      h = mh;
47163020Sbrian    } else {
47263020Sbrian      mp_ReadHeader(mp, q, &h);
47363020Sbrian
47463020Sbrian      if (m && isbefore(mp->local_is12bit, mh.seq, h.seq)) {
47563020Sbrian        /* Our received fragment fits in before this one, so link it in */
47663020Sbrian        if (last)
47763020Sbrian          last->m_nextpkt = m;
47863020Sbrian        else
47963020Sbrian          mp->inbufs = m;
48063020Sbrian        m->m_nextpkt = q;
48163020Sbrian        q = m;
48263020Sbrian        h = mh;
48363020Sbrian        m = NULL;
48463020Sbrian      }
48536285Sbrian    }
48636285Sbrian
48736285Sbrian    if (h.seq != seq) {
48836285Sbrian      /* we're missing something :-( */
48947712Sbrian      if (isbefore(mp->local_is12bit, seq, mp->seq.min_in)) {
49036285Sbrian        /* we're never gonna get it */
49136285Sbrian        struct mbuf *next;
49236285Sbrian
49336285Sbrian        /* Zap all older fragments */
49436285Sbrian        while (mp->inbufs != q) {
49536285Sbrian          log_Printf(LogDEBUG, "Drop frag\n");
49654912Sbrian          next = mp->inbufs->m_nextpkt;
49754912Sbrian          m_freem(mp->inbufs);
49836285Sbrian          mp->inbufs = next;
49936285Sbrian        }
50036285Sbrian
50136285Sbrian        /*
50236285Sbrian         * Zap everything until the next `end' fragment OR just before
50336285Sbrian         * the next `begin' fragment OR 'till seq.min_in - whichever
50436285Sbrian         * comes first.
50536285Sbrian         */
50636285Sbrian        do {
50736285Sbrian          mp_ReadHeader(mp, mp->inbufs, &h);
50836285Sbrian          if (h.begin) {
50936285Sbrian            /* We might be able to process this ! */
51036285Sbrian            h.seq--;  /* We're gonna look for fragment with h.seq+1 */
51136285Sbrian            break;
51236285Sbrian          }
51354912Sbrian          next = mp->inbufs->m_nextpkt;
51436285Sbrian          log_Printf(LogDEBUG, "Drop frag %u\n", h.seq);
51554912Sbrian          m_freem(mp->inbufs);
51636285Sbrian          mp->inbufs = next;
51747712Sbrian        } while (mp->inbufs && (isbefore(mp->local_is12bit, mp->seq.min_in,
51847712Sbrian                                         h.seq) || h.end));
51936285Sbrian
52036285Sbrian        /*
52136285Sbrian         * Continue processing things from here.
52236285Sbrian         * This deals with the possibility that we received a fragment
52336285Sbrian         * on the slowest link that invalidates some of our data (because
52436285Sbrian         * of the hole at `q'), but where there are subsequent `whole'
52536285Sbrian         * packets that have already been received.
52636285Sbrian         */
52736285Sbrian
52836285Sbrian        mp->seq.next_in = seq = inc_seq(mp->local_is12bit, h.seq);
52936285Sbrian        last = NULL;
53036285Sbrian        q = mp->inbufs;
53136285Sbrian      } else
53236285Sbrian        /* we may still receive the missing fragment */
53336285Sbrian        break;
53436285Sbrian    } else if (h.end) {
53536285Sbrian      /* We've got something, reassemble */
53636285Sbrian      struct mbuf **frag = &q;
53736285Sbrian      int len;
538134789Sbrian      long long first = -1;
53936285Sbrian
54036285Sbrian      do {
54136285Sbrian        *frag = mp->inbufs;
54254912Sbrian        mp->inbufs = mp->inbufs->m_nextpkt;
54336285Sbrian        len = mp_ReadHeader(mp, *frag, &h);
54436285Sbrian        if (first == -1)
54536285Sbrian          first = h.seq;
54636285Sbrian        if (frag == &q && !h.begin) {
54736285Sbrian          log_Printf(LogWARN, "Oops - MP frag %lu should have a begin flag\n",
54836285Sbrian                    (u_long)h.seq);
54954912Sbrian          m_freem(q);
55036285Sbrian          q = NULL;
55136285Sbrian        } else if (frag != &q && h.begin) {
55236285Sbrian          log_Printf(LogWARN, "Oops - MP frag %lu should have an end flag\n",
55336285Sbrian                    (u_long)h.seq - 1);
55436285Sbrian          /*
55536285Sbrian           * Stuff our fragment back at the front of the queue and zap
55696159Sbrian           * our half-assembled packet.
55736285Sbrian           */
55854912Sbrian          (*frag)->m_nextpkt = mp->inbufs;
55936285Sbrian          mp->inbufs = *frag;
56036285Sbrian          *frag = NULL;
56154912Sbrian          m_freem(q);
56236285Sbrian          q = NULL;
56336285Sbrian          frag = &q;
56436285Sbrian          h.end = 0;	/* just in case it's a whole packet */
56596159Sbrian        } else {
56696159Sbrian          (*frag)->m_offset += len;
56796159Sbrian          (*frag)->m_len -= len;
56896159Sbrian          (*frag)->m_nextpkt = NULL;
56936285Sbrian          do
57054912Sbrian            frag = &(*frag)->m_next;
57136285Sbrian          while (*frag != NULL);
57296159Sbrian        }
57336285Sbrian      } while (!h.end);
57436285Sbrian
57536285Sbrian      if (q) {
57654912Sbrian        q = m_pullup(q);
577134833Smarcel        log_Printf(LogDEBUG, "MP: Reassembled frags %lu-%lu, length %zd\n",
578134789Sbrian                   (u_long)first, (u_long)h.seq, m_length(q));
57954912Sbrian        link_PullPacket(&mp->link, MBUF_CTOP(q), q->m_len, mp->bundle);
58054912Sbrian        m_freem(q);
58136285Sbrian      }
58236285Sbrian
58336285Sbrian      mp->seq.next_in = seq = inc_seq(mp->local_is12bit, h.seq);
58436285Sbrian      last = NULL;
58536285Sbrian      q = mp->inbufs;
58636285Sbrian    } else {
58736285Sbrian      /* Look for the next fragment */
58836285Sbrian      seq = inc_seq(mp->local_is12bit, seq);
58936285Sbrian      last = q;
59054912Sbrian      q = q->m_nextpkt;
59136285Sbrian    }
59236285Sbrian  }
59336285Sbrian
59436285Sbrian  if (m) {
59536285Sbrian    /* We still have to find a home for our new fragment */
59636285Sbrian    last = NULL;
59754912Sbrian    for (q = mp->inbufs; q; last = q, q = q->m_nextpkt) {
59836285Sbrian      mp_ReadHeader(mp, q, &h);
59936285Sbrian      if (isbefore(mp->local_is12bit, mh.seq, h.seq))
60036285Sbrian        break;
60136285Sbrian    }
60236285Sbrian    /* Our received fragment fits in here */
60336285Sbrian    if (last)
60454912Sbrian      last->m_nextpkt = m;
60536285Sbrian    else
60636285Sbrian      mp->inbufs = m;
60754912Sbrian    m->m_nextpkt = q;
60836285Sbrian  }
60936285Sbrian}
61036285Sbrian
61146686Sbrianstruct mbuf *
61246686Sbrianmp_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
61346686Sbrian{
61446686Sbrian  struct physical *p = link2physical(l);
61546686Sbrian
61646686Sbrian  if (!bundle->ncp.mp.active)
61746686Sbrian    /* Let someone else deal with it ! */
61846686Sbrian    return bp;
61946686Sbrian
62046686Sbrian  if (p == NULL) {
62146686Sbrian    log_Printf(LogWARN, "DecodePacket: Can't do MP inside MP !\n");
62254912Sbrian    m_freem(bp);
62347695Sbrian  } else {
62454912Sbrian    m_settype(bp, MB_MPIN);
62546686Sbrian    mp_Assemble(&bundle->ncp.mp, bp, p);
62647695Sbrian  }
62746686Sbrian
62846686Sbrian  return NULL;
62946686Sbrian}
63046686Sbrian
63136285Sbrianstatic void
63246686Sbrianmp_Output(struct mp *mp, struct bundle *bundle, struct link *l,
63346686Sbrian          struct mbuf *m, u_int32_t begin, u_int32_t end)
63436285Sbrian{
63554912Sbrian  char prepend[4];
63636285Sbrian
63736285Sbrian  /* Stuff an MP header on the front of our packet and send it */
63854912Sbrian
63936285Sbrian  if (mp->peer_is12bit) {
64038814Sbrian    u_int16_t val;
64136285Sbrian
64238814Sbrian    val = (begin << 15) | (end << 14) | (u_int16_t)mp->out.seq;
64354912Sbrian    ua_htons(&val, prepend);
64454912Sbrian    m = m_prepend(m, prepend, 2, 0);
64536285Sbrian  } else {
64638814Sbrian    u_int32_t val;
64736285Sbrian
64838814Sbrian    val = (begin << 31) | (end << 30) | (u_int32_t)mp->out.seq;
64954912Sbrian    ua_htonl(&val, prepend);
65054912Sbrian    m = m_prepend(m, prepend, 4, 0);
65136285Sbrian  }
65236285Sbrian  if (log_IsKept(LogDEBUG))
653134833Smarcel    log_Printf(LogDEBUG, "MP[frag %d]: Send %zd bytes on link `%s'\n",
65454912Sbrian               mp->out.seq, m_length(m), l->name);
65536285Sbrian  mp->out.seq = inc_seq(mp->peer_is12bit, mp->out.seq);
65636285Sbrian
65779165Sbrian  if (l->ccp.fsm.state != ST_OPENED && ccp_Required(&l->ccp)) {
65879165Sbrian    log_Printf(LogPHASE, "%s: Not transmitting... waiting for CCP\n", l->name);
65979165Sbrian    return;
66079165Sbrian  }
66179165Sbrian
66254912Sbrian  link_PushPacket(l, m, bundle, LINK_QUEUES(l) - 1, PROTO_MP);
66336285Sbrian}
66436285Sbrian
66536285Sbrianint
66681634Sbrianmp_FillPhysicalQueues(struct bundle *bundle)
66736285Sbrian{
66836285Sbrian  struct mp *mp = &bundle->ncp.mp;
66936285Sbrian  struct datalink *dl, *fdl;
67054912Sbrian  size_t total, add, len;
67196552Sbrian  int thislink, nlinks, nopenlinks, sendasip;
67236285Sbrian  u_int32_t begin, end;
67336285Sbrian  struct mbuf *m, *mo;
67481634Sbrian  struct link *bestlink;
67536285Sbrian
67696552Sbrian  thislink = nlinks = nopenlinks = 0;
67736285Sbrian  for (fdl = NULL, dl = bundle->links; dl; dl = dl->next) {
67836312Sbrian    /* Include non-open links here as mp->out.link will stay more correct */
67936285Sbrian    if (!fdl) {
68036285Sbrian      if (thislink == mp->out.link)
68136285Sbrian        fdl = dl;
68236285Sbrian      else
68336285Sbrian        thislink++;
68436285Sbrian    }
68536285Sbrian    nlinks++;
68696552Sbrian    if (dl->state == DATALINK_OPEN)
68796552Sbrian      nopenlinks++;
68836285Sbrian  }
68936285Sbrian
69036285Sbrian  if (!fdl) {
69136285Sbrian    fdl = bundle->links;
69236285Sbrian    if (!fdl)
69336285Sbrian      return 0;
69436285Sbrian    thislink = 0;
69536285Sbrian  }
69636285Sbrian
69736285Sbrian  total = 0;
69836285Sbrian  for (dl = fdl; nlinks > 0; dl = dl->next, nlinks--, thislink++) {
69936285Sbrian    if (!dl) {
70036285Sbrian      dl = bundle->links;
70136285Sbrian      thislink = 0;
70236285Sbrian    }
70336285Sbrian
70436285Sbrian    if (dl->state != DATALINK_OPEN)
70536285Sbrian      continue;
70636285Sbrian
70736285Sbrian    if (dl->physical->out)
70836285Sbrian      /* this link has suffered a short write.  Let it continue */
70936285Sbrian      continue;
71036285Sbrian
71136285Sbrian    add = link_QueueLen(&dl->physical->link);
71264780Sbrian    if (add) {
71336285Sbrian      /* this link has got stuff already queued.  Let it continue */
71464780Sbrian      total += add;
71536285Sbrian      continue;
71664780Sbrian    }
71736285Sbrian
71881634Sbrian    if (!mp_QueueLen(mp)) {
71964780Sbrian      int mrutoosmall;
72036285Sbrian
72164780Sbrian      /*
72264780Sbrian       * If there's only a single open link in our bundle and we haven't got
72364780Sbrian       * MP level link compression, queue outbound traffic directly via that
72464780Sbrian       * link's protocol stack rather than using the MP link.  This results
72581634Sbrian       * in the outbound traffic going out as PROTO_IP or PROTO_IPV6 rather
72681634Sbrian       * than PROTO_MP.
72764780Sbrian       */
72836285Sbrian
72964780Sbrian      mrutoosmall = 0;
73096552Sbrian      sendasip = nopenlinks < 2;
73196552Sbrian      if (sendasip) {
73264780Sbrian        if (dl->physical->link.lcp.his_mru < mp->peer_mrru) {
73364780Sbrian          /*
73464780Sbrian           * Actually, forget it.  This test is done against the MRRU rather
73564780Sbrian           * than the packet size so that we don't end up sending some data
73664780Sbrian           * in MP fragments and some data in PROTO_IP packets.  That's just
73764780Sbrian           * too likely to upset some ppp implementations.
73864780Sbrian           */
73964780Sbrian          mrutoosmall = 1;
74096552Sbrian          sendasip = 0;
74136285Sbrian        }
74236285Sbrian      }
74336285Sbrian
74496552Sbrian      bestlink = sendasip ? &dl->physical->link : &mp->link;
74581634Sbrian      if (!ncp_PushPacket(&bundle->ncp, &mp->out.af, bestlink))
74681634Sbrian        break;	/* Nothing else to send */
74764780Sbrian
74864780Sbrian      if (mrutoosmall)
74964780Sbrian        log_Printf(LogDEBUG, "Don't send data as PROTO_IP, MRU < MRRU\n");
75096552Sbrian      else if (sendasip)
75164780Sbrian        log_Printf(LogDEBUG, "Sending data as PROTO_IP, not PROTO_MP\n");
75264780Sbrian
75396552Sbrian      if (sendasip) {
75464780Sbrian        add = link_QueueLen(&dl->physical->link);
75564780Sbrian        if (add) {
75664780Sbrian          /* this link has got stuff already queued.  Let it continue */
75764780Sbrian          total += add;
75864780Sbrian          continue;
75964780Sbrian        }
76036285Sbrian      }
76136285Sbrian    }
76264780Sbrian
76364780Sbrian    m = link_Dequeue(&mp->link);
76464780Sbrian    if (m) {
76564780Sbrian      len = m_length(m);
76664780Sbrian      begin = 1;
76764780Sbrian      end = 0;
76864780Sbrian
76964780Sbrian      while (!end) {
77064780Sbrian        if (dl->state == DATALINK_OPEN) {
77164780Sbrian          /* Write at most his_mru bytes to the physical link */
77264780Sbrian          if (len <= dl->physical->link.lcp.his_mru) {
77364780Sbrian            mo = m;
77464780Sbrian            end = 1;
77564780Sbrian            m_settype(mo, MB_MPOUT);
77664780Sbrian          } else {
77764780Sbrian            /* It's > his_mru, chop the packet (`m') into bits */
77864780Sbrian            mo = m_get(dl->physical->link.lcp.his_mru, MB_MPOUT);
77964780Sbrian            len -= mo->m_len;
78064780Sbrian            m = mbuf_Read(m, MBUF_CTOP(mo), mo->m_len);
78164780Sbrian          }
78264780Sbrian          mp_Output(mp, bundle, &dl->physical->link, mo, begin, end);
78364780Sbrian          begin = 0;
78464780Sbrian        }
78564780Sbrian
78664780Sbrian        if (!end) {
78764780Sbrian          nlinks--;
78864780Sbrian          dl = dl->next;
78964780Sbrian          if (!dl) {
79064780Sbrian            dl = bundle->links;
79164780Sbrian            thislink = 0;
79264780Sbrian          } else
79364780Sbrian            thislink++;
79464780Sbrian        }
79564780Sbrian      }
79664780Sbrian    }
79736285Sbrian  }
79836285Sbrian  mp->out.link = thislink;		/* Start here next time */
79936285Sbrian
80036285Sbrian  return total;
80136285Sbrian}
80236285Sbrian
80336285Sbrianint
80449434Sbrianmp_SetDatalinkBandwidth(struct cmdargs const *arg)
80536285Sbrian{
80636285Sbrian  int val;
80736285Sbrian
80836285Sbrian  if (arg->argc != arg->argn+1)
80936285Sbrian    return -1;
81064780Sbrian
81136285Sbrian  val = atoi(arg->argv[arg->argn]);
81249434Sbrian  if (val <= 0) {
81349434Sbrian    log_Printf(LogWARN, "The link bandwidth must be greater than zero\n");
81436285Sbrian    return 1;
81536285Sbrian  }
81649434Sbrian  arg->cx->mp.bandwidth = val;
81749434Sbrian
81849434Sbrian  if (arg->cx->state == DATALINK_OPEN)
81949434Sbrian    bundle_CalculateBandwidth(arg->bundle);
82049434Sbrian
82136285Sbrian  return 0;
82236285Sbrian}
82336285Sbrian
82436285Sbrianint
82536285Sbrianmp_ShowStatus(struct cmdargs const *arg)
82636285Sbrian{
82736285Sbrian  struct mp *mp = &arg->bundle->ncp.mp;
82836285Sbrian
82936285Sbrian  prompt_Printf(arg->prompt, "Multilink is %sactive\n", mp->active ? "" : "in");
83036285Sbrian  if (mp->active) {
83147695Sbrian    struct mbuf *m, *lm;
83236285Sbrian    int bufs = 0;
83336285Sbrian
83447700Sbrian    lm = NULL;
83536285Sbrian    prompt_Printf(arg->prompt, "Socket:         %s\n",
83636285Sbrian                  mp->server.socket.sun_path);
83754912Sbrian    for (m = mp->inbufs; m; m = m->m_nextpkt) {
83836285Sbrian      bufs++;
83947695Sbrian      lm = m;
84047695Sbrian    }
84147695Sbrian    prompt_Printf(arg->prompt, "Pending frags:  %d", bufs);
84247695Sbrian    if (bufs) {
84347695Sbrian      struct mp_header mh;
84447695Sbrian      unsigned long first, last;
84547695Sbrian
84647695Sbrian      first = mp_ReadHeader(mp, mp->inbufs, &mh) ? mh.seq : 0;
84747695Sbrian      last = mp_ReadHeader(mp, lm, &mh) ? mh.seq : 0;
84847712Sbrian      prompt_Printf(arg->prompt, " (Have %lu - %lu, want %lu, lowest %lu)\n",
84947695Sbrian                    first, last, (unsigned long)mp->seq.next_in,
85047695Sbrian                    (unsigned long)mp->seq.min_in);
85147712Sbrian      prompt_Printf(arg->prompt, "                First has %sbegin bit and "
85247712Sbrian                    "%send bit", mh.begin ? "" : "no ", mh.end ? "" : "no ");
85347695Sbrian    }
85447695Sbrian    prompt_Printf(arg->prompt, "\n");
85536285Sbrian  }
85636285Sbrian
85736285Sbrian  prompt_Printf(arg->prompt, "\nMy Side:\n");
85836285Sbrian  if (mp->active) {
85949434Sbrian    prompt_Printf(arg->prompt, " Output SEQ:    %u\n", mp->out.seq);
86036285Sbrian    prompt_Printf(arg->prompt, " MRRU:          %u\n", mp->local_mrru);
86136285Sbrian    prompt_Printf(arg->prompt, " Short Seq:     %s\n",
86236285Sbrian                  mp->local_is12bit ? "on" : "off");
86336285Sbrian  }
86436285Sbrian  prompt_Printf(arg->prompt, " Discriminator: %s\n",
86536285Sbrian                mp_Enddisc(mp->cfg.enddisc.class, mp->cfg.enddisc.address,
86636285Sbrian                           mp->cfg.enddisc.len));
86736285Sbrian
86836285Sbrian  prompt_Printf(arg->prompt, "\nHis Side:\n");
86936285Sbrian  if (mp->active) {
87036285Sbrian    prompt_Printf(arg->prompt, " Auth Name:     %s\n", mp->peer.authname);
87149434Sbrian    prompt_Printf(arg->prompt, " Input SEQ:     %u\n", mp->seq.next_in);
87236285Sbrian    prompt_Printf(arg->prompt, " MRRU:          %u\n", mp->peer_mrru);
87336285Sbrian    prompt_Printf(arg->prompt, " Short Seq:     %s\n",
87436285Sbrian                  mp->peer_is12bit ? "on" : "off");
87536285Sbrian  }
87636285Sbrian  prompt_Printf(arg->prompt,   " Discriminator: %s\n",
87736285Sbrian                mp_Enddisc(mp->peer.enddisc.class, mp->peer.enddisc.address,
87836285Sbrian                           mp->peer.enddisc.len));
87936285Sbrian
88036285Sbrian  prompt_Printf(arg->prompt, "\nDefaults:\n");
88164780Sbrian
88236285Sbrian  prompt_Printf(arg->prompt, " MRRU:          ");
88336285Sbrian  if (mp->cfg.mrru)
88436285Sbrian    prompt_Printf(arg->prompt, "%d (multilink enabled)\n", mp->cfg.mrru);
88536285Sbrian  else
88636285Sbrian    prompt_Printf(arg->prompt, "disabled\n");
88736285Sbrian  prompt_Printf(arg->prompt, " Short Seq:     %s\n",
88836285Sbrian                  command_ShowNegval(mp->cfg.shortseq));
88947858Sbrian  prompt_Printf(arg->prompt, " Discriminator: %s\n",
89047858Sbrian                  command_ShowNegval(mp->cfg.negenddisc));
89149434Sbrian  prompt_Printf(arg->prompt, " AutoLoad:      min %d%%, max %d%%,"
89249434Sbrian                " period %d secs\n", mp->cfg.autoload.min,
89349434Sbrian                mp->cfg.autoload.max, mp->cfg.autoload.period);
89436285Sbrian
89536285Sbrian  return 0;
89636285Sbrian}
89736285Sbrian
89836285Sbrianconst char *
899134789Sbrianmp_Enddisc(u_char c, const char *address, size_t len)
90036285Sbrian{
90137010Sbrian  static char result[100];	/* Used immediately after it's returned */
902134789Sbrian  unsigned f, header;
90336285Sbrian
90436285Sbrian  switch (c) {
90536285Sbrian    case ENDDISC_NULL:
90636285Sbrian      sprintf(result, "Null Class");
90736285Sbrian      break;
90836285Sbrian
90936285Sbrian    case ENDDISC_LOCAL:
910134833Smarcel	    snprintf(result, sizeof result, "Local Addr: %.*s", (int)len,
911134833Smarcel		address);
91236285Sbrian      break;
91336285Sbrian
91436285Sbrian    case ENDDISC_IP:
91536285Sbrian      if (len == 4)
91636285Sbrian        snprintf(result, sizeof result, "IP %s",
91736285Sbrian                 inet_ntoa(*(const struct in_addr *)address));
91836285Sbrian      else
919134833Smarcel        sprintf(result, "IP[%zd] ???", len);
92036285Sbrian      break;
92136285Sbrian
92236285Sbrian    case ENDDISC_MAC:
92336285Sbrian      if (len == 6) {
92436285Sbrian        const u_char *m = (const u_char *)address;
92536285Sbrian        snprintf(result, sizeof result, "MAC %02x:%02x:%02x:%02x:%02x:%02x",
92636285Sbrian                 m[0], m[1], m[2], m[3], m[4], m[5]);
92736285Sbrian      } else
928134833Smarcel        sprintf(result, "MAC[%zd] ???", len);
92936285Sbrian      break;
93036285Sbrian
93136285Sbrian    case ENDDISC_MAGIC:
93236285Sbrian      sprintf(result, "Magic: 0x");
93336285Sbrian      header = strlen(result);
934134789Sbrian      if (len + header + 1 > sizeof result)
93536285Sbrian        len = sizeof result - header - 1;
93636285Sbrian      for (f = 0; f < len; f++)
93736285Sbrian        sprintf(result + header + 2 * f, "%02x", address[f]);
93836285Sbrian      break;
93936285Sbrian
94036285Sbrian    case ENDDISC_PSN:
941134833Smarcel	    snprintf(result, sizeof result, "PSN: %.*s", (int)len, address);
94236285Sbrian      break;
94336285Sbrian
944134789Sbrian    default:
94536285Sbrian      sprintf(result, "%d: ", (int)c);
94636285Sbrian      header = strlen(result);
947134789Sbrian      if (len + header + 1 > sizeof result)
94836285Sbrian        len = sizeof result - header - 1;
94936285Sbrian      for (f = 0; f < len; f++)
95036285Sbrian        sprintf(result + header + 2 * f, "%02x", address[f]);
95136285Sbrian      break;
95236285Sbrian  }
95336285Sbrian  return result;
95436285Sbrian}
95536285Sbrian
95636285Sbrianint
95736285Sbrianmp_SetEnddisc(struct cmdargs const *arg)
95836285Sbrian{
95936285Sbrian  struct mp *mp = &arg->bundle->ncp.mp;
96036285Sbrian  struct in_addr addr;
96136285Sbrian
96240622Sbrian  switch (bundle_Phase(arg->bundle)) {
96340622Sbrian    case PHASE_DEAD:
96440622Sbrian      break;
96540622Sbrian    case PHASE_ESTABLISH:
96640622Sbrian      /* Make sure none of our links are DATALINK_LCP or greater */
96740622Sbrian      if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
968228990Suqs        log_Printf(LogWARN, "enddisc: Only changeable before"
96940622Sbrian                   " LCP negotiations\n");
97040622Sbrian        return 1;
97140622Sbrian      }
97240622Sbrian      break;
97340622Sbrian    default:
974228990Suqs      log_Printf(LogWARN, "enddisc: Only changeable at phase DEAD/ESTABLISH\n");
97540622Sbrian      return 1;
97636285Sbrian  }
97736285Sbrian
97836285Sbrian  if (arg->argc == arg->argn) {
97936285Sbrian    mp->cfg.enddisc.class = 0;
98036285Sbrian    *mp->cfg.enddisc.address = '\0';
98136285Sbrian    mp->cfg.enddisc.len = 0;
98236285Sbrian  } else if (arg->argc > arg->argn) {
98336285Sbrian    if (!strcasecmp(arg->argv[arg->argn], "label")) {
98436285Sbrian      mp->cfg.enddisc.class = ENDDISC_LOCAL;
98536285Sbrian      strcpy(mp->cfg.enddisc.address, arg->bundle->cfg.label);
98636285Sbrian      mp->cfg.enddisc.len = strlen(mp->cfg.enddisc.address);
98736285Sbrian    } else if (!strcasecmp(arg->argv[arg->argn], "ip")) {
98836285Sbrian      if (arg->bundle->ncp.ipcp.my_ip.s_addr == INADDR_ANY)
98981634Sbrian        ncprange_getip4addr(&arg->bundle->ncp.ipcp.cfg.my_range, &addr);
99036285Sbrian      else
99136285Sbrian        addr = arg->bundle->ncp.ipcp.my_ip;
99236285Sbrian      memcpy(mp->cfg.enddisc.address, &addr.s_addr, sizeof addr.s_addr);
99336285Sbrian      mp->cfg.enddisc.class = ENDDISC_IP;
99436285Sbrian      mp->cfg.enddisc.len = sizeof arg->bundle->ncp.ipcp.my_ip.s_addr;
99536285Sbrian    } else if (!strcasecmp(arg->argv[arg->argn], "mac")) {
99636285Sbrian      struct sockaddr_dl hwaddr;
99736285Sbrian
99836285Sbrian      if (arg->bundle->ncp.ipcp.my_ip.s_addr == INADDR_ANY)
99981634Sbrian        ncprange_getip4addr(&arg->bundle->ncp.ipcp.cfg.my_range, &addr);
100036285Sbrian      else
100136285Sbrian        addr = arg->bundle->ncp.ipcp.my_ip;
100236285Sbrian
1003134789Sbrian      if (arp_EtherAddr(addr, &hwaddr, 1)) {
100436285Sbrian        mp->cfg.enddisc.class = ENDDISC_MAC;
100536285Sbrian        memcpy(mp->cfg.enddisc.address, hwaddr.sdl_data + hwaddr.sdl_nlen,
100636285Sbrian               hwaddr.sdl_alen);
100736285Sbrian        mp->cfg.enddisc.len = hwaddr.sdl_alen;
100836285Sbrian      } else {
100936285Sbrian        log_Printf(LogWARN, "set enddisc: Can't locate MAC address for %s\n",
101036285Sbrian                  inet_ntoa(addr));
101136285Sbrian        return 4;
101236285Sbrian      }
101336285Sbrian    } else if (!strcasecmp(arg->argv[arg->argn], "magic")) {
101436285Sbrian      int f;
101536285Sbrian
101636285Sbrian      randinit();
101736285Sbrian      for (f = 0; f < 20; f += sizeof(long))
101836285Sbrian        *(long *)(mp->cfg.enddisc.address + f) = random();
101936285Sbrian      mp->cfg.enddisc.class = ENDDISC_MAGIC;
102036285Sbrian      mp->cfg.enddisc.len = 20;
102136285Sbrian    } else if (!strcasecmp(arg->argv[arg->argn], "psn")) {
102236285Sbrian      if (arg->argc > arg->argn+1) {
102336285Sbrian        mp->cfg.enddisc.class = ENDDISC_PSN;
102436285Sbrian        strcpy(mp->cfg.enddisc.address, arg->argv[arg->argn+1]);
102536285Sbrian        mp->cfg.enddisc.len = strlen(mp->cfg.enddisc.address);
102636285Sbrian      } else {
102736285Sbrian        log_Printf(LogWARN, "PSN endpoint requires additional data\n");
102836285Sbrian        return 5;
102936285Sbrian      }
103036285Sbrian    } else {
103136285Sbrian      log_Printf(LogWARN, "%s: Unrecognised endpoint type\n",
103236285Sbrian                arg->argv[arg->argn]);
103336285Sbrian      return 6;
103436285Sbrian    }
103536285Sbrian  }
103636285Sbrian
103736285Sbrian  return 0;
103836285Sbrian}
103936285Sbrian
104036285Sbrianstatic int
104158028Sbrianmpserver_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e,
104236285Sbrian                   int *n)
104336285Sbrian{
104436285Sbrian  struct mpserver *s = descriptor2mpserver(d);
104536314Sbrian  int result;
104636285Sbrian
104736314Sbrian  result = 0;
104836285Sbrian  if (s->send.dl != NULL) {
104936285Sbrian    /* We've connect()ed */
105036285Sbrian    if (!link_QueueLen(&s->send.dl->physical->link) &&
105136285Sbrian        !s->send.dl->physical->out) {
105236285Sbrian      /* Only send if we've transmitted all our data (i.e. the ConfigAck) */
105336314Sbrian      result -= datalink_RemoveFromSet(s->send.dl, r, w, e);
105436285Sbrian      bundle_SendDatalink(s->send.dl, s->fd, &s->socket);
105536285Sbrian      s->send.dl = NULL;
105636285Sbrian      s->fd = -1;
105736285Sbrian    } else
105836285Sbrian      /* Never read from a datalink that's on death row ! */
105936314Sbrian      result -= datalink_RemoveFromSet(s->send.dl, r, NULL, NULL);
106036285Sbrian  } else if (r && s->fd >= 0) {
106136285Sbrian    if (*n < s->fd + 1)
106236285Sbrian      *n = s->fd + 1;
106336285Sbrian    FD_SET(s->fd, r);
106436285Sbrian    log_Printf(LogTIMER, "mp: fdset(r) %d\n", s->fd);
106536314Sbrian    result++;
106636285Sbrian  }
106736314Sbrian  return result;
106836285Sbrian}
106936285Sbrian
107036285Sbrianstatic int
107158028Sbrianmpserver_IsSet(struct fdescriptor *d, const fd_set *fdset)
107236285Sbrian{
107336285Sbrian  struct mpserver *s = descriptor2mpserver(d);
107436285Sbrian  return s->fd >= 0 && FD_ISSET(s->fd, fdset);
107536285Sbrian}
107636285Sbrian
107736285Sbrianstatic void
1078134789Sbrianmpserver_Read(struct fdescriptor *d, struct bundle *bundle,
1079134789Sbrian	      const fd_set *fdset __unused)
108036285Sbrian{
108136285Sbrian  struct mpserver *s = descriptor2mpserver(d);
108236285Sbrian
108353684Sbrian  bundle_ReceiveDatalink(bundle, s->fd);
108436285Sbrian}
108536285Sbrian
108637141Sbrianstatic int
1087134789Sbrianmpserver_Write(struct fdescriptor *d __unused, struct bundle *bundle __unused,
1088134789Sbrian               const fd_set *fdset __unused)
108936285Sbrian{
109036285Sbrian  /* We never want to write here ! */
109137019Sbrian  log_Printf(LogALERT, "mpserver_Write: Internal error: Bad call !\n");
109237141Sbrian  return 0;
109336285Sbrian}
109436285Sbrian
109536285Sbrianvoid
109636285Sbrianmpserver_Init(struct mpserver *s)
109736285Sbrian{
109836285Sbrian  s->desc.type = MPSERVER_DESCRIPTOR;
109936285Sbrian  s->desc.UpdateSet = mpserver_UpdateSet;
110036285Sbrian  s->desc.IsSet = mpserver_IsSet;
110136285Sbrian  s->desc.Read = mpserver_Read;
110236285Sbrian  s->desc.Write = mpserver_Write;
110336285Sbrian  s->send.dl = NULL;
110436285Sbrian  s->fd = -1;
110536285Sbrian  memset(&s->socket, '\0', sizeof s->socket);
110636285Sbrian}
110736285Sbrian
110836285Sbrianint
110936285Sbrianmpserver_Open(struct mpserver *s, struct peerid *peer)
111036285Sbrian{
111153970Sbrian  int f, l;
111236285Sbrian  mode_t mask;
111336285Sbrian
111436285Sbrian  if (s->fd != -1) {
111537019Sbrian    log_Printf(LogALERT, "Internal error !  mpserver already open\n");
111636285Sbrian    mpserver_Close(s);
111736285Sbrian  }
111836285Sbrian
111936285Sbrian  l = snprintf(s->socket.sun_path, sizeof s->socket.sun_path, "%sppp-%s-%02x-",
112036285Sbrian               _PATH_VARRUN, peer->authname, peer->enddisc.class);
112181902Sbrian  if (l < 0) {
112281902Sbrian    log_Printf(LogERROR, "mpserver: snprintf(): %s\n", strerror(errno));
112381902Sbrian    return MPSERVER_FAILED;
112481902Sbrian  }
112536285Sbrian
1126134789Sbrian  for (f = 0;
1127134789Sbrian       f < peer->enddisc.len && (size_t)l < sizeof s->socket.sun_path - 2;
1128134789Sbrian       f++) {
112936285Sbrian    snprintf(s->socket.sun_path + l, sizeof s->socket.sun_path - l,
113036285Sbrian             "%02x", *(u_char *)(peer->enddisc.address+f));
113136285Sbrian    l += 2;
113236285Sbrian  }
113336285Sbrian
113436285Sbrian  s->socket.sun_family = AF_LOCAL;
113536285Sbrian  s->socket.sun_len = sizeof s->socket;
113689422Sbrian  s->fd = ID0socket(PF_LOCAL, SOCK_DGRAM, 0);
113736285Sbrian  if (s->fd < 0) {
113853684Sbrian    log_Printf(LogERROR, "mpserver: socket(): %s\n", strerror(errno));
113936285Sbrian    return MPSERVER_FAILED;
114036285Sbrian  }
114136285Sbrian
114236285Sbrian  setsockopt(s->fd, SOL_SOCKET, SO_REUSEADDR, (struct sockaddr *)&s->socket,
114336285Sbrian             sizeof s->socket);
114436285Sbrian  mask = umask(0177);
114553684Sbrian
114653684Sbrian  /*
114753970Sbrian   * Try to bind the socket.  If we succeed we play server, if we fail
114853970Sbrian   * we connect() and hand the link off.
114953684Sbrian   */
115053684Sbrian
115136285Sbrian  if (ID0bind_un(s->fd, &s->socket) < 0) {
115236285Sbrian    if (errno != EADDRINUSE) {
115336285Sbrian      log_Printf(LogPHASE, "mpserver: can't create bundle socket %s (%s)\n",
115436285Sbrian                s->socket.sun_path, strerror(errno));
115536285Sbrian      umask(mask);
115636285Sbrian      close(s->fd);
115736285Sbrian      s->fd = -1;
115836285Sbrian      return MPSERVER_FAILED;
115936285Sbrian    }
116053684Sbrian
116153970Sbrian    /* So we're the sender */
116236285Sbrian    umask(mask);
116336285Sbrian    if (ID0connect_un(s->fd, &s->socket) < 0) {
116436285Sbrian      log_Printf(LogPHASE, "mpserver: can't connect to bundle socket %s (%s)\n",
116536285Sbrian                s->socket.sun_path, strerror(errno));
116636285Sbrian      if (errno == ECONNREFUSED)
116753684Sbrian        log_Printf(LogPHASE, "          The previous server died badly !\n");
116836285Sbrian      close(s->fd);
116936285Sbrian      s->fd = -1;
117036285Sbrian      return MPSERVER_FAILED;
117136285Sbrian    }
117236285Sbrian
117336285Sbrian    /* Donate our link to the other guy */
117436285Sbrian    return MPSERVER_CONNECTED;
117536285Sbrian  }
117636285Sbrian
117736285Sbrian  return MPSERVER_LISTENING;
117836285Sbrian}
117936285Sbrian
118036285Sbrianvoid
118136285Sbrianmpserver_Close(struct mpserver *s)
118236285Sbrian{
118336285Sbrian  if (s->send.dl != NULL) {
118436285Sbrian    bundle_SendDatalink(s->send.dl, s->fd, &s->socket);
118536285Sbrian    s->send.dl = NULL;
118636285Sbrian    s->fd = -1;
118736285Sbrian  } else if (s->fd >= 0) {
118836285Sbrian    close(s->fd);
118936285Sbrian    if (ID0unlink(s->socket.sun_path) == -1)
119036285Sbrian      log_Printf(LogERROR, "%s: Failed to remove: %s\n", s->socket.sun_path,
119136285Sbrian                strerror(errno));
119236285Sbrian    memset(&s->socket, '\0', sizeof s->socket);
119336285Sbrian    s->fd = -1;
119436285Sbrian  }
119536285Sbrian}
119636312Sbrian
119736312Sbrianvoid
119836312Sbrianmp_LinkLost(struct mp *mp, struct datalink *dl)
119936312Sbrian{
120036312Sbrian  if (mp->seq.min_in == dl->mp.seq)
120136312Sbrian    /* We've lost the link that's holding everything up ! */
120246686Sbrian    mp_Assemble(mp, NULL, NULL);
120336312Sbrian}
120438544Sbrian
120581634Sbriansize_t
120681634Sbrianmp_QueueLen(struct mp *mp)
120738544Sbrian{
120881634Sbrian  return link_QueueLen(&mp->link);
120938544Sbrian}
1210