Subject: pppd status, part 2
To: None <current-users@netbsd.org>
From: John F. Woods <jfw@jfwhome.funhouse.com>
List: current-users
Date: 01/19/2003 12:57:12
OK, I have implemented a scheme for making pppd's LCP status visible, and
I wanted to let people take a look at it.  I've done it as a pppd plugin
(and therefore I've gotten pppd plugins working, as if that was a tremendous
amount of work), so it will be easy to expand or replace the status-reporting
mechanism (currently a file, but I'm going to toy with SNMP next).

The status plugin should be built as a shared library, copied to 
/usr/lib/pppd/2.4.1/libstatus.so.0.0 (or whatever directory would be
appropriate), and loaded with a pppd option of "plugin libstatus.so.0.0"
(actually, I made a symbolic link "status" in that directory and just use
"plugin status").  My status plugin then takes a pppd option "lcpstatus"
whose argument is the pathname into which a status word is to be written.

("cvs diff" claims that I am out of date on lcp.c, even though I did a
"cvs update" this morning.)

Index: Makefile
===================================================================
RCS file: /cvsroot/src/usr.sbin/pppd/pppd/Makefile,v
retrieving revision 1.30
diff -u -d -b -w -r1.30 Makefile
--- Makefile	2002/09/18 03:54:35	1.30
+++ Makefile	2003/01/19 16:54:24
@@ -14,9 +14,9 @@
 BINMODE=4555
 BINOWN=	root
 
-LDADD=	-lpcap -lcrypt -lutil
+LDADD=	-lpcap -lcrypt -lutil -Wl,--export-dynamic
 DPADD=	${LIBPCAP} ${LIBCRYPT} ${LIBUTIL}
-CPPFLAGS+= -I. -DHAVE_PATHS_H -DSUPPORT_UTMP -DSUPPORT_UTMPX
+CPPFLAGS+= -I. -DHAVE_PATHS_H -DSUPPORT_UTMP -DSUPPORT_UTMPX -DPLUGIN
 CPPFLAGS+= -I${PCAPDIR} -DPPP_FILTER
 CPPFLAGS+= -DCBCP_SUPPORT -DCHAPMS -DUSE_CRYPT -DMSLANMAN
 CPPFLAGS+= -DINET6

Index: pppd.h
===================================================================
RCS file: /cvsroot/src/usr.sbin/pppd/pppd/pppd.h,v
retrieving revision 1.23
diff -u -d -b -w -r1.23 pppd.h
--- pppd.h	2002/07/01 22:19:40	1.23
+++ pppd.h	2003/01/19 16:54:08
@@ -1,4 +1,4 @@
-/*	$NetBSD: pppd.h,v 1.23 2002/07/01 22:19:40 itojun Exp $	*/
+/*	$NetBSD: pppd.h,v 1.22 2002/05/29 19:06:33 christos Exp $	*/
 
 /*
  * pppd.h - PPP daemon global declarations.
@@ -638,6 +638,10 @@
 extern void (*ip_up_hook) __P((void));
 extern void (*ip_down_hook) __P((void));
 extern void (*ip_choose_hook) __P((u_int32_t *));
+void (*lcp_up_hook) __P((void));
+void (*lcp_down_hook) __P((void));
+void (*lcp_echo_hook) __P((int));
+void (*lcp_echoreply_hook) __P((int));
 
 /*
  * Inline versions of get/put char/short/long.

Index: lcp.c
===================================================================
RCS file: /cvsroot/src/usr.sbin/pppd/pppd/lcp.c,v
retrieving revision 1.24
diff -u -d -b -w -r1.24 lcp.c
--- lcp.c	2002/07/01 22:19:35	1.24
+++ lcp.c	2003/01/19 16:54:39
@@ -1,4 +1,4 @@
-/*	$NetBSD: lcp.c,v 1.24 2002/07/01 22:19:35 itojun Exp $	*/
+/*	$NetBSD: lcp.c,v 1.23 2002/05/29 19:06:32 christos Exp $	*/
 
 /*
  * lcp.c - PPP Link Control Protocol.
@@ -47,7 +47,7 @@
 #if 0
 #define RCSID	"Id: lcp.c,v 1.57 2001/03/08 05:11:14 paulus Exp "
 #else
-__RCSID("$NetBSD: lcp.c,v 1.24 2002/07/01 22:19:35 itojun Exp $");
+__RCSID("$NetBSD: lcp.c,v 1.23 2002/05/29 19:06:32 christos Exp $");
 #endif
 #endif
 
@@ -204,6 +204,18 @@
 lcp_options lcp_allowoptions[NUM_PPP];	/* Options we allow peer to request */
 lcp_options lcp_hisoptions[NUM_PPP];	/* Options that we ack'd */
 
+/* Hook for LCP up */
+void (*lcp_up_hook) __P((void)) = NULL;
+
+/* Hook for LCP down */
+void (*lcp_down_hook) __P((void)) = NULL;
+
+/* Hook for sending an LCP echo request, argument is pending count */
+void (*lcp_echo_hook) __P((int)) = NULL;
+
+/* Hook for receiving an LCP echo reply, argument is whether it's ours */
+void (*lcp_echoreply_hook) __P((int)) = NULL;
+
 static int lcp_echos_pending = 0;	/* Number of outstanding echo msgs */
 static int lcp_echo_number   = 0;	/* ID number of next echo frame */
 static int lcp_echo_timer_running = 0;  /* set if a timer is running */
@@ -1836,6 +1848,8 @@
     if (ho->neg_mru)
 	peer_mru[f->unit] = ho->mru;
 
+    if (lcp_up_hook) (*lcp_up_hook)();
+
     lcp_echo_lowerup(f->unit);  /* Enable echo messages */
 
     link_established(f->unit);
@@ -1857,6 +1871,8 @@
 
     link_down(f->unit);
 
+    if (lcp_down_hook) (*lcp_down_hook)();
+
     ppp_send_config(f->unit, PPP_MRU, 0xffffffff, 0, 0);
     ppp_recv_config(f->unit, PPP_MRU,
 		    (go->neg_asyncmap? go->asyncmap: 0xffffffff),
@@ -2181,8 +2197,10 @@
     if (lcp_gotoptions[f->unit].neg_magicnumber
 	&& magic == lcp_gotoptions[f->unit].magicnumber) {
 	warn("appear to have received our own echo-reply!");
+	if (lcp_echoreply_hook) (*lcp_echoreply_hook)(1);
 	return;
     }
+    if (lcp_echoreply_hook) (*lcp_echoreply_hook)(0);
 
     /* Reset the number of outstanding echo frames */
     lcp_echos_pending = 0;
@@ -2213,6 +2231,7 @@
      * Make and send the echo request frame.
      */
     if (f->state == OPENED) {
+      if (*lcp_echo_hook) (*lcp_echo_hook)(lcp_echos_pending);
         lcp_magic = lcp_gotoptions[f->unit].magicnumber;
 	pktp = pkt;
 	PUTLONG(lcp_magic, pktp);

And finally, status.c itself (copy into the plugins directory alongside
minconn, and build it manually):

/*
 * status.c - pppd plugin to implement an `lcpstatus' option.
 *
 * Copyright 2003 John Woods;
 * based on minconn.c, Copyright 1999 Paul Mackerras.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms.  The name of the author
 * may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */
#include <stddef.h>
#include <time.h>
#include <stdio.h>
#include "pppd.h"

char pppd_version[] = VERSION;

static char *statusfilename = 0;
static char *laststatus = 0;
static char UP[] = "up";
static char DOWN[] = "down";
static char MISS[] = "?";
static char MINE[] = "!";

static option_t my_options[] = {
	{ "lcpstatus", o_string, &statusfilename,
	  "Name of file to which LCP status string will be written" },
	{ NULL }
};

/* status should be one of the canned constants above. */
static void writestatus(char *status)
{
        FILE *statusfile;
	if (status == laststatus) return;  /* we knew that already */
	statusfile = fopen(statusfilename, "w");
	if (!statusfile) {
                warn("can't write %s to log LCP status", statusfilename);
		free(statusfilename);
		statusfilename = 0;
		return;
	}
	fprintf(statusfile, "%s\n", status);
	fclose(statusfile);
	laststatus = status;
}

static void my_lcp_up(void)
{
        if (!statusfilename) return; /* not enabled */
	writestatus(UP);
}

static void my_lcp_down(void)
{
        if (!statusfilename) return; /* not enabled */
	writestatus(DOWN);
}

static void my_lcp_echo(int pending)
{
        if (!statusfilename) return; /* not enabled */
	if (pending == 0)
                writestatus(UP);
	else if (laststatus != MINE)
                writestatus(MISS);
}

static void my_lcp_echoreply(int mine)
{
        if (!statusfilename) return; /* not enabled */
	if (mine == 0)
                writestatus(UP);
	else
                writestatus(MINE);
}        

void plugin_init(void)
{
	info("plugin_init");
	add_options(my_options);
	lcp_up_hook = my_lcp_up;
	lcp_down_hook = my_lcp_down;
	lcp_echo_hook = my_lcp_echo;
	lcp_echoreply_hook = my_lcp_echoreply;
}