| File: | src/usr.sbin/pppd/fsm.c |
| Warning: | line 809, column 2 Null pointer passed as 2nd argument to memory copy function |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: fsm.c,v 1.8 2009/10/27 23:59:53 deraadt Exp $ */ | |||
| 2 | ||||
| 3 | /* | |||
| 4 | * fsm.c - {Link, IP} Control Protocol Finite State Machine. | |||
| 5 | * | |||
| 6 | * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. | |||
| 7 | * | |||
| 8 | * Redistribution and use in source and binary forms, with or without | |||
| 9 | * modification, are permitted provided that the following conditions | |||
| 10 | * are met: | |||
| 11 | * | |||
| 12 | * 1. Redistributions of source code must retain the above copyright | |||
| 13 | * notice, this list of conditions and the following disclaimer. | |||
| 14 | * | |||
| 15 | * 2. Redistributions in binary form must reproduce the above copyright | |||
| 16 | * notice, this list of conditions and the following disclaimer in | |||
| 17 | * the documentation and/or other materials provided with the | |||
| 18 | * distribution. | |||
| 19 | * | |||
| 20 | * 3. The name "Carnegie Mellon University" must not be used to | |||
| 21 | * endorse or promote products derived from this software without | |||
| 22 | * prior written permission. For permission or any legal | |||
| 23 | * details, please contact | |||
| 24 | * Office of Technology Transfer | |||
| 25 | * Carnegie Mellon University | |||
| 26 | * 5000 Forbes Avenue | |||
| 27 | * Pittsburgh, PA 15213-3890 | |||
| 28 | * (412) 268-4387, fax: (412) 268-7395 | |||
| 29 | * tech-transfer@andrew.cmu.edu | |||
| 30 | * | |||
| 31 | * 4. Redistributions of any form whatsoever must retain the following | |||
| 32 | * acknowledgment: | |||
| 33 | * "This product includes software developed by Computing Services | |||
| 34 | * at Carnegie Mellon University (http://www.cmu.edu/computing/)." | |||
| 35 | * | |||
| 36 | * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO | |||
| 37 | * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | |||
| 38 | * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE | |||
| 39 | * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| 40 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN | |||
| 41 | * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | |||
| 42 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| 43 | */ | |||
| 44 | ||||
| 45 | /* | |||
| 46 | * TODO: | |||
| 47 | * Randomize fsm id on link/init. | |||
| 48 | * Deal with variable outgoing MTU. | |||
| 49 | */ | |||
| 50 | ||||
| 51 | #include <stdio.h> | |||
| 52 | #include <string.h> | |||
| 53 | #include <sys/types.h> | |||
| 54 | #include <syslog.h> | |||
| 55 | ||||
| 56 | #include "pppd.h" | |||
| 57 | #include "fsm.h" | |||
| 58 | ||||
| 59 | static void fsm_timeout(void *); | |||
| 60 | static void fsm_rconfreq(fsm *, int, u_char *, int); | |||
| 61 | static void fsm_rconfack(fsm *, int, u_char *, int); | |||
| 62 | static void fsm_rconfnakrej(fsm *, int, int, u_char *, int); | |||
| 63 | static void fsm_rtermreq(fsm *, int, u_char *, int); | |||
| 64 | static void fsm_rtermack(fsm *); | |||
| 65 | static void fsm_rcoderej(fsm *, u_char *, int); | |||
| 66 | static void fsm_sconfreq(fsm *, int); | |||
| 67 | ||||
| 68 | #define PROTO_NAME(f)((f)->callbacks->proto_name) ((f)->callbacks->proto_name) | |||
| 69 | ||||
| 70 | int peer_mru[NUM_PPP1]; | |||
| 71 | ||||
| 72 | ||||
| 73 | /* | |||
| 74 | * fsm_init - Initialize fsm. | |||
| 75 | * | |||
| 76 | * Initialize fsm state. | |||
| 77 | */ | |||
| 78 | void | |||
| 79 | fsm_init(f) | |||
| 80 | fsm *f; | |||
| 81 | { | |||
| 82 | f->state = INITIAL0; | |||
| 83 | f->flags = 0; | |||
| 84 | f->id = 0; /* XXX Start with random id? */ | |||
| 85 | f->timeouttime = DEFTIMEOUT3; | |||
| 86 | f->maxconfreqtransmits = DEFMAXCONFREQS10; | |||
| 87 | f->maxtermtransmits = DEFMAXTERMREQS2; | |||
| 88 | f->maxnakloops = DEFMAXNAKLOOPS5; | |||
| 89 | f->term_reason_len = 0; | |||
| 90 | } | |||
| 91 | ||||
| 92 | ||||
| 93 | /* | |||
| 94 | * fsm_lowerup - The lower layer is up. | |||
| 95 | */ | |||
| 96 | void | |||
| 97 | fsm_lowerup(f) | |||
| 98 | fsm *f; | |||
| 99 | { | |||
| 100 | switch( f->state ){ | |||
| 101 | case INITIAL0: | |||
| 102 | f->state = CLOSED2; | |||
| 103 | break; | |||
| 104 | ||||
| 105 | case STARTING1: | |||
| 106 | if( f->flags & OPT_SILENT4 ) | |||
| 107 | f->state = STOPPED3; | |||
| 108 | else { | |||
| 109 | /* Send an initial configure-request */ | |||
| 110 | fsm_sconfreq(f, 0); | |||
| 111 | f->state = REQSENT6; | |||
| 112 | } | |||
| 113 | break; | |||
| 114 | ||||
| 115 | default: | |||
| 116 | FSMDEBUG((LOG_INFO, "%s: Up event in state %d!", | |||
| 117 | PROTO_NAME(f), f->state)); | |||
| 118 | } | |||
| 119 | } | |||
| 120 | ||||
| 121 | ||||
| 122 | /* | |||
| 123 | * fsm_lowerdown - The lower layer is down. | |||
| 124 | * | |||
| 125 | * Cancel all timeouts and inform upper layers. | |||
| 126 | */ | |||
| 127 | void | |||
| 128 | fsm_lowerdown(f) | |||
| 129 | fsm *f; | |||
| 130 | { | |||
| 131 | switch( f->state ){ | |||
| 132 | case CLOSED2: | |||
| 133 | f->state = INITIAL0; | |||
| 134 | break; | |||
| 135 | ||||
| 136 | case STOPPED3: | |||
| 137 | f->state = STARTING1; | |||
| 138 | if( f->callbacks->starting ) | |||
| 139 | (*f->callbacks->starting)(f); | |||
| 140 | break; | |||
| 141 | ||||
| 142 | case CLOSING4: | |||
| 143 | f->state = INITIAL0; | |||
| 144 | UNTIMEOUT(fsm_timeout, f)untimeout((fsm_timeout), (f)); /* Cancel timeout */ | |||
| 145 | break; | |||
| 146 | ||||
| 147 | case STOPPING5: | |||
| 148 | case REQSENT6: | |||
| 149 | case ACKRCVD7: | |||
| 150 | case ACKSENT8: | |||
| 151 | f->state = STARTING1; | |||
| 152 | UNTIMEOUT(fsm_timeout, f)untimeout((fsm_timeout), (f)); /* Cancel timeout */ | |||
| 153 | break; | |||
| 154 | ||||
| 155 | case OPENED9: | |||
| 156 | if( f->callbacks->down ) | |||
| 157 | (*f->callbacks->down)(f); | |||
| 158 | f->state = STARTING1; | |||
| 159 | break; | |||
| 160 | ||||
| 161 | default: | |||
| 162 | FSMDEBUG((LOG_INFO, "%s: Down event in state %d!", | |||
| 163 | PROTO_NAME(f), f->state)); | |||
| 164 | } | |||
| 165 | } | |||
| 166 | ||||
| 167 | ||||
| 168 | /* | |||
| 169 | * fsm_open - Link is allowed to come up. | |||
| 170 | */ | |||
| 171 | void | |||
| 172 | fsm_open(f) | |||
| 173 | fsm *f; | |||
| 174 | { | |||
| 175 | switch( f->state ){ | |||
| 176 | case INITIAL0: | |||
| 177 | f->state = STARTING1; | |||
| 178 | if( f->callbacks->starting ) | |||
| 179 | (*f->callbacks->starting)(f); | |||
| 180 | break; | |||
| 181 | ||||
| 182 | case CLOSED2: | |||
| 183 | if( f->flags & OPT_SILENT4 ) | |||
| 184 | f->state = STOPPED3; | |||
| 185 | else { | |||
| 186 | /* Send an initial configure-request */ | |||
| 187 | fsm_sconfreq(f, 0); | |||
| 188 | f->state = REQSENT6; | |||
| 189 | } | |||
| 190 | break; | |||
| 191 | ||||
| 192 | case CLOSING4: | |||
| 193 | f->state = STOPPING5; | |||
| 194 | /* fall through */ | |||
| 195 | case STOPPED3: | |||
| 196 | case OPENED9: | |||
| 197 | if( f->flags & OPT_RESTART2 ){ | |||
| 198 | fsm_lowerdown(f); | |||
| 199 | fsm_lowerup(f); | |||
| 200 | } | |||
| 201 | break; | |||
| 202 | } | |||
| 203 | } | |||
| 204 | ||||
| 205 | ||||
| 206 | /* | |||
| 207 | * fsm_close - Start closing connection. | |||
| 208 | * | |||
| 209 | * Cancel timeouts and either initiate close or possibly go directly to | |||
| 210 | * the CLOSED state. | |||
| 211 | */ | |||
| 212 | void | |||
| 213 | fsm_close(f, reason) | |||
| 214 | fsm *f; | |||
| 215 | char *reason; | |||
| 216 | { | |||
| 217 | f->term_reason = reason; | |||
| ||||
| 218 | f->term_reason_len = (reason == NULL((void *)0)? 0: strlen(reason)); | |||
| 219 | switch( f->state ){ | |||
| 220 | case STARTING1: | |||
| 221 | f->state = INITIAL0; | |||
| 222 | break; | |||
| 223 | case STOPPED3: | |||
| 224 | f->state = CLOSED2; | |||
| 225 | break; | |||
| 226 | case STOPPING5: | |||
| 227 | f->state = CLOSING4; | |||
| 228 | break; | |||
| 229 | ||||
| 230 | case REQSENT6: | |||
| 231 | case ACKRCVD7: | |||
| 232 | case ACKSENT8: | |||
| 233 | case OPENED9: | |||
| 234 | if( f->state
| |||
| 235 | UNTIMEOUT(fsm_timeout, f)untimeout((fsm_timeout), (f)); /* Cancel timeout */ | |||
| 236 | else if( f->callbacks->down ) | |||
| 237 | (*f->callbacks->down)(f); /* Inform upper layers we're down */ | |||
| 238 | ||||
| 239 | /* Init restart counter, send Terminate-Request */ | |||
| 240 | f->retransmits = f->maxtermtransmits; | |||
| 241 | fsm_sdata(f, TERMREQ5, f->reqid = ++f->id, | |||
| 242 | (u_char *) f->term_reason, f->term_reason_len); | |||
| 243 | TIMEOUT(fsm_timeout, f, f->timeouttime)timeout((fsm_timeout), (f), (f->timeouttime)); | |||
| 244 | --f->retransmits; | |||
| 245 | ||||
| 246 | f->state = CLOSING4; | |||
| 247 | break; | |||
| 248 | } | |||
| 249 | } | |||
| 250 | ||||
| 251 | ||||
| 252 | /* | |||
| 253 | * fsm_timeout - Timeout expired. | |||
| 254 | */ | |||
| 255 | static void | |||
| 256 | fsm_timeout(arg) | |||
| 257 | void *arg; | |||
| 258 | { | |||
| 259 | fsm *f = (fsm *) arg; | |||
| 260 | ||||
| 261 | switch (f->state) { | |||
| 262 | case CLOSING4: | |||
| 263 | case STOPPING5: | |||
| 264 | if( f->retransmits <= 0 ){ | |||
| 265 | /* | |||
| 266 | * We've waited for an ack long enough. Peer probably heard us. | |||
| 267 | */ | |||
| 268 | f->state = (f->state == CLOSING4)? CLOSED2: STOPPED3; | |||
| 269 | if( f->callbacks->finished ) | |||
| 270 | (*f->callbacks->finished)(f); | |||
| 271 | } else { | |||
| 272 | /* Send Terminate-Request */ | |||
| 273 | fsm_sdata(f, TERMREQ5, f->reqid = ++f->id, | |||
| 274 | (u_char *) f->term_reason, f->term_reason_len); | |||
| 275 | TIMEOUT(fsm_timeout, f, f->timeouttime)timeout((fsm_timeout), (f), (f->timeouttime)); | |||
| 276 | --f->retransmits; | |||
| 277 | } | |||
| 278 | break; | |||
| 279 | ||||
| 280 | case REQSENT6: | |||
| 281 | case ACKRCVD7: | |||
| 282 | case ACKSENT8: | |||
| 283 | if (f->retransmits <= 0) { | |||
| 284 | syslog(LOG_WARNING4, "%s: timeout sending Config-Requests", | |||
| 285 | PROTO_NAME(f)((f)->callbacks->proto_name)); | |||
| 286 | f->state = STOPPED3; | |||
| 287 | if( (f->flags & OPT_PASSIVE1) == 0 && f->callbacks->finished ) | |||
| 288 | (*f->callbacks->finished)(f); | |||
| 289 | ||||
| 290 | } else { | |||
| 291 | /* Retransmit the configure-request */ | |||
| 292 | if (f->callbacks->retransmit) | |||
| 293 | (*f->callbacks->retransmit)(f); | |||
| 294 | fsm_sconfreq(f, 1); /* Re-send Configure-Request */ | |||
| 295 | if( f->state == ACKRCVD7 ) | |||
| 296 | f->state = REQSENT6; | |||
| 297 | } | |||
| 298 | break; | |||
| 299 | ||||
| 300 | default: | |||
| 301 | FSMDEBUG((LOG_INFO, "%s: Timeout event in state %d!", | |||
| 302 | PROTO_NAME(f), f->state)); | |||
| 303 | } | |||
| 304 | } | |||
| 305 | ||||
| 306 | ||||
| 307 | /* | |||
| 308 | * fsm_input - Input packet. | |||
| 309 | */ | |||
| 310 | void | |||
| 311 | fsm_input(f, inpacket, l) | |||
| 312 | fsm *f; | |||
| 313 | u_char *inpacket; | |||
| 314 | int l; | |||
| 315 | { | |||
| 316 | u_char *inp; | |||
| 317 | u_char code, id; | |||
| 318 | int len; | |||
| 319 | ||||
| 320 | /* | |||
| 321 | * Parse header (code, id and length). | |||
| 322 | * If packet too short, drop it. | |||
| 323 | */ | |||
| 324 | inp = inpacket; | |||
| 325 | if (l < HEADERLEN(sizeof (u_char) + sizeof (u_char) + sizeof (u_short))) { | |||
| 326 | FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.", | |||
| 327 | f->protocol)); | |||
| 328 | return; | |||
| 329 | } | |||
| 330 | GETCHAR(code, inp){ (code) = *(inp)++; }; | |||
| 331 | GETCHAR(id, inp){ (id) = *(inp)++; }; | |||
| 332 | GETSHORT(len, inp){ (len) = *(inp)++ << 8; (len) |= *(inp)++; }; | |||
| 333 | if (len < HEADERLEN(sizeof (u_char) + sizeof (u_char) + sizeof (u_short))) { | |||
| 334 | FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.", | |||
| 335 | f->protocol)); | |||
| 336 | return; | |||
| 337 | } | |||
| 338 | if (len > l) { | |||
| 339 | FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.", | |||
| 340 | f->protocol)); | |||
| 341 | return; | |||
| 342 | } | |||
| 343 | len -= HEADERLEN(sizeof (u_char) + sizeof (u_char) + sizeof (u_short)); /* subtract header length */ | |||
| 344 | ||||
| 345 | if( f->state == INITIAL0 || f->state == STARTING1 ){ | |||
| 346 | FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d.", | |||
| 347 | f->protocol, f->state)); | |||
| 348 | return; | |||
| 349 | } | |||
| 350 | ||||
| 351 | /* | |||
| 352 | * Action depends on code. | |||
| 353 | */ | |||
| 354 | switch (code) { | |||
| 355 | case CONFREQ1: | |||
| 356 | fsm_rconfreq(f, id, inp, len); | |||
| 357 | break; | |||
| 358 | ||||
| 359 | case CONFACK2: | |||
| 360 | fsm_rconfack(f, id, inp, len); | |||
| 361 | break; | |||
| 362 | ||||
| 363 | case CONFNAK3: | |||
| 364 | case CONFREJ4: | |||
| 365 | fsm_rconfnakrej(f, code, id, inp, len); | |||
| 366 | break; | |||
| 367 | ||||
| 368 | case TERMREQ5: | |||
| 369 | fsm_rtermreq(f, id, inp, len); | |||
| 370 | break; | |||
| 371 | ||||
| 372 | case TERMACK6: | |||
| 373 | fsm_rtermack(f); | |||
| 374 | break; | |||
| 375 | ||||
| 376 | case CODEREJ7: | |||
| 377 | fsm_rcoderej(f, inp, len); | |||
| 378 | break; | |||
| 379 | ||||
| 380 | default: | |||
| 381 | if( !f->callbacks->extcode | |||
| 382 | || !(*f->callbacks->extcode)(f, code, id, inp, len) ) | |||
| 383 | fsm_sdata(f, CODEREJ7, ++f->id, inpacket, len + HEADERLEN(sizeof (u_char) + sizeof (u_char) + sizeof (u_short))); | |||
| 384 | break; | |||
| 385 | } | |||
| 386 | } | |||
| 387 | ||||
| 388 | ||||
| 389 | /* | |||
| 390 | * fsm_rconfreq - Receive Configure-Request. | |||
| 391 | */ | |||
| 392 | static void | |||
| 393 | fsm_rconfreq(f, id, inp, len) | |||
| 394 | fsm *f; | |||
| 395 | u_char id; | |||
| 396 | u_char *inp; | |||
| 397 | int len; | |||
| 398 | { | |||
| 399 | int code, reject_if_disagree; | |||
| 400 | ||||
| 401 | FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d.", PROTO_NAME(f), id)); | |||
| 402 | switch( f->state ){ | |||
| 403 | case CLOSED2: | |||
| 404 | /* Go away, we're closed */ | |||
| 405 | fsm_sdata(f, TERMACK6, id, NULL((void *)0), 0); | |||
| 406 | return; | |||
| 407 | case CLOSING4: | |||
| 408 | case STOPPING5: | |||
| 409 | return; | |||
| 410 | ||||
| 411 | case OPENED9: | |||
| 412 | /* Go down and restart negotiation */ | |||
| 413 | if( f->callbacks->down ) | |||
| 414 | (*f->callbacks->down)(f); /* Inform upper layers */ | |||
| 415 | fsm_sconfreq(f, 0); /* Send initial Configure-Request */ | |||
| 416 | break; | |||
| 417 | ||||
| 418 | case STOPPED3: | |||
| 419 | /* Negotiation started by our peer */ | |||
| 420 | fsm_sconfreq(f, 0); /* Send initial Configure-Request */ | |||
| 421 | f->state = REQSENT6; | |||
| 422 | break; | |||
| 423 | } | |||
| 424 | ||||
| 425 | /* | |||
| 426 | * Pass the requested configuration options | |||
| 427 | * to protocol-specific code for checking. | |||
| 428 | */ | |||
| 429 | if (f->callbacks->reqci){ /* Check CI */ | |||
| 430 | reject_if_disagree = (f->nakloops >= f->maxnakloops); | |||
| 431 | code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); | |||
| 432 | } else if (len) | |||
| 433 | code = CONFREJ4; /* Reject all CI */ | |||
| 434 | else | |||
| 435 | code = CONFACK2; | |||
| 436 | ||||
| 437 | /* send the Ack, Nak or Rej to the peer */ | |||
| 438 | fsm_sdata(f, code, id, inp, len); | |||
| 439 | ||||
| 440 | if (code == CONFACK2) { | |||
| 441 | if (f->state == ACKRCVD7) { | |||
| 442 | UNTIMEOUT(fsm_timeout, f)untimeout((fsm_timeout), (f)); /* Cancel timeout */ | |||
| 443 | f->state = OPENED9; | |||
| 444 | if (f->callbacks->up) | |||
| 445 | (*f->callbacks->up)(f); /* Inform upper layers */ | |||
| 446 | } else | |||
| 447 | f->state = ACKSENT8; | |||
| 448 | f->nakloops = 0; | |||
| 449 | ||||
| 450 | } else { | |||
| 451 | /* we sent CONFACK or CONFREJ */ | |||
| 452 | if (f->state != ACKRCVD7) | |||
| 453 | f->state = REQSENT6; | |||
| 454 | if( code == CONFNAK3 ) | |||
| 455 | ++f->nakloops; | |||
| 456 | } | |||
| 457 | } | |||
| 458 | ||||
| 459 | ||||
| 460 | /* | |||
| 461 | * fsm_rconfack - Receive Configure-Ack. | |||
| 462 | */ | |||
| 463 | static void | |||
| 464 | fsm_rconfack(f, id, inp, len) | |||
| 465 | fsm *f; | |||
| 466 | int id; | |||
| 467 | u_char *inp; | |||
| 468 | int len; | |||
| 469 | { | |||
| 470 | FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d.", | |||
| 471 | PROTO_NAME(f), id)); | |||
| 472 | ||||
| 473 | if (id != f->reqid || f->seen_ack) /* Expected id? */ | |||
| 474 | return; /* Nope, toss... */ | |||
| 475 | if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): | |||
| 476 | (len == 0)) ){ | |||
| 477 | /* Ack is bad - ignore it */ | |||
| 478 | log_packet(inp, len, "Received bad configure-ack: ", LOG_ERR3); | |||
| 479 | FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)", | |||
| 480 | PROTO_NAME(f), len)); | |||
| 481 | return; | |||
| 482 | } | |||
| 483 | f->seen_ack = 1; | |||
| 484 | ||||
| 485 | switch (f->state) { | |||
| 486 | case CLOSED2: | |||
| 487 | case STOPPED3: | |||
| 488 | fsm_sdata(f, TERMACK6, id, NULL((void *)0), 0); | |||
| 489 | break; | |||
| 490 | ||||
| 491 | case REQSENT6: | |||
| 492 | f->state = ACKRCVD7; | |||
| 493 | f->retransmits = f->maxconfreqtransmits; | |||
| 494 | break; | |||
| 495 | ||||
| 496 | case ACKRCVD7: | |||
| 497 | /* Huh? an extra valid Ack? oh well... */ | |||
| 498 | UNTIMEOUT(fsm_timeout, f)untimeout((fsm_timeout), (f)); /* Cancel timeout */ | |||
| 499 | fsm_sconfreq(f, 0); | |||
| 500 | f->state = REQSENT6; | |||
| 501 | break; | |||
| 502 | ||||
| 503 | case ACKSENT8: | |||
| 504 | UNTIMEOUT(fsm_timeout, f)untimeout((fsm_timeout), (f)); /* Cancel timeout */ | |||
| 505 | f->state = OPENED9; | |||
| 506 | f->retransmits = f->maxconfreqtransmits; | |||
| 507 | if (f->callbacks->up) | |||
| 508 | (*f->callbacks->up)(f); /* Inform upper layers */ | |||
| 509 | break; | |||
| 510 | ||||
| 511 | case OPENED9: | |||
| 512 | /* Go down and restart negotiation */ | |||
| 513 | if (f->callbacks->down) | |||
| 514 | (*f->callbacks->down)(f); /* Inform upper layers */ | |||
| 515 | fsm_sconfreq(f, 0); /* Send initial Configure-Request */ | |||
| 516 | f->state = REQSENT6; | |||
| 517 | break; | |||
| 518 | } | |||
| 519 | } | |||
| 520 | ||||
| 521 | ||||
| 522 | /* | |||
| 523 | * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. | |||
| 524 | */ | |||
| 525 | static void | |||
| 526 | fsm_rconfnakrej(f, code, id, inp, len) | |||
| 527 | fsm *f; | |||
| 528 | int code, id; | |||
| 529 | u_char *inp; | |||
| 530 | int len; | |||
| 531 | { | |||
| 532 | int (*proc)(fsm *, u_char *, int); | |||
| 533 | int ret; | |||
| 534 | ||||
| 535 | FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d.", | |||
| 536 | PROTO_NAME(f), id)); | |||
| 537 | ||||
| 538 | if (id != f->reqid || f->seen_ack) /* Expected id? */ | |||
| 539 | return; /* Nope, toss... */ | |||
| 540 | proc = (code == CONFNAK3)? f->callbacks->nakci: f->callbacks->rejci; | |||
| 541 | if (!proc || !(ret = proc(f, inp, len))) { | |||
| 542 | /* Nak/reject is bad - ignore it */ | |||
| 543 | log_packet(inp, len, "Received bad configure-nak/rej: ", LOG_ERR3); | |||
| 544 | FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)", | |||
| 545 | PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len)); | |||
| 546 | return; | |||
| 547 | } | |||
| 548 | f->seen_ack = 1; | |||
| 549 | ||||
| 550 | switch (f->state) { | |||
| 551 | case CLOSED2: | |||
| 552 | case STOPPED3: | |||
| 553 | fsm_sdata(f, TERMACK6, id, NULL((void *)0), 0); | |||
| 554 | break; | |||
| 555 | ||||
| 556 | case REQSENT6: | |||
| 557 | case ACKSENT8: | |||
| 558 | /* They didn't agree to what we wanted - try another request */ | |||
| 559 | UNTIMEOUT(fsm_timeout, f)untimeout((fsm_timeout), (f)); /* Cancel timeout */ | |||
| 560 | if (ret < 0) | |||
| 561 | f->state = STOPPED3; /* kludge for stopping CCP */ | |||
| 562 | else | |||
| 563 | fsm_sconfreq(f, 0); /* Send Configure-Request */ | |||
| 564 | break; | |||
| 565 | ||||
| 566 | case ACKRCVD7: | |||
| 567 | /* Got a Nak/reject when we had already had an Ack?? oh well... */ | |||
| 568 | UNTIMEOUT(fsm_timeout, f)untimeout((fsm_timeout), (f)); /* Cancel timeout */ | |||
| 569 | fsm_sconfreq(f, 0); | |||
| 570 | f->state = REQSENT6; | |||
| 571 | break; | |||
| 572 | ||||
| 573 | case OPENED9: | |||
| 574 | /* Go down and restart negotiation */ | |||
| 575 | if (f->callbacks->down) | |||
| 576 | (*f->callbacks->down)(f); /* Inform upper layers */ | |||
| 577 | fsm_sconfreq(f, 0); /* Send initial Configure-Request */ | |||
| 578 | f->state = REQSENT6; | |||
| 579 | break; | |||
| 580 | } | |||
| 581 | } | |||
| 582 | ||||
| 583 | ||||
| 584 | /* | |||
| 585 | * fsm_rtermreq - Receive Terminate-Req. | |||
| 586 | */ | |||
| 587 | static void | |||
| 588 | fsm_rtermreq(f, id, p, len) | |||
| 589 | fsm *f; | |||
| 590 | int id; | |||
| 591 | u_char *p; | |||
| 592 | int len; | |||
| 593 | { | |||
| 594 | char str[80]; | |||
| 595 | ||||
| 596 | FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d.", | |||
| 597 | PROTO_NAME(f), id)); | |||
| 598 | ||||
| 599 | switch (f->state) { | |||
| 600 | case ACKRCVD7: | |||
| 601 | case ACKSENT8: | |||
| 602 | f->state = REQSENT6; /* Start over but keep trying */ | |||
| 603 | break; | |||
| 604 | ||||
| 605 | case OPENED9: | |||
| 606 | if (len > 0) { | |||
| 607 | fmtmsg(str, sizeof(str), "%0.*v", len, p); | |||
| 608 | syslog(LOG_INFO6, "%s terminated by peer (%s)", PROTO_NAME(f)((f)->callbacks->proto_name), str); | |||
| 609 | } else | |||
| 610 | syslog(LOG_INFO6, "%s terminated by peer", PROTO_NAME(f)((f)->callbacks->proto_name)); | |||
| 611 | if (f->callbacks->down) | |||
| 612 | (*f->callbacks->down)(f); /* Inform upper layers */ | |||
| 613 | f->retransmits = 0; | |||
| 614 | f->state = STOPPING5; | |||
| 615 | TIMEOUT(fsm_timeout, f, f->timeouttime)timeout((fsm_timeout), (f), (f->timeouttime)); | |||
| 616 | break; | |||
| 617 | } | |||
| 618 | ||||
| 619 | fsm_sdata(f, TERMACK6, id, NULL((void *)0), 0); | |||
| 620 | } | |||
| 621 | ||||
| 622 | ||||
| 623 | /* | |||
| 624 | * fsm_rtermack - Receive Terminate-Ack. | |||
| 625 | */ | |||
| 626 | static void | |||
| 627 | fsm_rtermack(f) | |||
| 628 | fsm *f; | |||
| 629 | { | |||
| 630 | FSMDEBUG((LOG_INFO, "fsm_rtermack(%s).", PROTO_NAME(f))); | |||
| 631 | ||||
| 632 | switch (f->state) { | |||
| 633 | case CLOSING4: | |||
| 634 | UNTIMEOUT(fsm_timeout, f)untimeout((fsm_timeout), (f)); | |||
| 635 | f->state = CLOSED2; | |||
| 636 | if( f->callbacks->finished ) | |||
| 637 | (*f->callbacks->finished)(f); | |||
| 638 | break; | |||
| 639 | case STOPPING5: | |||
| 640 | UNTIMEOUT(fsm_timeout, f)untimeout((fsm_timeout), (f)); | |||
| 641 | f->state = STOPPED3; | |||
| 642 | if( f->callbacks->finished ) | |||
| 643 | (*f->callbacks->finished)(f); | |||
| 644 | break; | |||
| 645 | ||||
| 646 | case ACKRCVD7: | |||
| 647 | f->state = REQSENT6; | |||
| 648 | break; | |||
| 649 | ||||
| 650 | case OPENED9: | |||
| 651 | if (f->callbacks->down) | |||
| 652 | (*f->callbacks->down)(f); /* Inform upper layers */ | |||
| 653 | fsm_sconfreq(f, 0); | |||
| 654 | break; | |||
| 655 | } | |||
| 656 | } | |||
| 657 | ||||
| 658 | ||||
| 659 | /* | |||
| 660 | * fsm_rcoderej - Receive an Code-Reject. | |||
| 661 | */ | |||
| 662 | static void | |||
| 663 | fsm_rcoderej(f, inp, len) | |||
| 664 | fsm *f; | |||
| 665 | u_char *inp; | |||
| 666 | int len; | |||
| 667 | { | |||
| 668 | u_char code, id; | |||
| 669 | ||||
| 670 | FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s).", PROTO_NAME(f))); | |||
| 671 | ||||
| 672 | if (len < HEADERLEN(sizeof (u_char) + sizeof (u_char) + sizeof (u_short))) { | |||
| 673 | FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!")); | |||
| 674 | return; | |||
| 675 | } | |||
| 676 | GETCHAR(code, inp){ (code) = *(inp)++; }; | |||
| 677 | GETCHAR(id, inp){ (id) = *(inp)++; }; | |||
| 678 | syslog(LOG_WARNING4, "%s: Rcvd Code-Reject for code %d, id %d", | |||
| 679 | PROTO_NAME(f)((f)->callbacks->proto_name), code, id); | |||
| 680 | ||||
| 681 | if( f->state == ACKRCVD7 ) | |||
| 682 | f->state = REQSENT6; | |||
| 683 | } | |||
| 684 | ||||
| 685 | ||||
| 686 | /* | |||
| 687 | * fsm_protreject - Peer doesn't speak this protocol. | |||
| 688 | * | |||
| 689 | * Treat this as a catastrophic error (RXJ-). | |||
| 690 | */ | |||
| 691 | void | |||
| 692 | fsm_protreject(f) | |||
| 693 | fsm *f; | |||
| 694 | { | |||
| 695 | switch( f->state ){ | |||
| 696 | case CLOSING4: | |||
| 697 | UNTIMEOUT(fsm_timeout, f)untimeout((fsm_timeout), (f)); /* Cancel timeout */ | |||
| 698 | /* fall through */ | |||
| 699 | case CLOSED2: | |||
| 700 | f->state = CLOSED2; | |||
| 701 | if( f->callbacks->finished ) | |||
| 702 | (*f->callbacks->finished)(f); | |||
| 703 | break; | |||
| 704 | ||||
| 705 | case STOPPING5: | |||
| 706 | case REQSENT6: | |||
| 707 | case ACKRCVD7: | |||
| 708 | case ACKSENT8: | |||
| 709 | UNTIMEOUT(fsm_timeout, f)untimeout((fsm_timeout), (f)); /* Cancel timeout */ | |||
| 710 | /* fall through */ | |||
| 711 | case STOPPED3: | |||
| 712 | f->state = STOPPED3; | |||
| 713 | if( f->callbacks->finished ) | |||
| 714 | (*f->callbacks->finished)(f); | |||
| 715 | break; | |||
| 716 | ||||
| 717 | case OPENED9: | |||
| 718 | if( f->callbacks->down ) | |||
| 719 | (*f->callbacks->down)(f); | |||
| 720 | ||||
| 721 | /* Init restart counter, send Terminate-Request */ | |||
| 722 | f->retransmits = f->maxtermtransmits; | |||
| 723 | fsm_sdata(f, TERMREQ5, f->reqid = ++f->id, | |||
| 724 | (u_char *) f->term_reason, f->term_reason_len); | |||
| 725 | TIMEOUT(fsm_timeout, f, f->timeouttime)timeout((fsm_timeout), (f), (f->timeouttime)); | |||
| 726 | --f->retransmits; | |||
| 727 | ||||
| 728 | f->state = STOPPING5; | |||
| 729 | break; | |||
| 730 | ||||
| 731 | default: | |||
| 732 | FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d!", | |||
| 733 | PROTO_NAME(f), f->state)); | |||
| 734 | } | |||
| 735 | } | |||
| 736 | ||||
| 737 | ||||
| 738 | /* | |||
| 739 | * fsm_sconfreq - Send a Configure-Request. | |||
| 740 | */ | |||
| 741 | static void | |||
| 742 | fsm_sconfreq(f, retransmit) | |||
| 743 | fsm *f; | |||
| 744 | int retransmit; | |||
| 745 | { | |||
| 746 | u_char *outp; | |||
| 747 | int cilen; | |||
| 748 | ||||
| 749 | if( f->state != REQSENT6 && f->state != ACKRCVD7 && f->state != ACKSENT8 ){ | |||
| 750 | /* Not currently negotiating - reset options */ | |||
| 751 | if( f->callbacks->resetci ) | |||
| 752 | (*f->callbacks->resetci)(f); | |||
| 753 | f->nakloops = 0; | |||
| 754 | } | |||
| 755 | ||||
| 756 | if( !retransmit ){ | |||
| 757 | /* New request - reset retransmission counter, use new ID */ | |||
| 758 | f->retransmits = f->maxconfreqtransmits; | |||
| 759 | f->reqid = ++f->id; | |||
| 760 | } | |||
| 761 | ||||
| 762 | f->seen_ack = 0; | |||
| 763 | ||||
| 764 | /* | |||
| 765 | * Make up the request packet | |||
| 766 | */ | |||
| 767 | outp = outpacket_buf + PPP_HDRLEN4 + HEADERLEN(sizeof (u_char) + sizeof (u_char) + sizeof (u_short)); | |||
| 768 | if( f->callbacks->cilen && f->callbacks->addci ){ | |||
| 769 | cilen = (*f->callbacks->cilen)(f); | |||
| 770 | if( cilen > peer_mru[f->unit] - HEADERLEN(sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) ) | |||
| 771 | cilen = peer_mru[f->unit] - HEADERLEN(sizeof (u_char) + sizeof (u_char) + sizeof (u_short)); | |||
| 772 | if (f->callbacks->addci) | |||
| 773 | (*f->callbacks->addci)(f, outp, &cilen); | |||
| 774 | } else | |||
| 775 | cilen = 0; | |||
| 776 | ||||
| 777 | /* send the request to our peer */ | |||
| 778 | fsm_sdata(f, CONFREQ1, f->reqid, outp, cilen); | |||
| 779 | ||||
| 780 | /* start the retransmit timer */ | |||
| 781 | --f->retransmits; | |||
| 782 | TIMEOUT(fsm_timeout, f, f->timeouttime)timeout((fsm_timeout), (f), (f->timeouttime)); | |||
| 783 | ||||
| 784 | FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d", | |||
| 785 | PROTO_NAME(f), f->reqid)); | |||
| 786 | } | |||
| 787 | ||||
| 788 | ||||
| 789 | /* | |||
| 790 | * fsm_sdata - Send some data. | |||
| 791 | * | |||
| 792 | * Used for all packets sent to our peer by this module. | |||
| 793 | */ | |||
| 794 | void | |||
| 795 | fsm_sdata(f, code, id, data, datalen) | |||
| 796 | fsm *f; | |||
| 797 | u_char code, id; | |||
| 798 | u_char *data; | |||
| 799 | int datalen; | |||
| 800 | { | |||
| 801 | u_char *outp; | |||
| 802 | int outlen; | |||
| 803 | ||||
| 804 | /* Adjust length to be smaller than MTU */ | |||
| 805 | outp = outpacket_buf; | |||
| 806 | if (datalen > peer_mru[f->unit] - HEADERLEN(sizeof (u_char) + sizeof (u_char) + sizeof (u_short))) | |||
| 807 | datalen = peer_mru[f->unit] - HEADERLEN(sizeof (u_char) + sizeof (u_char) + sizeof (u_short)); | |||
| 808 | if (datalen && data != outp + PPP_HDRLEN4 + HEADERLEN(sizeof (u_char) + sizeof (u_char) + sizeof (u_short))) | |||
| 809 | BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen)memcpy(outp + 4 + (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)), data, datalen); | |||
| ||||
| 810 | outlen = datalen + HEADERLEN(sizeof (u_char) + sizeof (u_char) + sizeof (u_short)); | |||
| 811 | MAKEHEADER(outp, f->protocol){ { *(outp)++ = (u_char) (0xff); }; { *(outp)++ = (u_char) (0x03 ); }; { *(outp)++ = (u_char) ((f->protocol) >> 8); * (outp)++ = (u_char) (f->protocol); }; }; | |||
| 812 | PUTCHAR(code, outp){ *(outp)++ = (u_char) (code); }; | |||
| 813 | PUTCHAR(id, outp){ *(outp)++ = (u_char) (id); }; | |||
| 814 | PUTSHORT(outlen, outp){ *(outp)++ = (u_char) ((outlen) >> 8); *(outp)++ = (u_char ) (outlen); }; | |||
| 815 | output(f->unit, outpacket_buf, outlen + PPP_HDRLEN4); | |||
| 816 | ||||
| 817 | FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d, id %d.", | |||
| 818 | PROTO_NAME(f), code, id)); | |||
| 819 | } |