* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#define RCSID "$Id: auth.c,v 1.100 2004/11/06 05:39:23 paulus Exp $"
+#define RCSID "$Id: auth.c,v 1.101 2004/11/12 10:30:51 paulus Exp $"
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
+#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <string.h>
link_required(unit)
int unit;
{
+ new_phase(PHASE_SERIALCONN);
+
+ devfd = the_channel->connect();
+ if (devfd < 0)
+ goto fail;
+
+ /* set up the serial device as a ppp interface */
+ /*
+ * N.B. we used to do tdb_writelock/tdb_writeunlock around this
+ * (from establish_ppp to set_ifunit). However, we won't be
+ * doing the set_ifunit in multilink mode, which is the only time
+ * we need the atomicity that the tdb_writelock/tdb_writeunlock
+ * gives us. Thus we don't need the tdb_writelock/tdb_writeunlock.
+ */
+ fd_ppp = the_channel->establish_ppp(devfd);
+ if (fd_ppp < 0) {
+ status = EXIT_FATAL_ERROR;
+ goto disconnect;
+ }
+
+ if (!demand && ifunit >= 0)
+ set_ifunit(1);
+
+ /*
+ * Start opening the connection and wait for
+ * incoming events (reply, timeout, etc.).
+ */
+ if (ifunit >= 0)
+ notice("Connect: %s <--> %s", ifname, ppp_devnam);
+ else
+ notice("Starting negotiation on %s", ppp_devnam);
+ add_fd(fd_ppp);
+
+ status = EXIT_NEGOTIATION_FAILED;
+ new_phase(PHASE_ESTABLISH);
+
+ lcp_lowerup(0);
+ return;
+
+ disconnect:
+ new_phase(PHASE_DISCONNECT);
+ if (the_channel->disconnect)
+ the_channel->disconnect();
+
+ fail:
+ new_phase(PHASE_DEAD);
+ if (the_channel->cleanup)
+ (*the_channel->cleanup)();
+
}
/*
link_terminated(unit)
int unit;
{
- if (phase == PHASE_DEAD)
+ if (phase == PHASE_DEAD || phase == PHASE_MASTER)
return;
+ new_phase(PHASE_DISCONNECT);
+
if (pap_logout_hook) {
pap_logout_hook();
} else {
if (logged_in)
plogout();
}
- new_phase(PHASE_DEAD);
- notice("Connection terminated.");
+
+ if (!doing_multilink) {
+ notice("Connection terminated.");
+ print_link_stats();
+ } else
+ notice("Link terminated.");
+
+ /*
+ * Delete pid files before disestablishing ppp. Otherwise it
+ * can happen that another pppd gets the same unit and then
+ * we delete its pid file.
+ */
+ if (!doing_multilink && !demand)
+ remove_pidfiles();
+
+ /*
+ * If we may want to bring the link up again, transfer
+ * the ppp unit back to the loopback. Set the
+ * real serial device back to its normal mode of operation.
+ */
+ if (fd_ppp >= 0) {
+ remove_fd(fd_ppp);
+ clean_check();
+ the_channel->disestablish_ppp(devfd);
+ if (doing_multilink)
+ mp_exit_bundle();
+ fd_ppp = -1;
+ }
+ if (!hungup)
+ lcp_lowerdown(0);
+ if (!doing_multilink && !demand)
+ script_unsetenv("IFNAME");
+
+ /*
+ * Run disconnector script, if requested.
+ * XXX we may not be able to do this if the line has hung up!
+ */
+ if (devfd >= 0 && the_channel->disconnect) {
+ the_channel->disconnect();
+ devfd = -1;
+ }
+
+ if (doing_multilink && multilink_master) {
+ if (!bundle_terminating)
+ new_phase(PHASE_MASTER);
+ else
+ mp_bundle_terminated();
+ } else
+ new_phase(PHASE_DEAD);
}
/*
void
link_down(unit)
int unit;
+{
+ if (auth_state != s_down) {
+ notify(link_down_notifier, 0);
+ auth_state = s_down;
+ if (auth_script_state == s_up && auth_script_pid == 0) {
+ update_link_stats(unit);
+ auth_script_state = s_down;
+ auth_script(_PATH_AUTHDOWN);
+ }
+ }
+ if (!doing_multilink) {
+ upper_layers_down(unit);
+ if (phase != PHASE_DEAD && phase != PHASE_MASTER)
+ new_phase(PHASE_ESTABLISH);
+ }
+ /* XXX if doing_multilink, should do something to stop
+ network-layer traffic on the link */
+}
+
+void upper_layers_down(int unit)
{
int i;
struct protent *protp;
- notify(link_down_notifier, 0);
- auth_state = s_down;
- if (auth_script_state == s_up && auth_script_pid == 0) {
- update_link_stats(unit);
- auth_script_state = s_down;
- auth_script(_PATH_AUTHDOWN);
- }
for (i = 0; (protp = protocols[i]) != NULL; ++i) {
if (!protp->enabled_flag)
continue;
}
num_np_open = 0;
num_np_up = 0;
- if (phase != PHASE_DEAD)
- new_phase(PHASE_ESTABLISH);
}
/*
/*
* Tell higher-level protocols that LCP is up.
*/
- for (i = 0; (protp = protocols[i]) != NULL; ++i)
- if (protp->protocol != PPP_LCP && protp->enabled_flag
- && protp->lowerup != NULL)
- (*protp->lowerup)(unit);
+ if (!doing_multilink) {
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol != PPP_LCP && protp->enabled_flag
+ && protp->lowerup != NULL)
+ (*protp->lowerup)(unit);
+ }
if (!auth_required && noauth_addrs != NULL)
set_allowed_addrs(unit, NULL, NULL);
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#define RCSID "$Id: lcp.c,v 1.71 2004/10/31 22:23:18 paulus Exp $"
+#define RCSID "$Id: lcp.c,v 1.72 2004/11/12 10:30:51 paulus Exp $"
/*
* TODO:
{
fsm *f = &lcp_fsm[unit];
- if (phase != PHASE_DEAD)
+ if (phase != PHASE_DEAD && phase != PHASE_MASTER)
new_phase(PHASE_TERMINATE);
if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
/*
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#define RCSID "$Id: main.c,v 1.144 2004/11/09 22:35:02 paulus Exp $"
+#define RCSID "$Id: main.c,v 1.145 2004/11/12 10:30:51 paulus Exp $"
#include <stdio.h>
#include <ctype.h>
void (*snoop_send_hook) __P((unsigned char *p, int len)) = NULL;
static int conn_running; /* we have a [dis]connector running */
-static int devfd; /* fd of underlying device */
-static int fd_ppp = -1; /* fd for talking PPP */
static int fd_loop; /* fd for getting demand-dial packets */
-static int fd_devnull; /* fd for /dev/null */
+int fd_devnull; /* fd for /dev/null */
+int devfd = -1; /* fd of underlying device */
+int fd_ppp = -1; /* fd for talking PPP */
int phase; /* where the link is at */
int kill_link;
int open_ccp_flag;
int error_count;
+bool bundle_eof;
+bool bundle_terminating;
+
/*
* We maintain a list of child process pids and
* functions to call when they exit.
* Configure the interface and mark it up, etc.
*/
demand_conf();
- create_linkpidfile(getpid());
}
do_callback = 0;
for (;;) {
+ bundle_eof = 0;
+ bundle_terminating = 0;
listen_time = 0;
need_holdoff = 1;
devfd = -1;
info("Starting link");
}
- new_phase(PHASE_SERIALCONN);
-
- devfd = the_channel->connect();
- if (devfd < 0)
- goto fail;
-
- /* set up the serial device as a ppp interface */
-#ifdef USE_TDB
- tdb_writelock(pppdb);
-#endif
- fd_ppp = the_channel->establish_ppp(devfd);
- if (fd_ppp < 0) {
-#ifdef USE_TDB
- tdb_writeunlock(pppdb);
-#endif
- status = EXIT_FATAL_ERROR;
- goto disconnect;
- }
- /* create the pid file, now that we've obtained a ppp interface */
- if (!demand)
- create_linkpidfile(getpid());
-
- if (!demand && ifunit >= 0)
- set_ifunit(1);
-#ifdef USE_TDB
- tdb_writeunlock(pppdb);
-#endif
-
- /*
- * Start opening the connection and wait for
- * incoming events (reply, timeout, etc.).
- */
- if (ifunit >= 0)
- notice("Connect: %s <--> %s", ifname, ppp_devnam);
- else
- notice("Starting negotiation on %s", ppp_devnam);
gettimeofday(&start_time, NULL);
script_unsetenv("CONNECT_TIME");
script_unsetenv("BYTES_SENT");
script_unsetenv("BYTES_RCVD");
- lcp_lowerup(0);
- add_fd(fd_ppp);
lcp_open(0); /* Start protocol */
- status = EXIT_NEGOTIATION_FAILED;
- new_phase(PHASE_ESTABLISH);
while (phase != PHASE_DEAD) {
handle_events();
get_input();
- if (kill_link)
+ if (kill_link) {
+ bundle_terminating = 1;
lcp_close(0, "User request");
+ if (phase == PHASE_MASTER)
+ mp_bundle_terminated();
+ }
if (open_ccp_flag) {
if (phase == PHASE_NETWORK || phase == PHASE_RUNNING) {
ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
}
}
- print_link_stats();
-
- /*
- * Delete pid file before disestablishing ppp. Otherwise it
- * can happen that another pppd gets the same unit and then
- * we delete its pid file.
- */
- if (!demand) {
- if (pidfilename[0] != 0
- && unlink(pidfilename) < 0 && errno != ENOENT)
- warn("unable to delete pid file %s: %m", pidfilename);
- pidfilename[0] = 0;
- }
-
- /*
- * If we may want to bring the link up again, transfer
- * the ppp unit back to the loopback. Set the
- * real serial device back to its normal mode of operation.
- */
- remove_fd(fd_ppp);
- clean_check();
- the_channel->disestablish_ppp(devfd);
- fd_ppp = -1;
- if (!hungup)
- lcp_lowerdown(0);
- if (!demand)
- script_unsetenv("IFNAME");
-
- /*
- * Run disconnector script, if requested.
- * XXX we may not be able to do this if the line has hung up!
- */
- disconnect:
- new_phase(PHASE_DISCONNECT);
- if (the_channel->disconnect)
- the_channel->disconnect();
-
- fail:
- if (the_channel->cleanup)
- (*the_channel->cleanup)();
-
- if (!demand) {
- if (pidfilename[0] != 0
- && unlink(pidfilename) < 0 && errno != ENOENT)
- warn("unable to delete pid file %s: %m", pidfilename);
- pidfilename[0] = 0;
- }
-
if (!persist || (maxfail > 0 && unsuccess >= maxfail))
break;
}
}
-static void
+void
create_linkpidfile(pid)
int pid;
{
}
}
+/*
+ * remove_pidfile - remove our pid files
+ */
+void remove_pidfiles()
+{
+ if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
+ warn("unable to delete pid file %s: %m", pidfilename);
+ pidfilename[0] = 0;
+ if (linkpidfile[0] != 0 && unlink(linkpidfile) < 0 && errno != ENOENT)
+ warn("unable to delete pid file %s: %m", linkpidfile);
+ linkpidfile[0] = 0;
+}
+
/*
* holdoff_end - called via a timeout when the holdoff period ends.
*/
return;
if (len == 0) {
+ if (bundle_eof && multilink_master) {
+ notice("Last channel has disconnected");
+ mp_bundle_terminated();
+ return;
+ }
notice("Modem hangup");
hungup = 1;
status = EXIT_HANGUP;
die(status)
int status;
{
- print_link_stats();
+ if (!doing_multilink || multilink_master)
+ print_link_stats();
cleanup();
notify(exitnotify, status);
syslog(LOG_INFO, "Exit.");
the_channel->disestablish_ppp(devfd);
if (the_channel->cleanup)
(*the_channel->cleanup)();
-
- if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
- warn("unable to delete pid file %s: %m", pidfilename);
- pidfilename[0] = 0;
- if (linkpidfile[0] != 0 && unlink(linkpidfile) < 0 && errno != ENOENT)
- warn("unable to delete pid file %s: %m", linkpidfile);
- linkpidfile[0] = 0;
+ remove_pidfiles();
#ifdef USE_TDB
if (pppdb != NULL)
vlen = 0;
for (i = 0; (p = script_env[i]) != 0; ++i)
vlen += strlen(p) + 1;
- vbuf = malloc(vlen);
+ vbuf = malloc(vlen + 1);
if (vbuf == 0)
novm("database entry");
q = vbuf;
#include <errno.h>
#include <signal.h>
#include <netinet/in.h>
+#include <unistd.h>
#include "pppd.h"
#include "fsm.h"
bool endpoint_specified; /* user gave explicit endpoint discriminator */
char *bundle_id; /* identifier for our bundle */
+char *blinks_id; /* key for the list of links */
+bool doing_multilink; /* multilink was enabled and agreed to */
+bool multilink_master; /* we own the multilink bundle */
extern TDB_CONTEXT *pppdb;
extern char db_key[];
+static void make_bundle_links __P((int append));
+static void remove_bundle_link __P((void));
+static void iterate_bundle_links __P((void (*func) __P((char *))));
+
static int get_default_epdisc __P((struct epdisc *));
static int parse_num __P((char *str, const char *key, int *valp));
static int owns_unit __P((TDB_DATA pid, int unit));
lcp_options *wo = &lcp_wantoptions[0];
lcp_options *ao = &lcp_allowoptions[0];
+ doing_multilink = 0;
if (!multilink)
return;
/* if we're doing multilink, we have to negotiate MRRU */
char *p;
TDB_DATA key, pid, rec;
+ if (doing_multilink) {
+ /* have previously joined a bundle */
+ if (!go->neg_mrru || !ho->neg_mrru) {
+ notice("oops, didn't get multilink on renegotiation");
+ lcp_close(0, "multilink required");
+ return 0;
+ }
+ /* XXX should check the peer_authname and ho->endpoint
+ are the same as previously */
+ return 0;
+ }
+
if (!go->neg_mrru || !ho->neg_mrru) {
/* not doing multilink */
if (go->neg_mrru)
return 0;
}
+ doing_multilink = 1;
+
/*
* Find the appropriate bundle or join a new one.
* First we make up a name for the bundle.
if (bundle_name)
p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
+ /* Make the key for the list of links belonging to the bundle */
+ l = p - bundle_id;
+ blinks_id = malloc(l + 7);
+ if (blinks_id == NULL)
+ novm("bundle links key");
+ slprintf(blinks_id, l + 7, "BUNDLE_LINKS=%s", bundle_id + 7);
+
/*
* For demand mode, we only need to configure the bundle
* and attach the link.
if (pid.dptr != NULL) {
/* bundle ID exists, see if the pppd record exists */
rec = tdb_fetch(pppdb, pid);
- if (rec.dptr != NULL) {
- /* it is, parse the interface number */
+ if (rec.dptr != NULL && rec.dsize > 0) {
+ /* make sure the string is null-terminated */
+ rec.dptr[rec.dsize-1] = 0;
+ /* parse the interface number */
parse_num(rec.dptr, "IFNAME=ppp", &unit);
/* check the pid value */
if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
if (bundle_attach(unit)) {
set_ifunit(0);
script_setenv("BUNDLE", bundle_id + 7, 0);
+ make_bundle_links(1);
tdb_writeunlock(pppdb);
info("Link attached to %s", ifname);
return 1;
set_ifunit(1);
netif_set_mtu(0, mtu);
script_setenv("BUNDLE", bundle_id + 7, 1);
+ make_bundle_links(0);
tdb_writeunlock(pppdb);
info("New bundle %s created", ifname);
+ multilink_master = 1;
return 0;
}
+void mp_exit_bundle()
+{
+ tdb_writelock(pppdb);
+ remove_bundle_link();
+ tdb_writeunlock(pppdb);
+}
+
+static void sendhup(char *str)
+{
+ int pid;
+
+ if (parse_num(str, "PPPD_PID=", &pid) && pid != getpid()) {
+ if (debug)
+ dbglog("sending SIGHUP to process %d", pid);
+ kill(pid, SIGHUP);
+ }
+}
+
+void mp_bundle_terminated()
+{
+ TDB_DATA key;
+
+ bundle_terminating = 1;
+ upper_layers_down(0);
+ notice("Connection terminated.");
+ print_link_stats();
+ if (!demand) {
+ remove_pidfiles();
+ script_unsetenv("IFNAME");
+ }
+
+ tdb_writelock(pppdb);
+ destroy_bundle();
+ iterate_bundle_links(sendhup);
+ key.dptr = blinks_id;
+ key.dsize = strlen(blinks_id);
+ tdb_delete(pppdb, key);
+ tdb_writeunlock(pppdb);
+
+new_phase(PHASE_DEAD);
+}
+
+static void make_bundle_links(int append)
+{
+ TDB_DATA key, rec;
+ char *p;
+ char entry[32];
+ int l;
+
+ key.dptr = blinks_id;
+ key.dsize = strlen(blinks_id);
+ slprintf(entry, sizeof(entry), "%s;", db_key);
+ p = entry;
+ if (append) {
+ rec = tdb_fetch(pppdb, key);
+ if (rec.dptr != NULL && rec.dsize > 0) {
+ rec.dptr[rec.dsize-1] = 0;
+ if (strstr(rec.dptr, db_key) != NULL) {
+ /* already in there? strange */
+ warn("link entry already exists in tdb");
+ return;
+ }
+ l = rec.dsize + strlen(entry);
+ p = malloc(l);
+ if (p == NULL)
+ novm("bundle link list");
+ slprintf(p, l, "%s%s", rec.dptr, entry);
+ } else {
+ warn("bundle link list not found");
+ }
+ if (rec.dptr != NULL)
+ free(rec.dptr);
+ }
+ rec.dptr = p;
+ rec.dsize = strlen(p) + 1;
+ if (tdb_store(pppdb, key, rec, TDB_REPLACE))
+ error("couldn't %s bundle link list",
+ append? "update": "create");
+ if (p != entry)
+ free(p);
+}
+
+static void remove_bundle_link()
+{
+ TDB_DATA key, rec;
+ char entry[32];
+ char *p, *q;
+ int l;
+
+ key.dptr = blinks_id;
+ key.dsize = strlen(blinks_id);
+ slprintf(entry, sizeof(entry), "%s;", db_key);
+
+ rec = tdb_fetch(pppdb, key);
+ if (rec.dptr == NULL || rec.dsize <= 0) {
+ if (rec.dptr != NULL)
+ free(rec.dptr);
+ return;
+ }
+ rec.dptr[rec.dsize-1] = 0;
+ p = strstr(rec.dptr, entry);
+ if (p != NULL) {
+ q = p + strlen(entry);
+ l = strlen(q) + 1;
+ memmove(p, q, l);
+ rec.dsize = p - rec.dptr + l;
+ if (tdb_store(pppdb, key, rec, TDB_REPLACE))
+ error("couldn't update bundle link list (removal)");
+ }
+ free(rec.dptr);
+}
+
+static void iterate_bundle_links(void (*func)(char *))
+{
+ TDB_DATA key, rec, pp;
+ char *p, *q;
+
+ key.dptr = blinks_id;
+ key.dsize = strlen(blinks_id);
+ rec = tdb_fetch(pppdb, key);
+ if (rec.dptr == NULL || rec.dsize <= 0) {
+ error("bundle link list not found (iterating list)");
+ if (rec.dptr != NULL)
+ free(rec.dptr);
+ return;
+ }
+ p = rec.dptr;
+ p[rec.dsize-1] = 0;
+ while ((q = strchr(p, ';')) != NULL) {
+ *q = 0;
+ key.dptr = p;
+ key.dsize = q - p;
+ pp = tdb_fetch(pppdb, key);
+ if (pp.dptr != NULL && pp.dsize > 0) {
+ pp.dptr[pp.dsize-1] = 0;
+ func(pp.dptr);
+ }
+ if (pp.dptr != NULL)
+ free(pp.dptr);
+ p = q + 1;
+ }
+ free(rec.dptr);
+}
+
static int
parse_num(str, key, valp)
char *str;
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
- * $Id: pppd.h,v 1.86 2004/11/06 05:42:29 paulus Exp $
+ * $Id: pppd.h,v 1.87 2004/11/12 10:30:51 paulus Exp $
*/
/*
extern char ifname[]; /* Interface name */
extern char hostname[]; /* Our hostname */
extern u_char outpacket_buf[]; /* Buffer for outgoing packets */
+extern int devfd; /* fd of underlying device */
+extern int fd_ppp; /* fd for talking PPP */
extern int phase; /* Current state of link - see values below */
extern int baud_rate; /* Current link speed in bits/sec */
extern char *progname; /* Name of this program */
extern int fd_devnull; /* fd open to /dev/null */
extern int listen_time; /* time to listen first (ms) */
+extern bool doing_multilink;
+extern bool multilink_master;
+extern bool bundle_eof;
+extern bool bundle_terminating;
+
extern struct notifier *pidchange; /* for notifications of pid changing */
extern struct notifier *phasechange; /* for notifications of phase changes */
extern struct notifier *exitnotify; /* for notification that we're exiting */
#define PHASE_TERMINATE 9
#define PHASE_DISCONNECT 10
#define PHASE_HOLDOFF 11
+#define PHASE_MASTER 12
/*
* The following struct gives the addresses of procedures to call
void notify __P((struct notifier *, int));
int ppp_send_config __P((int, int, u_int32_t, int, int));
int ppp_recv_config __P((int, int, u_int32_t, int, int));
+void remove_pidfiles __P((void));
/* Procedures exported from tty.c. */
void tty_init __P((void));
void link_required __P((int)); /* we are starting to use the link */
void link_terminated __P((int)); /* we are finished with the link */
void link_down __P((int)); /* the LCP layer has left the Opened state */
+void upper_layers_down __P((int));/* take all NCPs down */
void link_established __P((int)); /* the link is up; authenticate now */
void start_networks __P((int)); /* start all the network control protos */
void continue_networks __P((int)); /* start network [ip, etc] control protos */
int loop_frame __P((unsigned char *, int)); /* should we bring link up? */
/* Procedures exported from multilink.c */
+#ifdef HAVE_MULTILINK
void mp_check_options __P((void)); /* Check multilink-related options */
int mp_join_bundle __P((void)); /* join our link to an appropriate bundle */
+void mp_exit_bundle __P((void)); /* have disconnected our link from bundle */
+void mp_bundle_terminated __P((void));
char *epdisc_to_str __P((struct epdisc *)); /* string from endpoint discrim. */
int str_to_epdisc __P((struct epdisc *, char *)); /* endpt disc. from str */
+#else
+#define mp_bundle_terminated() /* nothing */
+#define mp_exit_bundle() /* nothing */
+#define doing_multilink 0
+#define multilink_master 0
+#endif
/* Procedures exported from sys-*.c */
void sys_init __P((void)); /* Do system-dependent initialization */
void make_new_bundle __P((int, int, int, int)); /* Create new bundle */
int bundle_attach __P((int)); /* Attach link to existing bundle */
void cfg_bundle __P((int, int, int, int)); /* Configure existing bundle */
+void destroy_bundle __P((void)); /* Tell driver to destroy 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 */
if (demand) {
modify_flags(ppp_dev_fd, 0, SC_LOOP_TRAFFIC);
looped = 1;
- } else if (ppp_dev_fd >= 0) {
+ } else if (!doing_multilink && ppp_dev_fd >= 0) {
close(ppp_dev_fd);
remove_fd(ppp_dev_fd);
ppp_dev_fd = -1;
return 1;
}
+/*
+ * destroy_bundle - tell the driver to destroy our bundle.
+ */
+void destroy_bundle(void)
+{
+ if (ppp_dev_fd >= 0) {
+ close(ppp_dev_fd);
+ remove_fd(ppp_dev_fd);
+ ppp_dev_fd = -1;
+ }
+}
+
/********************************************************************
*
* clean_check - Fetch the flags for the device and generate
if (nr < 0 && errno == ENXIO)
return 0;
}
- if (nr < 0 && new_style_driver && ppp_dev_fd >= 0) {
+ if (nr < 0 && new_style_driver && ppp_dev_fd >= 0 && !bundle_eof) {
/* 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 != EAGAIN
&& errno != EIO && errno != EINTR)
error("read /dev/ppp: %m");
if (nr < 0 && errno == ENXIO)
- return 0;
+ nr = 0;
+ if (nr == 0 && doing_multilink) {
+ remove_fd(ppp_dev_fd);
+ bundle_eof = 1;
+ }
}
+ if (new_style_driver && ppp_fd < 0 && ppp_dev_fd < 0)
+ nr = 0;
return (new_style_driver && nr > 0)? nr+2: nr;
}