/* PPP Compression Control Protocol */ #ident "$Revision: 1.17 $" #include #include #include #include #include "ppp.h" static void ccp_scr(struct ppp*); static void ccp_sca(struct ppp*, struct fsm*); static void ccp_act(struct ppp*, enum fsm_action); static struct fsm_ops ccp_ops = { ccp_scr, ccp_sca, fsm_scn, fsm_str, fsm_sta, fsm_scj, fsm_ser, ccp_act }; #define C ppp->ccp #define CNAME ppp->ccp.fsm.name #define STRUCT_PKT struct ccp_pkt #define STRUCT_CF struct ccp_cf #define BASE_P(bufp) (&(bufp)->ccp_un.cf[0]) /* shape of a CCP packet */ STRUCT_PKT { u_char code; u_char id; u_short len; union { STRUCT_CF { /* Configure-Req, Ack, Nak, Reject */ u_char type; u_char len; union { u_char info[1]; struct { /* common compression type */ u_char clen; u_char copt; } com; struct { u_char vers:3; u_char bits:5; } bsd; } cf_un; } cf[1]; struct { /* code reject */ u_char cj_code; u_char cj_id; u_short cj_len; u_char cj_info[1]; } cj; } ccp_un; }; /* CCP Configuration Options */ #define CCP_CO_PRED1 1 /* Predictor type 1 */ #define CCP_CO_PRED1_L 2 #define CCP_CO_PRED2 2 /* Predictor type 2 */ #define CCP_CO_PJ 3 /* Puddle Jumper */ #define CCP_CO_HP 16 /* Hewlett-Packard PPC */ #define CCP_CO_STAC 17 /* Stac Electronics LZS */ #define CCP_CO_MSOFT 18 /* Microsoft PPC */ #define CCP_CO_GANDALF 19 /* Gandalf FZA */ #define CCP_CO_V42BIS 20 /* V.42bis compression */ #define CCP_CO_BSD 21 /* UNIX LZW Compress */ #define CCP_CO_BSD_L 3 #define CCP_CO_DEFLATE 26 static char * ccp_name(u_char type) { static char res[32]; switch (type) { case CCP_CO_PRED1: return "Predictor Type 1"; case CCP_CO_PRED2: return "Predictor Type 2"; case CCP_CO_PJ: return "Puddle Jumper"; case CCP_CO_HP: return "Hewlett-Packard PPC"; case CCP_CO_STAC: return "Stac Electronics LZS"; case CCP_CO_MSOFT: return "Microsoft PPC"; case CCP_CO_GANDALF: return "Gandalf FZA"; case CCP_CO_V42BIS: return "V.42bis"; case CCP_CO_BSD: return "BSD Compress"; case CCP_CO_DEFLATE: return "Deflate"; default: sprintf(res,"CCP option #%#x", type); return res; } } /* get compression going */ void ccp_init(struct ppp *ppp) { C.fsm.protocol = PPP_CCP; C.fsm.ops = &ccp_ops; ccp_clear(ppp); fsm_init(&C.fsm); } /* Really tell the kernel */ static int /* 0=failure, 1=ok */ ccp_tx_change(struct ppp *ppp, u_char new) { struct ppp_arg arg; log_debug(6,CNAME, "SIOC_PPP_CCP_TX=%#x", new); bzero(&arg,sizeof(arg)); arg.v.ccp.proto = new; arg.v.ccp.debug = (debug > 2); arg.v.ccp.bits = C.neg_tx_bsd_bits; arg.v.ccp.unit = ppp->dv.unit; return (0 <= do_strioctl(ppp, SIOC_PPP_CCP_TX, &arg, "ioctl(CP TX change)")); } static int /* 0=failure, 1=ok */ ccp_rx_change(struct ppp *ppp, u_char new) { struct ppp_arg arg; log_debug(6,CNAME, "SIOC_PPP_CCP_RX=%#x", new); bzero(&arg,sizeof(arg)); arg.v.ccp.proto = new; arg.v.ccp.debug = (debug > 2); arg.v.ccp.bits = C.neg_rx_bsd_bits; /* adjust the MRU for the protocol field */ arg.v.ccp.mru = MAX(PPP_DEF_MRU,ppp->lcp.neg_mrru) + 2; arg.v.ccp.unit = ppp->dv.unit; return (0 <= do_strioctl(ppp, SIOC_PPP_CCP_RX, &arg, "ioctl(_CCP_RX change)")); } /* Tell the kernel which protocol to use, to reset the dictionary, * and to release input. */ static void ccp_set_rx(struct ppp *ppp, u_char new, int lvl) { if (new != C.neg_rx || debug > lvl) { C.neg_rx = new; if (new & SIOC_PPP_CCP_PRED1) { log_debug(1,CNAME,"turn on/reset RX %s", ccp_name(CCP_CO_PRED1)); } else if (new & SIOC_PPP_CCP_BSD) { log_debug(1,CNAME,"turn on/reset RX %d bit %s", C.neg_rx_bsd_bits, ccp_name(CCP_CO_BSD)); } else { log_debug(1,CNAME,"turn off RX compression"); } } (void)ccp_rx_change(ppp,new); } /* Tell the kernel which protocol to use and to reset the dictionary. * Output is released with the Configure-ACK in ccp_sca(). */ static void ccp_set_tx(struct ppp *ppp, u_char new, int lvl) { if (new != C.neg_tx || debug > lvl) { C.neg_tx = new; if (new & SIOC_PPP_CCP_PRED1) { log_debug(1,CNAME,"turn on/reset TX %s", ccp_name(CCP_CO_PRED1)); } else if (new & SIOC_PPP_CCP_BSD) { log_debug(1,CNAME,"turn on/reset TX %d bit %s", C.neg_tx_bsd_bits, ccp_name(CCP_CO_BSD)); } else { log_debug(1,CNAME,"turn off TX compression"); } } (void)ccp_tx_change(ppp,new); } void ccp_clear(struct ppp *ppp) { C.fsm.restart_ms = C.conf.restart_ms; C.fsm.restart_ms_lim = C.fsm.restart_ms; if (C.conf.max_rx_errors < 0) C.conf.max_rx_errors = 3; else if (C.conf.max_rx_errors == 0) C.conf.max_rx_errors = 1000; if (C.conf.max_tx_errors < 0) C.conf.max_tx_errors = 10; else if (C.conf.max_tx_errors == 0) C.conf.max_tx_errors = 1000; /* stay off if no compression is permitted */ C.bad_peer = (C.conf.rx != 0 || C.conf.tx != 0) ? 0 : 1; C.funny_ack = 0; C.neg_tx = 0; C.neg_rx = 0; C.prev_rx = SIOC_PPP_CCP_PROTOS; C.neg_rx_bsd_bits = C.conf.rx_bsd_bits; C.rej_rx = 0; C.nak_rx = 0; C.skip_rx = 0; C.reset_ack_pending = 0; /* do not tell kernel about compression until the device is * linked to the STREAMS mux */ if (ppp->dv.dev_index != -1) { (void)ccp_rx_change(ppp,0); (void)ccp_tx_change(ppp,0); } } /* give up because the peer cannot play */ static void ccp_quit(struct ppp *ppp) { if (!C.bad_peer) { C.bad_peer = 1; C.rej_rx = SIOC_PPP_CCP_PROTOS; ccp_event(ppp, FSM_CLOSE); } } /* Start compression machine now that the STREAMS modules are ready, * including linking this stream to the mux. */ int /* 0=failure, 1=ok */ ccp_go(struct ppp *l_ppp) { struct ppp *ppp = mp_ppp ? mp_ppp : l_ppp; /* MP state machine */ int res = 1; /* All of this must wait until connected to the STREAMS mux. * We cannot send a Configure-Ack until we are prepared to * clear the compression dictionary. We do not have a dictionary * until linked to the mux. */ if (l_ppp->dv.dev_index == -1) return 1; if (!C.bad_peer) { if (C.fsm.state <= FSM_INITIAL_0) ccp_event(ppp, FSM_OPEN); if (C.fsm.state <= FSM_STARTING_1) ccp_event(ppp, FSM_UP); } /* release output */ log_debug(6,CNAME, "SIOC_PPP_TX_OK=1"); if (0 > do_strioctl_ok(l_ppp, SIOC_PPP_TX_OK, 1, "ioctl(ccp_go() TX_OK on)")) res = 0; if (ppp->ipcp.fsm.state >= FSM_ACK_SENT_8) { log_debug(6,CNAME, "SIOC_PPP_IP_TX_OK=1"); if (0 > do_strioctl_ok(ppp, SIOC_PPP_IP_TX_OK, 1, "ioctl(ccp_go() IP_TX_OK on)")) res = 0; } return res; } /* Age the counts of compression problems */ void ccp_blip_age(struct ppp* ppp) { if (mp_ppp) ppp = mp_ppp; if (ppp) { C.cnts.rreq_rcvd_aged = C.cnts.rreq_rcvd_aged * 0.75; C.cnts.rreq_sent_aged = C.cnts.rreq_sent_aged * 0.75; } } /* Do something after a de-compression error. */ void ccp_blip(struct ppp *ppp, int force) /* do it even if already doing it */ { if (!force) { if (C.fsm.state <= FSM_REQ_SENT_6) return; log_debug(1,CNAME, "blip for bad compressed packet"); } /* If we recently complained to the peer about bad decompression, * then do not worry. */ if (C.reset_ack_pending && clk.tv_sec >= C.sent_reset_request && clk.tv_sec < C.sent_reset_request+2) { log_debug(2,CNAME,"omitting redundant Reset-Request"); return; } /* If the link has a bad error rate, then give up on CCP */ if (++C.cnts.rreq_sent_aged >= C.conf.max_rx_errors) { log_complain(CNAME, "input error rate %.1f>%d," " total=%d; give up on CCP", C.cnts.rreq_sent_aged, C.conf.max_rx_errors, C.cnts.rreq_sent); ccp_quit(ppp); return; } /* If the peer has rejected our Reset-Requests, then use the * original blip-out-of-OPENED-state hack to ask that the * compression dictionary be reset. */ if (C.seen_reset_rej) { /* Stop sending compressed packets, * send a Configure-Request, * and expect the same in response. */ ccp_set_rx(ppp,0,1); /* release input */ ccp_set_tx(ppp,0,1); /* stop compressed output */ log_debug(2, CNAME,"force %s->%s", fsm_state_name(C.fsm.state), fsm_state_name(FSM_CLOSED_2)); C.fsm.state = FSM_CLOSED_2; ccp_event(ppp, FSM_OPEN); return; } /* send reset-request */ OBUF_CP->code = PPP_CODE_RREQ; if (++C.fsm.id == PPP_CCP_DISARM_ID) ++C.fsm.id; OBUF_CP->id = C.fsm.id; OBUF_CP->len = 4; obuf.proto = PPP_CCP; obuf.bits = mp_ncp_bits; log_debug(1,CNAME,"send Reset-Request ID=%#x", C.fsm.id); ppp_send(ppp,&obuf,OBUF_CP->len); C.sent_reset_request = clk.tv_sec; C.reset_ack_pending = 1; C.cnts.rreq_sent++; } /* process result from FSM */ static void ccp_act(struct ppp *ppp, enum fsm_action act) { log_debug(2, CNAME,"action %s", fsm_action_name(act)); switch (act) { case FSM_TLU: break; case FSM_TLD: break; case FSM_TLS: case FSM_TLF: ccp_clear(ppp); break; } } /* Make something happen to the CCP machine. */ void ccp_event(struct ppp *ppp, enum fsm_event ev) { switch (ev) { case FSM_UP: /* stay dead if we do not want to play */ if (!C.bad_peer) { fsm_run(ppp,&C.fsm, ev); } else { log_debug(2, CNAME, "all compression disabled so stay off"); } break; case FSM_OPEN: case FSM_DOWN: case FSM_CLOSE: fsm_run(ppp,&C.fsm, ev); break; case FSM_TO_P: /* the restart timer has expired */ if (C.fsm.restart <= 0 || C.bad_peer) ev = FSM_TO_M; fsm_run(ppp,&C.fsm,ev); break; default: log_complain(CNAME,"unknown event #%d", ev); } } /* Parse a received Configure-Request. * This concerns compressed packets we will transmit. */ static enum fsm_event ccp_parse_cr(struct ppp *ppp) { STRUCT_CF *icfp; STRUCT_CF *jcfp = BASE_P(JBUF_CP); STRUCT_CF *ncfp = BASE_P(NBUF_CP); int i; # define BAD_LEN(cond, L) {if (icfp->len cond (L)) { \ log_complain(CNAME, " bad TX %s CR length=%d", \ ccp_name(icfp->type), icfp->len); \ ccp_quit(ppp); \ return FSM_RUC;}} rejbuf.proto = PPP_CCP; JBUF_CP->len = 4; nakbuf.proto = PPP_CCP; NBUF_CP->len = 4; log_debug(2,CNAME,"receive Configure-Request ID=%#x", IBUF_CP->id); /* "all options are always negotiated at once" */ C.new_tx = 0; /* Try the odd Ack of the first draft only once. * After one attempt, Nak until one compression choice is offered. */ if (C.funny_ack != 0) C.funny_ack = 2; /* stop compressing output & clear dictionary */ ccp_set_tx(ppp,0,1); for (icfp = BASE_P(IBUF_CP); icfp < (STRUCT_CF*)&ibuf.un.info[ibuf_info_len]; ADV(icfp)) { /* forget entire packet if the length of one option is bad */ if (icfp->len < 2) { log_complain(CNAME, " bad Configure-Request" " option length %#x", icfp->len); ccp_quit(ppp); return FSM_RUC; } switch (icfp->type) { case CCP_CO_BSD: BAD_LEN(!=, CCP_CO_BSD_L); /* Reject old version or impossible code size */ i = icfp->cf_un.bsd.bits; if (icfp->cf_un.bsd.vers != BSD_VERS || i < MIN_BSD_BITS) { log_complain(CNAME, " bad %d bit, vers %d" " TX BSD Compress", i, icfp->cf_un.bsd.vers); C.rej_rx |= SIOC_PPP_CCP_BSD; break; } /* Reject BSD Compress if turned off. * If we are doing a funny Ack, we will * later decide to not send the Reject. */ if (!(C.conf.tx & SIOC_PPP_CCP_BSD)) break; /* Reject BSD Compress if we have chosen otherwise. */ if (C.new_tx != 0) break; /* Because during incompressible data we * cannot send CLEAR symbols, we cannot let * the peer use a larger dictionary than * we use. */ if (i > (int)C.conf.tx_bsd_bits) { ncfp->cf_un.bsd.bits = C.conf.tx_bsd_bits; ncfp->cf_un.bsd.vers = BSD_VERS; GEN_CO(ncfp, CCP_CO_BSD, bsd); log_debug(2,CNAME, " ask for %d instead of" " %d bit BSD compression", C.conf.tx_bsd_bits, i); C.funny_ack = 2; continue; } /* We know this is the first usable choice. * Accept the code length proposed by the peer * if it is smaller than our preference. */ C.new_tx = SIOC_PPP_CCP_BSD; C.neg_tx_bsd_bits = C.conf.tx_bsd_bits; log_debug(2,CNAME," accept %d bit BSD compression", icfp->cf_un.bsd.bits); /* Generate ordinary Ack when only 1 choice offered. */ if (ibuf_info_len == 3+4) continue; /* If we have previously sent a funny Ack, * then do not do it again. * If this turns out to be the only choice, we will * Ack it. Otherwise we must reject the alternatives. */ if (C.funny_ack != 0) continue; /* generate strange CCP Ack (from the 1st draft) */ C.funny_ack = 1; ncfp = BASE_P(NBUF_CP); ncfp->cf_un.bsd.bits = icfp->cf_un.bsd.bits; ncfp->cf_un.bsd.vers = BSD_VERS; GEN_CO(ncfp, CCP_CO_BSD, bsd); NBUF_CP->len = 4 + ((char*)ncfp - (char*)BASE_P(NBUF_CP)); continue; case CCP_CO_PRED1: BAD_LEN(!=,CCP_CO_PRED1_L); /* Reject Predictor if turned off. * If we are doing a funny Ack, we will * later decide to not send the Reject. */ if (!(C.conf.tx & SIOC_PPP_CCP_PRED1)) break; /* Reject if we have chosen otherwise. */ if (C.new_tx != 0) break; /* We know this is the first usable choice. */ C.new_tx = SIOC_PPP_CCP_PRED1; log_debug(2, CNAME," accept Predictor 1"); /* Generate ordinary Ack when only 1 choice offered. */ if (ibuf_info_len == 2+4) continue; /* If we have previously sent a funny Ack, * then do not do it again. * If this turns out to be the only choice, we will * Ack it. Otherwise we must reject the alternatives. */ if (C.funny_ack != 0) continue; /* Generate the peculiar CCP Ack (from the 1st draft) */ C.funny_ack = 1; ncfp = BASE_P(NBUF_CP); GEN_CO_BOOL(ncfp, CCP_CO_PRED1); NBUF_CP->len = 4 + ((char*)ncfp - (char*)BASE_P(NBUF_CP)); continue; } /* reject anything we do not like */ if (C.funny_ack == 1) { log_debug(2,CNAME," would have rejected TX %s", ccp_name(icfp->type)); } else { log_debug(2,CNAME," reject TX %s", ccp_name(icfp->type)); bcopy(icfp, jcfp, icfp->len); ADV(jcfp); } } if (C.funny_ack == 1) return FSM_RCR_P; NBUF_CP->len = 4+((char*)ncfp - (char*)BASE_P(NBUF_CP)); JBUF_CP->len = 4+((char*)jcfp - (char*)BASE_P(JBUF_CP)); return ((JBUF_CP->len == 4 && NBUF_CP->len == 4) ? FSM_RCR_P : FSM_RCR_M); #undef BAD_LEN } /* Parse a received Configure-Ack. * This concerns received compressed packets. */ static void ccp_parse_ca(struct ppp *ppp) { STRUCT_CF *icfp; int i; u_char new_rx; int num_rx; # define BAD_LEN(cond, L) {if (icfp->len cond (L)) { \ log_complain(CNAME, " bad RX %s CA length=%d", \ ccp_name(icfp->type), icfp->len); \ ccp_quit(ppp); \ continue;}} log_debug(2,CNAME,"receive Configure-ACK ID=%#x", IBUF_CP->id); /* The other machine is supposed to send us a Configure-Ack * containing exactly one of our unchanged Configure-Request * options. So insist on exactly one. * * Instead of not checking the contents of the option or * insisting that it be identical, accept any changes * if possible. */ /* "all options are always negotiated at once" */ new_rx = 0; num_rx = 0; for (icfp = BASE_P(IBUF_CP); icfp < (STRUCT_CF*)&ibuf.un.info[ibuf_info_len]; ADV(icfp)) { num_rx++; switch (icfp->type) { case CCP_CO_BSD: BAD_LEN(!=,CCP_CO_BSD_L); i = icfp->cf_un.bsd.bits; if (i > (int)C.conf.rx_bsd_bits || i < MIN_BSD_BITS || icfp->cf_un.bsd.vers != BSD_VERS) { /* It was not what we configure-requested. * So give up. */ log_complain(CNAME," bad RX BSD compress" " Configure-Ack code size=%d" " vers=%d", i, icfp->cf_un.bsd.vers); ccp_quit(ppp); continue; } C.neg_rx_bsd_bits = i; new_rx = SIOC_PPP_CCP_BSD; break; case CCP_CO_PRED1: BAD_LEN(!=,CCP_CO_PRED1_L); new_rx = SIOC_PPP_CCP_PRED1; break; default: log_complain(CNAME," bogus Configure-ACK for %s", ccp_name(icfp->type)); ccp_quit(ppp); break; } } /* there should be exactly one option in the packet */ if (num_rx > 1 && !C.bad_peer) { log_complain(CNAME," wrong number of Configure-ACK options"); ccp_quit(ppp); } if (C.bad_peer) { /* Give up and say no more about compression if the * peer is an idiot. */ new_rx = 0; } C.prev_rx = new_rx; /* clear dictionary and release IP input */ ccp_set_rx(ppp,new_rx,1); #undef BAD_LEN } /* Parse a received Configure-Nak. * This concerns compressed packets we might receive. */ static void ccp_parse_cn(struct ppp *ppp) { STRUCT_CF *icfp; int i; log_debug(2,CNAME,"receive Configure-NAK ID=%#x", IBUF_CP->id); C.fsm.nak_recv++; for (icfp = BASE_P(IBUF_CP); icfp < (STRUCT_CF*)&ibuf.un.info[ibuf_info_len]; ADV(icfp)) { /* forget entire packet if the length of one option is bad */ if (icfp->len < 2) { log_complain(CNAME," bad Configure-Nak option " "length %#x", icfp->len); break; } switch (icfp->type) { case CCP_CO_BSD: i = icfp->cf_un.bsd.bits; if (i <= (int)C.conf.rx_bsd_bits && i >= MIN_BSD_BITS && icfp->cf_un.bsd.vers == BSD_VERS) { C.neg_rx_bsd_bits = i; log_debug(2,CNAME," peer wants" " %d-bit RX BSD Compress", i); } else { log_complain(CNAME," bad %d bit," " vers %d RX BSD compress", i, icfp->cf_un.bsd.vers); } C.nak_rx |= SIOC_PPP_CCP_BSD; C.skip_rx &= ~SIOC_PPP_CCP_BSD; break; case CCP_CO_PRED1: /* There are no Predictor parameters to NAK. * Maybe the peer wants us to pick one. */ log_debug(2,CNAME," peer refuses Predictor 1"); C.nak_rx |= SIOC_PPP_CCP_PRED1; C.skip_rx &= ~SIOC_PPP_CCP_PRED1; break; default: log_complain(CNAME, " ignore Configure-Nak for RX %s", ccp_name(icfp->type)); ccp_quit(ppp); break; } } } /* Parse a received Configure-Reject * This concerns compressed packets we won't be receiving after all. */ static void ccp_parse_confj(struct ppp *ppp) { STRUCT_CF *icfp; log_debug(2,CNAME,"receive Configure-Reject ID=%#x", IBUF_CP->id); C.fsm.nak_recv++; for (icfp = BASE_P(IBUF_CP); icfp < (STRUCT_CF*)&ibuf.un.info[ibuf_info_len]; ADV(icfp)) { /* forget entire packet if the length of one option is bad */ if (icfp->len < 2) { log_complain(CNAME, " bad Configure-Reject" " option length %#x", icfp->len); ccp_quit(ppp); break; } switch (icfp->type) { case CCP_CO_BSD: log_debug(2,CNAME, " peer is rejecting BSD Compress"); C.rej_rx |= SIOC_PPP_CCP_BSD; continue; case CCP_CO_PRED1: log_debug(2,CNAME," peer is rejecting Predictor 1"); C.rej_rx |= SIOC_PPP_CCP_PRED1; continue; default: log_complain(CNAME, " bogus Configure-Reject type %#x", icfp->type); ccp_quit(ppp); } } /* If the peer is rejecting an empty request, then give up. * If our request was not empty, then complain. */ if (!C.bad_peer && icfp == BASE_P(IBUF_CP)) { if ((C.conf.rx & ~C.rej_rx & ~C.skip_rx & C.prev_rx) != 0) log_complain(CNAME, " bogus peer Configure-Reject" " is empty"); ccp_quit(ppp); } } /* FSM action to send a Configure-Request */ static void ccp_scr(struct ppp *ppp) { struct ppp_arg arg; STRUCT_CF *p = BASE_P(OBUF_CP); u_char rx, rx_req; static char *reqs[] = { "none", "BSD Compress", "Predictor 1", "BSD Compress and Predictor 1"}; /* If there are no compression protocols that we can receive, * then send an empty Configure-Request. This gets us to the * Opened state so we can transmit compressed packets. */ if (++C.fsm.id == PPP_CCP_DISARM_ID) ++C.fsm.id; rx_req = 0; rx = (C.conf.rx & ~C.rej_rx & ~C.skip_rx & C.prev_rx); if (0 != rx) { /* Tell the kernel to stop accepting data packets as soon as * it sees the Configure-ACK. This is necessary to ensure * that we start running uncompressed data packets through * the dictionary starting with the very first packet * after the Configure-ACK. */ log_debug(6,CNAME,"SIOC_PPP_CCP_ARM_CA=%#x", C.fsm.id); bzero(&arg,sizeof(arg)); arg.v.ccp_arm.id = C.fsm.id; (void)do_strioctl(ppp, SIOC_PPP_CCP_ARM_CA, &arg, "ioctl(_CCP_ARM_CA)"); /* Request the compression protocols we like. */ if (rx & SIOC_PPP_CCP_BSD) { p->cf_un.bsd.vers = BSD_VERS; p->cf_un.bsd.bits = C.neg_rx_bsd_bits; GEN_CO(p, CCP_CO_BSD, bsd); rx_req = 1; /* If we have heard a Nak for BSD Compress, * then ask only for it. */ if (C.nak_rx & SIOC_PPP_CCP_BSD) { rx &= ~SIOC_PPP_CCP_PRED1; C.skip_rx |= SIOC_PPP_CCP_PRED1; } } if (rx & SIOC_PPP_CCP_PRED1) { GEN_CO_BOOL(p, CCP_CO_PRED1); rx_req |= 2; } } obuf.proto = PPP_CCP; obuf.bits = mp_ncp_bits; OBUF_CP->code = PPP_CODE_CR; OBUF_CP->id = C.fsm.id; OBUF_CP->len = 4+((char*)p - (char*)BASE_P(OBUF_CP)); if (0 != (rx_req & 1)) log_debug(2,CNAME, "send Configure-Request ID=%#x for %d bit %s", C.fsm.id, C.neg_rx_bsd_bits, reqs[rx_req]); else log_debug(2,CNAME,"send Configure-Request ID=%#x for %s", C.fsm.id, reqs[rx_req]); ppp_send(ppp,&obuf,OBUF_CP->len); } /* process incoming CCP frame */ void ccp_ipkt(struct ppp *ppp) { #define CK_ID() {if (fsm_ck_id(&C.fsm)) { \ C.cnts.bad_id++; \ return; \ }} /* forget it if everything is turned off or the peer is broken. */ if (C.conf.rx == 0 && C.conf.tx == 0) { log_debug(2, CNAME, "Protocol-Reject %s" " because all compression disabled", fsm_code_name(IBUF_CP->code)); lcp_pj(ppp,CNAME); return; } if (C.bad_peer) { lcp_pj(ppp,CNAME); return; } if (ibuf_info_len < (int)IBUF_CP->len) { log_complain(CNAME, "dropping %s with %d bytes but claiming %d", fsm_code_name(IBUF_CP->code), ibuf_info_len, IBUF_CP->len); C.cnts.bad_len++; /* bad packet length */ return; } if (ppp->phase != NET_PHASE) { log_debug(1,CNAME,"dropping %s because in %s, not %s phase", fsm_code_name(IBUF_CP->code), phase_name(ppp->phase), phase_name(NET_PHASE)); return; } if (C.fsm.state <= FSM_STARTING_1) { log_debug(1,CNAME,"dropping %s packet because in %s", fsm_code_name(IBUF_CP->code), fsm_state_name(C.fsm.state)); return; } switch (IBUF_CP->code) { case PPP_CODE_CR: /* Configure-Request */ /* When we receive a Configure-Request when things seemed * to be fine, it can mean the peer does not understand * Reset-Request, has lost compression synchronization, * and wants us to reset our transmit-dictionary. */ fsm_run(ppp, &C.fsm, ccp_parse_cr(ppp)); break; case PPP_CODE_CA: /* Configure-Ack */ CK_ID(); C.fsm.nak_recv = 0; ccp_parse_ca(ppp); fsm_run(ppp, &C.fsm, FSM_RCA); break; case PPP_CODE_CN: /* Configure-Nak */ CK_ID(); ccp_parse_cn(ppp); if (C.fsm.nak_recv > ppp->conf.max_fail) { log_complain(CNAME,"giving up after %d" " Configure-NAKs or Rejects", C.fsm.nak_recv); fsm_run(ppp, &C.fsm, FSM_RXJ_M); } else { fsm_run(ppp, &C.fsm, FSM_RCN); } break; case PPP_CODE_CONFJ: /* Configure-Reject */ CK_ID(); ccp_parse_confj(ppp); fsm_run(ppp, &C.fsm, FSM_RCN); break; case PPP_CODE_TR: /* Terminate-Request */ log_ipkt(1,CNAME,"receive Terminate-Request:"); /* If the peer has rejected all of our suggestions, * then give up on CCP entirely. */ if ((C.conf.rx & ~C.rej_rx) == 0) ccp_quit(ppp); fsm_run(ppp, &C.fsm, FSM_RTR); break; case PPP_CODE_TA: /* Terminate-Ack */ log_ipkt(2,CNAME,"receive Terminate-Ack:"); fsm_run(ppp, &C.fsm, FSM_RTA); break; case PPP_CODE_CJ: /* Code-Reject */ C.cnts.rcvd_code_rej++; /* If the peer is rejecting a reset request, simply * remember not to send one again and then blip out of OPEN. */ if (ibuf_info_len > 4 && IBUF_CP->ccp_un.cj.cj_code == PPP_CODE_RREQ) { log_complain(CNAME,"receive %s for %s", fsm_code_name(PPP_CODE_CJ), fsm_code_name(PPP_CODE_RREQ)); C.seen_reset_rej = 1; ccp_blip(ppp,1); } else { /* Otherwise turn CCP off */ fsm_rcj(ppp, &C.fsm); log_complain(CNAME, "give up"); ccp_quit(ppp); } break; case PPP_CODE_RREQ: C.cnts.rreq_rcvd++; if (C.fsm.state < FSM_REQ_SENT_6) { log_debug(2,CNAME, "receive stray Reset-Request ID=%#x", IBUF_CP->id); break; } log_debug(2,CNAME,"receive Reset-Request ID=%#x", IBUF_CP->id); /* If the link has a bad error rate, then give up on CCP */ if (IBUF_CP->id != C.rcvd_rreq_id && ++C.cnts.rreq_rcvd_aged >= C.conf.max_tx_errors) { log_complain(CNAME, "output error rate %.1f>%d," " total=%d; give up on CCP", C.cnts.rreq_rcvd_aged, C.conf.max_tx_errors, C.cnts.rreq_rcvd); ccp_quit(ppp); break; } /* If the link is ok, then stop IP output, reset dictionary, * send Reset-Ack, and release IP output. */ (void)do_strioctl_ok(ppp, SIOC_PPP_IP_TX_OK, 0, "ioctl(CCP RREQ IP_TX_OK off)"); log_debug(2,CNAME,"send Reset-Ack ID=%#x", IBUF_CP->id); C.rcvd_rreq_id = IBUF_CP->id; IBUF_CP->code = PPP_CODE_RACK; ibuf.bits = mp_ncp_bits; ppp_send(ppp,&ibuf,IBUF_CP->len); ccp_set_tx(ppp,C.neg_tx,1); if (ppp->ipcp.fsm.state >= FSM_ACK_SENT_8) (void)do_strioctl_ok(ppp, SIOC_PPP_IP_TX_OK,1, "CCP RREQ IP_TX_OK on)"); break; case PPP_CODE_RACK: C.cnts.rack_rcvd++; if (C.fsm.state < FSM_REQ_SENT_6) { log_debug(1,CNAME,"receive stray Reset-Ack ID=%#x", IBUF_CP->id); if (!ppp->in_stop) ccp_set_rx(ppp,C.neg_rx,1); } else { log_debug(2,CNAME, (C.reset_ack_pending ? "receive Reset-Ack ID=%#x" : "receive redundant Reset-Ack ID=%#x"), IBUF_CP->id); C.reset_ack_pending = 0; ccp_set_rx(ppp,C.neg_rx,1); } break; default: C.cnts.rcvd_bad++; log_complain(CNAME,"bad %s", fsm_code_name(IBUF_CP->code)); fsm_run(ppp, &C.fsm, FSM_RUC); break; } #undef CK_ID } /* Send a Configure-ACK, but for a funny Ack, use the NAK buffer since * it is not the same as the previously received Configure-Request. */ /* ARGSUSED */ static void ccp_sca(struct ppp *ppp, struct fsm *fsm) { /* stop IP output. */ (void)do_strioctl_ok(ppp, SIOC_PPP_IP_TX_OK, 0, "ioctl(ccp_sca() IP_TX_OK off)"); if (C.funny_ack == 1) { NBUF_CP->code = PPP_CODE_CA; NBUF_CP->id = IBUF_CP->id; nakbuf.bits = mp_ncp_bits; log_debug(2,CNAME,"send special Configure-ACK ID=%#x", IBUF_CP->id); ppp_send(ppp,&nakbuf,NBUF_CP->len); C.fsm.nak_sent = 0; } else { fsm_sca(ppp, &C.fsm); } /* Restore output after starting to compress. */ ccp_set_tx(ppp,C.new_tx,1); if (ppp->ipcp.fsm.state >= FSM_ACK_SENT_8) (void)do_strioctl_ok(ppp, SIOC_PPP_IP_TX_OK, 1, "ioctl(ccp_sca() IP_TX_OK on)"); }