| /* |
| * upap.c - User/Password Authentication Protocol. |
| * |
| * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The name "Carnegie Mellon University" must not be used to |
| * endorse or promote products derived from this software without |
| * prior written permission. For permission or any legal |
| * details, please contact |
| * Office of Technology Transfer |
| * Carnegie Mellon University |
| * 5000 Forbes Avenue |
| * Pittsburgh, PA 15213-3890 |
| * (412) 268-4387, fax: (412) 268-7395 |
| * tech-transfer@andrew.cmu.edu |
| * |
| * 4. Redistributions of any form whatsoever must retain the following |
| * acknowledgment: |
| * "This product includes software developed by Computing Services |
| * at Carnegie Mellon University (http://www.cmu.edu/computing/)." |
| * |
| * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO |
| * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
| * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE |
| * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN |
| * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
| * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include "netif/ppp/ppp_opts.h" |
| #if PPP_SUPPORT && PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ |
| |
| /* |
| * @todo: |
| */ |
| |
| #if 0 /* UNUSED */ |
| #include <stdio.h> |
| #include <string.h> |
| #endif /* UNUSED */ |
| |
| #include "netif/ppp/ppp_impl.h" |
| |
| #include "netif/ppp/upap.h" |
| |
| #if PPP_OPTIONS |
| /* |
| * Command-line options. |
| */ |
| static option_t pap_option_list[] = { |
| { "hide-password", o_bool, &hide_password, |
| "Don't output passwords to log", OPT_PRIO | 1 }, |
| { "show-password", o_bool, &hide_password, |
| "Show password string in debug log messages", OPT_PRIOSUB | 0 }, |
| |
| { "pap-restart", o_int, &upap[0].us_timeouttime, |
| "Set retransmit timeout for PAP", OPT_PRIO }, |
| { "pap-max-authreq", o_int, &upap[0].us_maxtransmits, |
| "Set max number of transmissions for auth-reqs", OPT_PRIO }, |
| { "pap-timeout", o_int, &upap[0].us_reqtimeout, |
| "Set time limit for peer PAP authentication", OPT_PRIO }, |
| |
| { NULL } |
| }; |
| #endif /* PPP_OPTIONS */ |
| |
| /* |
| * Protocol entry points. |
| */ |
| static void upap_init(ppp_pcb *pcb); |
| static void upap_lowerup(ppp_pcb *pcb); |
| static void upap_lowerdown(ppp_pcb *pcb); |
| static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l); |
| static void upap_protrej(ppp_pcb *pcb); |
| #if PRINTPKT_SUPPORT |
| static int upap_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg); |
| #endif /* PRINTPKT_SUPPORT */ |
| |
| const struct protent pap_protent = { |
| PPP_PAP, |
| upap_init, |
| upap_input, |
| upap_protrej, |
| upap_lowerup, |
| upap_lowerdown, |
| NULL, |
| NULL, |
| #if PRINTPKT_SUPPORT |
| upap_printpkt, |
| #endif /* PRINTPKT_SUPPORT */ |
| #if PPP_DATAINPUT |
| NULL, |
| #endif /* PPP_DATAINPUT */ |
| #if PRINTPKT_SUPPORT |
| "PAP", |
| NULL, |
| #endif /* PRINTPKT_SUPPORT */ |
| #if PPP_OPTIONS |
| pap_option_list, |
| NULL, |
| #endif /* PPP_OPTIONS */ |
| #if DEMAND_SUPPORT |
| NULL, |
| NULL |
| #endif /* DEMAND_SUPPORT */ |
| }; |
| |
| static void upap_timeout(void *arg); |
| #if PPP_SERVER |
| static void upap_reqtimeout(void *arg); |
| static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len); |
| #endif /* PPP_SERVER */ |
| static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len); |
| static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len); |
| static void upap_sauthreq(ppp_pcb *pcb); |
| #if PPP_SERVER |
| static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen); |
| #endif /* PPP_SERVER */ |
| |
| |
| /* |
| * upap_init - Initialize a UPAP unit. |
| */ |
| static void upap_init(ppp_pcb *pcb) { |
| pcb->upap.us_user = NULL; |
| pcb->upap.us_userlen = 0; |
| pcb->upap.us_passwd = NULL; |
| pcb->upap.us_passwdlen = 0; |
| pcb->upap.us_clientstate = UPAPCS_INITIAL; |
| #if PPP_SERVER |
| pcb->upap.us_serverstate = UPAPSS_INITIAL; |
| #endif /* PPP_SERVER */ |
| pcb->upap.us_id = 0; |
| } |
| |
| |
| /* |
| * upap_authwithpeer - Authenticate us with our peer (start client). |
| * |
| * Set new state and send authenticate's. |
| */ |
| void upap_authwithpeer(ppp_pcb *pcb, const char *user, const char *password) { |
| |
| if(!user || !password) |
| return; |
| |
| /* Save the username and password we're given */ |
| pcb->upap.us_user = user; |
| pcb->upap.us_userlen = (u8_t)LWIP_MIN(strlen(user), 0xff); |
| pcb->upap.us_passwd = password; |
| pcb->upap.us_passwdlen = (u8_t)LWIP_MIN(strlen(password), 0xff); |
| pcb->upap.us_transmits = 0; |
| |
| /* Lower layer up yet? */ |
| if (pcb->upap.us_clientstate == UPAPCS_INITIAL || |
| pcb->upap.us_clientstate == UPAPCS_PENDING) { |
| pcb->upap.us_clientstate = UPAPCS_PENDING; |
| return; |
| } |
| |
| upap_sauthreq(pcb); /* Start protocol */ |
| } |
| |
| #if PPP_SERVER |
| /* |
| * upap_authpeer - Authenticate our peer (start server). |
| * |
| * Set new state. |
| */ |
| void upap_authpeer(ppp_pcb *pcb) { |
| |
| /* Lower layer up yet? */ |
| if (pcb->upap.us_serverstate == UPAPSS_INITIAL || |
| pcb->upap.us_serverstate == UPAPSS_PENDING) { |
| pcb->upap.us_serverstate = UPAPSS_PENDING; |
| return; |
| } |
| |
| pcb->upap.us_serverstate = UPAPSS_LISTEN; |
| if (pcb->settings.pap_req_timeout > 0) |
| TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout); |
| } |
| #endif /* PPP_SERVER */ |
| |
| /* |
| * upap_timeout - Retransmission timer for sending auth-reqs expired. |
| */ |
| static void upap_timeout(void *arg) { |
| ppp_pcb *pcb = (ppp_pcb*)arg; |
| |
| if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) |
| return; |
| |
| if (pcb->upap.us_transmits >= pcb->settings.pap_max_transmits) { |
| /* give up in disgust */ |
| ppp_error("No response to PAP authenticate-requests"); |
| pcb->upap.us_clientstate = UPAPCS_BADAUTH; |
| auth_withpeer_fail(pcb, PPP_PAP); |
| return; |
| } |
| |
| upap_sauthreq(pcb); /* Send Authenticate-Request */ |
| } |
| |
| |
| #if PPP_SERVER |
| /* |
| * upap_reqtimeout - Give up waiting for the peer to send an auth-req. |
| */ |
| static void upap_reqtimeout(void *arg) { |
| ppp_pcb *pcb = (ppp_pcb*)arg; |
| |
| if (pcb->upap.us_serverstate != UPAPSS_LISTEN) |
| return; /* huh?? */ |
| |
| auth_peer_fail(pcb, PPP_PAP); |
| pcb->upap.us_serverstate = UPAPSS_BADAUTH; |
| } |
| #endif /* PPP_SERVER */ |
| |
| |
| /* |
| * upap_lowerup - The lower layer is up. |
| * |
| * Start authenticating if pending. |
| */ |
| static void upap_lowerup(ppp_pcb *pcb) { |
| |
| if (pcb->upap.us_clientstate == UPAPCS_INITIAL) |
| pcb->upap.us_clientstate = UPAPCS_CLOSED; |
| else if (pcb->upap.us_clientstate == UPAPCS_PENDING) { |
| upap_sauthreq(pcb); /* send an auth-request */ |
| } |
| |
| #if PPP_SERVER |
| if (pcb->upap.us_serverstate == UPAPSS_INITIAL) |
| pcb->upap.us_serverstate = UPAPSS_CLOSED; |
| else if (pcb->upap.us_serverstate == UPAPSS_PENDING) { |
| pcb->upap.us_serverstate = UPAPSS_LISTEN; |
| if (pcb->settings.pap_req_timeout > 0) |
| TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout); |
| } |
| #endif /* PPP_SERVER */ |
| } |
| |
| |
| /* |
| * upap_lowerdown - The lower layer is down. |
| * |
| * Cancel all timeouts. |
| */ |
| static void upap_lowerdown(ppp_pcb *pcb) { |
| |
| if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */ |
| UNTIMEOUT(upap_timeout, pcb); /* Cancel timeout */ |
| #if PPP_SERVER |
| if (pcb->upap.us_serverstate == UPAPSS_LISTEN && pcb->settings.pap_req_timeout > 0) |
| UNTIMEOUT(upap_reqtimeout, pcb); |
| #endif /* PPP_SERVER */ |
| |
| pcb->upap.us_clientstate = UPAPCS_INITIAL; |
| #if PPP_SERVER |
| pcb->upap.us_serverstate = UPAPSS_INITIAL; |
| #endif /* PPP_SERVER */ |
| } |
| |
| |
| /* |
| * upap_protrej - Peer doesn't speak this protocol. |
| * |
| * This shouldn't happen. In any case, pretend lower layer went down. |
| */ |
| static void upap_protrej(ppp_pcb *pcb) { |
| |
| if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ) { |
| ppp_error("PAP authentication failed due to protocol-reject"); |
| auth_withpeer_fail(pcb, PPP_PAP); |
| } |
| #if PPP_SERVER |
| if (pcb->upap.us_serverstate == UPAPSS_LISTEN) { |
| ppp_error("PAP authentication of peer failed (protocol-reject)"); |
| auth_peer_fail(pcb, PPP_PAP); |
| } |
| #endif /* PPP_SERVER */ |
| upap_lowerdown(pcb); |
| } |
| |
| |
| /* |
| * upap_input - Input UPAP packet. |
| */ |
| static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l) { |
| u_char *inp; |
| u_char code, id; |
| int len; |
| |
| /* |
| * Parse header (code, id and length). |
| * If packet too short, drop it. |
| */ |
| inp = inpacket; |
| if (l < UPAP_HEADERLEN) { |
| UPAPDEBUG(("pap_input: rcvd short header.")); |
| return; |
| } |
| GETCHAR(code, inp); |
| GETCHAR(id, inp); |
| GETSHORT(len, inp); |
| if (len < UPAP_HEADERLEN) { |
| UPAPDEBUG(("pap_input: rcvd illegal length.")); |
| return; |
| } |
| if (len > l) { |
| UPAPDEBUG(("pap_input: rcvd short packet.")); |
| return; |
| } |
| len -= UPAP_HEADERLEN; |
| |
| /* |
| * Action depends on code. |
| */ |
| switch (code) { |
| case UPAP_AUTHREQ: |
| #if PPP_SERVER |
| upap_rauthreq(pcb, inp, id, len); |
| #endif /* PPP_SERVER */ |
| break; |
| |
| case UPAP_AUTHACK: |
| upap_rauthack(pcb, inp, id, len); |
| break; |
| |
| case UPAP_AUTHNAK: |
| upap_rauthnak(pcb, inp, id, len); |
| break; |
| |
| default: /* XXX Need code reject */ |
| break; |
| } |
| } |
| |
| #if PPP_SERVER |
| /* |
| * upap_rauth - Receive Authenticate. |
| */ |
| static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len) { |
| u_char ruserlen, rpasswdlen; |
| char *ruser; |
| char *rpasswd; |
| char rhostname[256]; |
| int retcode; |
| const char *msg; |
| int msglen; |
| |
| if (pcb->upap.us_serverstate < UPAPSS_LISTEN) |
| return; |
| |
| /* |
| * If we receive a duplicate authenticate-request, we are |
| * supposed to return the same status as for the first request. |
| */ |
| if (pcb->upap.us_serverstate == UPAPSS_OPEN) { |
| upap_sresp(pcb, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ |
| return; |
| } |
| if (pcb->upap.us_serverstate == UPAPSS_BADAUTH) { |
| upap_sresp(pcb, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ |
| return; |
| } |
| |
| /* |
| * Parse user/passwd. |
| */ |
| if (len < 1) { |
| UPAPDEBUG(("pap_rauth: rcvd short packet.")); |
| return; |
| } |
| GETCHAR(ruserlen, inp); |
| len -= sizeof (u_char) + ruserlen + sizeof (u_char); |
| if (len < 0) { |
| UPAPDEBUG(("pap_rauth: rcvd short packet.")); |
| return; |
| } |
| ruser = (char *) inp; |
| INCPTR(ruserlen, inp); |
| GETCHAR(rpasswdlen, inp); |
| if (len < rpasswdlen) { |
| UPAPDEBUG(("pap_rauth: rcvd short packet.")); |
| return; |
| } |
| |
| rpasswd = (char *) inp; |
| |
| /* |
| * Check the username and password given. |
| */ |
| retcode = UPAP_AUTHNAK; |
| if (auth_check_passwd(pcb, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen)) { |
| retcode = UPAP_AUTHACK; |
| } |
| BZERO(rpasswd, rpasswdlen); |
| |
| #if 0 /* UNUSED */ |
| /* |
| * Check remote number authorization. A plugin may have filled in |
| * the remote number or added an allowed number, and rather than |
| * return an authenticate failure, is leaving it for us to verify. |
| */ |
| if (retcode == UPAP_AUTHACK) { |
| if (!auth_number()) { |
| /* We do not want to leak info about the pap result. */ |
| retcode = UPAP_AUTHNAK; /* XXX exit value will be "wrong" */ |
| warn("calling number %q is not authorized", remote_number); |
| } |
| } |
| |
| msglen = strlen(msg); |
| if (msglen > 255) |
| msglen = 255; |
| #endif /* UNUSED */ |
| |
| upap_sresp(pcb, retcode, id, msg, msglen); |
| |
| /* Null terminate and clean remote name. */ |
| ppp_slprintf(rhostname, sizeof(rhostname), "%.*v", ruserlen, ruser); |
| |
| if (retcode == UPAP_AUTHACK) { |
| pcb->upap.us_serverstate = UPAPSS_OPEN; |
| ppp_notice("PAP peer authentication succeeded for %q", rhostname); |
| auth_peer_success(pcb, PPP_PAP, 0, ruser, ruserlen); |
| } else { |
| pcb->upap.us_serverstate = UPAPSS_BADAUTH; |
| ppp_warn("PAP peer authentication failed for %q", rhostname); |
| auth_peer_fail(pcb, PPP_PAP); |
| } |
| |
| if (pcb->settings.pap_req_timeout > 0) |
| UNTIMEOUT(upap_reqtimeout, pcb); |
| } |
| #endif /* PPP_SERVER */ |
| |
| /* |
| * upap_rauthack - Receive Authenticate-Ack. |
| */ |
| static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len) { |
| u_char msglen; |
| char *msg; |
| LWIP_UNUSED_ARG(id); |
| |
| if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */ |
| return; |
| |
| /* |
| * Parse message. |
| */ |
| if (len < 1) { |
| UPAPDEBUG(("pap_rauthack: ignoring missing msg-length.")); |
| } else { |
| GETCHAR(msglen, inp); |
| if (msglen > 0) { |
| len -= sizeof (u_char); |
| if (len < msglen) { |
| UPAPDEBUG(("pap_rauthack: rcvd short packet.")); |
| return; |
| } |
| msg = (char *) inp; |
| PRINTMSG(msg, msglen); |
| } |
| } |
| |
| pcb->upap.us_clientstate = UPAPCS_OPEN; |
| |
| auth_withpeer_success(pcb, PPP_PAP, 0); |
| } |
| |
| |
| /* |
| * upap_rauthnak - Receive Authenticate-Nak. |
| */ |
| static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len) { |
| u_char msglen; |
| char *msg; |
| LWIP_UNUSED_ARG(id); |
| |
| if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */ |
| return; |
| |
| /* |
| * Parse message. |
| */ |
| if (len < 1) { |
| UPAPDEBUG(("pap_rauthnak: ignoring missing msg-length.")); |
| } else { |
| GETCHAR(msglen, inp); |
| if (msglen > 0) { |
| len -= sizeof (u_char); |
| if (len < msglen) { |
| UPAPDEBUG(("pap_rauthnak: rcvd short packet.")); |
| return; |
| } |
| msg = (char *) inp; |
| PRINTMSG(msg, msglen); |
| } |
| } |
| |
| pcb->upap.us_clientstate = UPAPCS_BADAUTH; |
| |
| ppp_error("PAP authentication failed"); |
| auth_withpeer_fail(pcb, PPP_PAP); |
| } |
| |
| |
| /* |
| * upap_sauthreq - Send an Authenticate-Request. |
| */ |
| static void upap_sauthreq(ppp_pcb *pcb) { |
| struct pbuf *p; |
| u_char *outp; |
| int outlen; |
| |
| outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + |
| pcb->upap.us_userlen + pcb->upap.us_passwdlen; |
| p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PPP_CTRL_PBUF_TYPE); |
| if(NULL == p) |
| return; |
| if(p->tot_len != p->len) { |
| pbuf_free(p); |
| return; |
| } |
| |
| outp = (u_char*)p->payload; |
| MAKEHEADER(outp, PPP_PAP); |
| |
| PUTCHAR(UPAP_AUTHREQ, outp); |
| PUTCHAR(++pcb->upap.us_id, outp); |
| PUTSHORT(outlen, outp); |
| PUTCHAR(pcb->upap.us_userlen, outp); |
| MEMCPY(outp, pcb->upap.us_user, pcb->upap.us_userlen); |
| INCPTR(pcb->upap.us_userlen, outp); |
| PUTCHAR(pcb->upap.us_passwdlen, outp); |
| MEMCPY(outp, pcb->upap.us_passwd, pcb->upap.us_passwdlen); |
| |
| ppp_write(pcb, p); |
| |
| TIMEOUT(upap_timeout, pcb, pcb->settings.pap_timeout_time); |
| ++pcb->upap.us_transmits; |
| pcb->upap.us_clientstate = UPAPCS_AUTHREQ; |
| } |
| |
| #if PPP_SERVER |
| /* |
| * upap_sresp - Send a response (ack or nak). |
| */ |
| static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen) { |
| struct pbuf *p; |
| u_char *outp; |
| int outlen; |
| |
| outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; |
| p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PPP_CTRL_PBUF_TYPE); |
| if(NULL == p) |
| return; |
| if(p->tot_len != p->len) { |
| pbuf_free(p); |
| return; |
| } |
| |
| outp = (u_char*)p->payload; |
| MAKEHEADER(outp, PPP_PAP); |
| |
| PUTCHAR(code, outp); |
| PUTCHAR(id, outp); |
| PUTSHORT(outlen, outp); |
| PUTCHAR(msglen, outp); |
| MEMCPY(outp, msg, msglen); |
| |
| ppp_write(pcb, p); |
| } |
| #endif /* PPP_SERVER */ |
| |
| #if PRINTPKT_SUPPORT |
| /* |
| * upap_printpkt - print the contents of a PAP packet. |
| */ |
| static const char* const upap_codenames[] = { |
| "AuthReq", "AuthAck", "AuthNak" |
| }; |
| |
| static int upap_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg) { |
| int code, id, len; |
| int mlen, ulen, wlen; |
| const u_char *user, *pwd, *msg; |
| const u_char *pstart; |
| |
| if (plen < UPAP_HEADERLEN) |
| return 0; |
| pstart = p; |
| GETCHAR(code, p); |
| GETCHAR(id, p); |
| GETSHORT(len, p); |
| if (len < UPAP_HEADERLEN || len > plen) |
| return 0; |
| |
| if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(upap_codenames)) |
| printer(arg, " %s", upap_codenames[code-1]); |
| else |
| printer(arg, " code=0x%x", code); |
| printer(arg, " id=0x%x", id); |
| len -= UPAP_HEADERLEN; |
| switch (code) { |
| case UPAP_AUTHREQ: |
| if (len < 1) |
| break; |
| ulen = p[0]; |
| if (len < ulen + 2) |
| break; |
| wlen = p[ulen + 1]; |
| if (len < ulen + wlen + 2) |
| break; |
| user = (const u_char *) (p + 1); |
| pwd = (const u_char *) (p + ulen + 2); |
| p += ulen + wlen + 2; |
| len -= ulen + wlen + 2; |
| printer(arg, " user="); |
| ppp_print_string(user, ulen, printer, arg); |
| printer(arg, " password="); |
| /* FIXME: require ppp_pcb struct as printpkt() argument */ |
| #if 0 |
| if (!pcb->settings.hide_password) |
| #endif |
| ppp_print_string(pwd, wlen, printer, arg); |
| #if 0 |
| else |
| printer(arg, "<hidden>"); |
| #endif |
| break; |
| case UPAP_AUTHACK: |
| case UPAP_AUTHNAK: |
| if (len < 1) |
| break; |
| mlen = p[0]; |
| if (len < mlen + 1) |
| break; |
| msg = (const u_char *) (p + 1); |
| p += mlen + 1; |
| len -= mlen + 1; |
| printer(arg, " "); |
| ppp_print_string(msg, mlen, printer, arg); |
| break; |
| default: |
| break; |
| } |
| |
| /* print the rest of the bytes in the packet */ |
| for (; len > 0; --len) { |
| GETCHAR(code, p); |
| printer(arg, " %.2x", code); |
| } |
| |
| return p - pstart; |
| } |
| #endif /* PRINTPKT_SUPPORT */ |
| |
| #endif /* PPP_SUPPORT && PAP_SUPPORT */ |