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.35 1999/05/14 09:36:06 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/param.h> 30 #include <sys/un.h> 31 32 #include <string.h> 33 #include <termios.h> 34 35 #include "layer.h" 36 #include "mbuf.h" 37 #include "log.h" 38 #include "defs.h" 39 #include "timer.h" 40 #include "fsm.h" 41 #include "acf.h" 42 #include "proto.h" 43 #include "lcp.h" 44 #include "lqr.h" 45 #include "hdlc.h" 46 #include "async.h" 47 #include "throughput.h" 48 #include "ccp.h" 49 #include "link.h" 50 #include "descriptor.h" 51 #include "physical.h" 52 #include "mp.h" 53 #include "chat.h" 54 #include "auth.h" 55 #include "chap.h" 56 #include "command.h" 57 #include "cbcp.h" 58 #include "datalink.h" 59 60 struct echolqr { 61 u_int32_t magic; 62 u_int32_t signature; 63 u_int32_t sequence; 64 }; 65 66 #define SIGNATURE 0x594e4f54 67 68 static void 69 SendEchoReq(struct lcp *lcp) 70 { 71 struct hdlc *hdlc = &link2physical(lcp->fsm.link)->hdlc; 72 struct echolqr echo; 73 74 echo.magic = htonl(lcp->want_magic); 75 echo.signature = htonl(SIGNATURE); 76 echo.sequence = htonl(hdlc->lqm.echo.seq_sent); 77 fsm_Output(&lcp->fsm, CODE_ECHOREQ, hdlc->lqm.echo.seq_sent++, 78 (u_char *)&echo, sizeof echo, MB_ECHOOUT); 79 } 80 81 struct mbuf * 82 lqr_RecvEcho(struct fsm *fp, struct mbuf *bp) 83 { 84 struct hdlc *hdlc = &link2physical(fp->link)->hdlc; 85 struct lcp *lcp = fsm2lcp(fp); 86 struct echolqr lqr; 87 88 if (mbuf_Length(bp) == sizeof lqr) { 89 bp = mbuf_Read(bp, &lqr, sizeof lqr); 90 lqr.magic = ntohl(lqr.magic); 91 lqr.signature = ntohl(lqr.signature); 92 lqr.sequence = ntohl(lqr.sequence); 93 94 /* Tolerate echo replies with either magic number */ 95 if (lqr.magic != 0 && lqr.magic != lcp->his_magic && 96 lqr.magic != lcp->want_magic) { 97 log_Printf(LogWARN, "%s: lqr_RecvEcho: Bad magic: expected 0x%08x," 98 " got 0x%08x\n", fp->link->name, lcp->his_magic, lqr.magic); 99 /* 100 * XXX: We should send a terminate request. But poor implementations may 101 * die as a result. 102 */ 103 } 104 if (lqr.signature == SIGNATURE) { 105 /* careful not to update lqm.echo.seq_recv with older values */ 106 if ((hdlc->lqm.echo.seq_recv > (u_int32_t)0 - 5 && lqr.sequence < 5) || 107 (hdlc->lqm.echo.seq_recv <= (u_int32_t)0 - 5 && 108 lqr.sequence > hdlc->lqm.echo.seq_recv)) 109 hdlc->lqm.echo.seq_recv = lqr.sequence; 110 } else 111 log_Printf(LogWARN, "lqr_RecvEcho: Got sig 0x%08lx, not 0x%08lx !\n", 112 (u_long)ntohl(lqr.signature), (u_long)SIGNATURE); 113 } else 114 log_Printf(LogWARN, "lqr_RecvEcho: Got packet size %d, expecting %ld !\n", 115 mbuf_Length(bp), (long)sizeof(struct echolqr)); 116 return bp; 117 } 118 119 void 120 lqr_ChangeOrder(struct lqrdata *src, struct lqrdata *dst) 121 { 122 u_int32_t *sp, *dp; 123 int n; 124 125 sp = (u_int32_t *) src; 126 dp = (u_int32_t *) dst; 127 for (n = 0; n < sizeof(struct lqrdata) / sizeof(u_int32_t); n++, sp++, dp++) 128 *dp = ntohl(*sp); 129 } 130 131 static void 132 SendLqrData(struct lcp *lcp) 133 { 134 struct mbuf *bp; 135 int extra; 136 137 extra = proto_WrapperOctets(lcp, PROTO_LQR) + 138 acf_WrapperOctets(lcp, PROTO_LQR); 139 bp = mbuf_Alloc(sizeof(struct lqrdata) + extra, MB_LQROUT); 140 bp->cnt -= extra; 141 bp->offset += extra; 142 link_PushPacket(lcp->fsm.link, bp, lcp->fsm.bundle, PRI_LINK, PROTO_LQR); 143 } 144 145 static void 146 SendLqrReport(void *v) 147 { 148 struct lcp *lcp = (struct lcp *)v; 149 struct physical *p = link2physical(lcp->fsm.link); 150 151 timer_Stop(&p->hdlc.lqm.timer); 152 153 if (p->hdlc.lqm.method & LQM_LQR) { 154 if (p->hdlc.lqm.lqr.resent > 5) { 155 /* XXX: Should implement LQM strategy */ 156 log_Printf(LogPHASE, "%s: ** Too many LQR packets lost **\n", 157 lcp->fsm.link->name); 158 log_Printf(LogLQM, "%s: Too many LQR packets lost\n", 159 lcp->fsm.link->name); 160 p->hdlc.lqm.method = 0; 161 datalink_Down(p->dl, CLOSE_NORMAL); 162 } else { 163 SendLqrData(lcp); 164 p->hdlc.lqm.lqr.resent++; 165 } 166 } else if (p->hdlc.lqm.method & LQM_ECHO) { 167 if ((p->hdlc.lqm.echo.seq_sent > 5 && 168 p->hdlc.lqm.echo.seq_sent - 5 > p->hdlc.lqm.echo.seq_recv) || 169 (p->hdlc.lqm.echo.seq_sent <= 5 && 170 p->hdlc.lqm.echo.seq_sent > p->hdlc.lqm.echo.seq_recv + 5)) { 171 log_Printf(LogPHASE, "%s: ** Too many ECHO LQR packets lost **\n", 172 lcp->fsm.link->name); 173 log_Printf(LogLQM, "%s: Too many ECHO LQR packets lost\n", 174 lcp->fsm.link->name); 175 p->hdlc.lqm.method = 0; 176 datalink_Down(p->dl, CLOSE_NORMAL); 177 } else 178 SendEchoReq(lcp); 179 } 180 if (p->hdlc.lqm.method && p->hdlc.lqm.timer.load) 181 timer_Start(&p->hdlc.lqm.timer); 182 } 183 184 struct mbuf * 185 lqr_Input(struct bundle *bundle, struct link *l, struct mbuf *bp) 186 { 187 struct physical *p = link2physical(l); 188 struct lcp *lcp = p->hdlc.lqm.owner; 189 int len; 190 191 if (p == NULL) { 192 log_Printf(LogERROR, "lqr_Input: Not a physical link - dropped\n"); 193 mbuf_Free(bp); 194 return NULL; 195 } 196 197 p->hdlc.lqm.lqr.SaveInLQRs++; 198 199 len = mbuf_Length(bp); 200 if (len != sizeof(struct lqrdata)) 201 log_Printf(LogWARN, "lqr_Input: Got packet size %d, expecting %ld !\n", 202 len, (long)sizeof(struct lqrdata)); 203 else if (!IsAccepted(l->lcp.cfg.lqr) && !(p->hdlc.lqm.method & LQM_LQR)) { 204 bp = mbuf_Contiguous(proto_Prepend(bp, PROTO_LQR, 0, 0)); 205 lcp_SendProtoRej(lcp, MBUF_CTOP(bp), bp->cnt); 206 } else { 207 struct lqrdata *lqr; 208 u_int32_t lastLQR; 209 210 bp = mbuf_Contiguous(bp); 211 lqr = (struct lqrdata *)MBUF_CTOP(bp); 212 if (ntohl(lqr->MagicNumber) != lcp->his_magic) 213 log_Printf(LogWARN, "lqr_Input: magic 0x%08lx is wrong," 214 " expecting 0x%08lx\n", 215 (u_long)ntohl(lqr->MagicNumber), (u_long)lcp->his_magic); 216 else { 217 /* 218 * Remember our PeerInLQRs, then convert byte order and save 219 */ 220 lastLQR = p->hdlc.lqm.lqr.peer.PeerInLQRs; 221 222 lqr_ChangeOrder(lqr, &p->hdlc.lqm.lqr.peer); 223 lqr_Dump(l->name, "Input", &p->hdlc.lqm.lqr.peer); 224 /* we have received an LQR from peer */ 225 p->hdlc.lqm.lqr.resent = 0; 226 227 /* 228 * Generate an LQR response if we're not running an LQR timer OR 229 * two successive LQR's PeerInLQRs are the same OR we're not going to 230 * send our next one before the peers max timeout. 231 */ 232 if (p->hdlc.lqm.timer.load == 0 || 233 !(p->hdlc.lqm.method & LQM_LQR) || 234 (lastLQR && lastLQR == p->hdlc.lqm.lqr.peer.PeerInLQRs) || 235 (p->hdlc.lqm.lqr.peer_timeout && 236 p->hdlc.lqm.timer.rest * 100 / SECTICKS > 237 p->hdlc.lqm.lqr.peer_timeout)) 238 SendLqrData(lcp); 239 } 240 } 241 mbuf_Free(bp); 242 return NULL; 243 } 244 245 /* 246 * When LCP is reached to opened state, We'll start LQM activity. 247 */ 248 249 static void 250 lqr_Setup(struct lcp *lcp) 251 { 252 struct physical *physical = link2physical(lcp->fsm.link); 253 254 physical->hdlc.lqm.lqr.resent = 0; 255 physical->hdlc.lqm.echo.seq_sent = 0; 256 physical->hdlc.lqm.echo.seq_recv = 0; 257 memset(&physical->hdlc.lqm.lqr.peer, '\0', 258 sizeof physical->hdlc.lqm.lqr.peer); 259 260 physical->hdlc.lqm.method = LQM_ECHO; 261 if (IsEnabled(lcp->cfg.lqr) && !REJECTED(lcp, TY_QUALPROTO)) 262 physical->hdlc.lqm.method |= LQM_LQR; 263 timer_Stop(&physical->hdlc.lqm.timer); 264 265 physical->hdlc.lqm.lqr.peer_timeout = lcp->his_lqrperiod; 266 if (lcp->his_lqrperiod) 267 log_Printf(LogLQM, "%s: Expecting LQR every %d.%02d secs\n", 268 physical->link.name, lcp->his_lqrperiod / 100, 269 lcp->his_lqrperiod % 100); 270 271 if (lcp->want_lqrperiod) { 272 log_Printf(LogLQM, "%s: Will send %s every %d.%02d secs\n", 273 physical->link.name, 274 physical->hdlc.lqm.method & LQM_LQR ? "LQR" : "ECHO LQR", 275 lcp->want_lqrperiod / 100, lcp->want_lqrperiod % 100); 276 physical->hdlc.lqm.timer.load = lcp->want_lqrperiod * SECTICKS / 100; 277 physical->hdlc.lqm.timer.func = SendLqrReport; 278 physical->hdlc.lqm.timer.name = "lqm"; 279 physical->hdlc.lqm.timer.arg = lcp; 280 } else { 281 physical->hdlc.lqm.timer.load = 0; 282 if (!lcp->his_lqrperiod) 283 log_Printf(LogLQM, "%s: LQR/ECHO LQR not negotiated\n", 284 physical->link.name); 285 } 286 } 287 288 void 289 lqr_Start(struct lcp *lcp) 290 { 291 struct physical *p = link2physical(lcp->fsm.link); 292 293 lqr_Setup(lcp); 294 if (p->hdlc.lqm.timer.load) 295 SendLqrReport(lcp); 296 } 297 298 void 299 lqr_reStart(struct lcp *lcp) 300 { 301 struct physical *p = link2physical(lcp->fsm.link); 302 303 lqr_Setup(lcp); 304 if (p->hdlc.lqm.timer.load) 305 timer_Start(&p->hdlc.lqm.timer); 306 } 307 308 void 309 lqr_StopTimer(struct physical *physical) 310 { 311 timer_Stop(&physical->hdlc.lqm.timer); 312 } 313 314 void 315 lqr_Stop(struct physical *physical, int method) 316 { 317 if (method == LQM_LQR) 318 log_Printf(LogLQM, "%s: Stop sending LQR, Use LCP ECHO instead.\n", 319 physical->link.name); 320 if (method == LQM_ECHO) 321 log_Printf(LogLQM, "%s: Stop sending LCP ECHO.\n", 322 physical->link.name); 323 physical->hdlc.lqm.method &= ~method; 324 if (physical->hdlc.lqm.method) 325 SendLqrReport(physical->hdlc.lqm.owner); 326 else 327 timer_Stop(&physical->hdlc.lqm.timer); 328 } 329 330 void 331 lqr_Dump(const char *link, const char *message, const struct lqrdata *lqr) 332 { 333 if (log_IsKept(LogLQM)) { 334 log_Printf(LogLQM, "%s: %s:\n", link, message); 335 log_Printf(LogLQM, " Magic: %08x LastOutLQRs: %08x\n", 336 lqr->MagicNumber, lqr->LastOutLQRs); 337 log_Printf(LogLQM, " LastOutPackets: %08x LastOutOctets: %08x\n", 338 lqr->LastOutPackets, lqr->LastOutOctets); 339 log_Printf(LogLQM, " PeerInLQRs: %08x PeerInPackets: %08x\n", 340 lqr->PeerInLQRs, lqr->PeerInPackets); 341 log_Printf(LogLQM, " PeerInDiscards: %08x PeerInErrors: %08x\n", 342 lqr->PeerInDiscards, lqr->PeerInErrors); 343 log_Printf(LogLQM, " PeerInOctets: %08x PeerOutLQRs: %08x\n", 344 lqr->PeerInOctets, lqr->PeerOutLQRs); 345 log_Printf(LogLQM, " PeerOutPackets: %08x PeerOutOctets: %08x\n", 346 lqr->PeerOutPackets, lqr->PeerOutOctets); 347 } 348 } 349 350 static struct mbuf * 351 lqr_LayerPush(struct bundle *b, struct link *l, struct mbuf *bp, 352 int pri, u_short *proto) 353 { 354 struct physical *p = link2physical(l); 355 int len; 356 357 if (!p) { 358 /* Oops - can't happen :-] */ 359 mbuf_Free(bp); 360 return NULL; 361 } 362 363 /* 364 * From rfc1989: 365 * 366 * All octets which are included in the FCS calculation MUST be counted, 367 * including the packet header, the information field, and any padding. 368 * The FCS octets MUST also be counted, and one flag octet per frame 369 * MUST be counted. All other octets (such as additional flag 370 * sequences, and escape bits or octets) MUST NOT be counted. 371 * 372 * As we're stacked before the HDLC layer (otherwise HDLC wouldn't be 373 * able to calculate the FCS), we must not forget about these additional 374 * bytes when we're asynchronous. 375 * 376 * We're also expecting to be stacked *before* the proto and acf layers. 377 * If we were after these, it makes alignment more of a pain, and we 378 * don't do LQR without these layers. 379 */ 380 381 bp = mbuf_Contiguous(bp); 382 len = mbuf_Length(bp); 383 384 if (!physical_IsSync(p)) 385 p->hdlc.lqm.OutOctets += hdlc_WrapperOctets(&l->lcp, *proto); 386 p->hdlc.lqm.OutOctets += acf_WrapperOctets(&l->lcp, *proto) + 387 proto_WrapperOctets(&l->lcp, *proto) + len + 1; 388 p->hdlc.lqm.OutPackets++; 389 390 if (*proto == PROTO_LQR) { 391 /* Overwrite the entire packet (created in SendLqrData()) */ 392 struct lqrdata lqr; 393 394 lqr.MagicNumber = p->link.lcp.want_magic; 395 lqr.LastOutLQRs = p->hdlc.lqm.lqr.peer.PeerOutLQRs; 396 lqr.LastOutPackets = p->hdlc.lqm.lqr.peer.PeerOutPackets; 397 lqr.LastOutOctets = p->hdlc.lqm.lqr.peer.PeerOutOctets; 398 lqr.PeerInLQRs = p->hdlc.lqm.lqr.SaveInLQRs; 399 lqr.PeerInPackets = p->hdlc.lqm.SaveInPackets; 400 lqr.PeerInDiscards = p->hdlc.lqm.SaveInDiscards; 401 lqr.PeerInErrors = p->hdlc.lqm.SaveInErrors; 402 lqr.PeerInOctets = p->hdlc.lqm.SaveInOctets; 403 lqr.PeerOutPackets = p->hdlc.lqm.OutPackets; 404 lqr.PeerOutOctets = p->hdlc.lqm.OutOctets; 405 if (p->hdlc.lqm.lqr.peer.LastOutLQRs == p->hdlc.lqm.lqr.OutLQRs) { 406 /* 407 * only increment if it's the first time or we've got a reply 408 * from the last one 409 */ 410 lqr.PeerOutLQRs = ++p->hdlc.lqm.lqr.OutLQRs; 411 lqr_Dump(l->name, "Output", &lqr); 412 } else { 413 lqr.PeerOutLQRs = p->hdlc.lqm.lqr.OutLQRs; 414 lqr_Dump(l->name, "Output (again)", &lqr); 415 } 416 lqr_ChangeOrder(&lqr, (struct lqrdata *)MBUF_CTOP(bp)); 417 } 418 419 return bp; 420 } 421 422 static struct mbuf * 423 lqr_LayerPull(struct bundle *b, struct link *l, struct mbuf *bp, u_short *proto) 424 { 425 /* 426 * We mark the packet as ours but don't do anything 'till it's dispatched 427 * to lqr_Input() 428 */ 429 if (*proto == PROTO_LQR) 430 mbuf_SetType(bp, MB_LQRIN); 431 return bp; 432 } 433 434 /* 435 * Statistics for pulled packets are recorded either in hdlc_PullPacket() 436 * or sync_PullPacket() 437 */ 438 439 struct layer lqrlayer = { LAYER_LQR, "lqr", lqr_LayerPush, lqr_LayerPull }; 440