Annotation of embedaddon/libpdel/ppp/ppp_fsm.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (c) 2001-2002 Packet Design, LLC.
! 4: * All rights reserved.
! 5: *
! 6: * Subject to the following obligations and disclaimer of warranty,
! 7: * use and redistribution of this software, in source or object code
! 8: * forms, with or without modifications are expressly permitted by
! 9: * Packet Design; provided, however, that:
! 10: *
! 11: * (i) Any and all reproductions of the source or object code
! 12: * must include the copyright notice above and the following
! 13: * disclaimer of warranties; and
! 14: * (ii) No rights are granted, in any manner or form, to use
! 15: * Packet Design trademarks, including the mark "PACKET DESIGN"
! 16: * on advertising, endorsements, or otherwise except as such
! 17: * appears in the above copyright notice or in the software.
! 18: *
! 19: * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
! 20: * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
! 21: * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
! 22: * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
! 23: * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
! 24: * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
! 25: * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
! 26: * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
! 27: * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
! 28: * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
! 29: * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
! 30: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
! 31: * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
! 32: * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
! 33: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 34: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
! 35: * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
! 36: * THE POSSIBILITY OF SUCH DAMAGE.
! 37: *
! 38: * Author: Archie Cobbs <archie@freebsd.org>
! 39: */
! 40:
! 41: #include "ppp/ppp_defs.h"
! 42: #include "ppp/ppp_log.h"
! 43: #include "ppp/ppp_util.h"
! 44: #include "ppp/ppp_fsm_option.h"
! 45: #include "ppp/ppp_fsm.h"
! 46:
! 47: /* FSM defaults */
! 48: #define FSM_MAX_CONFIGURE 10
! 49: #define FSM_MAX_TERMINATE 3
! 50: #define FSM_MAX_FAILURE 8
! 51: #define FSM_TIMEOUT 2
! 52:
! 53: /* Memory type */
! 54: #define FSM_MTYPE "ppp_fsm"
! 55:
! 56: /* Max amount of packet data to copy & send back */
! 57: #define MAX_PKTCOPY 200
! 58:
! 59: /* FSM events */
! 60: enum fsm_event {
! 61: UP =0,
! 62: DOWN =1,
! 63: OPEN =2,
! 64: CLOSE =3,
! 65: TO_P =4,
! 66: TO_M =5,
! 67: RCR_P =6,
! 68: RCR_M =7,
! 69: RCA =8,
! 70: RCN =9,
! 71: RTR =10,
! 72: RTA =11,
! 73: RXJ_P =12,
! 74: RXJ_M =13
! 75: };
! 76: #define FSM_EVENT_MAX 14
! 77:
! 78: /* Actions to take on events */
! 79: #define TLU 0x0010 /* this layer up */
! 80: #define TLD 0x0020 /* this layer down */
! 81: #define TLS 0x0040 /* this layer started */
! 82: #define TLF 0x0080 /* this layer finished */
! 83: #define IRC 0x0100 /* init restart counter */
! 84: #define ZRC 0x0200 /* zero restart counter */
! 85: #define SCR 0x0400 /* send config request */
! 86: #define SCA 0x0800 /* send config ack */
! 87: #define SCN 0x1000 /* send config nak/rej */
! 88: #define STR 0x2000 /* send terminate request */
! 89: #define STA 0x4000 /* send terminate ack */
! 90:
! 91: #define STMASK 0x000f /* next state mask */
! 92: #define NA 0xffff /* impossible event */
! 93:
! 94: /* Configurations we keep */
! 95: #define FSM_CONF_SELF 0 /* my requested config */
! 96: #define FSM_CONF_PEER 1 /* peer requested config */
! 97: #define FSM_CONF_NAK 2 /* nak'd peer config */
! 98: #define FSM_CONF_REJ 3 /* rejected peer config */
! 99: #define FSM_CONF_MAX 4
! 100:
! 101: /* Information describing an instance of an FSM */
! 102: struct ppp_fsm {
! 103: struct ppp_fsm_instance *inst; /* fsm instance object */
! 104: enum ppp_fsm_state state; /* fsm state */
! 105: struct ppp_log *log; /* log object */
! 106: struct ppp_fsm_options *config[FSM_CONF_MAX]; /* config options */
! 107: u_char ids[FSM_CODE_MAX]; /* packet ids */
! 108: u_char rejcode[FSM_CODE_MAX]; /* rejected codes */
! 109: short restart; /* restart counter */
! 110: short failure[2]; /* failure counter */
! 111: time_t last_heard; /* time last heard from */
! 112: struct ppp_fsm_output dead; /* if dead and reason why */
! 113: struct pevent_ctx *ev_ctx; /* event context */
! 114: pthread_mutex_t *mutex; /* mutex */
! 115: struct pevent *timer; /* restart timer */
! 116: struct mesg_port *outport; /* where output goes */
! 117: };
! 118:
! 119: #define FSM_TIMER_STATE(state) \
! 120: ((state) >= FSM_STATE_CLOSING && (state) != FSM_STATE_OPENED)
! 121:
! 122: #define FSM_DEAD(fsm) \
! 123: (((fsm)->state == FSM_STATE_INITIAL \
! 124: || (fsm)->state == FSM_STATE_CLOSED) \
! 125: && (fsm)->dead.u.down.reason != 0)
! 126:
! 127: /* Macro for logging */
! 128: #define LOG(sev, fmt, args...) PPP_LOG(fsm->log, sev, fmt , ## args)
! 129:
! 130: /*
! 131: * RFC 1661 PPP state transition table
! 132: *
! 133: * Differences from RFC 1661:
! 134: * TLS added in [OPEN, CLOSED]
! 135: * TLF added in [DOWN, CLOSING]
! 136: * TLS added in [RCR+, STOPPED]
! 137: * TLS added in [RCR-, STOPPED]
! 138: * RUC and RXR events removed (handled directly)
! 139: *
! 140: * The extra TLS/TLF actions are to keep intention of lower layer in sync
! 141: * with the intention of this layer (i.e., if TLS -> lower layer OPEN and
! 142: * TLF -> lower layer CLOSE).
! 143: */
! 144: static const u_int16_t fsm_actions[FSM_EVENT_MAX][FSM_STATE_MAX] = {
! 145:
! 146: /*INITL STARTNG CLOSED STOPPED CLOSING STOPPNG REQ-SNT ACK-RCD ACK-SNT OPENED*/
! 147:
! 148: /* Up */
! 149: { 2, IRC|SCR|6,
! 150: NA, NA, NA, NA, NA, NA, NA, NA },
! 151: /* Down */
! 152: { NA, NA, 0, TLS|1, TLF|0, 1, 1, 1, 1, TLD|1 },
! 153: /* Open */
! 154: { TLS|1,1, TLS|IRC|SCR|6,
! 155: 3, 5, 5, 6, 7, 8, 9 },
! 156: /* Close */
! 157: { 0, TLF|0, 2, 2, 4, 4, IRC|STR|4,
! 158: IRC|STR|4,
! 159: IRC|STR|4,
! 160: TLD|IRC|STR|4},
! 161: /* TO+ */
! 162: { NA, NA, NA, NA, STR|4, STR|5, SCR|6, SCR|6, SCR|8, NA },
! 163: /* TO- */
! 164: { NA, NA, NA, NA, TLF|2, TLF|3, TLF|3, TLF|3, TLF|3, NA },
! 165: /* RCR+ */
! 166: { NA, NA, STA|2, TLS|IRC|SCR|SCA|8,
! 167: 4, 5, SCA|8, SCA|TLU|9,
! 168: SCA|8,
! 169: TLD|SCR|SCA|8},
! 170: /* RCR- */
! 171: { NA, NA, STA|2, TLS|IRC|SCR|SCN|6,
! 172: 4, 5, SCN|6, SCN|7, SCN|6,
! 173: TLD|SCR|SCN|6},
! 174: /* RCA */
! 175: { NA, NA, STA|2, STA|3, 4, 5, IRC|7,
! 176: SCR|6, IRC|TLU|9,
! 177: TLD|SCR|6 },
! 178: /* RCN */
! 179: { NA, NA, STA|2, STA|3, 4, 5, IRC|SCR|6,
! 180: SCR|6, IRC|SCR|8,
! 181: TLD|SCR|6 },
! 182: /* RTR */
! 183: { NA, NA, STA|2, STA|3, STA|4, STA|5, STA|6, STA|6, STA|6,
! 184: TLD|ZRC|STA|5},
! 185: /* RTA */
! 186: { NA, NA, 2, 3, TLF|2, TLF|3, 6, 6, 8,
! 187: TLD|SCR|6 },
! 188: /* RXJ+ */
! 189: { NA, NA, 2, 3, 4, 5, 6, 6, 8, 9 },
! 190: /* RXJ- */
! 191: { NA, NA, TLF|2, TLF|3, TLF|2, TLF|3, TLF|3, TLF|3, TLF|3,
! 192: TLD|IRC|STR|5},
! 193: };
! 194:
! 195: /* Minimum packet data lengths */
! 196: static const u_char fsm_minlen[FSM_CODE_MAX] = {
! 197: 0, /* FSM_CODE_VENDOR */
! 198: 0, /* FSM_CODE_CONFIGREQ */
! 199: 0, /* FSM_CODE_CONFIGACK */
! 200: 0, /* FSM_CODE_CONFIGNAK */
! 201: 0, /* FSM_CODE_CONFIGREJ */
! 202: 0, /* FSM_CODE_TERMREQ */
! 203: 0, /* FSM_CODE_TERMACK */
! 204: 1, /* FSM_CODE_CODEREJ */
! 205: 2, /* FSM_CODE_PROTOREJ */
! 206: 4, /* FSM_CODE_ECHOREQ */
! 207: 4, /* FSM_CODE_ECHOREP */
! 208: 4, /* FSM_CODE_DISCREQ */
! 209: 4, /* FSM_CODE_IDENT */
! 210: 8, /* FSM_CODE_TIMEREM */
! 211: 0, /* FSM_CODE_RESETREQ */
! 212: 0, /* FSM_CODE_RESETACK */
! 213: };
! 214:
! 215: /*
! 216: * Internal functions
! 217: */
! 218: static void ppp_fsm_input_packet(struct ppp_fsm *fsm,
! 219: const u_char *data, u_int dlen);
! 220: static void ppp_fsm_event(struct ppp_fsm *fsm, int event);
! 221: static void ppp_fsm_send_config(struct ppp_fsm *fsm,
! 222: enum ppp_fsm_code code);
! 223: static void ppp_fsm_send_packet(struct ppp_fsm *fsm, u_char code,
! 224: const void *data, u_int len);
! 225: static void ppp_fsm_record(struct ppp_fsm *fsm,
! 226: /* enum ppp_fsm_reason reason, */ ...);
! 227: static int ppp_fsm_output(struct ppp_fsm *fsm,
! 228: enum ppp_fsmoutput type, ...);
! 229: static void ppp_fsm_output_build(struct ppp_fsm_output *output,
! 230: enum ppp_fsmoutput type, va_list args);
! 231: static void ppp_fsm_output_dead(struct ppp_fsm *fsm);
! 232: static void ppp_fsm_syserr(struct ppp_fsm *fsm, const char *func);
! 233:
! 234: static void ppp_fsm_log_pkt(struct ppp_fsm *fsm, int sev,
! 235: const char *prefix, const u_char *pkt);
! 236:
! 237: static pevent_handler_t ppp_fsm_timeout;
! 238:
! 239: static const char *st2str(u_int state);
! 240: static const char *ev2str(u_int event);
! 241: static const char *cd2str(u_int code);
! 242:
! 243: /***********************************************************************
! 244: PUBLIC API FUNCTIONS
! 245: ***********************************************************************/
! 246:
! 247: /*
! 248: * Create a new FSM.
! 249: *
! 250: * "inst" is freed when the FSM is destroyed.
! 251: */
! 252: struct ppp_fsm *
! 253: ppp_fsm_create(struct pevent_ctx *ev_ctx, pthread_mutex_t *mutex,
! 254: struct ppp_fsm_instance *inst, struct ppp_log *log)
! 255: {
! 256: struct ppp_fsm *fsm;
! 257: int i;
! 258:
! 259: /* Get new FSM object */
! 260: if ((fsm = MALLOC(FSM_MTYPE, sizeof(*fsm))) == NULL)
! 261: return (NULL);
! 262: memset(fsm, 0, sizeof(*fsm));
! 263: fsm->ev_ctx = ev_ctx;
! 264: fsm->mutex = mutex;
! 265: fsm->inst = inst;
! 266: fsm->state = FSM_STATE_INITIAL;
! 267:
! 268: /* Prefix log with FSM name */
! 269: if ((fsm->log = ppp_log_prefix(log,
! 270: "%s: ", inst->type->name)) == NULL) {
! 271: FREE(FSM_MTYPE, fsm);
! 272: return (NULL);
! 273: }
! 274:
! 275: /* Get message port */
! 276: if ((fsm->outport = mesg_port_create(inst->type->name)) == NULL) {
! 277: FREE(FSM_MTYPE, fsm);
! 278: return (NULL);
! 279: }
! 280:
! 281: /* For 'shell' FSM's like MP LCP, no config required */
! 282: if ((inst->type->sup_codes & (1 << FSM_CODE_CONFIGREQ)) == 0) {
! 283: fsm->state = FSM_STATE_OPENED;
! 284: return (fsm);
! 285: }
! 286:
! 287: /* Initialize configuration options */
! 288: for (i = 0; i < FSM_CONF_MAX; i++) {
! 289: if ((fsm->config[i] = ppp_fsm_option_create()) == NULL) {
! 290: while (i-- > 0)
! 291: ppp_fsm_option_destroy(&fsm->config[i]);
! 292: mesg_port_destroy(&fsm->outport);
! 293: FREE(FSM_MTYPE, fsm);
! 294: return (NULL);
! 295: }
! 296: }
! 297:
! 298: /* Done */
! 299: inst->fsm = fsm;
! 300: return (fsm);
! 301: }
! 302:
! 303: /*
! 304: * Destroy an FSM
! 305: */
! 306: void
! 307: ppp_fsm_destroy(struct ppp_fsm **fsmp)
! 308: {
! 309: struct ppp_fsm *const fsm = *fsmp;
! 310: struct ppp_fsm_output *output;
! 311: int i;
! 312:
! 313: if (fsm == NULL)
! 314: return;
! 315: *fsmp = NULL;
! 316: pevent_unregister(&fsm->timer);
! 317: for (i = 0; i < FSM_CONF_MAX; i++)
! 318: ppp_fsm_option_destroy(&fsm->config[i]);
! 319: while ((output = mesg_port_get(fsm->outport, 0)) != NULL)
! 320: ppp_fsm_free_output(output);
! 321: mesg_port_destroy(&fsm->outport);
! 322: (*fsm->inst->type->destroy)(fsm->inst);
! 323: ppp_log_close(&fsm->log);
! 324: FREE(FSM_MTYPE, fsm);
! 325: }
! 326:
! 327: /*
! 328: * Get output port.
! 329: */
! 330: struct mesg_port *
! 331: ppp_fsm_get_outport(struct ppp_fsm *fsm)
! 332: {
! 333: return (fsm->outport);
! 334: }
! 335:
! 336: /*
! 337: * Free an FSM output structure.
! 338: */
! 339: void
! 340: ppp_fsm_free_output(struct ppp_fsm_output *output)
! 341: {
! 342: switch (output->type) {
! 343: case FSM_OUTPUT_DATA:
! 344: FREE(FSM_MTYPE, output->u.data.data);
! 345: break;
! 346: default:
! 347: break;
! 348: }
! 349: FREE(FSM_MTYPE, output);
! 350: }
! 351:
! 352: /*
! 353: * Input something to the FSM.
! 354: */
! 355: void
! 356: ppp_fsm_input(struct ppp_fsm *fsm, enum ppp_fsm_input input, ...)
! 357: {
! 358: const u_char *data;
! 359: va_list args;
! 360: u_int dlen;
! 361:
! 362: /* If we're dead, ignore it */
! 363: if (FSM_DEAD(fsm))
! 364: return;
! 365:
! 366: /* Handle input */
! 367: va_start(args, input);
! 368: switch (input) {
! 369: case FSM_INPUT_OPEN:
! 370: ppp_fsm_event(fsm, OPEN);
! 371: break;
! 372: case FSM_INPUT_CLOSE:
! 373: ppp_fsm_record(fsm, FSM_REASON_CLOSE);
! 374: ppp_fsm_event(fsm, CLOSE);
! 375: break;
! 376: case FSM_INPUT_UP:
! 377: ppp_fsm_event(fsm, UP);
! 378: break;
! 379: case FSM_INPUT_DOWN_FATAL:
! 380: ppp_fsm_record(fsm, FSM_REASON_DOWN_FATAL);
! 381: ppp_fsm_event(fsm, DOWN);
! 382: ppp_fsm_event(fsm, CLOSE);
! 383: break;
! 384: case FSM_INPUT_DOWN_NONFATAL:
! 385: ppp_fsm_record(fsm, FSM_REASON_DOWN_NONFATAL);
! 386: ppp_fsm_event(fsm, DOWN);
! 387: break;
! 388: case FSM_INPUT_RECD_PROTOREJ:
! 389: {
! 390: u_int16_t proto;
! 391:
! 392: proto = va_arg(args, int);
! 393: ppp_fsm_record(fsm, FSM_REASON_PROTOREJ, proto);
! 394: ppp_fsm_event(fsm, RXJ_M);
! 395: break;
! 396: }
! 397: case FSM_INPUT_DATA:
! 398: data = va_arg(args, const u_char *);
! 399: dlen = va_arg(args, u_int);
! 400: ppp_fsm_input_packet(fsm, data, dlen);
! 401: break;
! 402: case FSM_INPUT_XMIT_PROTOREJ:
! 403: {
! 404: u_int16_t proto;
! 405: u_int16_t *prj;
! 406: u_int prlen;
! 407:
! 408: proto = va_arg(args, int);
! 409: data = va_arg(args, const u_char *);
! 410: dlen = va_arg(args, u_int);
! 411: prlen = 2 + MIN(dlen, MAX_PKTCOPY);
! 412: if ((prj = MALLOC(TYPED_MEM_TEMP, prlen)) == NULL)
! 413: break;
! 414: *prj = htons(proto);
! 415: memcpy((char *)prj + 2, data, MIN(dlen, MAX_PKTCOPY));
! 416: ppp_fsm_send_packet(fsm, FSM_CODE_PROTOREJ, prj, prlen);
! 417: FREE(TYPED_MEM_TEMP, prj);
! 418: break;
! 419: }
! 420: default:
! 421: LOG(LOG_ERR, "invalid input %d", input);
! 422: break;
! 423: }
! 424: va_end(args);
! 425: }
! 426:
! 427: /*
! 428: * Get FSM state.
! 429: */
! 430: enum ppp_fsm_state
! 431: ppp_fsm_get_state(struct ppp_fsm *fsm)
! 432: {
! 433: return (fsm->state);
! 434: }
! 435:
! 436: /*
! 437: * Get time we last heard from the peer.
! 438: */
! 439: time_t
! 440: ppp_fsm_last_heard(struct ppp_fsm *fsm)
! 441: {
! 442: return (fsm->last_heard);
! 443: }
! 444:
! 445: /*
! 446: * Get underlying FSM instance.
! 447: */
! 448: struct ppp_fsm_instance *
! 449: ppp_fsm_get_instance(struct ppp_fsm *fsm)
! 450: {
! 451: return (fsm->inst);
! 452: }
! 453:
! 454: /*
! 455: * Send a reset-request.
! 456: */
! 457: void
! 458: ppp_fsm_send_reset_req(struct ppp_fsm *fsm, const void *data, size_t dlen)
! 459: {
! 460: fsm->ids[FSM_CODE_RESETREQ]++;
! 461: ppp_fsm_send_packet(fsm, FSM_CODE_RESETREQ, data, dlen);
! 462: }
! 463:
! 464: /*
! 465: * Send a reset-ack.
! 466: */
! 467: void
! 468: ppp_fsm_send_reset_ack(struct ppp_fsm *fsm, const void *data, size_t dlen)
! 469: {
! 470: ppp_fsm_send_packet(fsm, FSM_CODE_RESETACK, data, dlen);
! 471: }
! 472:
! 473: /***********************************************************************
! 474: INTERNAL FUNCTIONS
! 475: ***********************************************************************/
! 476:
! 477: /*
! 478: * Handle an incoming packet
! 479: */
! 480: static void
! 481: ppp_fsm_input_packet(struct ppp_fsm *fsm, const u_char *pkt, u_int dlen)
! 482: {
! 483: const u_char *const payload = pkt + sizeof(struct ppp_fsm_pkt);
! 484: const struct ppp_fsm_type *const ftyp = fsm->inst->type;
! 485: struct ppp_fsm_options *opts = NULL;
! 486: struct ppp_fsm_pkt hdr;
! 487: u_int len;
! 488:
! 489: /* Drop packet if it's unexpected */
! 490: if (fsm->state == FSM_STATE_INITIAL
! 491: || fsm->state == FSM_STATE_STARTING)
! 492: goto done;
! 493:
! 494: /* Check packet length */
! 495: if (dlen < sizeof(hdr)) {
! 496: LOG(LOG_NOTICE, "rec'd %s packet (%u bytes)", "runt", dlen);
! 497: goto done;
! 498: }
! 499:
! 500: /* Copy packet header into aligned memory */
! 501: memcpy(&hdr, pkt, sizeof(hdr));
! 502:
! 503: /* Check packet length again */
! 504: if ((len = ntohs(hdr.length)) < sizeof(hdr)) {
! 505: LOG(LOG_NOTICE, "rec'd %s packet (%u bytes)", "runt", dlen);
! 506: goto done;
! 507: }
! 508: if (len > dlen) {
! 509: LOG(LOG_NOTICE, "rec'd %s packet (%u bytes)",
! 510: "truncated", dlen);
! 511: goto done;
! 512: }
! 513: len -= sizeof(hdr); /* get length of just the data part */
! 514:
! 515: /* Check code; send code-reject if not supported */
! 516: if (hdr.code >= FSM_CODE_MAX
! 517: || (ftyp->sup_codes & (1 << hdr.code)) == 0) {
! 518: code_reject: LOG(LOG_DEBUG, "rejecting unsupported code %u", hdr.code);
! 519: ppp_fsm_send_packet(fsm, FSM_CODE_CODEREJ,
! 520: pkt, MIN(dlen, MAX_PKTCOPY));
! 521: goto done;
! 522: }
! 523:
! 524: /* Check data length */
! 525: if (len < fsm_minlen[hdr.code]) {
! 526: LOG(LOG_DEBUG + 1, "ignoring truncated packet");
! 527: goto done;
! 528: }
! 529:
! 530: /* Reset peer's idle time */
! 531: fsm->last_heard = time(NULL);
! 532:
! 533: /* Initialize failure counters if appropriate */
! 534: if (fsm->state < FSM_STATE_REQSENT) {
! 535: fsm->failure[PPP_SELF] = FSM_MAX_FAILURE;
! 536: fsm->failure[PPP_PEER] = FSM_MAX_FAILURE;
! 537: }
! 538:
! 539: /* Logging */
! 540: ppp_fsm_log_pkt(fsm, (hdr.code == FSM_CODE_ECHOREQ
! 541: || hdr.code == FSM_CODE_ECHOREP) ? LOG_DEBUG : LOG_INFO,
! 542: "recv", pkt);
! 543:
! 544: /* Extract encoded config options */
! 545: switch (hdr.code) {
! 546: case FSM_CODE_CONFIGREQ:
! 547: case FSM_CODE_CONFIGACK:
! 548: case FSM_CODE_CONFIGNAK:
! 549: case FSM_CODE_CONFIGREJ:
! 550: ppp_fsm_record(fsm, FSM_REASON_CONF, hdr.code);
! 551: if ((opts = ppp_fsm_option_unpack(payload, len)) == NULL) {
! 552: ppp_fsm_syserr(fsm, "malloc");
! 553: goto close;
! 554: }
! 555: break;
! 556: default:
! 557: break;
! 558: }
! 559:
! 560: /* Check magic number */
! 561: switch (hdr.code) {
! 562: case FSM_CODE_ECHOREQ:
! 563: case FSM_CODE_ECHOREP:
! 564: case FSM_CODE_IDENT:
! 565: case FSM_CODE_DISCREQ:
! 566: case FSM_CODE_TIMEREM:
! 567: {
! 568: u_int32_t pkt_magic;
! 569: u_int32_t req_magic;
! 570:
! 571: /* Get what peer's magic number ought to be */
! 572: if (ftyp->get_magic == NULL)
! 573: break;
! 574: req_magic = (*ftyp->get_magic)(fsm->inst, PPP_PEER);
! 575:
! 576: /* Get actual magic number received */
! 577: memcpy(&pkt_magic, payload, 4);
! 578: pkt_magic = ntohl(pkt_magic);
! 579:
! 580: /*
! 581: * Only check if both magic numbers are non-zero and
! 582: * the FSM has reached the opened state.
! 583: */
! 584: if (req_magic == 0
! 585: || pkt_magic == 0
! 586: || fsm->state != FSM_STATE_OPENED)
! 587: break;
! 588:
! 589: /* If wrong, bail out */
! 590: if (pkt_magic != req_magic) {
! 591: LOG(LOG_NOTICE,
! 592: "rec'd %s with invalid magic# 0x%08x != 0x%08x",
! 593: cd2str(hdr.code), pkt_magic, req_magic);
! 594: ppp_fsm_record(fsm, FSM_REASON_BADMAGIC);
! 595: goto close;
! 596: }
! 597: break;
! 598: }
! 599: default:
! 600: break;
! 601: }
! 602:
! 603: /* Deal with packet */
! 604: switch (hdr.code) {
! 605: case FSM_CODE_VENDOR:
! 606: if (ftyp->recv_vendor == NULL)
! 607: goto code_reject;
! 608: (*ftyp->recv_vendor)(fsm->inst, payload, len);
! 609: break;
! 610:
! 611: case FSM_CODE_CONFIGREQ:
! 612: {
! 613: int ack;
! 614: int i;
! 615:
! 616: /* Update reply id's */
! 617: fsm->ids[FSM_CODE_CONFIGACK] = hdr.id;
! 618: fsm->ids[FSM_CODE_CONFIGNAK] = hdr.id;
! 619: fsm->ids[FSM_CODE_CONFIGREJ] = hdr.id;
! 620:
! 621: /* Update peer's requested options with new info */
! 622: ppp_fsm_option_destroy(&fsm->config[FSM_CONF_PEER]);
! 623: fsm->config[FSM_CONF_PEER] = opts;
! 624: opts = NULL; /* avoid double free */
! 625:
! 626: /* Reset nak and rej reply options */
! 627: ppp_fsm_option_zero(fsm->config[FSM_CONF_NAK]);
! 628: ppp_fsm_option_zero(fsm->config[FSM_CONF_REJ]);
! 629:
! 630: /* Examine peer's options for basic validity */
! 631: for (i = 0; i < fsm->config[FSM_CONF_PEER]->num; i++) {
! 632: const struct ppp_fsm_option *const opt
! 633: = &fsm->config[FSM_CONF_PEER]->opts[i];
! 634: const struct ppp_fsm_optdesc *const desc
! 635: = ppp_fsm_option_desc(ftyp->options, opt);
! 636:
! 637: /* If not supported or invalid, reject it */
! 638: if (desc == NULL
! 639: || !desc->supported
! 640: || opt->len < desc->min
! 641: || opt->len > desc->max) {
! 642:
! 643: /* Add to reject list */
! 644: if (ppp_fsm_option_add(
! 645: fsm->config[FSM_CONF_REJ],
! 646: opt->type, opt->len, opt->data) == -1) {
! 647: ppp_fsm_syserr(fsm, "malloc");
! 648: goto close;
! 649: }
! 650:
! 651: /* Remove from request list */
! 652: ppp_fsm_option_del(
! 653: fsm->config[FSM_CONF_PEER], i--);
! 654: }
! 655: }
! 656:
! 657: /* Call FSM type method to deal with remaining options */
! 658: if ((*ftyp->recv_conf_req)(fsm->inst,
! 659: fsm->config[FSM_CONF_PEER],
! 660: fsm->config[FSM_CONF_NAK],
! 661: fsm->config[FSM_CONF_REJ]) == -1)
! 662: goto config_error;
! 663:
! 664: /* Check if not converging */
! 665: if (fsm->config[FSM_CONF_NAK]->num > 0
! 666: && --fsm->failure[PPP_PEER] <= 0) {
! 667: LOG(LOG_NOTICE, "negotiation failed to converge:"
! 668: " configuration not accepted by %s", "me");
! 669: ppp_fsm_record(fsm, FSM_REASON_NEGOT);
! 670: goto close;
! 671: }
! 672:
! 673: /* Evoke RCR+ or RCR- event */
! 674: ack = (fsm->config[FSM_CONF_NAK]->num
! 675: + fsm->config[FSM_CONF_REJ]->num == 0);
! 676: if (ack)
! 677: fsm->failure[PPP_PEER] = FSM_MAX_FAILURE;
! 678: ppp_fsm_event(fsm, ack ? RCR_P : RCR_M);
! 679: break;
! 680: }
! 681:
! 682: case FSM_CODE_CONFIGACK:
! 683:
! 684: /* Validate id and contents */
! 685: if (hdr.id != fsm->ids[FSM_CODE_CONFIGREQ]) {
! 686: LOG(LOG_DEBUG + 1, "ignoring id #%u", hdr.id);
! 687: goto done;
! 688: }
! 689: if (!ppp_fsm_option_equal(opts, -1,
! 690: fsm->config[FSM_CONF_SELF], -1)) {
! 691: LOG(LOG_DEBUG + 1, "ignoring altered contents");
! 692: goto done;
! 693: }
! 694:
! 695: /* Generate RCA event */
! 696: fsm->ids[FSM_CODE_CONFIGREQ]++;
! 697: fsm->failure[PPP_SELF] = FSM_MAX_FAILURE;
! 698: ppp_fsm_event(fsm, RCA);
! 699: break;
! 700:
! 701: case FSM_CODE_CONFIGNAK:
! 702: case FSM_CODE_CONFIGREJ:
! 703: {
! 704: int (*func)(struct ppp_fsm_instance *inst,
! 705: struct ppp_fsm_options *rej);
! 706:
! 707: /* Validate id */
! 708: if (hdr.id != fsm->ids[FSM_CODE_CONFIGREQ]) {
! 709: LOG(LOG_DEBUG + 1, "ignoring id #%u", hdr.id);
! 710: goto done;
! 711: }
! 712:
! 713: /* Check if not converging */
! 714: if (hdr.code == FSM_CODE_CONFIGNAK
! 715: && --fsm->failure[PPP_SELF] <= 0) {
! 716: LOG(LOG_NOTICE, "negotiation failed to converge:"
! 717: " configuration not accepted by %s", "peer");
! 718: ppp_fsm_record(fsm, FSM_REASON_NEGOT);
! 719: goto close;
! 720: }
! 721:
! 722: /* Call implementation to deal with options */
! 723: func = (hdr.code == FSM_CODE_CONFIGNAK) ?
! 724: ftyp->recv_conf_nak : ftyp->recv_conf_rej;
! 725: if ((*func)(fsm->inst, opts) == -1) {
! 726: config_error: if (errno == ELOOP)
! 727: ppp_fsm_record(fsm, FSM_REASON_LOOPBACK);
! 728: else if (errno == EINVAL)
! 729: ppp_fsm_record(fsm, FSM_REASON_NEGOT);
! 730: else
! 731: ppp_fsm_record(fsm, FSM_REASON_SYSERR, errno);
! 732: goto close;
! 733: }
! 734:
! 735: /* Generate RCN event */
! 736: fsm->ids[FSM_CODE_CONFIGREQ]++;
! 737: ppp_fsm_event(fsm, RCN);
! 738: break;
! 739: }
! 740:
! 741: case FSM_CODE_TERMREQ:
! 742:
! 743: /* Generate RTR event */
! 744: fsm->ids[FSM_CODE_TERMACK] = hdr.id;
! 745: ppp_fsm_record(fsm, FSM_REASON_TERM);
! 746: ppp_fsm_event(fsm, RTR);
! 747: goto close;
! 748:
! 749: case FSM_CODE_TERMACK:
! 750:
! 751: /* Validate id */
! 752: if (hdr.id != fsm->ids[FSM_CODE_TERMREQ]) {
! 753: LOG(LOG_DEBUG + 1, "ignoring id #%u", hdr.id);
! 754: goto done;
! 755: }
! 756: fsm->ids[FSM_CODE_TERMREQ]++;
! 757:
! 758: /* Generate RTA event */
! 759: fsm->ids[FSM_CODE_TERMACK] = hdr.id;
! 760: ppp_fsm_event(fsm, RTA);
! 761: break;
! 762:
! 763: case FSM_CODE_CODEREJ:
! 764: {
! 765: const u_char code = payload[0];
! 766:
! 767: /* See if rejected code is required */
! 768: if (code >= FSM_CODE_MAX
! 769: || (ftyp->req_codes & (1 << code)) == 0) {
! 770: fsm->rejcode[code] = 1;
! 771: ppp_fsm_event(fsm, RXJ_P);
! 772: } else {
! 773: ppp_fsm_record(fsm, FSM_REASON_CODEREJ, code);
! 774: ppp_fsm_event(fsm, RXJ_M);
! 775: goto close;
! 776: }
! 777: break;
! 778: }
! 779:
! 780: case FSM_CODE_PROTOREJ:
! 781: {
! 782: u_int16_t proto;
! 783:
! 784: /* Get rejected protocol */
! 785: memcpy(&proto, payload, 2);
! 786: proto = ntohs(proto);
! 787: if (proto == ftyp->proto) {
! 788: ppp_fsm_record(fsm, FSM_REASON_PROTOREJ, proto);
! 789: ppp_fsm_event(fsm, RXJ_M);
! 790: goto close;
! 791: }
! 792: ppp_fsm_output(fsm, FSM_OUTPUT_PROTOREJ, proto);
! 793: ppp_fsm_event(fsm, RXJ_P); /* assume RXJ+ until hear o/w */
! 794: break;
! 795: }
! 796:
! 797: case FSM_CODE_ECHOREQ:
! 798: {
! 799: u_char buf[MAX_PKTCOPY];
! 800: u_int32_t magic;
! 801:
! 802: /* Update reply id */
! 803: fsm->ids[FSM_CODE_ECHOREP] = hdr.id;
! 804:
! 805: /* Only reply when in opened state (except MP LCP) */
! 806: if (fsm->state != FSM_STATE_OPENED) {
! 807: LOG(LOG_DEBUG + 1, "ignoring: not in %s yet",
! 808: st2str(FSM_STATE_OPENED));
! 809: goto done;
! 810: }
! 811:
! 812: /* Insert my magic number and reply */
! 813: memcpy(buf, payload, MIN(len, sizeof(buf)));
! 814: magic = (ftyp->get_magic != NULL) ?
! 815: (*ftyp->get_magic)(fsm->inst, PPP_SELF) : 0;
! 816: magic = htonl(magic);
! 817: memcpy(buf, &magic, 4);
! 818: ppp_fsm_send_packet(fsm, FSM_CODE_ECHOREP,
! 819: buf, MIN(len, sizeof(buf)));
! 820: break;
! 821: }
! 822: case FSM_CODE_RESETREQ:
! 823: if (ftyp->recv_reset_req == NULL)
! 824: goto code_reject;
! 825: fsm->ids[FSM_CODE_RESETACK] = hdr.id;
! 826: (*ftyp->recv_reset_req)(fsm->inst, payload, len);
! 827: break;
! 828:
! 829: case FSM_CODE_RESETACK:
! 830: if (ftyp->recv_reset_ack == NULL)
! 831: goto code_reject;
! 832: if (hdr.id != fsm->ids[FSM_CODE_RESETREQ])
! 833: break;
! 834: (*ftyp->recv_reset_ack)(fsm->inst, payload, len);
! 835: break;
! 836:
! 837: case FSM_CODE_ECHOREP: /* ignore these */
! 838: case FSM_CODE_IDENT:
! 839: case FSM_CODE_DISCREQ:
! 840: case FSM_CODE_TIMEREM:
! 841: break;
! 842:
! 843: default: /* already handled above */
! 844: break;
! 845: }
! 846:
! 847: /* Done */
! 848: goto done;
! 849:
! 850: close:
! 851: /* Handle failure by closing up shop */
! 852: ppp_fsm_event(fsm, CLOSE);
! 853:
! 854: done:
! 855: /* Clean up */
! 856: ppp_fsm_option_destroy(&opts);
! 857: }
! 858:
! 859: /*
! 860: * Handle an event
! 861: */
! 862: static void
! 863: ppp_fsm_event(struct ppp_fsm *fsm, int event)
! 864: {
! 865: const int ostate = fsm->state;
! 866: const int action = fsm_actions[event][fsm->state];
! 867:
! 868: /* Debugging */
! 869: if (action == NA) {
! 870: LOG(LOG_ERR, "%s event %s in state %s",
! 871: "invalid", ev2str(event), st2str(fsm->state));
! 872: return;
! 873: }
! 874: LOG(LOG_DEBUG, "event %s in state %s",
! 875: ev2str(event), st2str(fsm->state));
! 876:
! 877: /* Perform actions */
! 878: if ((action & TLU) != 0)
! 879: ppp_fsm_output(fsm, FSM_OUTPUT_UP);
! 880: if ((action & TLD) != 0)
! 881: ppp_fsm_output(fsm, FSM_OUTPUT_DOWN, fsm->dead.u.down.reason);
! 882: if ((action & TLS) != 0)
! 883: ppp_fsm_output(fsm, FSM_OUTPUT_OPEN);
! 884: if ((action & TLF) != 0)
! 885: ppp_fsm_output(fsm, FSM_OUTPUT_CLOSE);
! 886: if ((action & IRC) != 0) {
! 887: fsm->restart = (event == CLOSE || event == RXJ_M) ?
! 888: FSM_MAX_TERMINATE : FSM_MAX_CONFIGURE;
! 889: }
! 890: if ((action & SCR) != 0)
! 891: ppp_fsm_send_config(fsm, FSM_CODE_CONFIGREQ);
! 892: if ((action & SCA) != 0)
! 893: ppp_fsm_send_config(fsm, FSM_CODE_CONFIGACK);
! 894: if ((action & SCN) != 0) {
! 895: if (fsm->config[FSM_CONF_REJ]->num != 0)
! 896: ppp_fsm_send_config(fsm, FSM_CODE_CONFIGREJ);
! 897: if (fsm->config[FSM_CONF_NAK]->num != 0)
! 898: ppp_fsm_send_config(fsm, FSM_CODE_CONFIGNAK);
! 899: }
! 900: if ((action & STR) != 0)
! 901: ppp_fsm_send_packet(fsm, FSM_CODE_TERMREQ, NULL, 0);
! 902: if ((action & STA) != 0)
! 903: ppp_fsm_send_packet(fsm, FSM_CODE_TERMACK, NULL, 0);
! 904:
! 905: /* Transition to new state */
! 906: fsm->state = (action & STMASK);
! 907: if (fsm->state != ostate)
! 908: LOG(LOG_DEBUG, "%s -> %s", st2str(ostate), st2str(fsm->state));
! 909:
! 910: /* Initialize failure counter if appropriate */
! 911: if (ostate < FSM_STATE_REQSENT && fsm->state >= FSM_STATE_REQSENT) {
! 912: fsm->failure[PPP_SELF] = FSM_MAX_FAILURE;
! 913: fsm->failure[PPP_PEER] = FSM_MAX_FAILURE;
! 914: }
! 915:
! 916: /* Stop the restart timer if it's not supposed to be running */
! 917: if (!FSM_TIMER_STATE(fsm->state)) {
! 918: pevent_unregister(&fsm->timer);
! 919: goto no_timer;
! 920: }
! 921:
! 922: /* Check if timer is already running and doesn't need to be restarted */
! 923: if (FSM_TIMER_STATE(ostate) && (action & (SCR|STR)) == 0)
! 924: goto no_timer;
! 925:
! 926: /* (Re)start restart timer */
! 927: pevent_unregister(&fsm->timer);
! 928: if (pevent_register(fsm->ev_ctx, &fsm->timer, 0, fsm->mutex,
! 929: ppp_fsm_timeout, fsm, PEVENT_TIME, FSM_TIMEOUT * 1000) == -1) {
! 930: ppp_fsm_syserr(fsm, "pevent_register");
! 931: return;
! 932: }
! 933:
! 934: no_timer:
! 935: /* Emit 'dead' output if we're dead */
! 936: if (FSM_DEAD(fsm))
! 937: ppp_fsm_output_dead(fsm);
! 938: }
! 939:
! 940: /*
! 941: * Send a config req, ack, nak, or rej packet
! 942: */
! 943: static void
! 944: ppp_fsm_send_config(struct ppp_fsm *fsm, enum ppp_fsm_code code)
! 945: {
! 946: const struct ppp_fsm_type *const ftyp = fsm->inst->type;
! 947: struct ppp_fsm_options *opts;
! 948: u_char *buf;
! 949: u_int len;
! 950:
! 951: /* Get the corresponding config option data */
! 952: switch (code) {
! 953: case FSM_CODE_CONFIGREQ:
! 954: {
! 955: u_int i;
! 956: u_int j;
! 957:
! 958: /* Regenerate my requested config options */
! 959: opts = fsm->config[FSM_CONF_SELF];
! 960: ppp_fsm_option_zero(opts);
! 961: if ((*ftyp->build_conf_req)(fsm->inst, opts) == -1) {
! 962: if (errno == EINVAL)
! 963: ppp_fsm_record(fsm, FSM_REASON_NEGOT);
! 964: else
! 965: ppp_fsm_syserr(fsm, "build_conf_req");
! 966: return;
! 967: }
! 968:
! 969: /* Elide default values from within config-request */
! 970: if (fsm->inst->type->defaults == NULL)
! 971: break;
! 972: for (i = 0; i < fsm->inst->type->defaults->num; i++) {
! 973: for (j = 0; j < opts->num; j++) {
! 974: if (ppp_fsm_option_equal(
! 975: fsm->inst->type->defaults, i, opts, j))
! 976: ppp_fsm_option_del(opts, j--);
! 977: }
! 978: }
! 979: break;
! 980: }
! 981: case FSM_CODE_CONFIGACK:
! 982: opts = fsm->config[FSM_CONF_PEER];
! 983: break;
! 984: case FSM_CODE_CONFIGNAK:
! 985: opts = fsm->config[FSM_CONF_NAK];
! 986: break;
! 987: case FSM_CODE_CONFIGREJ:
! 988: opts = fsm->config[FSM_CONF_REJ];
! 989: break;
! 990: default:
! 991: assert (0);
! 992: return;
! 993: }
! 994:
! 995: /* Construct packet payload */
! 996: len = ppp_fsm_option_packlen(opts);
! 997: if ((buf = MALLOC(TYPED_MEM_TEMP, len)) == NULL) {
! 998: LOG(LOG_ERR, "%s: %m", "malloc");
! 999: ppp_fsm_syserr(fsm, "malloc");
! 1000: return;
! 1001: }
! 1002:
! 1003: /* Send packet */
! 1004: ppp_fsm_option_pack(opts, buf);
! 1005: ppp_fsm_send_packet(fsm, code, buf, len);
! 1006: FREE(TYPED_MEM_TEMP, buf);
! 1007: }
! 1008:
! 1009: /*
! 1010: * Handler for an FSM timeout event.
! 1011: */
! 1012: static void
! 1013: ppp_fsm_timeout(void *arg)
! 1014: {
! 1015: struct ppp_fsm *const fsm = arg;
! 1016:
! 1017: /* Cancel event */
! 1018: pevent_unregister(&fsm->timer);
! 1019:
! 1020: /* If we're dead, ignore it */
! 1021: if (FSM_DEAD(fsm))
! 1022: return;
! 1023:
! 1024: /* Send timeout event */
! 1025: if (--fsm->restart <= 0)
! 1026: ppp_fsm_event(fsm, TO_M);
! 1027: else
! 1028: ppp_fsm_event(fsm, TO_P);
! 1029: }
! 1030:
! 1031: /*
! 1032: * Send a packet.
! 1033: *
! 1034: * Handle any errors by shutting down the FSM.
! 1035: */
! 1036: static void
! 1037: ppp_fsm_send_packet(struct ppp_fsm *fsm,
! 1038: u_char code, const void *data, u_int len)
! 1039: {
! 1040: struct ppp_fsm_pkt *pkt;
! 1041:
! 1042: /* If peer rejected code, don't bother */
! 1043: if (fsm->rejcode[code])
! 1044: return;
! 1045:
! 1046: /* Build packet */
! 1047: if ((pkt = MALLOC(FSM_MTYPE, sizeof(*pkt) + len)) == NULL) {
! 1048: ppp_fsm_syserr(fsm, "malloc");
! 1049: return;
! 1050: }
! 1051: pkt->code = code;
! 1052: pkt->id = fsm->ids[code];
! 1053: pkt->length = htons(sizeof(*pkt) + len);
! 1054: memcpy(pkt->data, data, len);
! 1055:
! 1056: /* Logging */
! 1057: ppp_fsm_log_pkt(fsm, (pkt->code == FSM_CODE_ECHOREQ
! 1058: || pkt->code == FSM_CODE_ECHOREP) ? LOG_DEBUG : LOG_INFO,
! 1059: "xmit", (u_char *)pkt);
! 1060:
! 1061: /* Send packet */
! 1062: if (ppp_fsm_output(fsm, FSM_OUTPUT_DATA, pkt, sizeof(*pkt) + len) == -1)
! 1063: FREE(FSM_MTYPE, pkt);
! 1064: }
! 1065:
! 1066: /*
! 1067: * Record the reason for the FSM going down or dying.
! 1068: *
! 1069: * This information is saved until it can be output later.
! 1070: */
! 1071: static void
! 1072: ppp_fsm_record(struct ppp_fsm *fsm, ...)
! 1073: {
! 1074: struct ppp_fsm_output *const output = &fsm->dead;
! 1075: va_list args;
! 1076:
! 1077: /* Prioritize reason first on severity, then first come, first serve */
! 1078: if (fsm->dead.u.down.reason == 0)
! 1079: goto record;
! 1080: switch (fsm->dead.u.down.reason) {
! 1081: case FSM_REASON_CONF:
! 1082: case FSM_REASON_DOWN_NONFATAL:
! 1083: break;
! 1084: default:
! 1085: return;
! 1086: }
! 1087:
! 1088: record:
! 1089: /* Build output message */
! 1090: va_start(args, fsm);
! 1091: ppp_fsm_output_build(output, FSM_OUTPUT_DEAD, args);
! 1092: va_end(args);
! 1093: }
! 1094:
! 1095: /*
! 1096: * Emit some sort of output from the FSM.
! 1097: *
! 1098: * Any errors are handled internally by shutting down the FSM.
! 1099: */
! 1100: static int
! 1101: ppp_fsm_output(struct ppp_fsm *fsm, enum ppp_fsmoutput type, ...)
! 1102: {
! 1103: struct ppp_fsm_output *output;
! 1104: va_list args;
! 1105:
! 1106: /* Allocate new output message */
! 1107: if ((output = MALLOC(FSM_MTYPE, sizeof(*output))) == NULL) {
! 1108: ppp_fsm_syserr(fsm, "malloc");
! 1109: return (-1);
! 1110: }
! 1111: memset(output, 0, sizeof(*output));
! 1112:
! 1113: /* Build output message */
! 1114: va_start(args, type);
! 1115: ppp_fsm_output_build(output, type, args);
! 1116: va_end(args);
! 1117:
! 1118: /* Send it */
! 1119: if (mesg_port_put(fsm->outport, output) == -1) {
! 1120: ppp_fsm_syserr(fsm, "mesg_port_put");
! 1121: FREE(FSM_MTYPE, output);
! 1122: return (-1);
! 1123: }
! 1124:
! 1125: /* Done */
! 1126: return (0);
! 1127: }
! 1128:
! 1129: /*
! 1130: * Output FSM_OUTPUT_DEAD message because we're dead.
! 1131: *
! 1132: * This is called when we reach the initial state after a fatal error.
! 1133: * We copy the reason information previously recorded via ppp_fsm_record().
! 1134: */
! 1135: static void
! 1136: ppp_fsm_output_dead(struct ppp_fsm *fsm)
! 1137: {
! 1138: struct ppp_fsm_output *output;
! 1139:
! 1140: /* Create new output message */
! 1141: if ((output = MALLOC(FSM_MTYPE, sizeof(*output))) == NULL) {
! 1142: ppp_fsm_syserr(fsm, "malloc");
! 1143: return;
! 1144: }
! 1145: memset(output, 0, sizeof(*output));
! 1146:
! 1147: /* Copy previously recorded output message, changing DOWN to DEAD */
! 1148: *output = fsm->dead;
! 1149:
! 1150: /* Send it */
! 1151: if (mesg_port_put(fsm->outport, output) == -1) {
! 1152: ppp_fsm_syserr(fsm, "mesg_port_put");
! 1153: FREE(FSM_MTYPE, output);
! 1154: }
! 1155: }
! 1156:
! 1157: /*
! 1158: * Build an FSM output structure.
! 1159: */
! 1160: static void
! 1161: ppp_fsm_output_build(struct ppp_fsm_output *output,
! 1162: enum ppp_fsmoutput type, va_list args)
! 1163: {
! 1164: output->type = type;
! 1165: switch (type) {
! 1166: case FSM_OUTPUT_OPEN:
! 1167: case FSM_OUTPUT_CLOSE:
! 1168: case FSM_OUTPUT_UP:
! 1169: break;
! 1170: case FSM_OUTPUT_DOWN:
! 1171: case FSM_OUTPUT_DEAD:
! 1172: output->u.down.reason = va_arg(args, int);
! 1173: switch (output->u.down.reason) {
! 1174: case FSM_REASON_SYSERR:
! 1175: output->u.down.u.error = va_arg(args, int);
! 1176: break;
! 1177: case FSM_REASON_CONF:
! 1178: case FSM_REASON_CODEREJ:
! 1179: output->u.down.u.code = va_arg(args, int);
! 1180: break;
! 1181: case FSM_REASON_PROTOREJ:
! 1182: output->u.down.u.proto = va_arg(args, int);
! 1183: break;
! 1184: default:
! 1185: break;
! 1186: }
! 1187: break;
! 1188: case FSM_OUTPUT_DATA:
! 1189: output->u.data.data = va_arg(args, u_char *);
! 1190: output->u.data.length = va_arg(args, u_int);
! 1191: break;
! 1192: case FSM_OUTPUT_PROTOREJ:
! 1193: output->u.proto = va_arg(args, int);
! 1194: break;
! 1195: default:
! 1196: assert(0);
! 1197: }
! 1198: }
! 1199:
! 1200: /*
! 1201: * Handle system error
! 1202: */
! 1203: static void
! 1204: ppp_fsm_syserr(struct ppp_fsm *fsm, const char *func)
! 1205: {
! 1206: LOG(LOG_ERR, "%s: %m", func);
! 1207: ppp_fsm_record(fsm, FSM_REASON_SYSERR, errno);
! 1208: }
! 1209:
! 1210: /*
! 1211: * Decode and log contents of an FSM packet
! 1212: */
! 1213: static void
! 1214: ppp_fsm_log_pkt(struct ppp_fsm *fsm, int sev,
! 1215: const char *prefix, const u_char *pkt)
! 1216: {
! 1217: const u_char *const payload = pkt + sizeof(struct ppp_fsm_pkt);
! 1218: struct ppp_fsm_pkt hdr;
! 1219: char buf[512] = { '\0' };
! 1220: u_int16_t dlen;
! 1221:
! 1222: memcpy(&hdr, pkt, sizeof(hdr));
! 1223: dlen = ntohs(hdr.length) - sizeof(hdr);
! 1224: switch (hdr.code) {
! 1225: case FSM_CODE_CONFIGREQ:
! 1226: case FSM_CODE_CONFIGNAK:
! 1227: case FSM_CODE_CONFIGREJ:
! 1228: case FSM_CODE_CONFIGACK:
! 1229: ppp_fsm_options_decode(fsm->inst->type->options,
! 1230: payload, dlen, buf, sizeof(buf));
! 1231: break;
! 1232: case FSM_CODE_CODEREJ:
! 1233: snprintf(buf, sizeof(buf), "code=%u", payload[0]);
! 1234: break;
! 1235: case FSM_CODE_PROTOREJ:
! 1236: snprintf(buf, sizeof(buf),
! 1237: "proto=0x%02x%02x", payload[0], payload[1]);
! 1238: break;
! 1239: case FSM_CODE_IDENT:
! 1240: case FSM_CODE_TERMREQ:
! 1241: case FSM_CODE_TERMACK:
! 1242: if (dlen > 0) {
! 1243: strlcpy(buf, "msg=\"", sizeof(buf));
! 1244: ppp_util_ascify(buf + strlen(buf),
! 1245: sizeof(buf) - strlen(buf), payload + 4, dlen - 4);
! 1246: strlcat(buf, "\"", sizeof(buf));
! 1247: }
! 1248: break;
! 1249: case FSM_CODE_TIMEREM:
! 1250: {
! 1251: u_int32_t remain;
! 1252: char tbuf[32];
! 1253:
! 1254: memcpy(&remain, payload + 4, 4);
! 1255: remain = ntohl(remain);
! 1256: if (remain == ~0)
! 1257: strlcpy(tbuf, "unlimited", sizeof(tbuf));
! 1258: else {
! 1259: snprintf(tbuf, sizeof(tbuf),
! 1260: "%lu seconds", (u_long)remain);
! 1261: }
! 1262: strlcpy(buf, "remaining=", sizeof(buf));
! 1263: strlcat(buf, tbuf, sizeof(buf));
! 1264: if (dlen > 4) {
! 1265: strlcat(buf, " msg=\"", sizeof(buf));
! 1266: ppp_util_ascify(buf + strlen(buf),
! 1267: sizeof(buf) - strlen(buf), payload + 8, dlen - 8);
! 1268: strlcat(buf, "\"", sizeof(buf));
! 1269: }
! 1270: break;
! 1271: }
! 1272: default:
! 1273: break;
! 1274: }
! 1275:
! 1276: /* Finally, log it */
! 1277: if (*buf == '\0')
! 1278: LOG(sev, "%s %s #%u", prefix, cd2str(hdr.code), hdr.id);
! 1279: else {
! 1280: LOG(sev, "%s %s #%u: %s", prefix,
! 1281: cd2str(hdr.code), hdr.id, buf);
! 1282: }
! 1283: }
! 1284:
! 1285: static const char *
! 1286: st2str(u_int state)
! 1287: {
! 1288: static const char *const snames[FSM_STATE_MAX] = {
! 1289: "INITIAL", "STARTING", "CLOSED", "STOPPED", "CLOSING",
! 1290: "STOPPING", "REQ-SENT", "ACK-RCVD", "ACK-SENT", "OPENED"
! 1291: };
! 1292: static char buf[16];
! 1293:
! 1294: if (state >= sizeof(snames) / sizeof(*snames)) {
! 1295: snprintf(buf, sizeof(buf), "?[%u]", state);
! 1296: return (buf);
! 1297: }
! 1298: return (snames[state]);
! 1299: }
! 1300:
! 1301: static const char *
! 1302: ev2str(u_int event)
! 1303: {
! 1304: static const char *const enames[FSM_EVENT_MAX] = {
! 1305: "UP", "DOWN", "OPEN", "CLOSE", "TO+", "TO-", "RCR+",
! 1306: "RCR-", "RCA", "RCN", "RTR", "RTA", "RXJ+", "RXJ-"
! 1307: };
! 1308: static char buf[16];
! 1309:
! 1310: if (event >= sizeof(enames) / sizeof(*enames)) {
! 1311: snprintf(buf, sizeof(buf), "?[%u]", event);
! 1312: return (buf);
! 1313: }
! 1314: return (enames[event]);
! 1315: }
! 1316:
! 1317: static const char *
! 1318: cd2str(u_int code)
! 1319: {
! 1320: static const char *const cnames[FSM_CODE_MAX] = {
! 1321: "Vendor", "Conf-Req", "Conf-Ack", "Conf-Nak", "Conf-Rej",
! 1322: "Term-Req", "Term-Ack", "Code-Rej", "Proto-Rej", "Echo-Req",
! 1323: "Echo-Rsp", "Disc-Req", "Ident", "Time-Rem", "Reset-Req",
! 1324: "Reset-Ack"
! 1325: };
! 1326: static char buf[16];
! 1327:
! 1328: if (code >= sizeof(cnames) / sizeof(*cnames)) {
! 1329: snprintf(buf, sizeof(buf), "?[%u]", code);
! 1330: return (buf);
! 1331: }
! 1332: return (cnames[code]);
! 1333: }
! 1334:
! 1335: /***********************************************************************
! 1336: PUBLIC DEBUGGING FUNCTIONS
! 1337: ***********************************************************************/
! 1338:
! 1339: /*
! 1340: * Return a string describing FSM output.
! 1341: */
! 1342: const char *
! 1343: ppp_fsm_output_str(struct ppp_fsm_output *output)
! 1344: {
! 1345: static char buf[256];
! 1346:
! 1347: switch (output->type) {
! 1348: case FSM_OUTPUT_OPEN:
! 1349: snprintf(buf, sizeof(buf), "OPEN");
! 1350: break;
! 1351: case FSM_OUTPUT_CLOSE:
! 1352: snprintf(buf, sizeof(buf), "CLOSE");
! 1353: break;
! 1354: case FSM_OUTPUT_UP:
! 1355: snprintf(buf, sizeof(buf), "UP");
! 1356: break;
! 1357: case FSM_OUTPUT_DOWN:
! 1358: snprintf(buf, sizeof(buf), "DOWN reason=%s",
! 1359: ppp_fsm_reason_str(output));
! 1360: break;
! 1361: case FSM_OUTPUT_DATA:
! 1362: snprintf(buf, sizeof(buf), "DATA type=%s",
! 1363: cd2str(output->u.data.data[0]));
! 1364: break;
! 1365: case FSM_OUTPUT_PROTOREJ:
! 1366: snprintf(buf, sizeof(buf), "PROTOREJ proto=0x%04x",
! 1367: output->u.proto);
! 1368: break;
! 1369: case FSM_OUTPUT_DEAD:
! 1370: snprintf(buf, sizeof(buf), "DEAD reason=%s",
! 1371: ppp_fsm_reason_str(output));
! 1372: break;
! 1373: default:
! 1374: snprintf(buf, sizeof(buf), "?[%u]?", output->type);
! 1375: break;
! 1376: }
! 1377: return (buf);
! 1378: }
! 1379:
! 1380: /*
! 1381: * Return a string describing a FSM_OUTPUT_DOWN or FSM_OUTPUT_DEAD output.
! 1382: */
! 1383: const char *
! 1384: ppp_fsm_reason_str(struct ppp_fsm_output *output)
! 1385: {
! 1386: static char buf[64];
! 1387:
! 1388: /* Sanity check */
! 1389: if (output->type != FSM_OUTPUT_DEAD
! 1390: && output->type != FSM_OUTPUT_DOWN)
! 1391: return ("?not FSM_OUTPUT_DOWN or FSM_OUTPUT_DEAD");
! 1392:
! 1393: /* Describe reason */
! 1394: switch (output->u.down.reason) {
! 1395: case FSM_REASON_CLOSE:
! 1396: strlcpy(buf, "administratively closed", sizeof(buf));
! 1397: break;
! 1398: case FSM_REASON_DOWN_FATAL:
! 1399: strlcpy(buf, "the underlying packet delivery"
! 1400: " layer failed (fatal)", sizeof(buf));
! 1401: break;
! 1402: case FSM_REASON_DOWN_NONFATAL:
! 1403: strlcpy(buf, "the underlying packet delivery"
! 1404: " layer failed (non-fatal)", sizeof(buf));
! 1405: break;
! 1406: case FSM_REASON_CONF:
! 1407: snprintf(buf, sizeof(buf),
! 1408: "rec'd %s after reaching opened state",
! 1409: cd2str(output->u.down.u.code));
! 1410: break;
! 1411: case FSM_REASON_TERM:
! 1412: snprintf(buf, sizeof(buf),
! 1413: "rec'd a Terminate-Request from peer");
! 1414: break;
! 1415: case FSM_REASON_CODEREJ:
! 1416: snprintf(buf, sizeof(buf),
! 1417: "rec'd a fatal Code-Reject (code=%s)",
! 1418: cd2str(output->u.down.u.code));
! 1419: break;
! 1420: case FSM_REASON_PROTOREJ:
! 1421: snprintf(buf, sizeof(buf),
! 1422: "rec'd a fatal Protocol-Reject (proto=0x%04x)",
! 1423: output->u.down.u.proto);
! 1424: break;
! 1425: case FSM_REASON_NEGOT:
! 1426: strlcpy(buf, "local and remote configurations"
! 1427: " are not compatible", sizeof(buf));
! 1428: break;
! 1429: case FSM_REASON_BADMAGIC:
! 1430: strlcpy(buf, "rec'd a packet with an invalid magic number",
! 1431: sizeof(buf));
! 1432: break;
! 1433: case FSM_REASON_LOOPBACK:
! 1434: strlcpy(buf, "a loopback condition was detected", sizeof(buf));
! 1435: break;
! 1436: case FSM_REASON_TIMEOUT:
! 1437: strlcpy(buf, "timed out waiting for an echo response",
! 1438: sizeof(buf));
! 1439: break;
! 1440: case FSM_REASON_SYSERR:
! 1441: snprintf(buf, sizeof(buf), "internal system error, error=%s",
! 1442: strerror(output->u.down.u.error));
! 1443: break;
! 1444: default:
! 1445: snprintf(buf, sizeof(buf), "?[%u]", output->u.down.reason);
! 1446: break;
! 1447: }
! 1448: return (buf);
! 1449: }
! 1450:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>