xref: /freebsd/usr.sbin/ppp/lqr.c (revision a8445737e740901f5f2c8d24c12ef7fc8b00134e)
1 /*
2  *	      PPP Line Quality Monitoring (LQM) 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: lqr.c,v 1.29 1998/06/27 23:48:49 brian Exp $
21  *
22  *	o LQR based on RFC1333
23  *
24  * TODO:
25  *	o LQM policy
26  *	o Allow user to configure LQM method and interval.
27  */
28 
29 #include <sys/types.h>
30 #include <sys/un.h>
31 
32 #include <string.h>
33 #include <termios.h>
34 
35 #include "mbuf.h"
36 #include "log.h"
37 #include "defs.h"
38 #include "timer.h"
39 #include "fsm.h"
40 #include "lcpproto.h"
41 #include "lcp.h"
42 #include "lqr.h"
43 #include "hdlc.h"
44 #include "async.h"
45 #include "throughput.h"
46 #include "ccp.h"
47 #include "link.h"
48 #include "descriptor.h"
49 #include "physical.h"
50 #include "mp.h"
51 #include "chat.h"
52 #include "auth.h"
53 #include "chap.h"
54 #include "command.h"
55 #include "cbcp.h"
56 #include "datalink.h"
57 
58 struct echolqr {
59   u_int32_t magic;
60   u_int32_t signature;
61   u_int32_t sequence;
62 };
63 
64 #define	SIGNATURE  0x594e4f54
65 
66 static void
67 SendEchoReq(struct lcp *lcp)
68 {
69   struct hdlc *hdlc = &link2physical(lcp->fsm.link)->hdlc;
70   struct echolqr echo;
71 
72   echo.magic = htonl(lcp->want_magic);
73   echo.signature = htonl(SIGNATURE);
74   echo.sequence = htonl(hdlc->lqm.echo.seq_sent);
75   fsm_Output(&lcp->fsm, CODE_ECHOREQ, hdlc->lqm.echo.seq_sent++,
76             (u_char *)&echo, sizeof echo);
77 }
78 
79 void
80 lqr_RecvEcho(struct fsm *fp, struct mbuf * bp)
81 {
82   struct hdlc *hdlc = &link2physical(fp->link)->hdlc;
83   struct echolqr *lqr;
84   u_int32_t seq;
85 
86   if (mbuf_Length(bp) == sizeof(struct echolqr)) {
87     lqr = (struct echolqr *) MBUF_CTOP(bp);
88     if (ntohl(lqr->signature) == SIGNATURE) {
89       seq = ntohl(lqr->sequence);
90       /* careful not to update lqm.echo.seq_recv with older values */
91       if ((hdlc->lqm.echo.seq_recv > (u_int32_t)0 - 5 && seq < 5) ||
92           (hdlc->lqm.echo.seq_recv <= (u_int32_t)0 - 5 &&
93            seq > hdlc->lqm.echo.seq_recv))
94         hdlc->lqm.echo.seq_recv = seq;
95     } else
96       log_Printf(LogWARN, "lqr_RecvEcho: Got sig 0x%08lx, not 0x%08lx !\n",
97                 (u_long)ntohl(lqr->signature), (u_long)SIGNATURE);
98   } else
99     log_Printf(LogWARN, "lqr_RecvEcho: Got packet size %d, expecting %ld !\n",
100               mbuf_Length(bp), (long)sizeof(struct echolqr));
101 }
102 
103 void
104 lqr_ChangeOrder(struct lqrdata * src, struct lqrdata * dst)
105 {
106   u_int32_t *sp, *dp;
107   int n;
108 
109   sp = (u_int32_t *) src;
110   dp = (u_int32_t *) dst;
111   for (n = 0; n < sizeof(struct lqrdata) / sizeof(u_int32_t); n++)
112     *dp++ = ntohl(*sp++);
113 }
114 
115 static void
116 SendLqrData(struct lcp *lcp)
117 {
118   struct mbuf *bp;
119 
120   bp = mbuf_Alloc(sizeof(struct lqrdata), MB_LQR);
121   hdlc_Output(lcp->fsm.link, PRI_LINK, PROTO_LQR, bp);
122 }
123 
124 static void
125 SendLqrReport(void *v)
126 {
127   struct lcp *lcp = (struct lcp *)v;
128   struct physical *p = link2physical(lcp->fsm.link);
129 
130   timer_Stop(&p->hdlc.lqm.timer);
131 
132   if (p->hdlc.lqm.method & LQM_LQR) {
133     if (p->hdlc.lqm.lqr.resent > 5) {
134       /* XXX: Should implement LQM strategy */
135       log_Printf(LogPHASE, "%s: ** Too many LQR packets lost **\n",
136                 lcp->fsm.link->name);
137       log_Printf(LogLQM, "%s: Too many LQR packets lost\n",
138                 lcp->fsm.link->name);
139       p->hdlc.lqm.method = 0;
140       datalink_Down(p->dl, CLOSE_NORMAL);
141     } else {
142       SendLqrData(lcp);
143       p->hdlc.lqm.lqr.resent++;
144     }
145   } else if (p->hdlc.lqm.method & LQM_ECHO) {
146     if ((p->hdlc.lqm.echo.seq_sent > 5 &&
147          p->hdlc.lqm.echo.seq_sent - 5 > p->hdlc.lqm.echo.seq_recv) ||
148         (p->hdlc.lqm.echo.seq_sent <= 5 &&
149          p->hdlc.lqm.echo.seq_sent > p->hdlc.lqm.echo.seq_recv + 5)) {
150       log_Printf(LogPHASE, "%s: ** Too many ECHO LQR packets lost **\n",
151                 lcp->fsm.link->name);
152       log_Printf(LogLQM, "%s: Too many ECHO LQR packets lost\n",
153                 lcp->fsm.link->name);
154       p->hdlc.lqm.method = 0;
155       datalink_Down(p->dl, CLOSE_NORMAL);
156     } else
157       SendEchoReq(lcp);
158   }
159   if (p->hdlc.lqm.method && p->hdlc.lqm.timer.load)
160     timer_Start(&p->hdlc.lqm.timer);
161 }
162 
163 void
164 lqr_Input(struct physical *physical, struct mbuf *bp)
165 {
166   int len;
167 
168   len = mbuf_Length(bp);
169   if (len != sizeof(struct lqrdata))
170     log_Printf(LogWARN, "lqr_Input: Got packet size %d, expecting %ld !\n",
171               len, (long)sizeof(struct lqrdata));
172   else if (!IsAccepted(physical->link.lcp.cfg.lqr) &&
173            !(physical->hdlc.lqm.method & LQM_LQR)) {
174     bp->offset -= 2;
175     bp->cnt += 2;
176     lcp_SendProtoRej(physical->hdlc.lqm.owner, MBUF_CTOP(bp), bp->cnt);
177   } else {
178     struct lqrdata *lqr;
179     struct lcp *lcp;
180     u_int32_t lastLQR;
181 
182     lqr = (struct lqrdata *)MBUF_CTOP(bp);
183     lcp = physical->hdlc.lqm.owner;
184     if (ntohl(lqr->MagicNumber) != physical->hdlc.lqm.owner->his_magic)
185       log_Printf(LogWARN, "lqr_Input: magic 0x%08lx is wrong,"
186                  " expecting 0x%08lx\n",
187 		 (u_long)ntohl(lqr->MagicNumber),
188                  (u_long)physical->hdlc.lqm.owner->his_magic);
189     else {
190       /*
191        * Remember our PeerInLQRs, then convert byte order and save
192        */
193       lastLQR = physical->hdlc.lqm.lqr.peer.PeerInLQRs;
194 
195       lqr_ChangeOrder(lqr, &physical->hdlc.lqm.lqr.peer);
196       lqr_Dump(physical->link.name, "Input", &physical->hdlc.lqm.lqr.peer);
197       /* we have received an LQR from peer */
198       physical->hdlc.lqm.lqr.resent = 0;
199 
200       /*
201        * Generate an LQR response if we're not running an LQR timer OR
202        * two successive LQR's PeerInLQRs are the same OR we're not going to
203        * send our next one before the peers max timeout.
204        */
205       if (physical->hdlc.lqm.timer.load == 0 ||
206           !(physical->hdlc.lqm.method & LQM_LQR) ||
207           (lastLQR && lastLQR == physical->hdlc.lqm.lqr.peer.PeerInLQRs) ||
208           (physical->hdlc.lqm.lqr.peer_timeout &&
209            physical->hdlc.lqm.timer.rest * 100 / SECTICKS >
210            physical->hdlc.lqm.lqr.peer_timeout))
211         SendLqrData(physical->hdlc.lqm.owner);
212     }
213   }
214   mbuf_Free(bp);
215 }
216 
217 /*
218  *  When LCP is reached to opened state, We'll start LQM activity.
219  */
220 
221 static void
222 lqr_Setup(struct lcp *lcp)
223 {
224   struct physical *physical = link2physical(lcp->fsm.link);
225 
226   physical->hdlc.lqm.lqr.resent = 0;
227   physical->hdlc.lqm.echo.seq_sent = 0;
228   physical->hdlc.lqm.echo.seq_recv = 0;
229   memset(&physical->hdlc.lqm.lqr.peer, '\0',
230          sizeof physical->hdlc.lqm.lqr.peer);
231 
232   physical->hdlc.lqm.method = LQM_ECHO;
233   if (IsEnabled(lcp->cfg.lqr) && !REJECTED(lcp, TY_QUALPROTO))
234     physical->hdlc.lqm.method |= LQM_LQR;
235   timer_Stop(&physical->hdlc.lqm.timer);
236 
237   physical->hdlc.lqm.lqr.peer_timeout = lcp->his_lqrperiod;
238   if (lcp->his_lqrperiod)
239     log_Printf(LogLQM, "%s: Expecting LQR every %d.%02d secs\n",
240               physical->link.name, lcp->his_lqrperiod / 100,
241               lcp->his_lqrperiod % 100);
242 
243   if (lcp->want_lqrperiod) {
244     log_Printf(LogLQM, "%s: Will send %s every %d.%02d secs\n",
245               physical->link.name,
246               physical->hdlc.lqm.method & LQM_LQR ? "LQR" : "ECHO LQR",
247               lcp->want_lqrperiod / 100, lcp->want_lqrperiod % 100);
248     physical->hdlc.lqm.timer.load = lcp->want_lqrperiod * SECTICKS / 100;
249     physical->hdlc.lqm.timer.func = SendLqrReport;
250     physical->hdlc.lqm.timer.name = "lqm";
251     physical->hdlc.lqm.timer.arg = lcp;
252   } else {
253     physical->hdlc.lqm.timer.load = 0;
254     if (!lcp->his_lqrperiod)
255       log_Printf(LogLQM, "%s: LQR/ECHO LQR not negotiated\n",
256                  physical->link.name);
257   }
258 }
259 
260 void
261 lqr_Start(struct lcp *lcp)
262 {
263   struct physical *p = link2physical(lcp->fsm.link);
264 
265   lqr_Setup(lcp);
266   if (p->hdlc.lqm.timer.load)
267     SendLqrReport(lcp);
268 }
269 
270 void
271 lqr_reStart(struct lcp *lcp)
272 {
273   struct physical *p = link2physical(lcp->fsm.link);
274 
275   lqr_Setup(lcp);
276   if (p->hdlc.lqm.timer.load)
277     timer_Start(&p->hdlc.lqm.timer);
278 }
279 
280 void
281 lqr_StopTimer(struct physical *physical)
282 {
283   timer_Stop(&physical->hdlc.lqm.timer);
284 }
285 
286 void
287 lqr_Stop(struct physical *physical, int method)
288 {
289   if (method == LQM_LQR)
290     log_Printf(LogLQM, "%s: Stop sending LQR, Use LCP ECHO instead.\n",
291                physical->link.name);
292   if (method == LQM_ECHO)
293     log_Printf(LogLQM, "%s: Stop sending LCP ECHO.\n",
294                physical->link.name);
295   physical->hdlc.lqm.method &= ~method;
296   if (physical->hdlc.lqm.method)
297     SendLqrReport(physical->hdlc.lqm.owner);
298   else
299     timer_Stop(&physical->hdlc.lqm.timer);
300 }
301 
302 void
303 lqr_Dump(const char *link, const char *message, const struct lqrdata *lqr)
304 {
305   if (log_IsKept(LogLQM)) {
306     log_Printf(LogLQM, "%s: %s:\n", link, message);
307     log_Printf(LogLQM, "  Magic:          %08x   LastOutLQRs:    %08x\n",
308 	      lqr->MagicNumber, lqr->LastOutLQRs);
309     log_Printf(LogLQM, "  LastOutPackets: %08x   LastOutOctets:  %08x\n",
310 	      lqr->LastOutPackets, lqr->LastOutOctets);
311     log_Printf(LogLQM, "  PeerInLQRs:     %08x   PeerInPackets:  %08x\n",
312 	      lqr->PeerInLQRs, lqr->PeerInPackets);
313     log_Printf(LogLQM, "  PeerInDiscards: %08x   PeerInErrors:   %08x\n",
314 	      lqr->PeerInDiscards, lqr->PeerInErrors);
315     log_Printf(LogLQM, "  PeerInOctets:   %08x   PeerOutLQRs:    %08x\n",
316 	      lqr->PeerInOctets, lqr->PeerOutLQRs);
317     log_Printf(LogLQM, "  PeerOutPackets: %08x   PeerOutOctets:  %08x\n",
318 	      lqr->PeerOutPackets, lqr->PeerOutOctets);
319   }
320 }
321