1 /*
2 * cbcp - Call Back Configuration Protocol.
3 *
4 * Copyright (c) 2000 by Sun Microsystems, Inc.
5 * All rights reserved.
6 *
7 * Copyright (c) 1995 Pedro Roque Marques
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms are permitted
11 * provided that the above copyright notice and this paragraph are
12 * duplicated in all such forms and that any documentation,
13 * advertising materials, and other materials related to such
14 * distribution and use acknowledge that the software was developed
15 * by Pedro Roque Marques. The name of the author may not be used to
16 * endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
21 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 */
23
24 #pragma ident "%Z%%M% %I% %E% SMI"
25 #define RCSID "$Id: cbcp.c,v 1.10 1999/08/13 06:46:10 paulus Exp $"
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/time.h>
31
32 #include "pppd.h"
33 #include "cbcp.h"
34 #include "fsm.h"
35 #include "lcp.h"
36
37 #if !defined(lint) && !defined(_lint)
38 static const char rcsid[] = RCSID;
39 #endif
40
41 /*
42 * Options.
43 */
44 static int setcbcp __P((char **, option_t *));
45
46 static option_t cbcp_option_list[] = {
47 { "callback", o_special, (void *)setcbcp,
48 "Ask for callback" },
49 { NULL }
50 };
51
52 /*
53 * Protocol entry points.
54 */
55 static void cbcp_init __P((int unit));
56 static void cbcp_lowerup __P((int unit));
57 static void cbcp_input __P((int unit, u_char *pkt, int len));
58 static void cbcp_protrej __P((int unit));
59 static int cbcp_printpkt __P((u_char *pkt, int len,
60 void (*printer) __P((void *, const char *, ...)),
61 void *arg));
62
63 struct protent cbcp_protent = {
64 PPP_CBCP, /* PPP protocol number */
65 cbcp_init, /* Initialization procedure */
66 cbcp_input, /* Process a received packet */
67 cbcp_protrej, /* Process a received protocol-reject */
68 cbcp_lowerup, /* Lower layer has come up */
69 NULL, /* Lower layer has gone down */
70 NULL, /* Open the protocol */
71 NULL, /* Close the protocol */
72 cbcp_printpkt, /* Print a packet in readable form */
73 NULL, /* Process a received data packet */
74 0, /* 0 iff protocol is disabled */
75 "CBCP", /* Text name of protocol */
76 NULL, /* Text name of corresponding data protocol */
77 cbcp_option_list, /* List of command-line options */
78 NULL, /* Check requested options, assign defaults */
79 NULL, /* Configure interface for demand-dial */
80 NULL /* Say whether to bring up link for this pkt */
81 };
82
83 /* Not static'd for plug-ins */
84 cbcp_state cbcp[NUM_PPP];
85
86 /* internal prototypes */
87
88 static void cbcp_recvreq __P((cbcp_state *us, u_char *pckt, int len));
89 static void cbcp_recvack __P((cbcp_state *us, u_char *pckt, int len));
90 static void cbcp_send __P((cbcp_state *us, int code, u_char *buf, int len));
91
92 /* option processing */
93 /*ARGSUSED*/
94 static int
setcbcp(argv,opt)95 setcbcp(argv, opt)
96 char **argv;
97 option_t *opt;
98 {
99 lcp_wantoptions[0].neg_cbcp = 1;
100 cbcp_protent.enabled_flag = 1;
101 cbcp[0].us_number = strdup(*argv);
102 if (cbcp[0].us_number == NULL)
103 novm("callback number");
104 cbcp[0].us_type |= (1 << CB_CONF_USER);
105 cbcp[0].us_type |= (1 << CB_CONF_ADMIN);
106 return (1);
107 }
108
109 /* init state */
110 static void
cbcp_init(unit)111 cbcp_init(unit)
112 int unit;
113 {
114 cbcp_state *us;
115
116 us = &cbcp[unit];
117 BZERO(us, sizeof(cbcp_state));
118 us->us_unit = unit;
119 us->us_type |= (1 << CB_CONF_NO);
120 }
121
122 /* lower layer is up */
123 static void
cbcp_lowerup(unit)124 cbcp_lowerup(unit)
125 int unit;
126 {
127 cbcp_state *us = &cbcp[unit];
128
129 if (debug) {
130 dbglog("cbcp_lowerup: want: %d", us->us_type);
131
132 if (us->us_type == CB_CONF_USER)
133 dbglog("phone no: %s", us->us_number);
134 }
135 }
136
137 /* process an incoming packet */
138 static void
cbcp_input(unit,inpacket,pktlen)139 cbcp_input(unit, inpacket, pktlen)
140 int unit;
141 u_char *inpacket;
142 int pktlen;
143 {
144 u_char *inp;
145 u_char code, id;
146 u_short len;
147
148 cbcp_state *us = &cbcp[unit];
149
150 inp = inpacket;
151
152 if (pktlen < CBCP_MINLEN) {
153 error("CBCP packet is too small (%d < %d)", pktlen, CBCP_MINLEN);
154 return;
155 }
156
157 GETCHAR(code, inp);
158 GETCHAR(id, inp);
159 GETSHORT(len, inp);
160
161 if (len > pktlen) {
162 error("CBCP packet: invalid length (%d > %d)", len, pktlen);
163 return;
164 }
165
166 len -= CBCP_MINLEN;
167
168 switch (code) {
169 case CBCP_REQ:
170 us->us_id = id;
171 cbcp_recvreq(us, inp, len);
172 break;
173
174 case CBCP_RESP:
175 if (debug)
176 dbglog("CBCP Response received; no request sent");
177 break;
178
179 case CBCP_ACK:
180 if (id != us->us_id) {
181 if (debug)
182 dbglog("CBCP Ack ID %d doesn't match expected %d", id,
183 us->us_id);
184 break;
185 }
186
187 cbcp_recvack(us, inp, len);
188 break;
189
190 default:
191 if (debug)
192 dbglog("Unknown CBCP code number %d", code);
193 break;
194 }
195 }
196
197 /* protocol was rejected by foe */
198 /*ARGSUSED*/
199 static void
cbcp_protrej(int unit)200 cbcp_protrej(int unit)
201 {
202 start_networks();
203 }
204
205 static char *cbcp_codenames[] = {
206 "Request", "Response", "Ack"
207 };
208
209 static char *cbcp_optionnames[] = {
210 "NoCallback",
211 "UserDefined",
212 "AdminDefined",
213 "List"
214 };
215
216 /*
217 * Pretty print a packet. Return value is number of bytes parsed out
218 * of the packet and printed in some way. Caller (in util.c) will
219 * print the remainder of the packet.
220 */
221 static int
cbcp_printpkt(p,plen,printer,arg)222 cbcp_printpkt(p, plen, printer, arg)
223 u_char *p;
224 int plen;
225 void (*printer) __P((void *, const char *, ...));
226 void *arg;
227 {
228 int code, id, len, olen, alen;
229 u_char *pstart, cichar;
230
231 if (plen < HEADERLEN) {
232 printer(arg, "too short (%d<%d)", plen, HEADERLEN);
233 return (0);
234 }
235 pstart = p;
236 GETCHAR(code, p);
237 GETCHAR(id, p);
238 GETSHORT(len, p);
239
240 if (code >= 1 && code <= Dim(cbcp_codenames))
241 printer(arg, " %s", cbcp_codenames[code-1]);
242 else
243 printer(arg, " code=0x%x", code);
244
245 printer(arg, " id=0x%x", id);
246
247 if (len < HEADERLEN) {
248 printer(arg, " header length %d<%d", len, HEADERLEN);
249 return (HEADERLEN);
250 }
251 if (len > plen) {
252 printer(arg, " truncated (%d>%d)", len, plen);
253 len = plen;
254 }
255 len -= HEADERLEN;
256
257 switch (code) {
258 case CBCP_REQ:
259 case CBCP_RESP:
260 case CBCP_ACK:
261 while (len >= 2) {
262 GETCHAR(cichar, p);
263 GETCHAR(olen, p);
264
265 if (olen < 2)
266 break;
267
268 printer(arg, " <");
269
270 if (olen > len) {
271 printer(arg, "trunc[%d>%d] ", olen, len);
272 olen = len;
273 }
274 len -= olen;
275 olen -= 2;
276
277 if (cichar >= 1 && cichar <= Dim(cbcp_optionnames))
278 printer(arg, " %s", cbcp_optionnames[cichar-1]);
279 else
280 printer(arg, " option=0x%x", cichar);
281
282 if (olen > 0) {
283 GETCHAR(cichar, p);
284 olen--;
285 printer(arg, " delay=%d", cichar);
286 }
287
288 while (olen > 0) {
289 GETCHAR(cichar, p);
290 olen--;
291 if (cichar != 1)
292 printer(arg, " (type %d?)", cichar);
293 alen = strllen((const char *)p, olen);
294 if (olen > 0 && alen > 0)
295 printer(arg, " '%.*s'", alen, p);
296 else
297 printer(arg, " null");
298 p += alen + 1;
299 olen -= alen + 1;
300 }
301 printer(arg, ">");
302 }
303
304 default:
305 break;
306 }
307
308 if (len > 0) {
309 if (len > 8)
310 printer(arg, "%8B ...", p);
311 else
312 printer(arg, "%.*B", len, p);
313 }
314 p += len;
315
316 return p - pstart;
317 }
318
319 /*
320 * received CBCP request.
321 * No reason to print packet contents in detail here, since enabling
322 * debug mode will cause the print routine above to be invoked.
323 */
324 static void
cbcp_recvreq(us,pckt,pcktlen)325 cbcp_recvreq(us, pckt, pcktlen)
326 cbcp_state *us;
327 u_char *pckt;
328 int pcktlen;
329 {
330 u_char type, opt_len;
331 int len = pcktlen;
332 u_char cb_type;
333 u_char buf[256];
334 u_char *bufp = buf;
335
336 us->us_allowed = 0;
337 while (len > 0) {
338 GETCHAR(type, pckt);
339 GETCHAR(opt_len, pckt);
340
341 if (opt_len > 2) {
342 pckt++; /* ignore the delay time */
343 }
344
345 len -= opt_len;
346
347 /*
348 * Careful; don't use left-shift operator on numbers that are
349 * too big.
350 */
351 if (type > CB_CONF_LIST) {
352 if (debug)
353 dbglog("CBCP: ignoring unknown type %d", type);
354 continue;
355 }
356
357 us->us_allowed |= (1 << type);
358
359 switch (type) {
360 case CB_CONF_NO:
361 if (debug)
362 dbglog("CBCP: operation without callback allowed");
363 break;
364
365 case CB_CONF_USER:
366 if (debug)
367 dbglog("callback to user-specified number allowed");
368 break;
369
370 case CB_CONF_ADMIN:
371 if (debug)
372 dbglog("CBCP: callback to admin-defined address allowed");
373 break;
374
375 case CB_CONF_LIST:
376 if (debug)
377 dbglog("CBCP: callback to one out of list allowed");
378 break;
379 }
380 }
381
382 /* Now generate the response */
383 len = 0;
384 cb_type = us->us_allowed & us->us_type;
385
386 if (cb_type & ( 1 << CB_CONF_USER ) ) {
387 if (debug)
388 dbglog("CBCP Response: selecting user-specified number");
389 PUTCHAR(CB_CONF_USER, bufp);
390 len = 3 + 1 + strlen(us->us_number) + 1;
391 PUTCHAR(len , bufp);
392 PUTCHAR(5, bufp); /* delay */
393 PUTCHAR(1, bufp);
394 BCOPY(us->us_number, bufp, strlen(us->us_number) + 1);
395 cbcp_send(us, CBCP_RESP, buf, len);
396 return;
397 }
398
399 if (cb_type & ( 1 << CB_CONF_ADMIN ) ) {
400 if (debug)
401 dbglog("CBCP Response: selecting admin-specified number");
402 PUTCHAR(CB_CONF_ADMIN, bufp);
403 len = 3;
404 PUTCHAR(len, bufp);
405 PUTCHAR(5, bufp); /* delay */
406 cbcp_send(us, CBCP_RESP, buf, len);
407 return;
408 }
409
410 if (cb_type & ( 1 << CB_CONF_NO ) ) {
411 if (debug)
412 dbglog("CBCP Response: selecting no-callback mode");
413 PUTCHAR(CB_CONF_NO, bufp);
414 len = 3;
415 PUTCHAR(len , bufp);
416 PUTCHAR(0, bufp);
417 cbcp_send(us, CBCP_RESP, buf, len);
418 start_networks();
419 return;
420 }
421
422 if (debug)
423 dbglog("CBCP: no callback types in common");
424 lcp_close(us->us_unit, "No CBCP callback options available");
425 }
426
427 static void
cbcp_send(us,code,buf,len)428 cbcp_send(us, code, buf, len)
429 cbcp_state *us;
430 int code;
431 u_char *buf;
432 int len;
433 {
434 u_char *outp;
435 int outlen;
436
437 outp = outpacket_buf;
438
439 outlen = 4 + len;
440
441 MAKEHEADER(outp, PPP_CBCP);
442
443 PUTCHAR(code, outp);
444 PUTCHAR(us->us_id, outp);
445 PUTSHORT(outlen, outp);
446
447 if (len > 0)
448 BCOPY(buf, outp, len);
449
450 output(us->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
451 }
452
453 /*
454 * Received CBCP Acknowledgment message.
455 */
456 static void
cbcp_recvack(us,pckt,len)457 cbcp_recvack(us, pckt, len)
458 cbcp_state *us;
459 u_char *pckt;
460 int len;
461 {
462 u_char type, addr_type;
463 int opt_len;
464
465 if (len > 0) {
466 GETCHAR(type, pckt);
467 GETCHAR(opt_len, pckt);
468
469 if (type == CB_CONF_NO) {
470 if (debug)
471 dbglog("CBCP: proceeding without callback");
472 return;
473 }
474
475 /* just ignore the delay time */
476 pckt++;
477
478 if (opt_len > 4) {
479 GETCHAR(addr_type, pckt);
480 if (addr_type != 1)
481 warn("CBCP: unknown callback address type %d", addr_type);
482 }
483 if (debug && opt_len > 5)
484 dbglog("CBCP: peer will call %.*s", pckt, opt_len - 4);
485 }
486
487 persist = 0;
488 lcp_close(us->us_unit, "Call me back, please");
489 status = EXIT_CALLBACK;
490 }
491