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 */
33 #include <net/ethernet.h>
34 #include <netinet/in.h>
44 static char pidfilename
[PATH_MAX
]; /* name of pid file */
47 static int detached = 0;
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
{
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)
73 vpoe_slprintf (char *buf
, int buflen
, char *fmt
, va_list args
)
76 int width
, prec
, fillch
;
77 int base
, len
, neg
, quoted
;
78 unsigned long val
= 0;
83 static char hexchars
[] = "0123456789abcdef";
84 struct buffer_poe_info bufpoe_info
;
89 for (f
= fmt
; *f
!= '%' && *f
!= 0; ++f
);
94 memcpy (buf
, fmt
, len
);
110 width
= va_arg (args
, int);
114 while (isdigit (c
)) {
115 width
= width
* 10 + c
- '0';
122 prec
= va_arg (args
, int);
127 while (isdigit (c
)) {
128 prec
= prec
* 10 + c
- '0';
139 i
= va_arg (args
, int);
149 val
= va_arg (args
, unsigned int);
154 val
= va_arg (args
, unsigned int);
158 val
= (unsigned long) va_arg (args
, void *);
163 str
= va_arg (args
, char *);
166 num
[0] = va_arg (args
, int);
171 str
= strerror (errno
);
174 p
= va_arg (args
, unsigned char *);
175 for (n
= ETH_ALEN
; n
> 0; --n
) {
177 OUTCHAR (hexchars
[(c
>> 4) & 0xf]);
178 OUTCHAR (hexchars
[c
& 0xf]);
184 f
= va_arg (args
, char *);
186 n
= vpoe_slprintf (buf
, buflen
+ 1, f
, va_arg (args
, va_list));
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 *));
197 str
+= 4; /* chop off the day name */
198 str
[15] = 0; /* chop off year and newline */
200 case 'v': /* "visible" string */
201 case 'q': /* quoted string */
203 p
= va_arg (args
, unsigned char *);
204 if (fillch
== '0' && prec
>= 0) {
208 n
= strlen ((char *) p
);
209 if (prec
>= 0 && n
> prec
)
212 while (n
> 0 && buflen
> 0) {
215 if (!quoted
&& c
>= 0x80) {
220 if (quoted
&& (c
== '"' || c
== '\\'))
222 if (c
< 0x20 || (0x7f <= c
&& c
< 0xa0)) {
240 OUTCHAR (hexchars
[c
>> 4]);
241 OUTCHAR (hexchars
[c
& 0xf]);
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;
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;
275 p
= va_arg (args
, unsigned char *);
276 for (n
= prec
; n
> 0; --n
) {
280 OUTCHAR (hexchars
[(c
>> 4) & 0xf]);
281 OUTCHAR (hexchars
[c
& 0xf]);
287 --fmt
; /* so %z outputs %z etc. */
292 str
= num
+ sizeof (num
);
294 while (str
> num
+ neg
) {
295 *--str
= hexchars
[val
% base
];
297 if (--prec
<= 0 && val
== 0)
309 len
= num
+ sizeof (num
) - 1 - str
;
313 if (prec
>= 0 && len
> prec
)
319 if ((n
= width
- len
) > 0) {
327 memcpy (buf
, str
, len
);
336 * vslp_printer - used in processing a %P format
339 vslp_printer (void *arg
, char *fmt
,...)
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
);
356 * format_packet - make a readable representation of a packet,
357 * calling `printer(arg, format, ...)' to output it.
360 format_packet (struct pppoe_packet
*p
,
362 void (*printer
) (void *, char *,...),
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");
373 case ETH_P_PPPOE_SESS
:
374 printer (arg
, " (PPPOE Session)\n");
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
) {
385 printer (arg
, "(PADI)\n");
388 printer (arg
, "(PADO)\n");
391 printer (arg
, "(PADR)\n");
394 printer (arg
, "(PADS)\n");
397 printer (arg
, "(PADT)\n");
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.
417 format_tag (struct pppoe_tag
*t
,
418 void (*printer
) (void *, char *,...),
421 printer (arg
, " PPPoE tag: type=%04x length=%04x ",
422 ntohs (t
->tag_type
), ntohs (t
->tag_len
));
423 switch ( t
->tag_type
) {
425 printer (arg
, "(End of list)");
428 printer (arg
, "(Service name)");
431 printer (arg
, "(AC Name)");
434 printer (arg
, "(Host Uniq)");
437 printer (arg
, "(AC Cookie)");
440 printer (arg
, "(Vendor Specific)");
443 printer (arg
, "(Relay Session ID)");
446 printer (arg
, "(Service Name Error)");
449 printer (arg
, "(AC System Error)");
452 printer (arg
, "(Generic Error)");
455 printer (arg
, "(Unknown)");
457 if (ntohs (t
->tag_len
) > 0)
458 switch ( t
->tag_type
) {
463 case PTT_GEN_ERR
: /* ascii data */
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
);
478 printer (arg
, " data (bin): %.*B", ntohs (t
->tag_len
), (char *) (t
+ 1));
482 printer (arg
, " unrecognized data");
487 * poe_logit - does the hard work for poe_fatal et al.
490 poe_logit (struct session
*ses
,int level
, char *fmt
, va_list args
)
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')
500 if (write (log_to_fd
, buf
, n
) != n
)
506 * poe_fatal - log an poe_error message and poe_die horribly.
509 poe_fatal (struct session
*ses
, char *fmt
,...)
513 va_start (pvar
, fmt
);
515 poe_logit (ses
,LOG_ERR
, fmt
, pvar
);
518 poe_die(1); /* as promised */
522 * poe_error - log an poe_error message.
525 poe_error (struct session
*ses
,char *fmt
,...)
529 va_start (pvar
, fmt
);
531 poe_logit (ses
,LOG_ERR
, fmt
, pvar
);
536 * poe_warn - log a poe_warning message.
539 poe_warn (struct session
*ses
,char *fmt
,...)
543 va_start (pvar
, fmt
);
545 poe_logit (ses
,LOG_WARNING
, fmt
, pvar
);
550 * poe_info - log an poe_informational message.
553 poe_info (struct session
*ses
,char *fmt
,...)
557 va_start (pvar
, fmt
);
559 poe_logit (ses
,LOG_INFO
, fmt
, pvar
);
564 * poe_dbglog - log a debug message.
567 poe_dbglog (struct session
*ses
,char *fmt
,...)
571 va_start (pvar
, fmt
);
573 poe_logit (ses
,LOG_DEBUG
, fmt
, pvar
);
578 * Create a file containing our process ID.
581 poe_create_pidfile (struct session
*ses
)
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
);
591 poe_error (ses
,"Failed to create pid file %s: %m", pidfilename
);
597 * detach - detach us from the controlling terminal.
600 poe_detach (struct session
*ses
)
605 if ((daemon (0, 0)) < 0) {
606 poe_error (ses
,"Couldn't detach (daemon failed: %m)");
610 /* update pid files if they have been written already */
612 poe_create_pidfile (ses
);
616 * cleanup - restore anything which needs to be restored before we exit
622 if (pidfilename
[0] != 0 && unlink (pidfilename
) < 0 && errno
!= ENOENT
)
623 syslog (LOG_INFO
,"unable to delete pid file ");
628 * poe_die - clean up state and exit with the specified status.
634 syslog (LOG_INFO
, "Exit.");