We now put details of each connection in a tdb database.
At present the values we put in the database are just
the same as the environment strings.
#
# pppd makefile for Linux
-# $Id: Makefile.linux,v 1.36 2000/03/27 06:02:59 paulus Exp $
+# $Id: Makefile.linux,v 1.37 2000/04/04 07:06:48 paulus Exp $
#
# Default installation locations
PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \
- demand.c utils.c
+ demand.c utils.c multilink.c tdb.c
HEADERS = callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \
- ipxcp.h cbcp.h
+ ipxcp.h cbcp.h tdb.h
MANPAGES = pppd.8
PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
- auth.o options.o demand.o utils.o sys-linux.o ipxcp.o
+ auth.o options.o demand.o utils.o sys-linux.o ipxcp.o multilink.o \
+ tdb.o
all: pppd
INCLUDE_DIRS= -I../include
-COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MULTILINK
+COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MULTILINK -DHAVE_MMAP
CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS)
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#define RCSID "$Id: auth.c,v 1.62 2000/03/27 06:02:59 paulus Exp $"
+#define RCSID "$Id: auth.c,v 1.63 2000/04/04 07:06:49 paulus Exp $"
#include <stdio.h>
#include <stddef.h>
new_phase(PHASE_NETWORK);
+#ifdef HAVE_MULTILINK
+ if (multilink)
+ if (mp_join_bundle())
+ return;
+#endif /* HAVE_MULTILINK */
+
#if 0
if (!demand)
set_filters(&pass_filter, &active_filter);
namelen = sizeof(peer_authname) - 1;
BCOPY(name, peer_authname, namelen);
peer_authname[namelen] = 0;
- script_setenv("PEERNAME", peer_authname);
+ script_setenv("PEERNAME", peer_authname, 0);
/*
* If there is no more authentication still to be done,
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#define RCSID "$Id: ipcp.c,v 1.52 1999/12/23 01:25:33 paulus Exp $"
+#define RCSID "$Id: ipcp.c,v 1.53 2000/04/04 07:06:49 paulus Exp $"
/*
* TODO:
ipcp_close(f->unit, "Could not determine local IP address");
return;
}
- script_setenv("IPLOCAL", ip_ntoa(go->ouraddr));
- script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr));
+ script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0);
+ script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1);
if (usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) {
- script_setenv("USEPEERDNS", "1");
+ script_setenv("USEPEERDNS", "1", 0);
if (go->dnsaddr[0])
- script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]));
+ script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0);
if (go->dnsaddr[1])
- script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]));
+ script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]), 0);
create_resolv(go->dnsaddr[0], go->dnsaddr[1]);
}
ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr);
if (go->ouraddr != wo->ouraddr) {
warn("Local IP address changed to %I", go->ouraddr);
- script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr));
+ script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0);
wo->ouraddr = go->ouraddr;
} else
script_unsetenv("OLDIPLOCAL");
if (ho->hisaddr != wo->hisaddr) {
warn("Remote IP address changed to %I", ho->hisaddr);
- script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr));
+ script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0);
wo->hisaddr = ho->hisaddr;
} else
script_unsetenv("OLDIPREMOTE");
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
- * $Id: ipv6cp.c,v 1.7 1999/10/08 01:08:18 masputra Exp $
+ * $Id: ipv6cp.c,v 1.8 2000/04/04 07:06:50 paulus Exp $
*/
-#define RCSID "$Id: ipv6cp.c,v 1.7 1999/10/08 01:08:18 masputra Exp $"
+#define RCSID "$Id: ipv6cp.c,v 1.8 2000/04/04 07:06:50 paulus Exp $"
/*
* TODO:
return;
}
}
- script_setenv("LLLOCAL", llv6_ntoa(go->ourid));
- script_setenv("LLREMOTE", llv6_ntoa(ho->hisid));
+ script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0);
+ script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0);
#ifdef IPV6CP_COMP
/* set tcp compression */
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#define RCSID "$Id: lcp.c,v 1.48 2000/03/27 06:02:59 paulus Exp $";
+#define RCSID "$Id: lcp.c,v 1.49 2000/04/04 07:06:50 paulus Exp $";
/*
* TODO:
static int setescape __P((char **));
+#ifdef HAVE_MULTILINK
+bool noendpoint = 0; /* don't send/accept endpoint discriminator */
+static int setendpoint __P((char **));
+#endif /* HAVE_MULTILINK */
+
static option_t lcp_option_list[] = {
/* LCP options */
{ "noaccomp", o_bool, &lcp_wantoptions[0].neg_accompression,
{ "receive-all", o_bool, &lax_recv,
"Accept all received control characters", 1 },
#ifdef HAVE_MULTILINK
+ { "mrru", o_int, &lcp_wantoptions[0].mrru,
+ "Maximum received packet size for multilink bundle",
+ 0, &lcp_wantoptions[0].neg_mrru },
{ "mpshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
"Use short sequence numbers in multilink headers",
OPT_A2COPY, &lcp_allowoptions[0].neg_ssnhf },
{ "nompshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
"Don't use short sequence numbers in multilink headers",
OPT_A2COPY, &lcp_allowoptions[0].neg_ssnhf },
+ { "endpoint", o_special, setendpoint,
+ "Endpoint discriminator for multilink" },
+ { "noendpoint", o_bool, &noendpoint,
+ "Don't send or accept multilink endpoint discriminator", 1 },
#endif /* HAVE_MULTILINK */
{NULL}
};
return ret;
}
+#ifdef HAVE_MULTILINK
+static int
+setendpoint(argv)
+ char **argv;
+{
+ if (str_to_epdisc(&lcp_wantoptions[0].endpoint, *argv)) {
+ lcp_wantoptions[0].neg_endpoint = 1;
+ return 1;
+ }
+ option_error("Can't parse '%s' as an endpoint discriminator", *argv);
+ return 0;
+}
+#endif /* HAVE_MULTILINK */
+
/*
* lcp_init - Initialize LCP.
*/
fsm_init(f);
- wo->passive = 0;
- wo->silent = 0;
- wo->restart = 0; /* Set to 1 in kernels or multi-line
- implementations */
+ BZERO(wo, sizeof(*wo));
wo->neg_mru = 1;
wo->mru = DEFMRU;
wo->neg_asyncmap = 1;
- wo->asyncmap = 0;
- wo->neg_chap = 0; /* Set to 1 on server */
- wo->neg_upap = 0; /* Set to 1 on server */
wo->chap_mdtype = CHAP_DIGEST_MD5;
wo->neg_magicnumber = 1;
wo->neg_pcompression = 1;
wo->neg_accompression = 1;
- wo->neg_lqr = 0; /* no LQR implementation yet */
- wo->neg_cbcp = 0;
+ BZERO(ao, sizeof(*ao));
ao->neg_mru = 1;
ao->mru = MAXMRU;
ao->neg_asyncmap = 1;
- ao->asyncmap = 0;
ao->neg_chap = 1;
ao->chap_mdtype = CHAP_DIGEST_MD5;
ao->neg_upap = 1;
ao->neg_magicnumber = 1;
ao->neg_pcompression = 1;
ao->neg_accompression = 1;
- ao->neg_lqr = 0; /* no LQR implementation yet */
#ifdef CBCP_SUPPORT
ao->neg_cbcp = 1;
-#else
- ao->neg_cbcp = 0;
+#endif
+#ifdef HAVE_MULTILINK
+ ao->neg_endpoint = 1;
#endif
- memset(xmit_accm[unit], 0, sizeof(xmit_accm[0]));
+ BZERO(xmit_accm[unit], sizeof(xmit_accm[0]));
xmit_accm[unit][3] = 0x60000000;
}
fsm *f;
{
lcp_options *wo = &lcp_wantoptions[f->unit];
+ lcp_options *go = &lcp_gotoptions[f->unit];
wo->magicnumber = magic();
wo->numloops = 0;
- if (!wo->neg_multilink)
- wo->neg_ssnhf = 0;
- lcp_gotoptions[f->unit] = *wo;
+ *go = *wo;
+ if (!multilink) {
+ go->neg_mrru = 0;
+ go->neg_ssnhf = 0;
+ go->neg_endpoint = 0;
+ }
peer_mru[f->unit] = PPP_MRU;
auth_reset(f->unit);
}
LENCILONG(go->neg_magicnumber) +
LENCIVOID(go->neg_pcompression) +
LENCIVOID(go->neg_accompression) +
- LENCISHORT(go->neg_multilink) +
+ LENCISHORT(go->neg_mrru) +
LENCIVOID(go->neg_ssnhf) +
- (go->neg_endpoint? CILEN_CHAR + go->endp_len: 0));
+ (go->neg_endpoint? CILEN_CHAR + go->endpoint.length: 0));
}
ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
- ADDCISHORT(CI_MRRU, go->neg_multilink, go->mrru);
+ ADDCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
ADDCIVOID(CI_SSNHF, go->neg_ssnhf);
- ADDCIENDP(CI_EPDISC, go->neg_endpoint, go->endp_class, go->endpoint,
- go->endp_len);
+ ADDCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class,
+ go->endpoint.value, go->endpoint.length);
if (ucp - start_ucp != *lenp) {
/* this should never happen, because peer_mtu should be 1500 */
ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
- ACKCISHORT(CI_MRRU, go->neg_multilink, go->mrru);
+ ACKCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
ACKCIVOID(CI_SSNHF, go->neg_ssnhf);
- ACKCIENDP(CI_EPDISC, go->neg_endpoint, go->endp_class, go->endpoint,
- go->endp_len);
+ ACKCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class,
+ go->endpoint.value, go->endpoint.length);
/*
* If there are any remaining CIs, then this packet is bad.
* Nak for MRRU option - accept their value if it is smaller
* than the one we want.
*/
- if (go->neg_multilink) {
- NAKCISHORT(CI_MRRU, neg_multilink,
+ if (go->neg_mrru) {
+ NAKCISHORT(CI_MRRU, neg_mrru,
if (cishort <= wo->mrru)
try.mrru = cishort;
);
goto bad;
break;
case CI_MRRU:
- if (go->neg_multilink || no.neg_multilink || cilen != CILEN_SHORT)
+ if (go->neg_mrru || no.neg_mrru || cilen != CILEN_SHORT)
goto bad;
break;
case CI_SSNHF:
p[1] == CILEN_CHAR + vlen) { \
int i; \
len -= CILEN_CHAR + vlen; \
- INCPTR(p[1], p); \
+ INCPTR(2, p); \
GETCHAR(cichar, p); \
if (cichar != class) \
goto bad; \
REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
- REJCISHORT(CI_MRRU, neg_multilink, go->mrru);
+ REJCISHORT(CI_MRRU, neg_mrru, go->mrru);
REJCIVOID(CI_SSNHF, neg_ssnhf);
- REJCIENDP(CI_EPDISC, neg_endpoint, go->endp_class, go->endpoint,
- go->endp_len);
+ REJCIENDP(CI_EPDISC, neg_endpoint, go->endpoint.class,
+ go->endpoint.value, go->endpoint.length);
/*
* If there are any remaining CIs, then this packet is bad.
break;
case CI_MRRU:
- if (!ao->neg_multilink ||
+ if (!ao->neg_mrru || !multilink ||
cilen != CILEN_SHORT) {
orc = CONFREJ;
break;
GETSHORT(cishort, p);
/* possibly should insist on a minimum/maximum MRRU here */
- ho->neg_multilink = 1;
+ ho->neg_mrru = 1;
ho->mrru = cishort;
break;
case CI_SSNHF:
- if (!ao->neg_ssnhf ||
+ if (!ao->neg_ssnhf || !multilink ||
cilen != CILEN_VOID) {
orc = CONFREJ;
break;
break;
case CI_EPDISC:
- if (!ao->neg_endpoint ||
+ if (!ao->neg_endpoint || !multilink ||
cilen < CILEN_CHAR ||
cilen > CILEN_CHAR + MAX_ENDP_LEN) {
orc = CONFREJ;
GETCHAR(cichar, p);
cilen -= CILEN_CHAR;
ho->neg_endpoint = 1;
- ho->endp_class = cichar;
- ho->endp_len = cilen;
- BCOPY(p, ho->endpoint, cilen);
+ ho->endpoint.class = cichar;
+ ho->endpoint.length = cilen;
+ BCOPY(p, ho->endpoint.value, cilen);
INCPTR(cilen, p);
break;
"EchoReq", "EchoRep", "DiscReq"
};
-static char *endp_class_names[] = {
- "null", "local", "IP", "MAC", "magic", "phone"
-};
-
static int
lcp_printpkt(p, plen, printer, arg)
u_char *p;
{
int code, id, len, olen, i;
u_char *pstart, *optend;
- u_char cichar;
u_short cishort;
u_int32_t cilong;
break;
case CI_EPDISC:
if (olen >= CILEN_CHAR) {
+ struct epdisc epd;
p += 2;
- GETCHAR(cichar, p);
- if (cichar <= 5)
- printer(arg, "endpoint [%s]:",
- endp_class_names[cichar]);
- else
- printer(arg, "endpoint [%d]:");
+ GETCHAR(epd.class, p);
+ epd.length = olen - CILEN_CHAR;
+ if (epd.length > MAX_ENDP_LEN)
+ epd.length = MAX_ENDP_LEN;
+ if (epd.length > 0) {
+ BCOPY(p, epd.value, epd.length);
+ p += epd.length;
+ }
+ printer(arg, "endpoint [%s]", epdisc_to_str(&epd));
}
break;
}
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
- * $Id: lcp.h,v 1.14 2000/03/27 06:03:00 paulus Exp $
+ * $Id: lcp.h,v 1.15 2000/04/04 07:06:51 paulus Exp $
*/
/*
#define DISCREQ 11 /* Discard Request */
#define CBCP_OPT 6 /* Use callback control protocol */
-/* maximum length of endpoint discriminator value */
-#define MAX_ENDP_LEN 20
-
/*
* The state of options is described by an lcp_options structure.
*/
bool neg_accompression; /* HDLC Address/Control Field Compression? */
bool neg_lqr; /* Negotiate use of Link Quality Reports */
bool neg_cbcp; /* Negotiate use of CBCP */
- bool neg_multilink; /* negotiate multilink (MRRU) */
+ bool neg_mrru; /* negotiate multilink MRRU */
bool neg_ssnhf; /* negotiate short sequence numbers */
bool neg_endpoint; /* negotiate endpoint discriminator */
int mru; /* Value of MRU */
u_int32_t magicnumber;
int numloops; /* Number of loops during magic number neg. */
u_int32_t lqr_period; /* Reporting period for LQR 1/100ths second */
- int endp_class; /* endpoint discriminator class */
- int endp_len; /* endpoint discriminator length */
- u_char endpoint[MAX_ENDP_LEN]; /* endpoint discriminator value */
+ struct epdisc endpoint; /* endpoint discriminator */
} lcp_options;
extern fsm lcp_fsm[];
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#define RCSID "$Id: main.c,v 1.91 2000/03/27 06:03:01 paulus Exp $"
+#define RCSID "$Id: main.c,v 1.92 2000/04/04 07:06:51 paulus Exp $"
#include <stdio.h>
#include <ctype.h>
#include "ccp.h"
#include "pathnames.h"
#include "patchlevel.h"
+#include "tdb.h"
#ifdef CBCP_SUPPORT
#include "cbcp.h"
int do_callback; /* != 0 if we should do callback next */
int doing_callback; /* != 0 if we are doing callback */
char *callback_script; /* script for doing callback */
+TDB_CONTEXT *pppdb; /* database for storing status etc. */
+char db_key[32];
int (*holdoff_hook) __P((void)) = NULL;
int (*new_phase_hook) __P((int)) = NULL;
/* Prototypes for procedures local to this file. */
+static void setup_signals __P((void));
static void create_pidfile __P((void));
static void create_linkpidfile __P((void));
static void cleanup __P((void));
static int device_script __P((char *, int, int, int));
static int reap_kids __P((int waitfor));
static void record_child __P((int, char *, void (*) (void *), void *));
+static void update_db_entry __P((void));
+static void add_db_key __P((const char *));
+static void delete_db_key __P((const char *));
static int open_socket __P((char *));
static int start_charshunt __P((int, int));
static void charshunt_done __P((void *));
char *argv[];
{
int i, fdflags, t;
- struct sigaction sa;
char *p, *connector;
struct passwd *pw;
struct timeval timo;
uid = getuid();
privileged = uid == 0;
slprintf(numbuf, sizeof(numbuf), "%d", uid);
- script_setenv("ORIG_UID", numbuf);
+ script_setenv("ORIG_UID", numbuf, 0);
ngroups = getgroups(NGROUPS_MAX, groups);
if (!sys_check_options())
exit(EXIT_OPTION_ERROR);
auth_check_options();
+#ifdef HAVE_MULTILINK
+ mp_check_options();
+#endif
for (i = 0; (protp = protocols[i]) != NULL; ++i)
if (protp->check_options != NULL)
(*protp->check_options)();
&& S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev)
log_to_fd = -1;
- script_setenv("DEVICE", devnam);
-
/*
* Initialize system-dependent stuff.
*/
if (debug)
setlogmask(LOG_UPTO(LOG_DEBUG));
+ pppdb = tdb_open(_PATH_PPPDB, 0, TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0644);
+ if (pppdb != NULL) {
+ slprintf(db_key, sizeof(db_key), "pppd%d", getpid());
+ update_db_entry();
+ } else {
+ warn("Warning: couldn't open ppp database %s", _PATH_PPPDB);
+ if (multilink) {
+ warn("Warning: disabling multilink");
+ multilink = 0;
+ }
+ }
+
/*
* Detach ourselves from the terminal, if required,
* and identify who is running us.
}
syslog(LOG_NOTICE, "pppd %s.%d%s started by %s, uid %d",
VERSION, PATCHLEVEL, IMPLEMENTATION, p, uid);
- script_setenv("PPPLOGNAME", p);
+ script_setenv("PPPLOGNAME", p, 0);
- /*
- * Compute mask of all interesting signals and install signal handlers
- * for each. Only one signal handler may be active at a time. Therefore,
- * all other signals should be masked when any handler is executing.
- */
- sigemptyset(&mask);
- sigaddset(&mask, SIGHUP);
- sigaddset(&mask, SIGINT);
- sigaddset(&mask, SIGTERM);
- sigaddset(&mask, SIGCHLD);
- sigaddset(&mask, SIGUSR2);
+ if (devnam[0])
+ script_setenv("DEVICE", devnam, 1);
-#define SIGNAL(s, handler) do { \
- sa.sa_handler = handler; \
- if (sigaction(s, &sa, NULL) < 0) \
- fatal("Couldn't establish signal handler (%d): %m", s); \
- } while (0)
-
- sa.sa_mask = mask;
- sa.sa_flags = 0;
- SIGNAL(SIGHUP, hup); /* Hangup */
- SIGNAL(SIGINT, term); /* Interrupt */
- SIGNAL(SIGTERM, term); /* Terminate */
- SIGNAL(SIGCHLD, chld);
-
- SIGNAL(SIGUSR1, toggle_debug); /* Toggle debug flag */
- SIGNAL(SIGUSR2, open_ccp); /* Reopen CCP */
-
- /*
- * Install a handler for other signals which would otherwise
- * cause pppd to exit without cleaning up.
- */
- SIGNAL(SIGABRT, bad_signal);
- SIGNAL(SIGALRM, bad_signal);
- SIGNAL(SIGFPE, bad_signal);
- SIGNAL(SIGILL, bad_signal);
- SIGNAL(SIGPIPE, bad_signal);
- SIGNAL(SIGQUIT, bad_signal);
- SIGNAL(SIGSEGV, bad_signal);
-#ifdef SIGBUS
- SIGNAL(SIGBUS, bad_signal);
-#endif
-#ifdef SIGEMT
- SIGNAL(SIGEMT, bad_signal);
-#endif
-#ifdef SIGPOLL
- SIGNAL(SIGPOLL, bad_signal);
-#endif
-#ifdef SIGPROF
- SIGNAL(SIGPROF, bad_signal);
-#endif
-#ifdef SIGSYS
- SIGNAL(SIGSYS, bad_signal);
-#endif
-#ifdef SIGTRAP
- SIGNAL(SIGTRAP, bad_signal);
-#endif
-#ifdef SIGVTALRM
- SIGNAL(SIGVTALRM, bad_signal);
-#endif
-#ifdef SIGXCPU
- SIGNAL(SIGXCPU, bad_signal);
-#endif
-#ifdef SIGXFSZ
- SIGNAL(SIGXFSZ, bad_signal);
-#endif
-
- /*
- * Apparently we can get a SIGPIPE when we call syslog, if
- * syslogd has died and been restarted. Ignoring it seems
- * be sufficient.
- */
- signal(SIGPIPE, SIG_IGN);
+ setup_signals();
waiting = 0;
* Open the loopback channel and set it up to be the ppp interface.
*/
fd_loop = open_ppp_loopback();
-
- syslog(LOG_INFO, "Using interface ppp%d", ifunit);
- slprintf(ifname, sizeof(ifname), "ppp%d", ifunit);
- script_setenv("IFNAME", ifname);
-
- create_pidfile(); /* write pid to file */
+ set_ifunit(1);
/*
* Configure the interface and mark it up, etc.
}
slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
- script_setenv("SPEED", numbuf);
+ script_setenv("SPEED", numbuf, 0);
/* run welcome script, if any */
if (welcomer && welcomer[0]) {
goto disconnect;
}
- if (!demand && ifunit >= 0) {
-
- info("Using interface ppp%d", ifunit);
- slprintf(ifname, sizeof(ifname), "ppp%d", ifunit);
- script_setenv("IFNAME", ifname);
-
- create_pidfile(); /* write pid to file */
- }
+ if (!demand && ifunit >= 0)
+ set_ifunit(1);
/*
* Start opening the connection and wait for
return 0;
}
+/*
+ * setup_signals - initialize signal handling.
+ */
+static void
+setup_signals()
+{
+ struct sigaction sa;
+ sigset_t mask;
+
+ /*
+ * Compute mask of all interesting signals and install signal handlers
+ * for each. Only one signal handler may be active at a time. Therefore,
+ * all other signals should be masked when any handler is executing.
+ */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGHUP);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGCHLD);
+ sigaddset(&mask, SIGUSR2);
+
+#define SIGNAL(s, handler) do { \
+ sa.sa_handler = handler; \
+ if (sigaction(s, &sa, NULL) < 0) \
+ fatal("Couldn't establish signal handler (%d): %m", s); \
+ } while (0)
+
+ sa.sa_mask = mask;
+ sa.sa_flags = 0;
+ SIGNAL(SIGHUP, hup); /* Hangup */
+ SIGNAL(SIGINT, term); /* Interrupt */
+ SIGNAL(SIGTERM, term); /* Terminate */
+ SIGNAL(SIGCHLD, chld);
+
+ SIGNAL(SIGUSR1, toggle_debug); /* Toggle debug flag */
+ SIGNAL(SIGUSR2, open_ccp); /* Reopen CCP */
+
+ /*
+ * Install a handler for other signals which would otherwise
+ * cause pppd to exit without cleaning up.
+ */
+ SIGNAL(SIGABRT, bad_signal);
+ SIGNAL(SIGALRM, bad_signal);
+ SIGNAL(SIGFPE, bad_signal);
+ SIGNAL(SIGILL, bad_signal);
+ SIGNAL(SIGPIPE, bad_signal);
+ SIGNAL(SIGQUIT, bad_signal);
+ SIGNAL(SIGSEGV, bad_signal);
+#ifdef SIGBUS
+ SIGNAL(SIGBUS, bad_signal);
+#endif
+#ifdef SIGEMT
+ SIGNAL(SIGEMT, bad_signal);
+#endif
+#ifdef SIGPOLL
+ SIGNAL(SIGPOLL, bad_signal);
+#endif
+#ifdef SIGPROF
+ SIGNAL(SIGPROF, bad_signal);
+#endif
+#ifdef SIGSYS
+ SIGNAL(SIGSYS, bad_signal);
+#endif
+#ifdef SIGTRAP
+ SIGNAL(SIGTRAP, bad_signal);
+#endif
+#ifdef SIGVTALRM
+ SIGNAL(SIGVTALRM, bad_signal);
+#endif
+#ifdef SIGXCPU
+ SIGNAL(SIGXCPU, bad_signal);
+#endif
+#ifdef SIGXFSZ
+ SIGNAL(SIGXFSZ, bad_signal);
+#endif
+
+ /*
+ * Apparently we can get a SIGPIPE when we call syslog, if
+ * syslogd has died and been restarted. Ignoring it seems
+ * be sufficient.
+ */
+ signal(SIGPIPE, SIG_IGN);
+}
+
+/*
+ * set_ifunit - do things we need to do once we know which ppp
+ * unit we are using.
+ */
+void
+set_ifunit(iskey)
+ int iskey;
+{
+ info("Using interface ppp%d", ifunit);
+ slprintf(ifname, sizeof(ifname), "ppp%d", ifunit);
+ script_setenv("IFNAME", ifname, iskey);
+ create_pidfile(); /* write pid to file */
+ create_linkpidfile();
+}
+
/*
* detach - detach us from the controlling terminal.
*/
pidfilename[0] = 0;
}
slprintf(numbuf, sizeof(numbuf), "%d", getpid());
- script_setenv("PPPD_PID", numbuf);
- if (linkpidfile[0])
- create_linkpidfile();
+ script_setenv("PPPD_PID", numbuf, 1);
}
static void
_PATH_VARRUN, linkname);
if ((pidfile = fopen(linkpidfile, "w")) != NULL) {
fprintf(pidfile, "%d\n", getpid());
- if (pidfilename[0])
+ if (ifname[0])
fprintf(pidfile, "%s\n", ifname);
(void) fclose(pidfile);
} else {
error("Failed to create pid file %s: %m", linkpidfile);
linkpidfile[0] = 0;
}
- script_setenv("LINKNAME", linkname);
+ script_setenv("LINKNAME", linkname, 1);
}
/*
if (locked)
unlock();
+
+ if (pppdb != NULL) {
+ TDB_DATA key;
+
+ key.dptr = db_key;
+ key.dsize = strlen(db_key);
+ tdb_delete(pppdb, key);
+ }
}
/*
link_stats_valid = 1;
slprintf(numbuf, sizeof(numbuf), "%d", link_connect_time);
- script_setenv("CONNECT_TIME", numbuf);
+ script_setenv("CONNECT_TIME", numbuf, 0);
slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_out);
- script_setenv("BYTES_SENT", numbuf);
+ script_setenv("BYTES_SENT", numbuf, 0);
slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_in);
- script_setenv("BYTES_RCVD", numbuf);
+ script_setenv("BYTES_RCVD", numbuf, 0);
}
* for scripts that we run (e.g. ip-up, auth-up, etc.)
*/
void
-script_setenv(var, value)
+script_setenv(var, value, iskey)
char *var, *value;
+ int iskey;
{
- size_t vl = strlen(var) + strlen(value) + 2;
+ size_t varl = strlen(var);
+ size_t vl = varl + strlen(value) + 2;
int i;
char *p, *newstring;
/* check if this variable is already set */
if (script_env != 0) {
for (i = 0; (p = script_env[i]) != 0; ++i) {
- if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
+ if (strncmp(p, var, varl) == 0 && p[varl] == '=') {
+ if (iskey && pppdb != NULL) {
+ delete_db_key(p);
+ add_db_key(newstring);
+ }
free(p);
script_env[i] = newstring;
return;
script_env[i] = newstring;
script_env[i+1] = 0;
+
+ if (pppdb != NULL) {
+ if (iskey)
+ add_db_key(newstring);
+ update_db_entry();
+ }
}
/*
break;
}
}
+ if (pppdb != NULL)
+ update_db_entry();
+}
+
+/*
+ * update_db_entry - update our entry in the database.
+ */
+static void
+update_db_entry()
+{
+ TDB_DATA key, dbuf;
+ int vlen, i;
+ char *p, *q, *vbuf;
+
+ if (script_env == NULL)
+ return;
+ vlen = 0;
+ for (i = 0; (p = script_env[i]) != 0; ++i)
+ vlen += strlen(p) + 1;
+ vbuf = malloc(vlen);
+ if (vbuf == 0)
+ novm("database entry");
+ q = vbuf;
+ for (i = 0; (p = script_env[i]) != 0; ++i)
+ q += slprintf(q, vbuf + vlen - q, "%s;", p);
+
+ key.dptr = db_key;
+ key.dsize = strlen(db_key);
+ dbuf.dptr = vbuf;
+ dbuf.dsize = vlen;
+ if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
+ error("tdb_store failed: %s", tdb_error(pppdb));
+
+}
+
+/*
+ * add_db_key - add a key that we can use to look up our database entry.
+ */
+static void
+add_db_key(str)
+ const char *str;
+{
+ TDB_DATA key, dbuf;
+
+ key.dptr = (char *) str;
+ key.dsize = strlen(str);
+ dbuf.dptr = db_key;
+ dbuf.dsize = strlen(db_key);
+ if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
+ error("tdb_store key failed: %s", tdb_error(pppdb));
+}
+
+/*
+ * delete_db_key - delete a key for looking up our database entry.
+ */
+static void
+delete_db_key(str)
+ const char *str;
+{
+ TDB_DATA key;
+
+ key.dptr = (char *) str;
+ key.dsize = strlen(str);
+ tdb_delete(pppdb, key);
}
/*
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#define RCSID "$Id: options.c,v 1.71 2000/03/27 06:03:03 paulus Exp $"
+#define RCSID "$Id: options.c,v 1.72 2000/04/04 07:06:52 paulus Exp $"
#include <ctype.h>
#include <stdio.h>
int max_data_rate; /* max bytes/sec through charshunt */
int req_unit = -1; /* requested interface unit */
bool multilink = 0; /* Enable multilink operation */
+char *bundle_name = NULL; /* bundle name for multilink */
extern option_t auth_options[];
extern struct stat devstat;
"Enable multilink operation", 1 },
{ "nomp", o_bool, &multilink,
"Disable multilink operation", 0 },
+ { "bundle", o_string, &bundle_name,
+ "Bundle name for multilink" },
#endif /* HAVE_MULTILINK */
#ifdef PLUGIN
{ "plugin", o_special, loadplugin,
setnetmask(argv)
char **argv;
{
- u_int32_t mask, b;
- int n, ok;
- char *p, *endp;
+ u_int32_t mask;
+ int n;
+ char *p;
/*
* Unfortunately, if we use inet_addr, we can't tell whether
* a result of all 1s is an error or a valid 255.255.255.255.
*/
p = *argv;
- ok = 0;
- mask = 0;
- for (n = 3;; --n) {
- b = strtoul(p, &endp, 0);
- if (endp == p)
- break;
- if (b > 255) {
- if (n == 3) {
- /* accept e.g. 0xffffff00 */
- p = endp;
- mask = b;
- }
- break;
- }
- mask |= b << (n * 8);
- p = endp;
- if (*p != '.' || n == 0)
- break;
- ++p;
- }
+ n = parse_dotted_ip(p, &mask);
mask = htonl(mask);
- if (*p != 0 || (netmask & ~mask) != 0) {
+ if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) {
option_error("invalid netmask value '%s'", *argv);
return 0;
}
return (1);
}
+int
+parse_dotted_ip(p, vp)
+ char *p;
+ u_int32_t *vp;
+{
+ int n;
+ u_int32_t v, b;
+ char *endp, *p0 = p;
+
+ v = 0;
+ for (n = 3;; --n) {
+ b = strtoul(p, &endp, 0);
+ if (endp == p)
+ return 0;
+ if (b > 255) {
+ if (n < 3)
+ return 0;
+ /* accept e.g. 0xffffff00 */
+ *vp = b;
+ return endp - p0;
+ }
+ v |= b << (n * 8);
+ p = endp;
+ if (n == 0)
+ break;
+ if (*p != '.')
+ return 0;
+ ++p;
+ }
+ *vp = v;
+ return p - p0;
+}
+
static int
setxonxoff(argv)
char **argv;
-/* $Id: patchlevel.h,v 1.45 1999/12/23 01:41:08 paulus Exp $ */
-#define PATCHLEVEL 11
+/* $Id: patchlevel.h,v 1.46 2000/04/04 07:06:52 paulus Exp $ */
+#define PATCHLEVEL 0
-#define VERSION "2.3"
-#define IMPLEMENTATION ""
-#define DATE "23 December 1999"
+#define VERSION "2.4"
+#define IMPLEMENTATION "b1"
+#define DATE "30 March 2000"
/*
* define path names
*
- * $Id: pathnames.h,v 1.12 1999/11/19 09:46:08 masputra Exp $
+ * $Id: pathnames.h,v 1.13 2000/04/04 07:06:52 paulus Exp $
*/
#ifdef HAVE_PATHS_H
#include <paths.h>
-#else
+#else /* HAVE_PATHS_H */
#ifndef _PATH_VARRUN
#define _PATH_VARRUN "/etc/ppp/"
#endif
#define _PATH_DEVNULL "/dev/null"
-#endif
+#endif /* HAVE_PATHS_H */
#ifndef _ROOT_PATH
#define _ROOT_PATH
#define _PATH_IPXUP _ROOT_PATH "/etc/ppp/ipx-up"
#define _PATH_IPXDOWN _ROOT_PATH "/etc/ppp/ipx-down"
#endif /* IPX_CHANGE */
+
+#ifdef __STDC__
+#define _PATH_PPPDB _ROOT_PATH _PATH_VARRUN "pppd.tdb"
+#else /* __STDC__ */
+#ifdef HAVE_PATHS_H
+#define _PATH_PPPDB "/var/run/pppd.tdb"
+#else
+#define _PATH_PPPDB "/etc/ppp/pppd.tdb"
+#endif
+#endif /* __STDC__ */
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
- * $Id: pppd.h,v 1.51 2000/03/27 06:03:06 paulus Exp $
+ * $Id: pppd.h,v 1.52 2000/04/04 07:06:53 paulus Exp $
*/
/*
char *word;
};
+/* An endpoint discriminator, used with multilink. */
+#define MAX_ENDP_LEN 20 /* maximum length of discriminator value */
+struct epdisc {
+ unsigned char class;
+ unsigned char length;
+ unsigned char value[MAX_ENDP_LEN];
+};
+
+/* values for epdisc.class */
+#define EPD_NULL 0 /* null discriminator, no data */
+#define EPD_LOCAL 1
+#define EPD_IP 2
+#define EPD_MAC 3
+#define EPD_MAGIC 4
+#define EPD_PHONENUM 5
+
/*
* Global variables.
*/
extern int max_data_rate; /* max bytes/sec through charshunt */
extern int req_unit; /* interface unit number to use */
extern bool multilink; /* enable multilink operation */
+extern bool noendpoint; /* don't send or accept endpt. discrim. */
+extern char *bundle_name; /* bundle name for multilink */
#ifdef PPP_FILTER
extern struct bpf_program pass_filter; /* Filter for pkts to pass */
*/
/* Procedures exported from main.c. */
+void set_ifunit __P((int)); /* set stuff that depends on ifunit */
void detach __P((void)); /* Detach from controlling tty */
void die __P((int)); /* Cleanup and exit */
void quit __P((void)); /* like die(1) */
/* Run program prog with args in child */
void reopen_log __P((void)); /* (re)open the connection to syslog */
void update_link_stats __P((int)); /* Get stats at link termination */
-void script_setenv __P((char *, char *)); /* set script env var */
+void script_setenv __P((char *, char *, int)); /* set script env var */
void script_unsetenv __P((char *)); /* unset script env var */
void new_phase __P((int)); /* signal start of new phase */
int loop_chars __P((unsigned char *, int)); /* process chars from loopback */
int loop_frame __P((unsigned char *, int)); /* should we bring link up? */
+/* Procedures exported from multilink.c */
+void mp_check_options __P((void)); /* Check multilink-related options */
+int mp_join_bundle __P((void)); /* join our link to an appropriate bundle */
+char *epdisc_to_str __P((struct epdisc *)); /* string from endpoint discrim. */
+int str_to_epdisc __P((struct epdisc *, char *)); /* endpt disc. from str */
+
/* Procedures exported from sys-*.c */
void sys_init __P((void)); /* Do system-dependent initialization */
void sys_cleanup __P((void)); /* Restore system state before exiting */
int establish_ppp __P((int)); /* Turn serial port into a ppp interface */
void restore_loop __P((void)); /* Transfer ppp unit back to loopback */
void disestablish_ppp __P((int)); /* Restore port to normal operation */
+void make_new_bundle __P((int, int, int, int)); /* Create new bundle */
+int bundle_attach __P((int)); /* Attach link to existing bundle */
void clean_check __P((void)); /* Check if line was 8-bit clean */
void set_up_tty __P((int, int)); /* Set up port's speed, parameters, etc. */
void restore_tty __P((int)); /* Restore port's original parameters */
int sipxfaddr __P((int, unsigned long, unsigned char *));
int cipxfaddr __P((int));
#endif
+int get_if_hwaddr __P((u_char *addr, char *name));
+char *get_first_ethernet __P((void));
/* Procedures exported from options.c */
int parse_args __P((int argc, char **argv));
int int_option __P((char *, int *));
/* Simplified number_option for decimal ints */
void add_options __P((option_t *)); /* Add extra options */
+int parse_dotted_ip __P((char *, u_int32_t *));
/*
* This structure is used to store information about certain
static u_int32_t our_old_addr; /* for detecting address changes */
static int dynaddr_set; /* 1 if ip_dynaddr set */
static int looped; /* 1 if using loop */
+static int link_mtu; /* mtu for the link (not bundle) */
static struct utsname utsname; /* for the kernel version */
static int kernel_version;
static void decode_version (char *buf, int *version, int *mod, int *patch);
static int set_kdebugflag(int level);
static int ppp_registered(void);
+static int make_ppp_unit(void);
extern u_char inpacket_buf[]; /* borrowed from main.c */
/*
* Create a new PPP unit.
*/
- ifunit = req_unit;
- x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
- if (x < 0 && req_unit >= 0 && errno == EEXIST) {
- warn("Couldn't allocate PPP unit %d as it is already in use");
- ifunit = -1;
- x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
- }
- if (x < 0) {
- error("Couldn't create new ppp unit: %m");
+ if (make_ppp_unit() < 0)
goto err_close;
- }
}
if (looped)
if (new_style_driver) {
close(ppp_fd);
ppp_fd = -1;
- if (!looped && ioctl(ppp_dev_fd, PPPIOCDETACH) < 0)
+ if (!looped && ifunit >= 0 && ioctl(ppp_dev_fd, PPPIOCDETACH) < 0)
error("Couldn't release PPP unit: %m");
if (!multilink)
remove_fd(ppp_dev_fd);
}
/*
- * bundle_attach - attach our link to a given PPP unit.
+ * make_ppp_unit - make a new ppp unit for ppp_dev_fd.
+ * Assumes new_style_driver.
*/
-int bundle_attach(int ifnum)
+static int make_ppp_unit()
{
- int mrru = 1500;
+ int x;
- if (!new_style_driver)
- return -1;
+ ifunit = req_unit;
+ x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+ if (x < 0 && req_unit >= 0 && errno == EEXIST) {
+ warn("Couldn't allocate PPP unit %d as it is already in use");
+ ifunit = -1;
+ x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+ }
+ if (x < 0)
+ error("Couldn't create new ppp unit: %m");
+ return x;
+}
- if (ioctl(ppp_dev_fd, PPPIOCATTACH, &ifnum) < 0) {
- error("Couldn't attach to interface unit %d: %m\n", ifnum);
- return -1;
- }
- set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_MULTILINK);
- if (ioctl(ppp_dev_fd, PPPIOCSMRRU, &mrru) < 0)
- error("Couldn't set interface MRRU: %m");
+/*
+ * make_new_bundle - create a new PPP unit (i.e. a bundle)
+ * and connect our channel to it. This should only get called
+ * if `multilink' was set at the time establish_ppp was called.
+ */
+void make_new_bundle(int mrru, int mtru, int rssn, int tssn)
+{
+ int flags;
+ struct ifreq ifr;
+
+ if (looped || !new_style_driver)
+ return;
+ dbglog("make_new_bundle(%d,%d,%d,%d)", mrru, mtru, rssn, tssn);
+
+ /* make us a ppp unit */
+ if (make_ppp_unit() < 0)
+ die(1);
+
+ /* set the mrru, mtu and flags */
+ if (ioctl(ppp_dev_fd, PPPIOCSMRRU, &mrru) < 0)
+ error("Couldn't set MRRU: %m");
+ flags = get_flags(ppp_dev_fd);
+ flags &= ~(SC_MP_SHORTSEQ | SC_MP_XSHORTSEQ);
+ flags |= (rssn? SC_MP_SHORTSEQ: 0) | (tssn? SC_MP_XSHORTSEQ: 0);
+
+ if (mtru > 0 && mtru != link_mtu) {
+ memset(&ifr, 0, sizeof(ifr));
+ slprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "ppp%d", ifunit);
+ ifr.ifr_mtu = mtru;
+ if (ioctl(sock_fd, SIOCSIFMTU, &ifr) < 0)
+ error("Couldn't set interface MTU: %m");
+ flags |= SC_MULTILINK;
+ }
- if (ioctl(ppp_fd, PPPIOCCONNECT, &ifnum) < 0) {
- error("Couldn't connect to interface unit %d: %m", ifnum);
- return -1;
- }
+ set_flags(ppp_dev_fd, flags);
- dbglog("bundle_attach succeeded");
- return 0;
+ /* connect up the channel */
+ if (ioctl(ppp_fd, PPPIOCCONNECT, &ifunit) < 0)
+ fatal("Couldn't attach to PPP unit %d: %m", ifunit);
+ add_fd(ppp_dev_fd);
+}
+
+/*
+ * bundle_attach - attach our link to a given PPP unit.
+ */
+int bundle_attach(int ifnum)
+{
+ if (!new_style_driver)
+ return -1;
+
+ if (ioctl(ppp_dev_fd, PPPIOCATTACH, &ifnum) < 0) {
+ if (errno == ENXIO)
+ return 0; /* doesn't still exist */
+ fatal("Couldn't attach to interface unit %d: %m\n", ifnum);
+ }
+ if (ioctl(ppp_fd, PPPIOCCONNECT, &ifnum) < 0)
+ fatal("Couldn't connect to interface unit %d: %m", ifnum);
+ set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_MULTILINK);
+
+ ifunit = ifnum;
+ dbglog("bundle_attach succeeded");
+ return 1;
}
/********************************************************************
p += 2;
len -= 2;
proto = (p[0] << 8) + p[1];
- if (!multilink && proto != PPP_LCP)
+ if (ifunit >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG))
fd = ppp_dev_fd;
}
if (write(fd, p, len) < 0) {
if (nr < 0 && errno != EWOULDBLOCK && errno != EIO)
error("read: %m");
}
- if (nr < 0 && new_style_driver && !multilink) {
+ if (nr < 0 && new_style_driver && ifunit >= 0) {
/* N.B. we read ppp_fd first since LCP packets come in there. */
nr = read(ppp_dev_fd, buf, len);
if (nr < 0 && errno != EWOULDBLOCK && errno != EIO)
if (ifunit >= 0 && ioctl(sock_fd, SIOCSIFMTU, (caddr_t) &ifr) < 0)
fatal("ioctl(SIOCSIFMTU): %m(%d)", errno);
-
+ link_mtu = mtu;
+
if (!still_ppp())
return;
SYSDEBUG ((LOG_DEBUG, "send_config: asyncmap = %lx\n", asyncmap));
return 1;
}
+/*
+ * get_if_hwaddr - get the hardware address for the specified
+ * network interface device.
+ */
+int
+get_if_hwaddr(u_char *addr, char *name)
+{
+ struct ifreq ifreq;
+ int ret, sock_fd;
+
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd < 0)
+ return 0;
+ memset(&ifreq.ifr_hwaddr, 0, sizeof(struct sockaddr));
+ strlcpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name));
+ ret = ioctl(sock_fd, SIOCGIFHWADDR, &ifreq);
+ close(sock_fd);
+ if (ret >= 0)
+ memcpy(addr, ifreq.ifr_hwaddr.sa_data, 6);
+ return ret;
+}
+
+/*
+ * get_first_ethernet - return the name of the first ethernet-style
+ * interface on this system.
+ */
+char *
+get_first_ethernet()
+{
+ return "eth0";
+}
+
/********************************************************************
*
* Return user specified netmask, modified by any mask we might determine
int
open_ppp_loopback(void)
{
- int flags, x;
+ int flags;
looped = 1;
if (new_style_driver) {
/* allocate ourselves a ppp unit */
- ifunit = req_unit;
- x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
- if (x < 0 && req_unit >= 0 && errno == EEXIST) {
- warn("Couldn't allocate PPP unit %d as it is already in use");
- ifunit = -1;
- x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
- }
- if (x < 0)
- fatal("Couldn't create PPP unit: %m");
+ if (make_ppp_unit() < 0)
+ die(1);
set_flags(ppp_dev_fd, SC_LOOP_TRAFFIC);
set_kdebugflag(kdebugflag);
ppp_fd = -1;
--- /dev/null
+/*
+ * Database functions
+ * Copyright (C) Andrew Tridgell 1999
+ *
+ * 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 AND provided that this software or
+ * any derived work is only used as part of the PPP daemon (pppd)
+ * and related utilities.
+ * 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.
+ *
+ * Note: this software is also available under the Gnu Public License
+ * version 2 or later.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include "tdb.h"
+
+#define TDB_VERSION (0x26011967 + 1)
+#define TDB_MAGIC (0x26011999U)
+#define TDB_FREE_MAGIC (~TDB_MAGIC)
+#define TDB_ALIGN 4
+#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGN)
+#define DEFAULT_HASH_SIZE 128
+#define TDB_PAGE_SIZE 0x2000
+#define TDB_LEN_MULTIPLIER 10
+#define FREELIST_TOP (sizeof(struct tdb_header))
+
+#define LOCK_SET 1
+#define LOCK_CLEAR 0
+
+/* lock offsets */
+#define GLOBAL_LOCK 0
+#define ACTIVE_LOCK 4
+#define LIST_LOCK_BASE 1024
+
+#define BUCKET(hash) ((hash) % tdb->header.hash_size)
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+/* the body of the database is made of one list_struct for the free space
+ plus a separate data list for each hash value */
+struct list_struct {
+ tdb_len rec_len; /* total byte length of record */
+ tdb_off next; /* offset of the next record in the list */
+ tdb_len key_len; /* byte length of key */
+ tdb_len data_len; /* byte length of data */
+ unsigned full_hash; /* the full 32 bit hash of the key */
+ unsigned magic; /* try to catch errors */
+ /*
+ the following union is implied
+ union {
+ char record[rec_len];
+ struct {
+ char key[key_len];
+ char data[data_len];
+ }
+ }
+ */
+};
+
+/* a null data record - useful for error returns */
+static TDB_DATA null_data;
+
+/* a byte range locking function - return 0 on success
+ this functions locks/unlocks 1 byte at the specified offset */
+static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset,
+ int set, int rw_type, int lck_type)
+{
+#if NOLOCK
+ return 0;
+#else
+ struct flock fl;
+
+ if (tdb->fd == -1) return 0; /* for in memory tdb */
+
+ if (tdb->read_only) return -1;
+
+ fl.l_type = set==LOCK_SET?rw_type:F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = offset;
+ fl.l_len = 1;
+ fl.l_pid = 0;
+
+ if (fcntl(tdb->fd, lck_type, &fl) != 0) {
+#if TDB_DEBUG
+ if (lck_type == F_SETLKW) {
+ printf("lock %d failed at %d (%s)\n",
+ set, offset, strerror(errno));
+ }
+#endif
+ tdb->ecode = TDB_ERR_LOCK;
+ return -1;
+ }
+ return 0;
+#endif
+}
+
+/* lock a list in the database. list -1 is the alloc list */
+static int tdb_lock(TDB_CONTEXT *tdb, int list)
+{
+ if (list < -1 || list >= (int)tdb->header.hash_size) {
+#if TDB_DEBUG
+ printf("bad list %d\n", list);
+#endif
+ return -1;
+ }
+ if (tdb->locked[list+1] == 0) {
+ if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_SET,
+ F_WRLCK, F_SETLKW) != 0) {
+ return -1;
+ }
+ }
+ tdb->locked[list+1]++;
+ return 0;
+}
+
+/* unlock the database. */
+static int tdb_unlock(TDB_CONTEXT *tdb, int list)
+{
+ if (list < -1 || list >= (int)tdb->header.hash_size) {
+#if TDB_DEBUG
+ printf("bad unlock list %d\n", list);
+#endif
+ return -1;
+ }
+
+ if (tdb->locked[list+1] == 0) {
+#if TDB_DEBUG
+ printf("not locked %d\n", list);
+#endif
+ tdb->ecode = TDB_ERR_LOCK;
+ return -1;
+ }
+ if (tdb->locked[list+1] == 1) {
+ if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_CLEAR,
+ F_WRLCK, F_SETLKW) != 0) {
+ return -1;
+ }
+ }
+ tdb->locked[list+1]--;
+ return 0;
+}
+
+/* the hash algorithm - turn a key into an integer
+ This is based on the hash agorithm from gdbm */
+static unsigned tdb_hash(TDB_DATA *key)
+{
+ unsigned value; /* Used to compute the hash value. */
+ unsigned i; /* Used to cycle through random values. */
+
+ /* Set the initial value from the key size. */
+ value = 0x238F13AF * key->dsize;
+ for (i=0; i < key->dsize; i++) {
+ value = (value + (key->dptr[i] << (i*5 % 24)));
+ }
+
+ value = (1103515243 * value + 12345);
+
+ return value;
+}
+
+/* find the top of the hash chain for an open database */
+static tdb_off tdb_hash_top(TDB_CONTEXT *tdb, unsigned hash)
+{
+ tdb_off ret;
+ hash = BUCKET(hash);
+ ret = FREELIST_TOP + (hash+1)*sizeof(tdb_off);
+ return ret;
+}
+
+
+/* check for an out of bounds access - if it is out of bounds then
+ see if the database has been expanded by someone else and expand
+ if necessary */
+static int tdb_oob(TDB_CONTEXT *tdb, tdb_off offset)
+{
+ struct stat st;
+ if ((offset <= tdb->map_size) || (tdb->fd == -1)) return 0;
+
+ fstat(tdb->fd, &st);
+ if (st.st_size <= (ssize_t)offset) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ }
+
+#if HAVE_MMAP
+ if (tdb->map_ptr) {
+ munmap(tdb->map_ptr, tdb->map_size);
+ tdb->map_ptr = NULL;
+ }
+#endif
+
+ tdb->map_size = st.st_size;
+#if HAVE_MMAP
+ tdb->map_ptr = (void *)mmap(NULL, tdb->map_size,
+ tdb->read_only?PROT_READ:PROT_READ|PROT_WRITE,
+ MAP_SHARED | MAP_FILE, tdb->fd, 0);
+#endif
+ return 0;
+}
+
+
+/* write a lump of data at a specified offset */
+static int tdb_write(TDB_CONTEXT *tdb, tdb_off offset, const char *buf, tdb_len len)
+{
+ if (tdb_oob(tdb, offset + len) != 0) {
+ /* oops - trying to write beyond the end of the database! */
+ return -1;
+ }
+
+ if (tdb->map_ptr) {
+ memcpy(offset + (char *)tdb->map_ptr, buf, len);
+ } else {
+ if (lseek(tdb->fd, offset, SEEK_SET) != offset ||
+ write(tdb->fd, buf, len) != (ssize_t)len) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* read a lump of data at a specified offset */
+static int tdb_read(TDB_CONTEXT *tdb, tdb_off offset, char *buf, tdb_len len)
+{
+ if (tdb_oob(tdb, offset + len) != 0) {
+ /* oops - trying to read beyond the end of the database! */
+ return -1;
+ }
+
+ if (tdb->map_ptr) {
+ memcpy(buf, offset + (char *)tdb->map_ptr, len);
+ } else {
+ if (lseek(tdb->fd, offset, SEEK_SET) != offset ||
+ read(tdb->fd, buf, len) != (ssize_t)len) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+/* read a lump of data, allocating the space for it */
+static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len)
+{
+ char *buf;
+
+ buf = (char *)malloc(len);
+
+ if (!buf) {
+ tdb->ecode = TDB_ERR_OOM;
+ return NULL;
+ }
+
+ if (tdb_read(tdb, offset, buf, len) == -1) {
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
+/* convenience routine for writing a record */
+static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+ return tdb_write(tdb, offset, (char *)rec, sizeof(*rec));
+}
+
+/* convenience routine for writing a tdb_off */
+static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+ return tdb_write(tdb, offset, (char *)d, sizeof(*d));
+}
+
+/* read a tdb_off from the store */
+static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+ return tdb_read(tdb, offset, (char *)d, sizeof(*d));
+}
+
+/* read a record and check for simple errors */
+static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+ if (tdb_read(tdb, offset, (char *)rec, sizeof(*rec)) == -1) return -1;
+ if (rec->magic != TDB_MAGIC) {
+#if TDB_DEBUG
+ printf("bad magic 0x%08x at offset %d\n",
+ rec->magic, offset);
+#endif
+ tdb->ecode = TDB_ERR_CORRUPT;
+ return -1;
+ }
+ if (tdb_oob(tdb, rec->next) != 0) {
+ return -1;
+ }
+ return 0;
+}
+
+/* expand the database at least length bytes by expanding the
+ underlying file and doing the mmap again if necessary */
+static int tdb_expand(TDB_CONTEXT *tdb, tdb_off length)
+{
+ struct list_struct rec;
+ tdb_off offset, ptr;
+ char b = 0;
+
+ tdb_lock(tdb,-1);
+
+ /* make sure we know about any previous expansions by another
+ process */
+ tdb_oob(tdb,tdb->map_size + 1);
+
+ /* always make room for at least 10 more records */
+ length *= TDB_LEN_MULTIPLIER;
+
+ /* and round the database up to a multiple of TDB_PAGE_SIZE */
+ length = ((tdb->map_size + length + TDB_PAGE_SIZE) & ~(TDB_PAGE_SIZE - 1)) - tdb->map_size;
+
+ /* expand the file itself */
+ if (tdb->fd != -1) {
+ lseek(tdb->fd, tdb->map_size + length - 1, SEEK_SET);
+ if (write(tdb->fd, &b, 1) != 1) goto fail;
+ }
+
+ /* form a new freelist record */
+ offset = FREELIST_TOP;
+ rec.rec_len = length - sizeof(rec);
+ rec.magic = TDB_FREE_MAGIC;
+ if (ofs_read(tdb, offset, &rec.next) == -1) {
+ goto fail;
+ }
+
+#if HAVE_MMAP
+ if (tdb->fd != -1 && tdb->map_ptr) {
+ munmap(tdb->map_ptr, tdb->map_size);
+ tdb->map_ptr = NULL;
+ }
+#endif
+
+ tdb->map_size += length;
+
+ if (tdb->fd == -1) {
+ tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size);
+ }
+
+ /* write it out */
+ if (rec_write(tdb, tdb->map_size - length, &rec) == -1) {
+ goto fail;
+ }
+
+ /* link it into the free list */
+ ptr = tdb->map_size - length;
+ if (ofs_write(tdb, offset, &ptr) == -1) goto fail;
+
+#if HAVE_MMAP
+ if (tdb->fd != -1) {
+ tdb->map_ptr = (void *)mmap(NULL, tdb->map_size,
+ PROT_READ|PROT_WRITE,
+ MAP_SHARED | MAP_FILE, tdb->fd, 0);
+ }
+#endif
+
+ tdb_unlock(tdb, -1);
+ return 0;
+
+ fail:
+ tdb_unlock(tdb,-1);
+ return -1;
+}
+
+/* allocate some space from the free list. The offset returned points
+ to a unconnected list_struct within the database with room for at
+ least length bytes of total data
+
+ 0 is returned if the space could not be allocated
+ */
+static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length)
+{
+ tdb_off offset, rec_ptr, last_ptr;
+ struct list_struct rec, lastrec, newrec;
+
+ tdb_lock(tdb, -1);
+
+ again:
+ last_ptr = 0;
+ offset = FREELIST_TOP;
+
+ /* read in the freelist top */
+ if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+ goto fail;
+ }
+
+ /* keep looking until we find a freelist record that is big
+ enough */
+ while (rec_ptr) {
+ if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
+ goto fail;
+ }
+
+ if (rec.magic != TDB_FREE_MAGIC) {
+#if TDB_DEBUG
+ printf("bad magic 0x%08x in free list\n", rec.magic);
+#endif
+ goto fail;
+ }
+
+ if (rec.rec_len >= length) {
+ /* found it - now possibly split it up */
+ if (rec.rec_len > length + MIN_REC_SIZE) {
+ length = (length + TDB_ALIGN) & ~(TDB_ALIGN-1);
+
+ newrec.rec_len = rec.rec_len - (sizeof(rec) + length);
+ newrec.next = rec.next;
+ newrec.magic = TDB_FREE_MAGIC;
+
+ rec.rec_len = length;
+ rec.next = rec_ptr + sizeof(rec) + rec.rec_len;
+
+ if (rec_write(tdb, rec.next, &newrec) == -1) {
+ goto fail;
+ }
+
+ if (rec_write(tdb, rec_ptr, &rec) == -1) {
+ goto fail;
+ }
+ }
+
+ /* remove it from the list */
+ if (last_ptr == 0) {
+ offset = FREELIST_TOP;
+
+ if (ofs_write(tdb, offset, &rec.next) == -1) {
+ goto fail;
+ }
+ } else {
+ lastrec.next = rec.next;
+ if (rec_write(tdb, last_ptr, &lastrec) == -1) {
+ goto fail;
+ }
+ }
+
+ /* all done - return the new record offset */
+ tdb_unlock(tdb, -1);
+ return rec_ptr;
+ }
+
+ /* move to the next record */
+ lastrec = rec;
+ last_ptr = rec_ptr;
+ rec_ptr = rec.next;
+ }
+
+ /* we didn't find enough space. See if we can expand the
+ database and if we can then try again */
+ if (tdb_expand(tdb, length + sizeof(rec)) == 0) goto again;
+
+ fail:
+#if TDB_DEBUG
+ printf("tdb_allocate failed for size %u\n", length);
+#endif
+ tdb_unlock(tdb, -1);
+ return 0;
+}
+
+/* initialise a new database with a specified hash size */
+static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size)
+{
+ struct tdb_header header;
+ tdb_off offset;
+ int i, size = 0;
+ tdb_off buf[16];
+
+ /* create the header */
+ memset(&header, 0, sizeof(header));
+ memcpy(header.magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
+ header.version = TDB_VERSION;
+ header.hash_size = hash_size;
+ lseek(tdb->fd, 0, SEEK_SET);
+ ftruncate(tdb->fd, 0);
+
+ if (tdb->fd != -1 && write(tdb->fd, &header, sizeof(header)) !=
+ sizeof(header)) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ } else size += sizeof(header);
+
+ /* the freelist and hash pointers */
+ offset = 0;
+ memset(buf, 0, sizeof(buf));
+
+ for (i=0;(hash_size+1)-i >= 16; i += 16) {
+ if (tdb->fd != -1 && write(tdb->fd, buf, sizeof(buf)) !=
+ sizeof(buf)) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ } else size += sizeof(buf);
+ }
+
+ for (;i<hash_size+1; i++) {
+ if (tdb->fd != -1 && write(tdb->fd, buf, sizeof(tdb_off)) !=
+ sizeof(tdb_off)) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ } else size += sizeof(tdb_off);
+ }
+
+ if (tdb->fd == -1) {
+ tdb->map_ptr = calloc(size, 1);
+ tdb->map_size = size;
+ if (tdb->map_ptr == NULL) {
+ tdb->ecode = TDB_ERR_IO;
+ return -1;
+ }
+ memcpy(&tdb->header, &header, sizeof(header));
+ }
+
+#if TDB_DEBUG
+ printf("initialised database of hash_size %u\n",
+ hash_size);
+#endif
+ return 0;
+}
+
+/* Returns 0 on fail. On success, return offset of record, and fills
+ in rec */
+static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, unsigned int hash,
+ struct list_struct *rec)
+{
+ tdb_off offset, rec_ptr;
+
+ /* find the top of the hash chain */
+ offset = tdb_hash_top(tdb, hash);
+
+ /* read in the hash top */
+ if (ofs_read(tdb, offset, &rec_ptr) == -1)
+ return 0;
+
+ /* keep looking until we find the right record */
+ while (rec_ptr) {
+ if (rec_read(tdb, rec_ptr, rec) == -1)
+ return 0;
+
+ if (hash == rec->full_hash && key.dsize == rec->key_len) {
+ char *k;
+ /* a very likely hit - read the key */
+ k = tdb_alloc_read(tdb, rec_ptr + sizeof(*rec),
+ rec->key_len);
+
+ if (!k)
+ return 0;
+
+ if (memcmp(key.dptr, k, key.dsize) == 0) {
+ free(k);
+ return rec_ptr;
+ }
+ free(k);
+ }
+
+ /* move to the next record */
+ rec_ptr = rec->next;
+ }
+ return 0;
+}
+
+/*
+ return an error string for the last tdb error
+*/
+char *tdb_error(TDB_CONTEXT *tdb)
+{
+ int i;
+ static struct {
+ enum TDB_ERROR ecode;
+ char *estring;
+ } emap[] = {
+ {TDB_SUCCESS, "Success"},
+ {TDB_ERR_CORRUPT, "Corrupt database"},
+ {TDB_ERR_IO, "IO Error"},
+ {TDB_ERR_LOCK, "Locking error"},
+ {TDB_ERR_OOM, "Out of memory"},
+ {TDB_ERR_EXISTS, "Record exists"},
+ {-1, NULL}};
+ if (tdb != NULL) {
+ for (i=0;emap[i].estring;i++) {
+ if (tdb->ecode == emap[i].ecode) return emap[i].estring;
+ }
+ } else {
+ return "Invalid tdb context";
+ }
+ return "Invalid error code";
+}
+
+
+/* update an entry in place - this only works if the new data size
+ is <= the old data size and the key exists.
+ on failure return -1
+*/
+int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf)
+{
+ unsigned hash;
+ struct list_struct rec;
+ tdb_off rec_ptr;
+ int ret = -1;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_update() called with null context\n");
+#endif
+ return -1;
+ }
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+
+ tdb_lock(tdb, BUCKET(hash));
+ rec_ptr = tdb_find(tdb, key, hash, &rec);
+
+ if (!rec_ptr)
+ goto out;
+
+ /* must be long enough */
+ if (rec.rec_len < key.dsize + dbuf.dsize)
+ goto out;
+
+ if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
+ dbuf.dptr, dbuf.dsize) == -1)
+ goto out;
+
+ if (dbuf.dsize != rec.data_len) {
+ /* update size */
+ rec.data_len = dbuf.dsize;
+ ret = rec_write(tdb, rec_ptr, &rec);
+ } else
+ ret = 0;
+
+ out:
+ tdb_unlock(tdb, BUCKET(hash));
+ return ret;
+}
+
+/* find an entry in the database given a key */
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ unsigned hash;
+ tdb_off rec_ptr;
+ struct list_struct rec;
+ TDB_DATA ret = null_data;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_fetch() called with null context\n");
+#endif
+ return null_data;
+ }
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+
+ tdb_lock(tdb, BUCKET(hash));
+ rec_ptr = tdb_find(tdb, key, hash, &rec);
+
+ if (rec_ptr) {
+ ret.dptr = tdb_alloc_read(tdb,
+ rec_ptr + sizeof(rec) + rec.key_len,
+ rec.data_len);
+ ret.dsize = rec.data_len;
+ }
+
+ tdb_unlock(tdb, BUCKET(hash));
+ return ret;
+}
+
+/* check if an entry in the database exists
+
+ note that 1 is returned if the key is found and 0 is returned if not found
+ this doesn't match the conventions in the rest of this module, but is
+ compatible with gdbm
+*/
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ unsigned hash;
+ tdb_off rec_ptr;
+ struct list_struct rec;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_exists() called with null context\n");
+#endif
+ return 0;
+ }
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+
+ tdb_lock(tdb, BUCKET(hash));
+ rec_ptr = tdb_find(tdb, key, hash, &rec);
+ tdb_unlock(tdb, BUCKET(hash));
+
+ return rec_ptr != 0;
+}
+
+/* traverse the entire database - calling fn(tdb, key, data) on each element.
+ return -1 on error or the record count traversed
+ if fn is NULL then it is not called
+ a non-zero return value from fn() indicates that the traversal should stop
+ */
+int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void* state), void* state)
+{
+ int count = 0;
+ unsigned h;
+ tdb_off offset, rec_ptr;
+ struct list_struct rec;
+ char *data;
+ TDB_DATA key, dbuf;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_traverse() called with null context\n");
+#endif
+ return -1;
+ }
+
+ /* loop over all hash chains */
+ for (h = 0; h < tdb->header.hash_size; h++) {
+ tdb_lock(tdb, BUCKET(h));
+
+ /* read in the hash top */
+ offset = tdb_hash_top(tdb, h);
+ if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+ goto fail;
+ }
+
+ /* traverse all records for this hash */
+ while (rec_ptr) {
+ if (rec_read(tdb, rec_ptr, &rec) == -1) {
+ goto fail;
+ }
+
+ /* now read the full record */
+ data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec),
+ rec.key_len + rec.data_len);
+ if (!data) {
+ goto fail;
+ }
+
+ key.dptr = data;
+ key.dsize = rec.key_len;
+ dbuf.dptr = data + rec.key_len;
+ dbuf.dsize = rec.data_len;
+ count++;
+
+ if (fn && fn(tdb, key, dbuf, state) != 0) {
+ /* they want us to stop traversing */
+ free(data);
+ tdb_unlock(tdb, BUCKET(h));
+ return count;
+ }
+
+ /* a miss - drat */
+ free(data);
+
+ /* move to the next record */
+ rec_ptr = rec.next;
+ }
+ tdb_unlock(tdb, BUCKET(h));
+ }
+
+ /* return the number traversed */
+ return count;
+
+ fail:
+ tdb_unlock(tdb, BUCKET(h));
+ return -1;
+}
+
+
+/* find the first entry in the database and return its key */
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb)
+{
+ tdb_off offset, rec_ptr;
+ struct list_struct rec;
+ unsigned hash;
+ TDB_DATA ret;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_firstkey() called with null context\n");
+#endif
+ return null_data;
+ }
+
+ /* look for a non-empty hash chain */
+ for (hash = 0, rec_ptr = 0;
+ hash < tdb->header.hash_size;
+ hash++) {
+ /* find the top of the hash chain */
+ offset = tdb_hash_top(tdb, hash);
+
+ tdb_lock(tdb, BUCKET(hash));
+
+ /* read in the hash top */
+ if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+ goto fail;
+ }
+
+ if (rec_ptr) break;
+
+ tdb_unlock(tdb, BUCKET(hash));
+ }
+
+ if (rec_ptr == 0) return null_data;
+
+ /* we've found a non-empty chain, now read the record */
+ if (rec_read(tdb, rec_ptr, &rec) == -1) {
+ goto fail;
+ }
+
+ /* allocate and read the key space */
+ ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len);
+ ret.dsize = rec.key_len;
+ tdb_unlock(tdb, BUCKET(hash));
+ return ret;
+
+ fail:
+ tdb_unlock(tdb, BUCKET(hash));
+ return null_data;
+}
+
+/* find the next entry in the database, returning its key */
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ unsigned hash, hbucket;
+ tdb_off rec_ptr, offset;
+ struct list_struct rec;
+ TDB_DATA ret;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_nextkey() called with null context\n");
+#endif
+ return null_data;
+ }
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+ hbucket = BUCKET(hash);
+
+ tdb_lock(tdb, hbucket);
+ rec_ptr = tdb_find(tdb, key, hash, &rec);
+ if (rec_ptr) {
+ /* we want the next record after this one */
+ rec_ptr = rec.next;
+ }
+
+ /* not found or last in hash: look for next non-empty hash chain */
+ while (rec_ptr == 0) {
+ tdb_unlock(tdb, hbucket);
+
+ if (++hbucket >= tdb->header.hash_size - 1)
+ return null_data;
+
+ offset = tdb_hash_top(tdb, hbucket);
+ tdb_lock(tdb, hbucket);
+ /* read in the hash top */
+ if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+ tdb_unlock(tdb, hbucket);
+ return null_data;
+ }
+ }
+
+ /* Read the record. */
+ if (rec_read(tdb, rec_ptr, &rec) == -1) {
+ tdb_unlock(tdb, hbucket);
+ return null_data;
+ }
+ /* allocate and read the key */
+ ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len);
+ ret.dsize = rec.key_len;
+ tdb_unlock(tdb, hbucket);
+
+ return ret;
+}
+
+/* delete an entry in the database given a key */
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ unsigned hash;
+ tdb_off offset, rec_ptr, last_ptr;
+ struct list_struct rec, lastrec;
+ char *data = NULL;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_delete() called with null context\n");
+#endif
+ return -1;
+ }
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+
+ tdb_lock(tdb, BUCKET(hash));
+
+ /* find the top of the hash chain */
+ offset = tdb_hash_top(tdb, hash);
+
+ /* read in the hash top */
+ if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+ goto fail;
+ }
+
+ last_ptr = 0;
+
+ /* keep looking until we find the right record */
+ while (rec_ptr) {
+ if (rec_read(tdb, rec_ptr, &rec) == -1) {
+ goto fail;
+ }
+
+ if (hash == rec.full_hash && key.dsize == rec.key_len) {
+ /* a very likely hit - read the record and full key */
+ data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec),
+ rec.key_len);
+ if (!data) {
+ goto fail;
+ }
+
+ if (memcmp(key.dptr, data, key.dsize) == 0) {
+ /* a definite match - delete it */
+ if (last_ptr == 0) {
+ offset = tdb_hash_top(tdb, hash);
+ if (ofs_write(tdb, offset, &rec.next) == -1) {
+ goto fail;
+ }
+ } else {
+ lastrec.next = rec.next;
+ if (rec_write(tdb, last_ptr, &lastrec) == -1) {
+ goto fail;
+ }
+ }
+ tdb_unlock(tdb, BUCKET(hash));
+ tdb_lock(tdb, -1);
+ /* and recover the space */
+ offset = FREELIST_TOP;
+ if (ofs_read(tdb, offset, &rec.next) == -1) {
+ goto fail2;
+ }
+ rec.magic = TDB_FREE_MAGIC;
+ if (rec_write(tdb, rec_ptr, &rec) == -1) {
+ goto fail2;
+ }
+ if (ofs_write(tdb, offset, &rec_ptr) == -1) {
+ goto fail2;
+ }
+
+ /* yipee - all done */
+ free(data);
+ tdb_unlock(tdb, -1);
+ return 0;
+ }
+
+ /* a miss - drat */
+ free(data);
+ data = NULL;
+ }
+
+ /* move to the next record */
+ last_ptr = rec_ptr;
+ lastrec = rec;
+ rec_ptr = rec.next;
+ }
+
+ fail:
+ if (data) free(data);
+ tdb_unlock(tdb, BUCKET(hash));
+ return -1;
+
+ fail2:
+ if (data) free(data);
+ tdb_unlock(tdb, -1);
+ return -1;
+}
+
+
+/* store an element in the database, replacing any existing element
+ with the same key
+
+ return 0 on success, -1 on failure
+*/
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
+{
+ struct list_struct rec;
+ unsigned hash;
+ tdb_off rec_ptr, offset;
+ char *p = NULL;
+
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_store() called with null context\n");
+#endif
+ return -1;
+ }
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+
+ /* check for it existing */
+ if (flag == TDB_INSERT && tdb_exists(tdb, key)) {
+ tdb->ecode = TDB_ERR_EXISTS;
+ return -1;
+ }
+
+ /* first try in-place update */
+ if (flag != TDB_INSERT && tdb_update(tdb, key, dbuf) == 0) {
+ return 0;
+ }
+
+ rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize);
+ if (rec_ptr == 0) {
+ return -1;
+ }
+
+ tdb_lock(tdb, BUCKET(hash));
+
+ /* delete any existing record - if it doesn't exist we don't care */
+ if (flag != TDB_INSERT) {
+ tdb_delete(tdb, key);
+ }
+
+ /* read the newly created record */
+ if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
+ goto fail;
+ }
+
+ if (rec.magic != TDB_FREE_MAGIC) goto fail;
+
+ /* find the top of the hash chain */
+ offset = tdb_hash_top(tdb, hash);
+
+ /* read in the hash top diretcly into our next pointer */
+ if (ofs_read(tdb, offset, &rec.next) == -1) {
+ goto fail;
+ }
+
+ rec.key_len = key.dsize;
+ rec.data_len = dbuf.dsize;
+ rec.full_hash = hash;
+ rec.magic = TDB_MAGIC;
+
+ p = (char *)malloc(sizeof(rec) + key.dsize + dbuf.dsize);
+ if (!p) {
+ tdb->ecode = TDB_ERR_OOM;
+ goto fail;
+ }
+
+ memcpy(p, &rec, sizeof(rec));
+ memcpy(p+sizeof(rec), key.dptr, key.dsize);
+ memcpy(p+sizeof(rec)+key.dsize, dbuf.dptr, dbuf.dsize);
+
+ if (tdb_write(tdb, rec_ptr, p, sizeof(rec)+key.dsize+dbuf.dsize) == -1)
+ goto fail;
+
+ free(p);
+ p = NULL;
+
+ /* and point the top of the hash chain at it */
+ if (ofs_write(tdb, offset, &rec_ptr) == -1) goto fail;
+
+ tdb_unlock(tdb, BUCKET(hash));
+ return 0;
+
+ fail:
+#if TDB_DEBUG
+ printf("store failed for hash 0x%08x in bucket %u\n", hash, BUCKET(hash));
+#endif
+ if (p) free(p);
+ tdb_unlock(tdb, BUCKET(hash));
+ return -1;
+}
+
+
+/* open the database, creating it if necessary
+
+ The open_flags and mode are passed straight to the open call on the database
+ file. A flags value of O_WRONLY is invalid
+
+ The hash size is advisory, use zero for a default value.
+
+ return is NULL on error
+*/
+TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode)
+{
+ TDB_CONTEXT tdb, *ret;
+ struct stat st;
+
+ memset(&tdb, 0, sizeof(tdb));
+
+ tdb.fd = -1;
+ tdb.name = NULL;
+ tdb.map_ptr = NULL;
+
+ if ((open_flags & O_ACCMODE) == O_WRONLY) {
+ goto fail;
+ }
+
+ if (hash_size == 0) hash_size = DEFAULT_HASH_SIZE;
+
+ tdb.read_only = ((open_flags & O_ACCMODE) == O_RDONLY);
+
+ if (name != NULL) {
+ tdb.fd = open(name, open_flags, mode);
+ if (tdb.fd == -1) {
+ goto fail;
+ }
+ }
+
+ /* ensure there is only one process initialising at once */
+ tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_SET, F_WRLCK, F_SETLKW);
+
+ if (tdb_flags & TDB_CLEAR_IF_FIRST) {
+ /* we need to zero the database if we are the only
+ one with it open */
+ if (tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_WRLCK, F_SETLK) == 0) {
+ ftruncate(tdb.fd, 0);
+ tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLK);
+ }
+ }
+
+ /* leave this lock in place */
+ tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_RDLCK, F_SETLKW);
+
+ if (read(tdb.fd, &tdb.header, sizeof(tdb.header)) != sizeof(tdb.header) ||
+ strcmp(tdb.header.magic_food, TDB_MAGIC_FOOD) != 0 ||
+ tdb.header.version != TDB_VERSION) {
+ /* its not a valid database - possibly initialise it */
+ if (!(open_flags & O_CREAT)) {
+ goto fail;
+ }
+ if (tdb_new_database(&tdb, hash_size) == -1) goto fail;
+
+ lseek(tdb.fd, 0, SEEK_SET);
+ if (tdb.fd != -1 && read(tdb.fd, &tdb.header,
+ sizeof(tdb.header)) !=
+ sizeof(tdb.header))
+ goto fail;
+ }
+
+ if (tdb.fd != -1) {
+ fstat(tdb.fd, &st);
+
+ /* map the database and fill in the return structure */
+ tdb.name = (char *)strdup(name);
+ tdb.map_size = st.st_size;
+ }
+
+ tdb.locked = (int *)calloc(tdb.header.hash_size+1,
+ sizeof(tdb.locked[0]));
+ if (!tdb.locked) {
+ goto fail;
+ }
+
+#if HAVE_MMAP
+ if (tdb.fd != -1) {
+ tdb.map_ptr = (void *)mmap(NULL, st.st_size,
+ tdb.read_only? PROT_READ : PROT_READ|PROT_WRITE,
+ MAP_SHARED | MAP_FILE, tdb.fd, 0);
+ }
+#endif
+
+ ret = (TDB_CONTEXT *)malloc(sizeof(tdb));
+ if (!ret) goto fail;
+
+ *ret = tdb;
+
+#if TDB_DEBUG
+ printf("mapped database of hash_size %u map_size=%u\n",
+ hash_size, tdb.map_size);
+#endif
+
+ tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLKW);
+ return ret;
+
+ fail:
+ if (tdb.name) free(tdb.name);
+ if (tdb.fd != -1) close(tdb.fd);
+ if (tdb.map_ptr) munmap(tdb.map_ptr, tdb.map_size);
+
+ return NULL;
+}
+
+/* close a database */
+int tdb_close(TDB_CONTEXT *tdb)
+{
+ if (!tdb) return -1;
+
+ if (tdb->name) free(tdb->name);
+ if (tdb->fd != -1) close(tdb->fd);
+ if (tdb->locked) free(tdb->locked);
+
+ if (tdb->map_ptr) {
+ if (tdb->fd != -1) {
+ munmap(tdb->map_ptr, tdb->map_size);
+ } else {
+ free(tdb->map_ptr);
+ }
+ }
+
+ memset(tdb, 0, sizeof(*tdb));
+ free(tdb);
+
+ return 0;
+}
+
+/* lock the database. If we already have it locked then don't do anything */
+int tdb_writelock(TDB_CONTEXT *tdb)
+{
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_writelock() called with null context\n");
+#endif
+ return -1;
+ }
+
+ return tdb_lock(tdb, -1);
+}
+
+/* unlock the database. */
+int tdb_writeunlock(TDB_CONTEXT *tdb)
+{
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_writeunlock() called with null context\n");
+#endif
+ return -1;
+ }
+
+ return tdb_unlock(tdb, -1);
+}
+
+/* lock one hash chain. This is meant to be used to reduce locking
+ contention - it cannot guarantee how many records will be locked */
+int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_lockchain() called with null context\n");
+#endif
+ return -1;
+ }
+
+ return tdb_lock(tdb, BUCKET(tdb_hash(&key)));
+}
+
+
+/* unlock one hash chain */
+int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ if (tdb == NULL) {
+#ifdef TDB_DEBUG
+ printf("tdb_unlockchain() called with null context\n");
+#endif
+ return -1;
+ }
+
+ return tdb_unlock(tdb, BUCKET(tdb_hash(&key)));
+}
--- /dev/null
+#define STANDALONE 1
+/*
+ * Database functions
+ * Copyright (C) Andrew Tridgell 1999
+ *
+ * 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 AND provided that this software or
+ * any derived work is only used as part of the PPP daemon (pppd)
+ * and related utilities.
+ * 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.
+ *
+ * Note: this software is also available under the Gnu Public License
+ * version 2 or later.
+ */
+
+typedef unsigned tdb_len;
+typedef unsigned tdb_off;
+
+#define TDB_MAGIC_FOOD "TDB file\n"
+
+/* this is stored at the front of every database */
+struct tdb_header {
+ char magic_food[32]; /* for /etc/magic */
+ unsigned version; /* version of the code */
+ unsigned hash_size; /* number of hash entries */
+};
+
+typedef struct {
+ char *dptr;
+ size_t dsize;
+} TDB_DATA;
+
+/* this is the context structure that is returned from a db open */
+typedef struct {
+ char *name; /* the name of the database */
+ void *map_ptr; /* where it is currently mapped */
+ int fd; /* open file descriptor for the database */
+ tdb_len map_size; /* how much space has been mapped */
+ int read_only; /* opened read-only */
+ int *locked; /* set if we have a chain locked */
+ int ecode; /* error code for last tdb error */
+ struct tdb_header header; /* a cached copy of the header */
+} TDB_CONTEXT;
+
+/* flags to tdb_store() */
+#define TDB_REPLACE 1
+#define TDB_INSERT 2
+
+/* flags for tdb_open() */
+#define TDB_CLEAR_IF_FIRST 1
+
+/* error codes */
+enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK,
+ TDB_ERR_OOM, TDB_ERR_EXISTS};
+
+#if STANDALONE
+TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode);
+char *tdb_error(TDB_CONTEXT *tdb);
+int tdb_writelock(TDB_CONTEXT *tdb);
+int tdb_writeunlock(TDB_CONTEXT *tdb);
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+int tdb_close(TDB_CONTEXT *tdb);
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_traverse(TDB_CONTEXT *tdb,
+ int (*fn)(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state),
+ void *state);
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+#endif