Annotation of embedaddon/libpdel/ppp/ppp_ccp.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_fsm_option.h"
! 44: #include "ppp/ppp_fsm.h"
! 45: #include "ppp/ppp_auth.h"
! 46: #include "ppp/ppp_node.h"
! 47: #include "ppp/ppp_ccp.h"
! 48:
! 49: #include <netgraph/ng_mppc.h>
! 50:
! 51: #ifndef MPPE_56
! 52: #define MPPE_56 0x00000080
! 53: #undef MPPE_BITS
! 54: #define MPPE_BITS 0x000000e0
! 55: #endif
! 56:
! 57: /*
! 58: * CCP FSM
! 59: *
! 60: * XXX This is hard coded to only support MPPC/MPPE compression.
! 61: * XXX It should be generalized to support multiple PPP compression types.
! 62: */
! 63:
! 64: #define MPPC_SUPPORTED (MPPC_BIT | MPPE_BITS | MPPE_STATELESS)
! 65:
! 66: /* Memory type */
! 67: #define CCP_MTYPE "ccp"
! 68:
! 69: /* CCP compression types */
! 70: enum ccp_type {
! 71: CCP_OPT_OUI =0, /* oui */
! 72: CCP_OPT_PRED1 =1, /* predictor type 1 */
! 73: CCP_OPT_PRED2 =2, /* predictor type 2 */
! 74: CCP_OPT_PUDDLE =3, /* puddle jumper */
! 75: CCP_OPT_HWPPC =16, /* hewlett-packard ppc */
! 76: CCP_OPT_STAC =17, /* stac electronics lzs */
! 77: CCP_OPT_MPPC =18, /* microsoft mppc/mppe */
! 78: CCP_OPT_GAND =19, /* gandalf fza */
! 79: CCP_OPT_V42BIS =20, /* v.42bis compression */
! 80: CCP_OPT_BSD =21, /* bsd lzw compress */
! 81: CCP_OPT_DEFLATE =24, /* gzip "deflate" compression */
! 82: };
! 83:
! 84: /* Supported and required FSM codes */
! 85: #define CCP_SUPPORTED_CODES \
! 86: (1 << FSM_CODE_CONFIGREQ) \
! 87: | (1 << FSM_CODE_CONFIGACK) \
! 88: | (1 << FSM_CODE_CONFIGNAK) \
! 89: | (1 << FSM_CODE_CONFIGREJ) \
! 90: | (1 << FSM_CODE_TERMREQ) \
! 91: | (1 << FSM_CODE_TERMACK) \
! 92: | (1 << FSM_CODE_CODEREJ) \
! 93: | (1 << FSM_CODE_RESETREQ) \
! 94: | (1 << FSM_CODE_RESETACK)
! 95: #define CCP_REQUIRED_CODES \
! 96: (1 << FSM_CODE_CONFIGREQ) \
! 97: | (1 << FSM_CODE_CONFIGACK) \
! 98: | (1 << FSM_CODE_CONFIGNAK) \
! 99: | (1 << FSM_CODE_CONFIGREJ) \
! 100: | (1 << FSM_CODE_TERMREQ) \
! 101: | (1 << FSM_CODE_TERMACK) \
! 102: | (1 << FSM_CODE_CODEREJ) \
! 103: | (1 << FSM_CODE_RESETREQ) \
! 104: | (1 << FSM_CODE_RESETACK)
! 105:
! 106: /* FSM options descriptors */
! 107: static opt_pr_t ppp_cpp_pr_mppc;
! 108:
! 109: static const struct ppp_fsm_optdesc ccp_opt_desc[] = {
! 110: { "OUI", CCP_OPT_OUI, 0, 255, 0, NULL },
! 111: { "Predictor-1",CCP_OPT_PRED1, 0, 255, 0, NULL },
! 112: { "Predictor-2",CCP_OPT_PRED2, 0, 255, 0, NULL },
! 113: { "Puddle", CCP_OPT_PUDDLE, 0, 255, 0, NULL },
! 114: { "HWPPC", CCP_OPT_HWPPC, 0, 255, 0, NULL },
! 115: { "Stac", CCP_OPT_STAC, 0, 255, 0, NULL },
! 116: { "MPPC/MPPE", CCP_OPT_MPPC, 4, 4, 1, ppp_cpp_pr_mppc },
! 117: { "Gandalf", CCP_OPT_GAND, 0, 255, 0, NULL },
! 118: { "v.42bis", CCP_OPT_V42BIS, 0, 255, 0, NULL },
! 119: { "BSD-LZW", CCP_OPT_BSD, 0, 255, 0, NULL },
! 120: { "Deflate", CCP_OPT_DEFLATE,0, 255, 0, NULL },
! 121: { NULL, 0, 0, 0, 0, NULL }
! 122: };
! 123:
! 124: /* FSM type for CCP */
! 125: static ppp_fsm_type_destroy_t ppp_ccp_destroy;
! 126: static ppp_fsm_type_build_conf_req_t ppp_ccp_build_conf_req;
! 127: static ppp_fsm_type_recv_conf_req_t ppp_ccp_recv_conf_req;
! 128: static ppp_fsm_type_recv_conf_rej_t ppp_ccp_recv_conf_rej;
! 129: static ppp_fsm_type_recv_conf_nak_t ppp_ccp_recv_conf_nak;
! 130: static ppp_fsm_type_recv_reset_req_t ppp_ccp_recv_reset_req;
! 131: static ppp_fsm_type_recv_reset_ack_t ppp_ccp_recv_reset_ack;
! 132:
! 133: const struct ppp_fsm_type ppp_fsm_ccp = {
! 134: "CCP",
! 135: PPP_PROTO_CCP,
! 136: CCP_SUPPORTED_CODES,
! 137: CCP_REQUIRED_CODES,
! 138: ccp_opt_desc,
! 139: NULL,
! 140: ppp_ccp_destroy,
! 141: ppp_ccp_build_conf_req,
! 142: ppp_ccp_recv_conf_req,
! 143: ppp_ccp_recv_conf_rej,
! 144: ppp_ccp_recv_conf_nak,
! 145: NULL,
! 146: ppp_ccp_recv_reset_req,
! 147: ppp_ccp_recv_reset_ack,
! 148: NULL
! 149: };
! 150:
! 151: /* CCP instance state */
! 152: struct ccp {
! 153: struct ppp_fsm_instance *inst; /* backpointer to instance */
! 154: struct ppp_ccp_config conf; /* initial config */
! 155: struct ppp_ccp_req req; /* current request state */
! 156: struct ppp_node *node; /* ng_ppp(4) node */
! 157: };
! 158:
! 159: /* Internal functions */
! 160: static ppp_node_recvmsg_t ppp_ccp_send_reset_req;
! 161:
! 162: /***********************************************************************
! 163: PUBLIC FUNCTIONS
! 164: ***********************************************************************/
! 165:
! 166: struct ppp_fsm_instance *
! 167: ppp_ccp_create(struct ppp_ccp_config *conf, struct ppp_node *node)
! 168: {
! 169: struct ppp_fsm_instance *inst;
! 170: struct ppp_ccp_req *req;
! 171: struct ccp *ccp = NULL;
! 172:
! 173: /* Construct instance object */
! 174: if ((inst = MALLOC(CCP_MTYPE, sizeof(*inst))) == NULL)
! 175: return (NULL);
! 176: memset(inst, 0, sizeof(*inst));
! 177: inst->type = &ppp_fsm_ccp;
! 178:
! 179: /* Attach private data */
! 180: if ((ccp = MALLOC(CCP_MTYPE, sizeof(*ccp))) == NULL)
! 181: goto fail;
! 182: memset(ccp, 0, sizeof(*ccp));
! 183: ccp->conf = *conf;
! 184: ccp->node = node;
! 185: ccp->inst = inst;
! 186: inst->arg = ccp;
! 187:
! 188: /* Initialize local request state */
! 189: req = &ccp->req;
! 190: req->mppc[PPP_SELF] = conf->mppc[PPP_SELF];
! 191: req->mppe40[PPP_SELF] = conf->mppe40[PPP_SELF];
! 192: req->mppe56[PPP_SELF] = conf->mppe56[PPP_SELF];
! 193: req->mppe128[PPP_SELF] = conf->mppe128[PPP_SELF];
! 194: req->mppe_stateless[PPP_SELF] = conf->mppe_stateless[PPP_SELF];
! 195:
! 196: /* Register handler for reset-req control message from ng_mppc(4) */
! 197: if (ppp_node_set_recvmsg(ccp->node, NGM_MPPC_COOKIE,
! 198: NGM_MPPC_RESETREQ, ppp_ccp_send_reset_req, ccp) == -1)
! 199: goto fail;
! 200:
! 201: /* Done */
! 202: return (inst);
! 203:
! 204: fail:
! 205: /* Clean up after failure */
! 206: if (ccp != NULL)
! 207: FREE(CCP_MTYPE, ccp);
! 208: FREE(CCP_MTYPE, inst);
! 209: return (NULL);
! 210: }
! 211:
! 212: /*
! 213: * Get CCP request state.
! 214: */
! 215: void
! 216: ppp_ccp_get_req(struct ppp_fsm *fsm, struct ppp_ccp_req *req)
! 217: {
! 218: struct ppp_fsm_instance *const inst = ppp_fsm_get_instance(fsm);
! 219: struct ccp *const ccp = inst->arg;
! 220:
! 221: assert(inst->type == &ppp_fsm_ccp);
! 222: memcpy(req, &ccp->req, sizeof(*req));
! 223: }
! 224:
! 225: /***********************************************************************
! 226: FSM CALLBACKS
! 227: ***********************************************************************/
! 228:
! 229: static void
! 230: ppp_ccp_destroy(struct ppp_fsm_instance *inst)
! 231: {
! 232: struct ccp *const ccp = inst->arg;
! 233:
! 234: ppp_node_set_recvmsg(ccp->node,
! 235: NGM_MPPC_COOKIE, NGM_MPPC_RESETREQ, NULL, NULL);
! 236: FREE(CCP_MTYPE, ccp);
! 237: FREE(CCP_MTYPE, inst);
! 238: }
! 239:
! 240: static int
! 241: ppp_ccp_build_conf_req(struct ppp_fsm_instance *fsm,
! 242: struct ppp_fsm_options *opts)
! 243: {
! 244: struct ccp *const ccp = (struct ccp *)fsm->arg;
! 245: struct ppp_ccp_req *const req = &ccp->req;
! 246: u_int32_t mppo = 0;
! 247:
! 248: /* Add MPPC/MPPE requested config options */
! 249: if (req->mppc[PPP_SELF])
! 250: mppo |= MPPC_BIT;
! 251: if (req->mppe40[PPP_SELF])
! 252: mppo |= MPPE_40;
! 253: if (req->mppe56[PPP_SELF])
! 254: mppo |= MPPE_56;
! 255: if (req->mppe128[PPP_SELF])
! 256: mppo |= MPPE_128;
! 257: if (req->mppe_stateless[PPP_SELF])
! 258: mppo |= MPPE_STATELESS;
! 259:
! 260: /* If no options left to try, don't ask for anything */
! 261: if ((mppo & (MPPC_BIT | MPPE_BITS)) == 0) {
! 262: errno = EINVAL;
! 263: return (-1);
! 264: }
! 265:
! 266: /* Add option */
! 267: mppo = htonl(mppo);
! 268: if (ppp_fsm_option_add(opts, CCP_OPT_MPPC, 4, &mppo) == -1)
! 269: return (-1);
! 270:
! 271: /* Done */
! 272: return (0);
! 273: }
! 274:
! 275: static int
! 276: ppp_ccp_recv_conf_req(struct ppp_fsm_instance *fsm,
! 277: struct ppp_fsm_options *crq, struct ppp_fsm_options *nak,
! 278: struct ppp_fsm_options *rej)
! 279: {
! 280: struct ccp *const ccp = (struct ccp *)fsm->arg;
! 281: struct ppp_ccp_config *const conf = &ccp->conf;
! 282: struct ppp_ccp_req *const req = &ccp->req;
! 283: int i;
! 284:
! 285: /* Initialize peer's request state */
! 286: req->mppc[PPP_PEER] = 0;
! 287: req->mppe40[PPP_PEER] = 0;
! 288: req->mppe56[PPP_PEER] = 0;
! 289: req->mppe128[PPP_PEER] = 0;
! 290: req->mppe_stateless[PPP_PEER] = 0;
! 291:
! 292: /* Process options */
! 293: for (i = 0; i < crq->num; i++) {
! 294: const struct ppp_fsm_option *const opt = &crq->opts[i];
! 295:
! 296: switch (opt->type) {
! 297: case CCP_OPT_MPPC:
! 298: {
! 299: u_int32_t obits;
! 300: u_int32_t bits;
! 301:
! 302: /* Get requested bits */
! 303: memcpy(&obits, opt->data, 4);
! 304: obits = ntohl(obits);
! 305: bits = obits;
! 306:
! 307: /* Filter out bits we can't handle */
! 308: bits &= MPPC_SUPPORTED;
! 309: if ((bits & MPPC_BIT) != 0 && !conf->mppc[PPP_PEER])
! 310: bits &= ~MPPC_BIT;
! 311: if ((bits & MPPE_40) != 0 && !conf->mppe40[PPP_PEER])
! 312: bits &= ~MPPE_40;
! 313: if ((bits & MPPE_56) != 0 && !conf->mppe56[PPP_PEER])
! 314: bits &= ~MPPE_56;
! 315: if ((bits & MPPE_128) != 0 && !conf->mppe128[PPP_PEER])
! 316: bits &= ~MPPE_128;
! 317: if ((bits & MPPE_STATELESS) != 0
! 318: && !conf->mppe_stateless[PPP_PEER])
! 319: bits &= ~MPPE_STATELESS;
! 320:
! 321: /*
! 322: * It doesn't really make sense to do MPPE encryption
! 323: * in only one direction. Also, Win95/98 PPTP can't
! 324: * handle uni-directional encryption. So if the remote
! 325: * side doesn't request encryption, try to prompt it.
! 326: * This is broken wrt. normal PPP negotiation: typical
! 327: * Microsoft.
! 328: */
! 329: if ((bits & MPPE_BITS) == 0) {
! 330: if (req->mppe40[PPP_SELF])
! 331: bits |= MPPE_40;
! 332: if (req->mppe56[PPP_SELF])
! 333: bits |= MPPE_56;
! 334: if (req->mppe128[PPP_SELF])
! 335: bits |= MPPE_128;
! 336: }
! 337:
! 338: /* Make sure we're not left with no options */
! 339: if ((bits & MPPE_BITS) == 0) {
! 340: if (ccp->conf.mppe40[PPP_SELF])
! 341: bits |= MPPE_40;
! 342: if (ccp->conf.mppe56[PPP_SELF])
! 343: bits |= MPPE_56;
! 344: if (ccp->conf.mppe128[PPP_SELF])
! 345: bits |= MPPE_128;
! 346: }
! 347:
! 348: /* Now choose the strongest encryption available */
! 349: if ((bits & MPPE_128) != 0)
! 350: bits &= ~(MPPE_56|MPPE_40);
! 351: else if ((bits & MPPE_56) != 0)
! 352: bits &= ~(MPPE_128|MPPE_40);
! 353: else if ((bits & MPPE_40) != 0)
! 354: bits &= ~(MPPE_128|MPPE_56);
! 355:
! 356: /* Send back a nak if there were any changes */
! 357: if (bits != obits) {
! 358: bits = htonl(bits);
! 359: if (ppp_fsm_option_add(nak,
! 360: opt->type, 4, &bits) == -1)
! 361: return (-1);
! 362: break;
! 363: }
! 364:
! 365: /* Peer's request is accepted */
! 366: req->mppc[PPP_PEER] = (bits & MPPC_BIT) != 0;
! 367: req->mppe40[PPP_PEER] = (bits & MPPE_40) != 0;
! 368: req->mppe56[PPP_PEER] = (bits & MPPE_56) != 0;
! 369: req->mppe128[PPP_PEER] = (bits & MPPE_128) != 0;
! 370: req->mppe_stateless[PPP_PEER]
! 371: = (bits & MPPE_STATELESS) != 0;
! 372: break;
! 373: }
! 374: default:
! 375: goto reject;
! 376: }
! 377:
! 378: /* OK */
! 379: continue;
! 380:
! 381: reject:
! 382: /* Reject this requested option */
! 383: if (ppp_fsm_option_add(rej,
! 384: opt->type, opt->len, opt->data) == -1)
! 385: return (-1);
! 386: }
! 387:
! 388: /* If there were no options, shut down */
! 389: if (crq->num == 0) {
! 390: errno = EINVAL;
! 391: return (-1);
! 392: }
! 393:
! 394: /* Done */
! 395: return (0);
! 396: }
! 397:
! 398: static int
! 399: ppp_ccp_recv_conf_rej(struct ppp_fsm_instance *fsm,
! 400: struct ppp_fsm_options *rej)
! 401: {
! 402: int i;
! 403:
! 404: for (i = 0; i < rej->num; i++) {
! 405: const struct ppp_fsm_option *const opt = &rej->opts[i];
! 406:
! 407: switch (opt->type) {
! 408: case CCP_OPT_MPPC:
! 409: errno = EINVAL;
! 410: return (-1);
! 411: default:
! 412: break;
! 413: }
! 414: }
! 415:
! 416: /* Done */
! 417: return (0);
! 418: }
! 419:
! 420: static int
! 421: ppp_ccp_recv_conf_nak(struct ppp_fsm_instance *fsm,
! 422: struct ppp_fsm_options *nak)
! 423: {
! 424: struct ccp *const ccp = (struct ccp *)fsm->arg;
! 425: struct ppp_ccp_req *const req = &ccp->req;
! 426: int i;
! 427:
! 428: for (i = 0; i < nak->num; i++) {
! 429: const struct ppp_fsm_option *const opt = &nak->opts[i];
! 430:
! 431: switch (opt->type) {
! 432: case CCP_OPT_MPPC:
! 433: {
! 434: u_int32_t bits;
! 435:
! 436: memcpy(&bits, opt->data, 4);
! 437: bits = ntohl(bits);
! 438:
! 439: /* Mask away bits the client didn't like */
! 440: if ((bits & MPPC_BIT) == 0)
! 441: req->mppc[PPP_SELF] = 0;
! 442: if ((bits & MPPE_40) == 0)
! 443: req->mppe40[PPP_SELF] = 0;
! 444: if ((bits & MPPE_56) == 0)
! 445: req->mppe56[PPP_SELF] = 0;
! 446: if ((bits & MPPE_128) == 0)
! 447: req->mppe128[PPP_SELF] = 0;
! 448: if ((bits & MPPE_STATELESS) == 0)
! 449: req->mppe_stateless[PPP_SELF] = 0;
! 450:
! 451: /* Make sure we're not left with no options */
! 452: if ((bits & MPPE_BITS) == 0) {
! 453: if (ccp->conf.mppe40[PPP_SELF])
! 454: bits |= MPPE_40;
! 455: if (ccp->conf.mppe56[PPP_SELF])
! 456: bits |= MPPE_56;
! 457: if (ccp->conf.mppe128[PPP_SELF])
! 458: bits |= MPPE_128;
! 459: }
! 460: break;
! 461: }
! 462: default:
! 463: break;
! 464: }
! 465: }
! 466:
! 467: /* Done */
! 468: return (0);
! 469: }
! 470:
! 471: /*
! 472: * Receive a Reset-Req from peer. Relay request to the ng_mppc(4) node.
! 473: */
! 474: static void
! 475: ppp_ccp_recv_reset_req(struct ppp_fsm_instance *fsm,
! 476: const u_char *data, u_int len)
! 477: {
! 478: struct ccp *const ccp = (struct ccp *)fsm->arg;
! 479:
! 480: ppp_node_send_msg(ccp->node, NG_PPP_HOOK_COMPRESS,
! 481: NGM_MPPC_COOKIE, NGM_MPPC_RESETREQ, NULL, 0);
! 482: }
! 483:
! 484: /*
! 485: * Receive a Reset-Ack.
! 486: */
! 487: static void
! 488: ppp_ccp_recv_reset_ack(struct ppp_fsm_instance *fsm,
! 489: const u_char *data, u_int len)
! 490: {
! 491: return; /* not used by MPPC/MPPE, just ignore it */
! 492: }
! 493:
! 494: /***********************************************************************
! 495: INTERNAL FUNCTIONS
! 496: ***********************************************************************/
! 497:
! 498: static void
! 499: ppp_ccp_send_reset_req(void *arg, struct ng_mesg *msg)
! 500: {
! 501: struct ccp *const ccp = arg;
! 502: struct ppp_fsm *const fsm = ccp->inst->fsm;
! 503:
! 504: ppp_fsm_send_reset_req(fsm, NULL, 0);
! 505: }
! 506:
! 507: static void
! 508: ppp_cpp_pr_mppc(const struct ppp_fsm_optdesc *desc,
! 509: const struct ppp_fsm_option *opt, char *buf, size_t bmax)
! 510: {
! 511: static const struct {
! 512: u_int32_t bit;
! 513: const char *name;
! 514: } mppe_bits[] = {
! 515: { MPPC_BIT, "MPPC" },
! 516: { MPPE_BITS, "MPPE" },
! 517: { MPPE_40, "40 bit" },
! 518: { MPPE_56, "56 bit" },
! 519: { MPPE_128, "128 bit" },
! 520: { MPPE_STATELESS, "stateless" },
! 521: { 0 }
! 522: };
! 523: u_int32_t mppo;
! 524: int first;
! 525: int i;
! 526:
! 527: if (opt->len < 4) {
! 528: snprintf(buf, bmax, "<truncated>");
! 529: return;
! 530: }
! 531: memcpy(&mppo, opt->data, 4);
! 532: mppo = ntohl(mppo);
! 533: *buf = '\0';
! 534: for (first = 1, i = 0; mppe_bits[i].bit != 0; i++) {
! 535: if ((mppo & mppe_bits[i].bit) != 0) {
! 536: snprintf(buf + strlen(buf), bmax - strlen(buf),
! 537: "%s%s", first ? "" : ", ", mppe_bits[i].name);
! 538: first = 0;
! 539: }
! 540: }
! 541: }
! 542:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>