CPU: Wrong CPU Load %.
[tomato.git] / release / src / router / ppp / pppd / plugins / pppoe / utils.c
blob8d4725ebb1d3f2e3947476e8be43e5609c9c83c7
2 /*
3 * utils.c - various utility functions used in pppoed.
5 * mostly stolen from ppp-2.3.10 by Marc Boucher <marc@mbsi.ca>
7 * Feb 18/2000 Made fully re-entrant (JHS)
9 * Copyright (c) 1999 The Australian National University.
10 * All rights reserved.
12 * Redistribution and use in source and binary forms are permitted
13 * provided that the above copyright poe_notice and this paragraph are
14 * duplicated in all such forms and that any documentation,
15 * advertising materials, and other materials related to such
16 * distribution and use acknowledge that the software was developed
17 * by the Australian National University. The name of the University
18 * may not be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
22 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
25 #include <stdio.h> /* stdio */
26 #include <stdlib.h> /* strtoul(), realloc() */
27 #include <string.h> /* memcpy() */
28 #include <unistd.h> /* STDIN_FILENO,exec */
29 #include <errno.h> /* errno */
31 #include <sys/time.h>
33 #include <net/ethernet.h>
34 #include <netinet/in.h>
36 #include <stdarg.h>
37 #include <ctype.h>
38 #include <syslog.h>
39 #include <limits.h>
40 #include <paths.h>
42 #include "pppoe.h"
44 static char pidfilename[PATH_MAX]; /* name of pid file */
47 static int detached = 0;
48 log_to_fd = -1;
51 static void vslp_printer (void *, char *,...);
52 static void format_packet (struct pppoe_packet *, int, void (*)(void *, char *,...), void *);
53 static void format_tag (struct pppoe_tag *, void (*)(void *, char *,...), void *);
54 struct buffer_poe_info {
55 char *ptr;
56 int len;
59 void poe_die (int status);
63 * vpoe_slprintf - like vsprintf, except we
64 * also specify the length of the output buffer, and we handle
65 * %r (recursive format), %m (poe_error message), %v (visible string),
66 * %q (quoted string), %t (current time) and %E (Ether address) formats.
67 * Doesn't do floating-point formats.
68 * Returns the number of chars put into buf.
70 #define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
72 int
73 vpoe_slprintf (char *buf, int buflen, char *fmt, va_list args)
75 int c, i, n;
76 int width, prec, fillch;
77 int base, len, neg, quoted;
78 unsigned long val = 0;
79 char *str, *f, *buf0;
80 unsigned char *p;
81 char num[32];
82 time_t t;
83 static char hexchars[] = "0123456789abcdef";
84 struct buffer_poe_info bufpoe_info;
86 buf0 = buf;
87 --buflen;
88 while (buflen > 0) {
89 for (f = fmt; *f != '%' && *f != 0; ++f);
90 if (f > fmt) {
91 len = f - fmt;
92 if (len > buflen)
93 len = buflen;
94 memcpy (buf, fmt, len);
95 buf += len;
96 buflen -= len;
97 fmt = f;
99 if (*fmt == 0)
100 break;
101 c = *++fmt;
102 width = 0;
103 prec = -1;
104 fillch = ' ';
105 if (c == '0') {
106 fillch = '0';
107 c = *++fmt;
109 if (c == '*') {
110 width = va_arg (args, int);
111 c = *++fmt;
113 else {
114 while (isdigit (c)) {
115 width = width * 10 + c - '0';
116 c = *++fmt;
119 if (c == '.') {
120 c = *++fmt;
121 if (c == '*') {
122 prec = va_arg (args, int);
123 c = *++fmt;
125 else {
126 prec = 0;
127 while (isdigit (c)) {
128 prec = prec * 10 + c - '0';
129 c = *++fmt;
133 str = 0;
134 base = 0;
135 neg = 0;
136 ++fmt;
137 switch (c) {
138 case 'd':
139 i = va_arg (args, int);
140 if (i < 0) {
141 neg = 1;
142 val = -i;
144 else
145 val = i;
146 base = 10;
147 break;
148 case 'o':
149 val = va_arg (args, unsigned int);
150 base = 8;
151 break;
152 case 'x':
153 case 'X':
154 val = va_arg (args, unsigned int);
155 base = 16;
156 break;
157 case 'p':
158 val = (unsigned long) va_arg (args, void *);
159 base = 16;
160 neg = 2;
161 break;
162 case 's':
163 str = va_arg (args, char *);
164 break;
165 case 'c':
166 num[0] = va_arg (args, int);
167 num[1] = 0;
168 str = num;
169 break;
170 case 'm':
171 str = strerror (errno);
172 break;
173 case 'E':
174 p = va_arg (args, unsigned char *);
175 for (n = ETH_ALEN; n > 0; --n) {
176 c = *p++;
177 OUTCHAR (hexchars[(c >> 4) & 0xf]);
178 OUTCHAR (hexchars[c & 0xf]);
179 if (n > 1)
180 OUTCHAR (':');
182 continue;
183 case 'r':
184 f = va_arg (args, char *);
185 #ifndef __powerpc__
186 n = vpoe_slprintf (buf, buflen + 1, f, va_arg (args, va_list));
187 #else
188 /* On the powerpc, a va_list is an array of 1 structure */
189 n = vpoe_slprintf (buf, buflen + 1, f, va_arg (args, void *));
190 #endif
191 buf += n;
192 buflen -= n;
193 continue;
194 case 't':
195 time (&t);
196 str = ctime (&t);
197 str += 4; /* chop off the day name */
198 str[15] = 0; /* chop off year and newline */
199 break;
200 case 'v': /* "visible" string */
201 case 'q': /* quoted string */
202 quoted = c == 'q';
203 p = va_arg (args, unsigned char *);
204 if (fillch == '0' && prec >= 0) {
205 n = prec;
207 else {
208 n = strlen ((char *) p);
209 if (prec >= 0 && n > prec)
210 n = prec;
212 while (n > 0 && buflen > 0) {
213 c = *p++;
214 --n;
215 if (!quoted && c >= 0x80) {
216 OUTCHAR ('M');
217 OUTCHAR ('-');
218 c -= 0x80;
220 if (quoted && (c == '"' || c == '\\'))
221 OUTCHAR ('\\');
222 if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
223 if (quoted) {
224 OUTCHAR ('\\');
225 switch (c) {
226 case '\t':
227 OUTCHAR ('t');
228 break;
229 case '\n':
230 OUTCHAR ('n');
231 break;
232 case '\b':
233 OUTCHAR ('b');
234 break;
235 case '\f':
236 OUTCHAR ('f');
237 break;
238 default:
239 OUTCHAR ('x');
240 OUTCHAR (hexchars[c >> 4]);
241 OUTCHAR (hexchars[c & 0xf]);
244 else {
245 if (c == '\t')
246 OUTCHAR (c);
247 else {
248 OUTCHAR ('^');
249 OUTCHAR (c ^ 0x40);
253 else
254 OUTCHAR (c);
256 continue;
257 case 'P': /* print PPPoE packet */
258 bufpoe_info.ptr = buf;
259 bufpoe_info.len = buflen + 1;
260 p = va_arg (args, unsigned char *);
261 n = va_arg (args, int);
262 format_packet ((struct pppoe_packet *) p, n, vslp_printer, &bufpoe_info);
263 buf = bufpoe_info.ptr;
264 buflen = bufpoe_info.len - 1;
265 continue;
266 case 'T': /* print PPPoE tag */
267 bufpoe_info.ptr = buf;
268 bufpoe_info.len = buflen + 1;
269 p = va_arg (args, unsigned char *);
270 format_tag ((struct pppoe_tag *) p, vslp_printer, &bufpoe_info);
271 buf = bufpoe_info.ptr;
272 buflen = bufpoe_info.len - 1;
273 continue;
274 case 'B':
275 p = va_arg (args, unsigned char *);
276 for (n = prec; n > 0; --n) {
277 c = *p++;
278 if (fillch == ' ')
279 OUTCHAR (' ');
280 OUTCHAR (hexchars[(c >> 4) & 0xf]);
281 OUTCHAR (hexchars[c & 0xf]);
283 continue;
284 default:
285 *buf++ = '%';
286 if (c != '%')
287 --fmt; /* so %z outputs %z etc. */
288 --buflen;
289 continue;
291 if (base != 0) {
292 str = num + sizeof (num);
293 *--str = 0;
294 while (str > num + neg) {
295 *--str = hexchars[val % base];
296 val = val / base;
297 if (--prec <= 0 && val == 0)
298 break;
300 switch (neg) {
301 case 1:
302 *--str = '-';
303 break;
304 case 2:
305 *--str = 'x';
306 *--str = '0';
307 break;
309 len = num + sizeof (num) - 1 - str;
311 else {
312 len = strlen (str);
313 if (prec >= 0 && len > prec)
314 len = prec;
316 if (width > 0) {
317 if (width > buflen)
318 width = buflen;
319 if ((n = width - len) > 0) {
320 buflen -= n;
321 for (; n > 0; --n)
322 *buf++ = fillch;
325 if (len > buflen)
326 len = buflen;
327 memcpy (buf, str, len);
328 buf += len;
329 buflen -= len;
331 *buf = 0;
332 return buf - buf0;
336 * vslp_printer - used in processing a %P format
338 static void
339 vslp_printer (void *arg, char *fmt,...)
341 int n;
342 va_list pvar;
343 struct buffer_poe_info *bi;
345 va_start (pvar, fmt);
347 bi = (struct buffer_poe_info *) arg;
348 n = vpoe_slprintf (bi->ptr, bi->len, fmt, pvar);
349 va_end (pvar);
351 bi->ptr += n;
352 bi->len -= n;
356 * format_packet - make a readable representation of a packet,
357 * calling `printer(arg, format, ...)' to output it.
359 static void
360 format_packet (struct pppoe_packet *p,
361 int len,
362 void (*printer) (void *, char *,...),
363 void *arg)
365 struct pppoe_tag *t;
367 printer (arg, "Ether addr: %E\n", p->addr.sll_addr);
369 switch ((unsigned) ntohs (p->addr.sll_protocol)) {
370 case ETH_P_PPPOE_DISC:
371 printer (arg, " (PPPOE Discovery)\n");
372 break;
373 case ETH_P_PPPOE_SESS:
374 printer (arg, " (PPPOE Session)\n");
375 break;
378 printer (arg, " PPPoE hdr: ver=0x%01x type=0x%01x code=0x%02x "
379 "sid=0x%04x length=0x%04x ", (unsigned) p->hdr->ver,
380 (unsigned) p->hdr->type, (unsigned) p->hdr->code, (unsigned) p->hdr->sid,
381 (unsigned) ntohs (p->hdr->length));
383 switch (p->hdr->code) {
384 case PADI_CODE:
385 printer (arg, "(PADI)\n");
386 break;
387 case PADO_CODE:
388 printer (arg, "(PADO)\n");
389 break;
390 case PADR_CODE:
391 printer (arg, "(PADR)\n");
392 break;
393 case PADS_CODE:
394 printer (arg, "(PADS)\n");
395 break;
396 case PADT_CODE:
397 printer (arg, "(PADT)\n");
398 break;
399 default:
400 printer (arg, "(Unknown)\n");
404 for(t = (struct pppoe_tag *) (&p->hdr->tag);
405 (t < (struct pppoe_tag *) ((char *) (&p->hdr->tag) + ntohs (p->hdr->length))) &&
406 ntohs (t->tag_type) != PTT_EOL;
407 t = (struct pppoe_tag *) ((char *) (t + 1) + ntohs (t->tag_len))) {
408 format_tag (t, printer, arg);
413 * format_tag - make a readable representation of a tag,
414 * calling `printer(arg, format, ...)' to output it.
416 static void
417 format_tag (struct pppoe_tag *t,
418 void (*printer) (void *, char *,...),
419 void *arg)
421 printer (arg, " PPPoE tag: type=%04x length=%04x ",
422 ntohs (t->tag_type), ntohs (t->tag_len));
423 switch ( t->tag_type ) {
424 case PTT_EOL:
425 printer (arg, "(End of list)");
426 break;
427 case PTT_SRV_NAME:
428 printer (arg, "(Service name)");
429 break;
430 case PTT_AC_NAME:
431 printer (arg, "(AC Name)");
432 break;
433 case PTT_HOST_UNIQ:
434 printer (arg, "(Host Uniq)");
435 break;
436 case PTT_AC_COOKIE:
437 printer (arg, "(AC Cookie)");
438 break;
439 case PTT_VENDOR:
440 printer (arg, "(Vendor Specific)");
441 break;
442 case PTT_RELAY_SID:
443 printer (arg, "(Relay Session ID)");
444 break;
445 case PTT_SRV_ERR:
446 printer (arg, "(Service Name Error)");
447 break;
448 case PTT_SYS_ERR:
449 printer (arg, "(AC System Error)");
450 break;
451 case PTT_GEN_ERR:
452 printer (arg, "(Generic Error)");
453 break;
454 default:
455 printer (arg, "(Unknown)");
457 if (ntohs (t->tag_len) > 0)
458 switch ( t->tag_type ) {
459 case PTT_SRV_NAME:
460 case PTT_AC_NAME:
461 case PTT_SRV_ERR:
462 case PTT_SYS_ERR:
463 case PTT_GEN_ERR: /* ascii data */
465 char *buf;
466 buf = malloc (ntohs (t->tag_len) + 1);
467 memset (buf, 0, ntohs (t->tag_len) + 1);
468 strncpy (buf, (char *) (t + 1), ntohs (t->tag_len));
469 // buf[ntohs (t->tag_len)] = '\0';
470 printer (arg, " data (UTF-8): %s", buf);
471 free (buf);
472 break;
475 case PTT_HOST_UNIQ:
476 case PTT_AC_COOKIE:
477 case PTT_RELAY_SID:
478 printer (arg, " data (bin): %.*B", ntohs (t->tag_len), (char *) (t + 1));
479 break;
481 default:
482 printer (arg, " unrecognized data");
487 * poe_logit - does the hard work for poe_fatal et al.
489 static void
490 poe_logit (struct session *ses,int level, char *fmt, va_list args)
492 int n;
493 char buf[256];
495 n = vpoe_slprintf (buf, sizeof (buf), fmt, args);
496 syslog (level, "%s", buf);
497 if (log_to_fd >= 0 && (level != LOG_DEBUG || ses->opt_debug)) {
498 if (buf[n - 1] != '\n')
499 buf[n++] = '\n';
500 if (write (log_to_fd, buf, n) != n)
501 log_to_fd = -1;
506 * poe_fatal - log an poe_error message and poe_die horribly.
508 void
509 poe_fatal (struct session *ses, char *fmt,...)
511 va_list pvar;
513 va_start (pvar, fmt);
515 poe_logit (ses,LOG_ERR, fmt, pvar);
516 va_end (pvar);
518 poe_die(1); /* as promised */
522 * poe_error - log an poe_error message.
524 void
525 poe_error (struct session *ses,char *fmt,...)
527 va_list pvar;
529 va_start (pvar, fmt);
531 poe_logit (ses,LOG_ERR, fmt, pvar);
532 va_end (pvar);
536 * poe_warn - log a poe_warning message.
538 void
539 poe_warn (struct session *ses,char *fmt,...)
541 va_list pvar;
543 va_start (pvar, fmt);
545 poe_logit (ses,LOG_WARNING, fmt, pvar);
546 va_end (pvar);
550 * poe_info - log an poe_informational message.
552 void
553 poe_info (struct session *ses,char *fmt,...)
555 va_list pvar;
557 va_start (pvar, fmt);
559 poe_logit (ses,LOG_INFO, fmt, pvar);
560 va_end (pvar);
564 * poe_dbglog - log a debug message.
566 void
567 poe_dbglog (struct session *ses ,char *fmt,...)
569 va_list pvar;
571 va_start (pvar, fmt);
573 poe_logit (ses,LOG_DEBUG, fmt, pvar);
574 va_end (pvar);
578 * Create a file containing our process ID.
580 void
581 poe_create_pidfile (struct session *ses)
583 FILE *pidfile;
585 sprintf (pidfilename, "%s%s.pid", _PATH_VARRUN, "pppoed");
586 if ((pidfile = fopen (pidfilename, "w")) != NULL) {
587 fprintf (pidfile, "%d\n", getpid ());
588 (void) fclose (pidfile);
590 else {
591 poe_error (ses,"Failed to create pid file %s: %m", pidfilename);
592 pidfilename[0] = 0;
597 * detach - detach us from the controlling terminal.
599 void
600 poe_detach (struct session *ses)
602 if (ses->detached)
603 return;
605 if ((daemon (0, 0)) < 0) {
606 poe_error (ses,"Couldn't detach (daemon failed: %m)");
608 ses->detached = 1;
609 ses->log_to_fd = -1;
610 /* update pid files if they have been written already */
611 if (pidfilename[0])
612 poe_create_pidfile (ses);
616 * cleanup - restore anything which needs to be restored before we exit
618 /* ARGSUSED */
619 static void
620 cleanup ()
622 if (pidfilename[0] != 0 && unlink (pidfilename) < 0 && errno != ENOENT)
623 syslog (LOG_INFO,"unable to delete pid file ");
624 pidfilename[0] = 0;
628 * poe_die - clean up state and exit with the specified status.
630 void
631 poe_die (int status)
633 cleanup ();
634 syslog (LOG_INFO, "Exit.");
635 exit (status);