lcp.c revision 26516
1/*
2 *	      PPP Link Control Protocol (LCP) Module
3 *
4 *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
5 *
6 *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
7 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the Internet Initiative Japan, Inc.  The name of the
14 * IIJ may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 *
20 * $Id: lcp.c,v 1.22 1997/06/01 03:43:22 brian Exp $
21 *
22 * TODO:
23 *      o Validate magic number received from peer.
24 *	o Limit data field length by MRU
25 */
26#include <sys/time.h>
27#include "fsm.h"
28#include "lcp.h"
29#include "ipcp.h"
30#include "lcpproto.h"
31#include "os.h"
32#include "hdlc.h"
33#include "ccp.h"
34#include "lqr.h"
35#include "phase.h"
36#include "loadalias.h"
37#include "vars.h"
38#include "auth.h"
39#include <arpa/inet.h>
40
41extern void IpcpUp();
42extern void IpcpOpen();
43extern void StartPapChallenge();
44extern void StartChapChallenge();
45extern void SetLinkParams(struct lcpstate *);
46extern void Prompt();
47extern void StopIdleTimer();
48extern void OsLinkdown();
49extern void Cleanup();
50extern struct pppTimer IpcpReportTimer;
51extern int randinit;
52
53struct lcpstate LcpInfo;
54
55static void LcpSendConfigReq(struct fsm *);
56static void LcpSendTerminateReq(struct fsm *fp);
57static void LcpSendTerminateAck(struct fsm *fp);
58static void LcpDecodeConfig(u_char *cp, int flen,int mode);
59static void LcpInitRestartCounter(struct fsm *);
60static void LcpLayerUp(struct fsm *);
61static void LcpLayerDown(struct fsm *);
62static void LcpLayerStart(struct fsm *);
63static void LcpLayerFinish(struct fsm *);
64
65extern int ModemSpeed();
66
67#define	REJECTED(p, x)	(p->his_reject & (1<<x))
68
69static char *cftypes[] = {
70  "???", "MRU", "ACCMAP", "AUTHPROTO", "QUALPROTO", "MAGICNUM",
71  "RESERVED", "PROTOCOMP", "ACFCOMP", "FCSALT", "SDP",
72};
73
74struct fsm LcpFsm = {
75  "LCP",			/* Name of protocol */
76  PROTO_LCP,			/* Protocol Number */
77  LCP_MAXCODE,
78  OPEN_ACTIVE,
79  ST_INITIAL,			/* State of machine */
80  0, 0, 0,
81
82  0,
83  { 0, 0, 0, NULL, NULL, NULL },
84
85  LcpLayerUp,
86  LcpLayerDown,
87  LcpLayerStart,
88  LcpLayerFinish,
89  LcpInitRestartCounter,
90  LcpSendConfigReq,
91  LcpSendTerminateReq,
92  LcpSendTerminateAck,
93  LcpDecodeConfig,
94};
95
96static struct pppTimer LcpReportTimer;
97
98char *PhaseNames[] = {
99  "Dead", "Establish", "Authenticate", "Network", "Terminate"
100};
101
102void
103NewPhase(new)
104int new;
105{
106  struct lcpstate *lcp = &LcpInfo;
107
108  phase = new;
109  LogPrintf(LogPHASE, "NewPhase: %s\n", PhaseNames[phase]);
110  switch (phase) {
111  case PHASE_AUTHENTICATE:
112    lcp->auth_ineed = lcp->want_auth;
113    lcp->auth_iwait = lcp->his_auth;
114    if (lcp->his_auth || lcp->want_auth) {
115      LogPrintf(LogPHASE, " his = %x, mine = %x\n", lcp->his_auth, lcp->want_auth);
116      if (lcp->his_auth == PROTO_PAP)
117	StartAuthChallenge(&AuthPapInfo);
118      if (lcp->want_auth == PROTO_CHAP)
119	StartAuthChallenge(&AuthChapInfo);
120    } else
121      NewPhase(PHASE_NETWORK);
122    break;
123  case PHASE_NETWORK:
124    IpcpUp();
125    IpcpOpen();
126    CcpUp();
127    CcpOpen();
128    break;
129  case PHASE_DEAD:
130    if (mode & MODE_DIRECT)
131      Cleanup(EX_DEAD);
132    if (mode & MODE_BACKGROUND && reconnectState != RECON_TRUE)
133      Cleanup(EX_DEAD);
134    break;
135  }
136}
137
138static void
139LcpReportTime()
140{
141  if (LogIsKept(LogDEBUG)) {
142    time_t t;
143
144    time(&t);
145    LogPrintf(LogDEBUG, "LcpReportTime: %s", ctime(&t));
146  }
147  StopTimer(&LcpReportTimer);
148  LcpReportTimer.state = TIMER_STOPPED;
149  StartTimer(&LcpReportTimer);
150  HdlcErrorCheck();
151}
152
153int
154ReportLcpStatus()
155{
156  struct lcpstate *lcp = &LcpInfo;
157  struct fsm *fp = &LcpFsm;
158
159  if (!VarTerm)
160    return 1;
161
162  fprintf(VarTerm, "%s [%s]\n", fp->name, StateNames[fp->state]);
163  fprintf(VarTerm,
164    " his side: MRU %ld, ACCMAP %08lx, PROTOCOMP %d, ACFCOMP %d, MAGIC %08lx,\n"
165    "           REJECT %04lx\n",
166    lcp->his_mru, lcp->his_accmap, lcp->his_protocomp, lcp->his_acfcomp,
167    lcp->his_magic, lcp->his_reject);
168  fprintf(VarTerm,
169    " my  side: MRU %ld, ACCMAP %08lx, PROTOCOMP %d, ACFCOMP %d, MAGIC %08lx,\n"
170    "           REJECT %04lx\n",
171    lcp->want_mru, lcp->want_accmap, lcp->want_protocomp, lcp->want_acfcomp,
172    lcp->want_magic, lcp->my_reject);
173  fprintf(VarTerm, "\nDefaults:   MRU = %ld, ACCMAP = %08x\t", VarMRU, VarAccmap);
174  fprintf(VarTerm, "Open Mode: %s\n", (VarOpenMode == OPEN_ACTIVE)? "active" : "passive");
175  return 0;
176}
177
178/*
179 * Generate random number which will be used as magic number.
180 */
181u_long
182GenerateMagic()
183{
184  if (!randinit) {
185    randinit = 1;
186    if (srandomdev() < 0)
187      srandom((unsigned long)(time(NULL) ^ getpid()));
188  }
189
190  return (random());
191}
192
193void
194LcpInit()
195{
196  struct lcpstate *lcp = &LcpInfo;
197
198  FsmInit(&LcpFsm);
199  HdlcInit();
200
201  bzero(lcp, sizeof(struct lcpstate));
202  lcp->want_mru = VarMRU;
203  lcp->his_mru = DEF_MRU;
204  lcp->his_accmap = 0xffffffff;
205  lcp->want_accmap = VarAccmap;
206  lcp->want_magic = GenerateMagic();
207  lcp->want_auth = lcp->his_auth = 0;
208  if (Enabled(ConfChap))
209    lcp->want_auth = PROTO_CHAP;
210  else if (Enabled(ConfPap))
211    lcp->want_auth = PROTO_PAP;
212  if (Enabled(ConfLqr)) lcp->want_lqrperiod = VarLqrTimeout * 100;
213  if (Enabled(ConfAcfcomp)) lcp->want_acfcomp = 1;
214  if (Enabled(ConfProtocomp)) lcp->want_protocomp = 1;
215  LcpFsm.maxconfig = 10;
216}
217
218static void
219LcpInitRestartCounter(fp)
220struct fsm *fp;
221{
222  fp->FsmTimer.load = VarRetryTimeout * SECTICKS;
223  fp->restart = 5;
224}
225
226void
227PutConfValue(cpp, types, type, len, val)
228u_char **cpp;
229char **types;
230u_char type;
231int len;
232u_long val;
233{
234  u_char *cp;
235  struct in_addr ina;
236
237  cp = *cpp;
238  *cp++ = type; *cp++ = len;
239  if (len == 6) {
240    if (type == TY_IPADDR) {
241      ina.s_addr = htonl(val);
242      LogPrintf(LogLCP, " %s [%d] %s\n",
243	types[type], len, inet_ntoa(ina));
244    } else {
245      LogPrintf(LogLCP, " %s [%d] %08x\n", types[type], len, val);
246    }
247    *cp++ = (val >> 24) & 0377;
248    *cp++ = (val >> 16) & 0377;
249  } else
250    LogPrintf(LogLCP, " %s [%d] %d\n", types[type], len, val);
251  *cp++ = (val >> 8) & 0377;
252  *cp++ = val & 0377;
253  *cpp = cp;
254}
255
256static void
257LcpSendConfigReq(fp)
258struct fsm *fp;
259{
260  u_char *cp;
261  struct lcpstate *lcp = &LcpInfo;
262  struct lqrreq *req;
263
264  LogPrintf(LogLCP, "LcpSendConfigReq\n");
265  cp = ReqBuff;
266  if (!DEV_IS_SYNC) {
267    if (lcp->want_acfcomp && !REJECTED(lcp, TY_ACFCOMP)) {
268      *cp++ = TY_ACFCOMP; *cp++ = 2;
269      LogPrintf(LogLCP, " %s\n", cftypes[TY_ACFCOMP]);
270    }
271    if (lcp->want_protocomp && !REJECTED(lcp, TY_PROTOCOMP)) {
272      *cp++ = TY_PROTOCOMP; *cp++ = 2;
273      LogPrintf(LogLCP, " %s\n", cftypes[TY_PROTOCOMP]);
274    }
275    if (!REJECTED(lcp, TY_ACCMAP))
276      PutConfValue(&cp, cftypes, TY_ACCMAP, 6, lcp->want_accmap);
277  }
278  if (!REJECTED(lcp, TY_MRU))
279    PutConfValue(&cp, cftypes, TY_MRU, 4, lcp->want_mru);
280  if (lcp->want_magic && !REJECTED(lcp, TY_MAGICNUM))
281    PutConfValue(&cp, cftypes, TY_MAGICNUM, 6, lcp->want_magic);
282  if (lcp->want_lqrperiod && !REJECTED(lcp, TY_QUALPROTO)) {
283    req = (struct lqrreq *)cp;
284    req->type = TY_QUALPROTO; req->length = sizeof(struct lqrreq);
285    req->proto = htons(PROTO_LQR);
286    req->period = htonl(lcp->want_lqrperiod);
287    cp += sizeof(struct lqrreq);
288    LogPrintf(LogLCP, " %s (%d)\n", cftypes[TY_QUALPROTO], lcp->want_lqrperiod);
289  }
290  switch (lcp->want_auth) {
291  case PROTO_PAP:
292    PutConfValue(&cp, cftypes, TY_AUTHPROTO, 4, lcp->want_auth);
293    break;
294  case PROTO_CHAP:
295    PutConfValue(&cp, cftypes, TY_AUTHPROTO, 5, lcp->want_auth);
296    *cp++ = 5;		/* Use MD5 */
297    break;
298  }
299  FsmOutput(fp, CODE_CONFIGREQ, fp->reqid++, ReqBuff, cp - ReqBuff);
300}
301
302void
303LcpSendProtoRej(option, count)
304u_char *option;
305int count;
306{
307  struct fsm *fp = &LcpFsm;
308
309  LogPrintf(LogLCP, "LcpSendProtoRej\n");
310  FsmOutput(fp, CODE_PROTOREJ, fp->reqid, option, count);
311}
312
313static void
314LcpSendTerminateReq(fp)
315struct fsm *fp;
316{
317   /* Most thins are done in fsm layer. Nothing to to. */
318}
319
320static void
321LcpSendTerminateAck(fp)
322struct fsm *fp;
323{
324  LogPrintf(LogLCP, "LcpSendTerminateAck.\n");
325  FsmOutput(fp, CODE_TERMACK, fp->reqid++, NULL, 0);
326}
327
328static void
329LcpLayerStart(fp)
330struct fsm *fp;
331{
332  LogPrintf(LogLCP, "LcpLayerStart\n");
333  NewPhase(PHASE_ESTABLISH);
334}
335
336static void
337StopAllTimers()
338{
339  StopTimer(&LcpReportTimer);
340  StopTimer(&IpcpReportTimer);
341  StopIdleTimer();
342  StopTimer(&AuthPapInfo.authtimer);
343  StopTimer(&AuthChapInfo.authtimer);
344  StopLqrTimer();
345}
346
347static void
348LcpLayerFinish(fp)
349struct fsm *fp;
350{
351  LogPrintf(LogLCP, "LcpLayerFinish\n");
352  OsCloseLink(1);
353  NewPhase(PHASE_DEAD);
354  StopAllTimers();
355  (void)OsInterfaceDown(0);
356  Prompt();
357}
358
359static void
360LcpLayerUp(fp)
361struct fsm *fp;
362{
363  LogPrintf(LogLCP, "LcpLayerUp\n");
364  OsSetInterfaceParams(23, LcpInfo.his_mru, ModemSpeed());
365  SetLinkParams(&LcpInfo);
366
367  NewPhase(PHASE_AUTHENTICATE);
368
369  StartLqm();
370  StopTimer(&LcpReportTimer);
371  LcpReportTimer.state = TIMER_STOPPED;
372  LcpReportTimer.load = 60 * SECTICKS;
373  LcpReportTimer.func = LcpReportTime;
374  StartTimer(&LcpReportTimer);
375}
376
377static void
378LcpLayerDown(fp)
379struct fsm *fp;
380{
381  LogPrintf(LogLCP, "LcpLayerDown\n");
382  StopAllTimers();
383  OsLinkdown();
384  NewPhase(PHASE_TERMINATE);
385}
386
387void
388LcpUp()
389{
390  FsmUp(&LcpFsm);
391}
392
393void
394LcpDown()			/* Sudden death */
395{
396  NewPhase(PHASE_DEAD);
397  StopAllTimers();
398  FsmDown(&LcpFsm);
399}
400
401void
402LcpOpen(mode)
403int mode;
404{
405  LcpFsm.open_mode = mode;
406  FsmOpen(&LcpFsm);
407}
408
409void
410LcpClose()
411{
412  FsmClose(&LcpFsm);
413}
414
415/*
416 *	XXX: Should validate option length
417 */
418static void
419LcpDecodeConfig(cp, plen, mode)
420u_char *cp;
421int plen;
422int mode;
423{
424  char *request;
425  int type, length, mru;
426  u_long *lp, magic, accmap;
427  u_short *sp, proto;
428  struct lqrreq *req;
429
430  ackp = AckBuff;
431  nakp = NakBuff;
432  rejp = RejBuff;
433
434  while (plen >= sizeof(struct fsmconfig)) {
435    type = *cp;
436    length = cp[1];
437    if (type <= TY_ACFCOMP)
438      request = cftypes[type];
439    else
440      request = "???";
441
442    switch (type) {
443    case TY_MRU:
444      sp = (u_short *)(cp + 2);
445      mru = htons(*sp);
446      LogPrintf(LogLCP, " %s %d\n", request, mru);
447
448      switch (mode) {
449      case MODE_REQ:
450	if (mru > MAX_MRU) {
451	  *sp = htons(MAX_MRU);
452	  bcopy(cp, nakp, 4); nakp += 4;
453	} else if (mru < MIN_MRU) {
454	  *sp = htons(MIN_MRU);
455	  bcopy(cp, nakp, 4); nakp += 4;
456	} else {
457	  LcpInfo.his_mru = mru;
458	  bcopy(cp, ackp, 4); ackp += 4;
459	}
460	break;
461      case MODE_NAK:
462	if (mru >= MIN_MRU || mru <= MAX_MRU)
463	  LcpInfo.want_mru = mru;
464	break;
465      case MODE_REJ:
466	LcpInfo.his_reject |= (1 << type);
467	break;
468      }
469      break;
470    case TY_ACCMAP:
471      lp = (u_long *)(cp + 2);
472      accmap = htonl(*lp);
473      LogPrintf(LogLCP, " %s %08x\n", request, accmap);
474
475      switch (mode) {
476      case MODE_REQ:
477	LcpInfo.his_accmap = accmap;
478	bcopy(cp, ackp, 6); ackp += 6;
479	break;
480      case MODE_NAK:
481	LcpInfo.want_accmap = accmap;
482	break;
483      case MODE_REJ:
484	LcpInfo.his_reject |= (1 << type);
485	break;
486      }
487      break;
488    case TY_AUTHPROTO:
489      sp = (u_short *)(cp + 2);
490      proto = ntohs(*sp);
491      LogPrintf(LogLCP, " %s proto = %04x\n", request, proto);
492
493      switch (mode) {
494      case MODE_REQ:
495	switch (proto) {
496	case PROTO_PAP:
497	  if (length != 4) {
498	    LogPrintf(LogLCP, " %s bad length (%d)\n", request, length);
499	    goto reqreject;
500	  }
501	  if (Acceptable(ConfPap)) {
502	    LcpInfo.his_auth = proto;
503	    bcopy(cp, ackp, length); ackp += length;
504	  } else if (Acceptable(ConfChap)) {
505	    *nakp++ = *cp; *nakp++ = 5;
506	    *nakp++ = (unsigned char)(PROTO_CHAP >> 8);
507	    *nakp++ = (unsigned char)PROTO_CHAP;
508	    *nakp++ = 5;
509	  } else
510	    goto reqreject;
511	  break;
512	case PROTO_CHAP:
513	  if (length < 5) {
514	    LogPrintf(LogLCP, " %s bad length (%d)\n", request, length);
515	    goto reqreject;
516	  }
517	  if (Acceptable(ConfChap) && cp[4] == 5) {
518	    LcpInfo.his_auth = proto;
519	    bcopy(cp, ackp, length); ackp += length;
520	  } else if (Acceptable(ConfPap)) {
521	    *nakp++ = *cp; *nakp++ = 4;
522	    *nakp++ = (unsigned char)(PROTO_PAP >> 8);
523	    *nakp++ = (unsigned char)PROTO_PAP;
524	  } else
525	    goto reqreject;
526	  break;
527	default:
528 	    LogPrintf(LogLCP, " %s not implemented, NAK.\n", request);
529            bcopy(cp, nakp, length);
530            nakp += length;
531            break;
532	}
533	break;
534      case MODE_NAK:
535	break;
536      case MODE_REJ:
537	LcpInfo.his_reject |= (1 << type);
538	break;
539      }
540      break;
541    case TY_QUALPROTO:
542      req = (struct lqrreq *)cp;
543      LogPrintf(LogLCP, " %s proto: %x, interval: %dms\n",
544		request, ntohs(req->proto), ntohl(req->period)*10);
545      switch (mode) {
546      case MODE_REQ:
547	if (ntohs(req->proto) != PROTO_LQR || !Acceptable(ConfLqr))
548	  goto reqreject;
549	else {
550	  LcpInfo.his_lqrperiod = ntohl(req->period);
551	  if (LcpInfo.his_lqrperiod < 500)
552	    LcpInfo.his_lqrperiod = 500;
553	  req->period = htonl(LcpInfo.his_lqrperiod);
554	  bcopy(cp, ackp, length); ackp += length;
555	}
556	break;
557      case MODE_NAK:
558	break;
559      case MODE_REJ:
560	LcpInfo.his_reject |= (1 << type);
561	break;
562      }
563      break;
564    case TY_MAGICNUM:
565      lp = (u_long *)(cp + 2);
566      magic = ntohl(*lp);
567      LogPrintf(LogLCP, " %s %08x\n", request, magic);
568
569      switch (mode) {
570      case MODE_REQ:
571	if (LcpInfo.want_magic) {
572	  /* Validate magic number */
573	  if (magic == LcpInfo.want_magic) {
574	    LogPrintf(LogLCP, "Magic is same (%08x)\n", magic);
575	    LcpInfo.want_magic = GenerateMagic();
576	    bcopy(cp, nakp, 6);
577            nakp += 6;
578          } else {
579	    LcpInfo.his_magic = magic;
580	    bcopy(cp, ackp, length); ackp += length;
581          }
582	} else {
583	  LcpInfo.my_reject |= (1 << type);
584	  goto reqreject;
585	}
586	break;
587      case MODE_NAK:
588	LogPrintf(LogLCP, " %s magic %08x has NAKed\n", request, magic);
589	LcpInfo.want_magic = GenerateMagic();
590	break;
591      case MODE_REJ:
592	LogPrintf(LogLCP, " %s magic has REJected\n", request);
593	LcpInfo.want_magic = 0;
594	LcpInfo.his_reject |= (1 << type);
595	break;
596      }
597      break;
598    case TY_PROTOCOMP:
599      LogPrintf(LogLCP, " %s\n", request);
600
601      switch (mode) {
602      case MODE_REQ:
603        if (Acceptable(ConfProtocomp)) {
604	  LcpInfo.his_protocomp = 1;
605	  bcopy(cp, ackp, 2); ackp += 2;
606        } else {
607#ifdef OLDMST
608	  /*
609	   * MorningStar before v1.3 needs NAK
610	   */
611	  bcopy(cp, nakp, 2); nakp += 2;
612#else
613	  bcopy(cp, rejp, 2); rejp += 2;
614	  LcpInfo.my_reject |= (1 << type);
615#endif
616        }
617	break;
618      case MODE_NAK:
619      case MODE_REJ:
620	LcpInfo.want_protocomp = 0;
621	LcpInfo.his_reject |= (1 << type);
622	break;
623      }
624      break;
625    case TY_ACFCOMP:
626      LogPrintf(LogLCP, " %s\n", request);
627      switch (mode) {
628      case MODE_REQ:
629        if (Acceptable(ConfAcfcomp)) {
630	  LcpInfo.his_acfcomp = 1;
631	  bcopy(cp, ackp, 2);
632	  ackp += 2;
633        } else {
634#ifdef OLDMST
635	  /*
636	   * MorningStar before v1.3 needs NAK
637	   */
638	  bcopy(cp, nakp, 2);
639	  nakp += 2;
640#else
641	  bcopy(cp, rejp, 2);
642	  rejp += 2;
643	  LcpInfo.my_reject |= (1 << type);
644#endif
645	}
646	break;
647      case MODE_NAK:
648      case MODE_REJ:
649	LcpInfo.want_acfcomp = 0;
650	LcpInfo.his_reject |= (1 << type);
651	break;
652      }
653      break;
654    case TY_SDP:
655      LogPrintf(LogLCP, " %s\n", request);
656      switch (mode) {
657      case MODE_REQ:
658      case MODE_NAK:
659      case MODE_REJ:
660	break;
661      }
662      break;
663    default:
664      LogPrintf(LogLCP, " ???[%02x]\n", type);
665      if (mode == MODE_REQ) {
666reqreject:
667        bcopy(cp, rejp, length);
668        rejp += length;
669        LcpInfo.my_reject |= (1 << type);
670      }
671      break;
672    }
673    /* to avoid inf. loop */
674    if (length == 0) {
675      LogPrintf(LogLCP, "LCP size zero\n");
676      break;
677    }
678
679    plen -= length;
680    cp += length;
681  }
682}
683
684void
685LcpInput(struct mbuf *bp)
686{
687  FsmInput(&LcpFsm, bp);
688}
689