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>