xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/pppd/cbcp.c (revision 8a2b682e57a046b828f37bcde1776f131ef4629f)
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
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
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
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
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
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
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
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
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
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