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