From 637346ba24c289c5a0485651c05baa3f7c22a6b8 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 12 Nov 2004 10:30:51 +0000 Subject: [PATCH] Multilink improvements. This involved moving some logic from the main loop in main.c into link_required() and link_terminated() in auth.c and adding code to multilink.c. We now make a tdb entry with the list of pppd pids for all the links in the bundle, and the master pppd uses this to send a SIGHUP to each one when the bundle is terminated. We still have one pppd controlling both the bundle and the first link, but when that link goes down, assuming that other links still exist, the first link's pppd will clean up after that link but then stay running until all the links have disconnected. So it is possible to lose the first link without losing the bundle. This requires a small kernel patch which I will be sending to the kernel maintainers shortly. --- pppd/auth.c | 146 ++++++++++++++++++++++++++++++++----- pppd/lcp.c | 4 +- pppd/main.c | 141 ++++++++++-------------------------- pppd/multilink.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++- pppd/pppd.h | 22 +++++- pppd/sys-linux.c | 24 ++++++- 6 files changed, 391 insertions(+), 129 deletions(-) diff --git a/pppd/auth.c b/pppd/auth.c index b9115b9..8a8b2fb 100644 --- a/pppd/auth.c +++ b/pppd/auth.c @@ -68,12 +68,13 @@ * 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 #include #include #include +#include #include #include #include @@ -531,6 +532,55 @@ void 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)(); + } /* @@ -541,16 +591,65 @@ void 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); } /* @@ -559,17 +658,30 @@ link_terminated(unit) 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; @@ -580,8 +692,6 @@ link_down(unit) } num_np_open = 0; num_np_up = 0; - if (phase != PHASE_DEAD) - new_phase(PHASE_ESTABLISH); } /* @@ -602,10 +712,12 @@ link_established(unit) /* * 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); diff --git a/pppd/lcp.c b/pppd/lcp.c index dde1381..a06300a 100644 --- a/pppd/lcp.c +++ b/pppd/lcp.c @@ -40,7 +40,7 @@ * 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: @@ -401,7 +401,7 @@ lcp_close(unit, reason) { 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)) { /* diff --git a/pppd/main.c b/pppd/main.c index 4a91e06..285f219 100644 --- a/pppd/main.c +++ b/pppd/main.c @@ -66,7 +66,7 @@ * 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 #include @@ -165,11 +165,11 @@ void (*snoop_recv_hook) __P((unsigned char *p, int len)) = NULL; 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; @@ -207,6 +207,9 @@ int link_stats_valid; int error_count; +bool bundle_eof; +bool bundle_terminating; + /* * We maintain a list of child process pids and * functions to call when they exit. @@ -488,12 +491,13 @@ main(argc, argv) * 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; @@ -527,57 +531,21 @@ main(argc, argv) 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 */ @@ -586,54 +554,6 @@ main(argc, argv) } } - 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; @@ -897,7 +817,7 @@ create_pidfile(pid) } } -static void +void create_linkpidfile(pid) int pid; { @@ -919,6 +839,19 @@ create_linkpidfile(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. */ @@ -1031,6 +964,11 @@ get_input() 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; @@ -1159,7 +1097,8 @@ void die(status) int status; { - print_link_stats(); + if (!doing_multilink || multilink_master) + print_link_stats(); cleanup(); notify(exitnotify, status); syslog(LOG_INFO, "Exit."); @@ -1179,13 +1118,7 @@ cleanup() 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) @@ -2004,7 +1937,7 @@ update_db_entry() 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; diff --git a/pppd/multilink.c b/pppd/multilink.c index dd6d23f..7444162 100644 --- a/pppd/multilink.c +++ b/pppd/multilink.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "pppd.h" #include "fsm.h" @@ -42,10 +43,17 @@ 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)); @@ -71,6 +79,7 @@ mp_check_options() 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 */ @@ -103,6 +112,18 @@ mp_join_bundle() 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) @@ -122,6 +143,8 @@ mp_join_bundle() return 0; } + doing_multilink = 1; + /* * Find the appropriate bundle or join a new one. * First we make up a name for the bundle. @@ -147,6 +170,13 @@ mp_join_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. @@ -170,8 +200,10 @@ mp_join_bundle() 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) @@ -188,6 +220,7 @@ mp_join_bundle() 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; @@ -200,11 +233,157 @@ mp_join_bundle() 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; diff --git a/pppd/pppd.h b/pppd/pppd.h index f4f5381..de3a6a1 100644 --- a/pppd/pppd.h +++ b/pppd/pppd.h @@ -39,7 +39,7 @@ * 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 $ */ /* @@ -215,6 +215,8 @@ extern int ifunit; /* Interface unit number */ 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 */ @@ -246,6 +248,11 @@ extern int ppp_session_number; /* Session number (eg PPPoE session) */ 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 */ @@ -375,6 +382,7 @@ extern int option_priority; /* priority of current options */ #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 @@ -484,6 +492,7 @@ void remove_notifier __P((struct notifier **, notify_func, void *)); 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)); @@ -515,6 +524,7 @@ ssize_t complete_read __P((int, void *, size_t)); 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 */ @@ -554,10 +564,19 @@ 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 */ +#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 */ @@ -574,6 +593,7 @@ int generic_establish_ppp __P((int dev_fd)); /* Make a ppp interface */ 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 */ diff --git a/pppd/sys-linux.c b/pppd/sys-linux.c index 73843e7..e963c79 100644 --- a/pppd/sys-linux.c +++ b/pppd/sys-linux.c @@ -593,7 +593,7 @@ void generic_disestablish_ppp(int dev_fd) 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; @@ -712,6 +712,18 @@ int bundle_attach(int ifnum) 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 @@ -1086,15 +1098,21 @@ int read_packet (unsigned char *buf) 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; } -- 2.39.2