1 /* $OpenBSD: ccp.c,v 1.8 2019/02/27 04:52:19 denis Exp $ */
2
3 /*-
4 * Copyright (c) 2009 Internet Initiative Japan Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28 /**@file
29 * This file provides functions for CCP (Compression Control Protocol).
30 * MPPE is supported as a CCP option.
31 * $Id: ccp.c,v 1.8 2019/02/27 04:52:19 denis Exp $
32 */
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/time.h>
36 #include <net/if_dl.h>
37 #include <netinet/in.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <syslog.h>
41 #include <string.h>
42 #include <event.h>
43
44 #include "npppd.h"
45 #include "fsm.h"
46
47 #ifdef CCP_DEBUG
48 #define CCPDEBUG(x) fsm_log(x)
49 #define CCP_ASSERT(x) ASSERT(x)
50 #else
51 #define CCPDEBUG(x)
52 #define CCP_ASSERT(x)
53 #endif
54
55 static int ccp_reqci (fsm *, u_char *, int *, int);
56 static void ccp_open (fsm *);
57 static void ccp_close (fsm *);
58 static void ccp_start (fsm *);
59 static void ccp_stop (fsm *);
60 static void ccp_resetci (fsm *);
61 static int ccp_cilen (fsm *);
62 static void ccp_addci (fsm *, u_char *, int *);
63 static int ccp_ackci (fsm *, u_char *, int);
64 static int ccp_rejci (fsm *, u_char *, int);
65 static int ccp_nakci (fsm *, u_char *, int);
66 static int ccp_nackackci (fsm *, u_char *, int, int, int);
67 static int ccp_ext (fsm *, int, int, u_char *, int);
68
69 static struct fsm_callbacks ccp_callbacks = {
70 .cilen = ccp_cilen,
71 .resetci = ccp_resetci,
72 .addci = ccp_addci,
73 .ackci = ccp_ackci,
74 .nakci = ccp_nakci,
75 .rejci = ccp_rejci,
76 .reqci = ccp_reqci,
77 .up = ccp_open,
78 .down = ccp_close,
79 .starting = ccp_start,
80 .finished = ccp_stop,
81 .extcode = ccp_ext,
82 .proto_name = "ccp",
83 };
84
85 /** Initialize the context for ccp */
86 void
ccp_init(ccp * _this,npppd_ppp * ppp)87 ccp_init(ccp *_this, npppd_ppp *ppp)
88 {
89 struct tunnconf *conf;
90
91 memset(_this, 0, sizeof(ccp));
92
93 _this->ppp = ppp;
94 _this->fsm.callbacks = &ccp_callbacks;
95 _this->fsm.protocol = PPP_PROTO_NCP | NCP_CCP;
96 _this->fsm.ppp = ppp;
97
98 fsm_init(&_this->fsm);
99
100 conf = ppp_get_tunnconf(ppp);
101 PPP_FSM_CONFIG(&_this->fsm, timeouttime, conf->ccp_timeout);
102 PPP_FSM_CONFIG(&_this->fsm, maxconfreqtransmits,
103 conf->ccp_max_configure);
104 PPP_FSM_CONFIG(&_this->fsm, maxtermtransmits,
105 conf->ccp_max_terminate);
106 PPP_FSM_CONFIG(&_this->fsm, maxnakloops,
107 conf->ccp_max_nak_loop);
108 }
109
110 /** Request Command Interpreter */
111 static int
ccp_reqci(fsm * f,u_char * pktp,int * lpktp,int reject_if_disagree)112 ccp_reqci(fsm *f, u_char *pktp, int *lpktp, int reject_if_disagree)
113 {
114 int type, len, rcode, lrej, lnak;
115 u_char *rejbuf, *nakbuf, *nakbuf0, *pktp0;
116 #ifdef USE_NPPPD_MPPE
117 uint32_t peer_bits, our_bits;
118 #endif
119 npppd_ppp *ppp;
120
121 ppp = f->ppp;
122
123 rejbuf = NULL;
124 rcode = CONFACK;
125 pktp0 = pktp;
126 lrej = 0;
127 lnak = 0;
128
129 if ((rejbuf = malloc(*lpktp)) == NULL) {
130 return rcode;
131 }
132 if ((nakbuf0 = malloc(*lpktp)) == NULL) {
133 free(rejbuf);
134 return rcode;
135 }
136 nakbuf = nakbuf0;
137 #define remlen() (*lpktp - (pktp - pktp0))
138
139 while (remlen() >= 2) {
140 GETCHAR(type, pktp);
141 GETCHAR(len, pktp);
142 if (len <= 0 || remlen() + 2 < len)
143 goto fail;
144
145 switch (type) {
146 #ifdef USE_NPPPD_MPPE
147 case CCP_MPPE:
148 if (len < 6)
149 goto fail;
150
151 if (ppp->mppe.enabled == 0)
152 goto reject;
153 GETLONG(peer_bits, pktp);
154 our_bits = mppe_create_our_bits(&ppp->mppe, peer_bits);
155 if (our_bits != peer_bits) {
156 if (reject_if_disagree) {
157 pktp -= 4;
158 goto reject;
159 }
160 if (lrej > 0) {
161 /* don't nak because we are doing rej */
162 } else {
163 PUTCHAR(type, nakbuf);
164 PUTCHAR(6, nakbuf);
165 PUTLONG(our_bits, nakbuf);
166 rcode = CONFNAK;
167 }
168 } else
169 ppp->ccp.mppe_p_bits = our_bits;
170 break;
171 reject:
172 #endif
173 default:
174 pktp -= 2;
175 memcpy(rejbuf + lrej, pktp, len);
176 lrej += len;
177 pktp += len;
178 rcode = CONFREJ;
179 }
180 continue;
181 }
182 fail:
183 switch (rcode) {
184 case CONFREJ:
185 memcpy(pktp0, rejbuf, lrej);
186 *lpktp = lrej;
187 break;
188 case CONFNAK:
189 len = nakbuf - nakbuf0;
190 memcpy(pktp0, nakbuf0, len);
191 *lpktp = len;
192 break;
193 }
194 free(rejbuf);
195 free(nakbuf0);
196
197 return rcode;
198 #undef remlen
199 }
200
201 static void
ccp_open(fsm * f)202 ccp_open(fsm *f)
203 {
204 ppp_ccp_opened(f->ppp);
205 }
206
207 static void
ccp_close(fsm * f)208 ccp_close(fsm *f)
209 {
210 }
211
212 static void
ccp_start(fsm * f)213 ccp_start(fsm *f)
214 {
215 }
216
217 static void
ccp_stop(fsm * f)218 ccp_stop(fsm *f)
219 {
220 #ifdef USE_NPPPD_MPPE
221 fsm_log(f, LOG_INFO, "CCP is stopped");
222 ppp_ccp_stopped(f->ppp);
223 #endif
224 }
225
226 static void
ccp_resetci(fsm * f)227 ccp_resetci(fsm *f)
228 {
229 #ifdef USE_NPPPD_MPPE
230 if (f->ppp->mppe_started == 0)
231 f->ppp->ccp.mppe_o_bits =
232 mppe_create_our_bits(&f->ppp->mppe, 0);
233 /* don't reset if the ccp is started. */
234 #endif
235 }
236
237 static int
ccp_cilen(fsm * f)238 ccp_cilen(fsm *f)
239 {
240 return f->ppp->mru;
241 }
242
243 /** Create a Confugre-Request */
244 static void
ccp_addci(fsm * f,u_char * pktp,int * lpktp)245 ccp_addci(fsm *f, u_char *pktp, int *lpktp)
246 {
247 u_char *pktp0;
248
249 pktp0 = pktp;
250
251 if (f->ppp->ccp.mppe_rej == 0) {
252 PUTCHAR(CCP_MPPE, pktp);
253 PUTCHAR(6, pktp);
254 PUTLONG(f->ppp->ccp.mppe_o_bits, pktp);
255
256 *lpktp = pktp - pktp0;
257 } else
258 *lpktp = 0;
259 }
260
261 static int
ccp_ackci(fsm * f,u_char * pktp,int lpkt)262 ccp_ackci(fsm *f, u_char *pktp, int lpkt)
263 {
264 return ccp_nackackci(f, pktp, lpkt, 0, 0);
265 }
266
267
268 static int
ccp_nakci(fsm * f,u_char * pktp,int lpkt)269 ccp_nakci(fsm *f, u_char *pktp, int lpkt)
270 {
271 return ccp_nackackci(f, pktp, lpkt, 1, 0);
272 }
273
274 static int
ccp_rejci(fsm * f,u_char * pktp,int lpkt)275 ccp_rejci(fsm *f, u_char *pktp, int lpkt)
276 {
277 return ccp_nackackci(f, pktp, lpkt, 0, 1);
278 }
279
280 static int
ccp_nackackci(fsm * f,u_char * pktp,int lpkt,int is_nak,int is_rej)281 ccp_nackackci(fsm *f, u_char *pktp, int lpkt, int is_nak, int is_rej)
282 {
283 int type, len;
284 u_char *pktp0;
285 #ifdef USE_NPPPD_MPPE
286 uint32_t peer_bits, our_bits;
287 #endif
288 npppd_ppp *ppp;
289
290 ppp = f->ppp;
291
292 pktp0 = pktp;
293
294 #define remlen() (lpkt - (pktp - pktp0))
295 while (remlen() >= 2) {
296 GETCHAR(type, pktp);
297 GETCHAR(len, pktp);
298 if (len <= 0 || remlen() + 2 < len)
299 goto fail;
300
301 switch (type) {
302 #ifdef USE_NPPPD_MPPE
303 case CCP_MPPE:
304 if (len < 6)
305 goto fail;
306 if (is_rej) {
307 f->ppp->ccp.mppe_rej = 1;
308 return 1;
309 }
310 if (ppp->mppe_started != 0) {
311 /* resend silently */
312 return 1;
313 }
314 GETLONG(peer_bits, pktp);
315 /*
316 * With Yamaha RTX-1000 that is configured as
317 * "ppp ccp mppe-any",
318 *
319 * npppd ConfReq (40,56,128) => RTX 1000
320 * npppd <= (40,128) ConfNAK RTX 1000
321 * npppd ConfReq (40,56,128) => RTX 1000
322 * npppd <= (40,128) ConfNAK RTX 1000
323 *
324 * both peers never decide the final bits. We insist
325 * the longest bit if our request is nacked.
326 */
327 our_bits = mppe_create_our_bits(&ppp->mppe, peer_bits);
328 if (peer_bits == our_bits || is_nak)
329 ppp->ccp.mppe_o_bits = our_bits;
330
331 break;
332 #endif
333 default:
334 goto fail;
335 }
336 }
337 return 1;
338 fail:
339 return 0;
340 }
341
342 #define RESET_REQ 0x0e
343 #define RESET_ACK 0x0f
344
345 static int
ccp_ext(fsm * f,int code,int id,u_char * pktp,int lpktp)346 ccp_ext(fsm *f, int code, int id, u_char *pktp, int lpktp)
347 {
348 switch (code) {
349 case RESET_REQ:
350 fsm_log(f, LOG_DEBUG, "Received ResetReq %d", id);
351 #ifdef USE_NPPPD_MPPE
352 mppe_recv_ccp_reset(&f->ppp->mppe);
353 #endif
354 /*
355 * RFC 3078 says MPPE can be synchronized without Reset-Ack,
356 * but it doesn't tell about necessity of Reset-Ack. But
357 * in fact, windows peer will complain Reset-Ack with
358 * Code-Reject. So we don't send Reset-Ack.
359 */
360 return 1;
361 case RESET_ACK:
362 fsm_log(f, LOG_DEBUG, "Received ResetAck %d", id);
363 return 1;
364 }
365 return 0;
366 }
367