xref: /freebsd/usr.sbin/ppp/bundle.c (revision 8390b576621390705c322cd2a7781df9174356f7)
17a6f8720SBrian Somers /*-
27a6f8720SBrian Somers  * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
37a6f8720SBrian Somers  * All rights reserved.
47a6f8720SBrian Somers  *
57a6f8720SBrian Somers  * Redistribution and use in source and binary forms, with or without
67a6f8720SBrian Somers  * modification, are permitted provided that the following conditions
77a6f8720SBrian Somers  * are met:
87a6f8720SBrian Somers  * 1. Redistributions of source code must retain the above copyright
97a6f8720SBrian Somers  *    notice, this list of conditions and the following disclaimer.
107a6f8720SBrian Somers  * 2. Redistributions in binary form must reproduce the above copyright
117a6f8720SBrian Somers  *    notice, this list of conditions and the following disclaimer in the
127a6f8720SBrian Somers  *    documentation and/or other materials provided with the distribution.
137a6f8720SBrian Somers  *
147a6f8720SBrian Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
157a6f8720SBrian Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
167a6f8720SBrian Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
177a6f8720SBrian Somers  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
187a6f8720SBrian Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
197a6f8720SBrian Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
207a6f8720SBrian Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
217a6f8720SBrian Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
227a6f8720SBrian Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
237a6f8720SBrian Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
247a6f8720SBrian Somers  * SUCH DAMAGE.
257a6f8720SBrian Somers  *
268390b576SBrian Somers  *	$Id: bundle.c,v 1.1.2.34 1998/04/03 19:21:06 brian Exp $
277a6f8720SBrian Somers  */
287a6f8720SBrian Somers 
297a6f8720SBrian Somers #include <sys/param.h>
307a6f8720SBrian Somers #include <sys/time.h>
317a6f8720SBrian Somers #include <sys/socket.h>
327a6f8720SBrian Somers #include <netinet/in.h>
337a6f8720SBrian Somers #include <net/if.h>
347a6f8720SBrian Somers #include <arpa/inet.h>
357a6f8720SBrian Somers #include <net/route.h>
367a6f8720SBrian Somers #include <net/if_dl.h>
37eaa4df37SBrian Somers #include <netinet/in_systm.h>
38eaa4df37SBrian Somers #include <netinet/ip.h>
397a6f8720SBrian Somers 
407a6f8720SBrian Somers #include <errno.h>
417a6f8720SBrian Somers #include <fcntl.h>
427a6f8720SBrian Somers #include <stdio.h>
437a6f8720SBrian Somers #include <string.h>
447a6f8720SBrian Somers #include <sys/ioctl.h>
457a6f8720SBrian Somers #include <termios.h>
467a6f8720SBrian Somers #include <unistd.h>
477a6f8720SBrian Somers 
487a6f8720SBrian Somers #include "command.h"
497a6f8720SBrian Somers #include "mbuf.h"
507a6f8720SBrian Somers #include "log.h"
517a6f8720SBrian Somers #include "id.h"
527a6f8720SBrian Somers #include "defs.h"
537a6f8720SBrian Somers #include "timer.h"
547a6f8720SBrian Somers #include "fsm.h"
557a6f8720SBrian Somers #include "iplist.h"
56879ed6faSBrian Somers #include "lqr.h"
57455aabc3SBrian Somers #include "hdlc.h"
587a6f8720SBrian Somers #include "throughput.h"
59eaa4df37SBrian Somers #include "slcompress.h"
607a6f8720SBrian Somers #include "ipcp.h"
615ca5389aSBrian Somers #include "filter.h"
622f786681SBrian Somers #include "descriptor.h"
637a6f8720SBrian Somers #include "loadalias.h"
647a6f8720SBrian Somers #include "vars.h"
657a6f8720SBrian Somers #include "arp.h"
667a6f8720SBrian Somers #include "systems.h"
677a6f8720SBrian Somers #include "route.h"
687a6f8720SBrian Somers #include "lcp.h"
697a6f8720SBrian Somers #include "ccp.h"
703b0f8d2eSBrian Somers #include "link.h"
713b0f8d2eSBrian Somers #include "mp.h"
723b0f8d2eSBrian Somers #include "bundle.h"
73455aabc3SBrian Somers #include "async.h"
74455aabc3SBrian Somers #include "physical.h"
752289f246SBrian Somers #include "modem.h"
76455aabc3SBrian Somers #include "main.h"
77455aabc3SBrian Somers #include "auth.h"
78455aabc3SBrian Somers #include "lcpproto.h"
79455aabc3SBrian Somers #include "pap.h"
80455aabc3SBrian Somers #include "chap.h"
81455aabc3SBrian Somers #include "tun.h"
8285b542cfSBrian Somers #include "prompt.h"
833006ec67SBrian Somers #include "chat.h"
843006ec67SBrian Somers #include "datalink.h"
853006ec67SBrian Somers #include "ip.h"
867a6f8720SBrian Somers 
87455aabc3SBrian Somers static const char *PhaseNames[] = {
88455aabc3SBrian Somers   "Dead", "Establish", "Authenticate", "Network", "Terminate"
89455aabc3SBrian Somers };
90455aabc3SBrian Somers 
91455aabc3SBrian Somers const char *
92455aabc3SBrian Somers bundle_PhaseName(struct bundle *bundle)
937a6f8720SBrian Somers {
94455aabc3SBrian Somers   return bundle->phase <= PHASE_TERMINATE ?
95455aabc3SBrian Somers     PhaseNames[bundle->phase] : "unknown";
967a6f8720SBrian Somers }
977a6f8720SBrian Somers 
98455aabc3SBrian Somers void
995563ebdeSBrian Somers bundle_NewPhase(struct bundle *bundle, u_int new)
100455aabc3SBrian Somers {
101aef795ccSBrian Somers   if (new == bundle->phase)
102aef795ccSBrian Somers     return;
103aef795ccSBrian Somers 
104e2ebb036SBrian Somers   if (new <= PHASE_TERMINATE)
105e2ebb036SBrian Somers     LogPrintf(LogPHASE, "bundle: %s\n", PhaseNames[new]);
1067a6f8720SBrian Somers 
107455aabc3SBrian Somers   switch (new) {
108455aabc3SBrian Somers   case PHASE_DEAD:
109455aabc3SBrian Somers     bundle->phase = new;
110455aabc3SBrian Somers     break;
111455aabc3SBrian Somers 
112455aabc3SBrian Somers   case PHASE_ESTABLISH:
113455aabc3SBrian Somers     bundle->phase = new;
114455aabc3SBrian Somers     break;
115455aabc3SBrian Somers 
116455aabc3SBrian Somers   case PHASE_AUTHENTICATE:
117455aabc3SBrian Somers     bundle->phase = new;
11885b542cfSBrian Somers     prompt_Display(&prompt, bundle);
119455aabc3SBrian Somers     break;
120455aabc3SBrian Somers 
121455aabc3SBrian Somers   case PHASE_NETWORK:
1225828db6dSBrian Somers     ipcp_Setup(&bundle->ncp.ipcp);
1235828db6dSBrian Somers     FsmUp(&bundle->ncp.ipcp.fsm);
1245828db6dSBrian Somers     FsmOpen(&bundle->ncp.ipcp.fsm);
125455aabc3SBrian Somers     /* Fall through */
126455aabc3SBrian Somers 
127455aabc3SBrian Somers   case PHASE_TERMINATE:
128455aabc3SBrian Somers     bundle->phase = new;
12985b542cfSBrian Somers     prompt_Display(&prompt, bundle);
130455aabc3SBrian Somers     break;
1317a6f8720SBrian Somers   }
1327a6f8720SBrian Somers }
1337a6f8720SBrian Somers 
1347a6f8720SBrian Somers static int
1357a6f8720SBrian Somers bundle_CleanInterface(const struct bundle *bundle)
1367a6f8720SBrian Somers {
1377a6f8720SBrian Somers   int s;
1387a6f8720SBrian Somers   struct ifreq ifrq;
1397a6f8720SBrian Somers   struct ifaliasreq ifra;
1407a6f8720SBrian Somers 
1417a6f8720SBrian Somers   s = ID0socket(AF_INET, SOCK_DGRAM, 0);
1427a6f8720SBrian Somers   if (s < 0) {
1437a6f8720SBrian Somers     LogPrintf(LogERROR, "bundle_CleanInterface: socket(): %s\n",
1447a6f8720SBrian Somers               strerror(errno));
1457a6f8720SBrian Somers     return (-1);
1467a6f8720SBrian Somers   }
1477a6f8720SBrian Somers   strncpy(ifrq.ifr_name, bundle->ifname, sizeof ifrq.ifr_name - 1);
1487a6f8720SBrian Somers   ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
1497a6f8720SBrian Somers   while (ID0ioctl(s, SIOCGIFADDR, &ifrq) == 0) {
1507a6f8720SBrian Somers     memset(&ifra.ifra_mask, '\0', sizeof ifra.ifra_mask);
1517a6f8720SBrian Somers     strncpy(ifra.ifra_name, bundle->ifname, sizeof ifra.ifra_name - 1);
1527a6f8720SBrian Somers     ifra.ifra_name[sizeof ifra.ifra_name - 1] = '\0';
1537a6f8720SBrian Somers     ifra.ifra_addr = ifrq.ifr_addr;
1547a6f8720SBrian Somers     if (ID0ioctl(s, SIOCGIFDSTADDR, &ifrq) < 0) {
1557a6f8720SBrian Somers       if (ifra.ifra_addr.sa_family == AF_INET)
1567a6f8720SBrian Somers         LogPrintf(LogERROR,
1577a6f8720SBrian Somers                   "bundle_CleanInterface: Can't get dst for %s on %s !\n",
1587a6f8720SBrian Somers                   inet_ntoa(((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr),
1597a6f8720SBrian Somers                   bundle->ifname);
1607a6f8720SBrian Somers       return 0;
1617a6f8720SBrian Somers     }
1627a6f8720SBrian Somers     ifra.ifra_broadaddr = ifrq.ifr_dstaddr;
1637a6f8720SBrian Somers     if (ID0ioctl(s, SIOCDIFADDR, &ifra) < 0) {
1647a6f8720SBrian Somers       if (ifra.ifra_addr.sa_family == AF_INET)
1657a6f8720SBrian Somers         LogPrintf(LogERROR,
1667a6f8720SBrian Somers                   "bundle_CleanInterface: Can't delete %s address on %s !\n",
1677a6f8720SBrian Somers                   inet_ntoa(((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr),
1687a6f8720SBrian Somers                   bundle->ifname);
1697a6f8720SBrian Somers       return 0;
1707a6f8720SBrian Somers     }
1717a6f8720SBrian Somers   }
1727a6f8720SBrian Somers 
1737a6f8720SBrian Somers   return 1;
1747a6f8720SBrian Somers }
1757a6f8720SBrian Somers 
1766d666775SBrian Somers static void
1776d666775SBrian Somers bundle_LayerStart(void *v, struct fsm *fp)
1787a6f8720SBrian Somers {
1793006ec67SBrian Somers   /* The given FSM is about to start up ! */
1807a6f8720SBrian Somers }
1817a6f8720SBrian Somers 
1825cf4388bSBrian Somers 
1835cf4388bSBrian Somers static void
1845cf4388bSBrian Somers bundle_Notify(struct bundle *bundle, char c)
1855cf4388bSBrian Somers {
1865cf4388bSBrian Somers   if (bundle->notify.fd != -1) {
1875cf4388bSBrian Somers     if (write(bundle->notify.fd, &c, 1) == 1)
1885cf4388bSBrian Somers       LogPrintf(LogPHASE, "Parent notified of success.\n");
1895cf4388bSBrian Somers     else
1905cf4388bSBrian Somers       LogPrintf(LogPHASE, "Failed to notify parent of success.\n");
1915cf4388bSBrian Somers     close(bundle->notify.fd);
1925cf4388bSBrian Somers     bundle->notify.fd = -1;
1935cf4388bSBrian Somers   }
1945cf4388bSBrian Somers }
1953b0f8d2eSBrian Somers 
1966d666775SBrian Somers static void
1973b0f8d2eSBrian Somers bundle_vLayerUp(void *v, struct fsm *fp)
1983b0f8d2eSBrian Somers {
1993b0f8d2eSBrian Somers   bundle_LayerUp((struct bundle *)v, fp);
2003b0f8d2eSBrian Somers }
2013b0f8d2eSBrian Somers 
2023b0f8d2eSBrian Somers void
2033b0f8d2eSBrian Somers bundle_LayerUp(struct bundle *bundle, struct fsm *fp)
2047a6f8720SBrian Somers {
2053006ec67SBrian Somers   /*
2063006ec67SBrian Somers    * The given fsm is now up
2073b0f8d2eSBrian Somers    * If it's an LCP (including MP initialisation), set our mtu
2083b0f8d2eSBrian Somers    * (This routine is also called from mp_Init() with it's LCP)
209f4768038SBrian Somers    * If it's an NCP, tell our background mode parent to go away.
2103b0f8d2eSBrian Somers    * If it's the first NCP, start the idle timer.
2113006ec67SBrian Somers    */
2126d666775SBrian Somers 
2135563ebdeSBrian Somers   if (fp->proto == PROTO_LCP) {
2143b0f8d2eSBrian Somers     if (bundle->ncp.mp.active) {
2153b0f8d2eSBrian Somers       int speed;
2163b0f8d2eSBrian Somers       struct datalink *dl;
2175563ebdeSBrian Somers 
2183b0f8d2eSBrian Somers       for (dl = bundle->links, speed = 0; dl; dl = dl->next)
2193b0f8d2eSBrian Somers         speed += modem_Speed(dl->physical);
2203b0f8d2eSBrian Somers       if (speed)
2213b0f8d2eSBrian Somers         tun_configure(bundle, bundle->ncp.mp.link.lcp.his_mru, speed);
2223b0f8d2eSBrian Somers     } else
2233b0f8d2eSBrian Somers       tun_configure(bundle, fsm2lcp(fp)->his_mru,
2243b0f8d2eSBrian Somers                     modem_Speed(link2physical(fp->link)));
2253b0f8d2eSBrian Somers   } else if (fp->proto == PROTO_IPCP) {
226ab886ad0SBrian Somers     bundle_StartIdleTimer(bundle);
2275cf4388bSBrian Somers     bundle_Notify(bundle, EX_NORMAL);
2287a6f8720SBrian Somers   }
229ab886ad0SBrian Somers }
2307a6f8720SBrian Somers 
2316d666775SBrian Somers static void
2326d666775SBrian Somers bundle_LayerDown(void *v, struct fsm *fp)
2336d666775SBrian Somers {
2346d666775SBrian Somers   /*
2356d666775SBrian Somers    * The given FSM has been told to come down.
236ab886ad0SBrian Somers    * If it's our last NCP, stop the idle timer.
2373b0f8d2eSBrian Somers    * If it's our last NCP *OR* LCP, enter TERMINATE phase.
2383b0f8d2eSBrian Somers    * If it's an LCP and we're in multilink mode, adjust our tun speed.
2396d666775SBrian Somers    */
240ab886ad0SBrian Somers 
241ab886ad0SBrian Somers   struct bundle *bundle = (struct bundle *)v;
242ab886ad0SBrian Somers 
2433b0f8d2eSBrian Somers   if (fp->proto == PROTO_IPCP) {
244ab886ad0SBrian Somers     bundle_StopIdleTimer(bundle);
2453b0f8d2eSBrian Somers     bundle_NewPhase(bundle, PHASE_TERMINATE);
2463b0f8d2eSBrian Somers   } else if (fp->proto == PROTO_LCP) {
2473b0f8d2eSBrian Somers     int speed, others_active;
2483b0f8d2eSBrian Somers     struct datalink *dl;
2493b0f8d2eSBrian Somers 
2503b0f8d2eSBrian Somers     others_active = 0;
2513b0f8d2eSBrian Somers     for (dl = bundle->links, speed = 0; dl; dl = dl->next)
2523b0f8d2eSBrian Somers       if (fp != &dl->physical->link.lcp.fsm &&
2533b0f8d2eSBrian Somers           dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
2543b0f8d2eSBrian Somers         speed += modem_Speed(dl->physical);
2553b0f8d2eSBrian Somers         others_active++;
2563b0f8d2eSBrian Somers       }
2573b0f8d2eSBrian Somers     if (bundle->ncp.mp.active && speed)
2583b0f8d2eSBrian Somers       tun_configure(bundle, bundle->ncp.mp.link.lcp.his_mru, speed);
2593b0f8d2eSBrian Somers 
2603b0f8d2eSBrian Somers     if (!others_active)
2613b0f8d2eSBrian Somers       bundle_NewPhase(bundle, PHASE_TERMINATE);
2623b0f8d2eSBrian Somers   }
2636d666775SBrian Somers }
2646d666775SBrian Somers 
2656d666775SBrian Somers static void
2666d666775SBrian Somers bundle_LayerFinish(void *v, struct fsm *fp)
2676d666775SBrian Somers {
2686d666775SBrian Somers   /* The given fsm is now down (fp cannot be NULL)
2696d666775SBrian Somers    *
2706d666775SBrian Somers    * If it's the last LCP, FsmDown all NCPs
2713b0f8d2eSBrian Somers    * If it's the last NCP, FsmClose all LCPs
2726d666775SBrian Somers    */
2736d666775SBrian Somers 
2746d666775SBrian Somers   struct bundle *bundle = (struct bundle *)v;
2756d666775SBrian Somers   struct datalink *dl;
2766d666775SBrian Somers 
2773b0f8d2eSBrian Somers   if (fp->proto == PROTO_IPCP) {
2786d666775SBrian Somers     for (dl = bundle->links; dl; dl = dl->next)
2793b0f8d2eSBrian Somers       datalink_Close(dl, 0);
2803b0f8d2eSBrian Somers     FsmDown(fp);
2813b0f8d2eSBrian Somers     FsmClose(fp);
2823b0f8d2eSBrian Somers   } else if (fp->proto == PROTO_LCP) {
2833b0f8d2eSBrian Somers     int others_active;
284a611cad6SBrian Somers 
2853b0f8d2eSBrian Somers     others_active = 0;
2863b0f8d2eSBrian Somers     for (dl = bundle->links; dl; dl = dl->next)
2873b0f8d2eSBrian Somers       if (fp != &dl->physical->link.lcp.fsm &&
2883b0f8d2eSBrian Somers           dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP)
2893b0f8d2eSBrian Somers         others_active++;
2903b0f8d2eSBrian Somers 
2913b0f8d2eSBrian Somers     if (!others_active) {
2925828db6dSBrian Somers       FsmDown(&bundle->ncp.ipcp.fsm);
2935828db6dSBrian Somers       FsmClose(&bundle->ncp.ipcp.fsm);		/* ST_INITIAL please */
2946d666775SBrian Somers     }
2953b0f8d2eSBrian Somers   }
2963b0f8d2eSBrian Somers }
2976d666775SBrian Somers 
2987a6f8720SBrian Somers int
2997a6f8720SBrian Somers bundle_LinkIsUp(const struct bundle *bundle)
3007a6f8720SBrian Somers {
3015828db6dSBrian Somers   return bundle->ncp.ipcp.fsm.state == ST_OPENED;
3027a6f8720SBrian Somers }
3037a6f8720SBrian Somers 
3047a6f8720SBrian Somers void
3053006ec67SBrian Somers bundle_Close(struct bundle *bundle, const char *name, int staydown)
3067a6f8720SBrian Somers {
307455aabc3SBrian Somers   /*
3083006ec67SBrian Somers    * Please close the given datalink.
3093b0f8d2eSBrian Somers    * If name == NULL or name is the last datalink, enter TERMINATE phase
3103b0f8d2eSBrian Somers    * and FsmClose all NCPs (except our MP)
3113b0f8d2eSBrian Somers    * If it isn't the last datalink, just Close that datalink.
312455aabc3SBrian Somers    */
3137a6f8720SBrian Somers 
3143b0f8d2eSBrian Somers   struct datalink *dl, *this_dl;
3153b0f8d2eSBrian Somers   int others_active;
3163006ec67SBrian Somers 
3173b0f8d2eSBrian Somers   if (bundle->phase == PHASE_TERMINATE || bundle->phase == PHASE_DEAD)
3183b0f8d2eSBrian Somers     return;
3193b0f8d2eSBrian Somers 
3203b0f8d2eSBrian Somers   others_active = 0;
3213b0f8d2eSBrian Somers   this_dl = NULL;
3223b0f8d2eSBrian Somers 
3233b0f8d2eSBrian Somers   for (dl = bundle->links; dl; dl = dl->next) {
3243b0f8d2eSBrian Somers     if (name && !strcasecmp(name, dl->name))
3253b0f8d2eSBrian Somers       this_dl = dl;
3263b0f8d2eSBrian Somers     if (name == NULL || this_dl == dl) {
327d345321bSBrian Somers       if (staydown)
3283006ec67SBrian Somers         datalink_StayDown(dl);
3293b0f8d2eSBrian Somers     } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP)
3303b0f8d2eSBrian Somers       others_active++;
3313b0f8d2eSBrian Somers   }
3323b0f8d2eSBrian Somers 
3333b0f8d2eSBrian Somers   if (name && this_dl == NULL) {
3343b0f8d2eSBrian Somers     LogPrintf(LogWARN, "%s: Invalid datalink name\n", name);
3353b0f8d2eSBrian Somers     return;
3363b0f8d2eSBrian Somers   }
3373b0f8d2eSBrian Somers 
3383b0f8d2eSBrian Somers   if (!others_active) {
3393b0f8d2eSBrian Somers     if (bundle->ncp.ipcp.fsm.state > ST_CLOSED ||
3403b0f8d2eSBrian Somers         bundle->ncp.ipcp.fsm.state == ST_STARTING)
3413b0f8d2eSBrian Somers       FsmClose(&bundle->ncp.ipcp.fsm);
3423b0f8d2eSBrian Somers     else {
3433b0f8d2eSBrian Somers       /* This shouldn't happen ! */
3443b0f8d2eSBrian Somers       LogPrintf(LogWARN, "Tidying up hung FSMs\n");
3455828db6dSBrian Somers       if (bundle->ncp.ipcp.fsm.state > ST_INITIAL) {
3465828db6dSBrian Somers         FsmClose(&bundle->ncp.ipcp.fsm);
3475828db6dSBrian Somers         FsmDown(&bundle->ncp.ipcp.fsm);
348d2fd8d77SBrian Somers       }
349d345321bSBrian Somers       for (dl = bundle->links; dl; dl = dl->next)
350d345321bSBrian Somers         datalink_Close(dl, staydown);
3517a6f8720SBrian Somers     }
3523b0f8d2eSBrian Somers   } else if (this_dl && this_dl->state != DATALINK_CLOSED &&
3533b0f8d2eSBrian Somers              this_dl->state != DATALINK_HANGUP)
3543b0f8d2eSBrian Somers     datalink_Close(this_dl, staydown);
355d2fd8d77SBrian Somers }
3567a6f8720SBrian Somers 
3572f786681SBrian Somers static int
3582f786681SBrian Somers bundle_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
3592f786681SBrian Somers {
3602f786681SBrian Somers   struct bundle *bundle = descriptor2bundle(d);
3612f786681SBrian Somers   struct datalink *dl;
3622f786681SBrian Somers   int result;
3632f786681SBrian Somers 
3642f786681SBrian Somers   result = 0;
3652f786681SBrian Somers   for (dl = bundle->links; dl; dl = dl->next)
3662f786681SBrian Somers     result += descriptor_UpdateSet(&dl->desc, r, w, e, n);
3672f786681SBrian Somers 
3682f786681SBrian Somers   return result;
3692f786681SBrian Somers }
3702f786681SBrian Somers 
3712f786681SBrian Somers static int
3722f786681SBrian Somers bundle_IsSet(struct descriptor *d, const fd_set *fdset)
3732f786681SBrian Somers {
3742f786681SBrian Somers   struct bundle *bundle = descriptor2bundle(d);
3752f786681SBrian Somers   struct datalink *dl;
3762f786681SBrian Somers 
3772f786681SBrian Somers   for (dl = bundle->links; dl; dl = dl->next)
3782f786681SBrian Somers     if (descriptor_IsSet(&dl->desc, fdset))
3792f786681SBrian Somers       return 1;
3802f786681SBrian Somers 
3812f786681SBrian Somers   return 0;
3822f786681SBrian Somers }
3832f786681SBrian Somers 
3842f786681SBrian Somers static void
3852f786681SBrian Somers bundle_DescriptorRead(struct descriptor *d, struct bundle *bundle,
3862f786681SBrian Somers                       const fd_set *fdset)
3872f786681SBrian Somers {
3882f786681SBrian Somers   struct datalink *dl;
3892f786681SBrian Somers 
3902f786681SBrian Somers   for (dl = bundle->links; dl; dl = dl->next)
3912f786681SBrian Somers     if (descriptor_IsSet(&dl->desc, fdset))
3922f786681SBrian Somers       descriptor_Read(&dl->desc, bundle, fdset);
3932f786681SBrian Somers }
3942f786681SBrian Somers 
3952f786681SBrian Somers static void
3962f786681SBrian Somers bundle_DescriptorWrite(struct descriptor *d, struct bundle *bundle,
3972f786681SBrian Somers                        const fd_set *fdset)
3982f786681SBrian Somers {
3992f786681SBrian Somers   struct datalink *dl;
4002f786681SBrian Somers 
4012f786681SBrian Somers   for (dl = bundle->links; dl; dl = dl->next)
4022f786681SBrian Somers     if (descriptor_IsSet(&dl->desc, fdset))
4032f786681SBrian Somers       descriptor_Write(&dl->desc, bundle, fdset);
4042f786681SBrian Somers }
4052f786681SBrian Somers 
4067a6f8720SBrian Somers 
4077a6f8720SBrian Somers #define MAX_TUN 256
4087a6f8720SBrian Somers /*
4097a6f8720SBrian Somers  * MAX_TUN is set at 256 because that is the largest minor number
4103b0f8d2eSBrian Somers  * we can use (certainly with mknod(1) anyway).  The search for a
4117a6f8720SBrian Somers  * device aborts when it reaches the first `Device not configured'
4127a6f8720SBrian Somers  * (ENXIO) or the third `No such file or directory' (ENOENT) error.
4137a6f8720SBrian Somers  */
4147a6f8720SBrian Somers struct bundle *
4157a6f8720SBrian Somers bundle_Create(const char *prefix)
4167a6f8720SBrian Somers {
4177a6f8720SBrian Somers   int s, enoentcount, err;
4187a6f8720SBrian Somers   struct ifreq ifrq;
4197a6f8720SBrian Somers   static struct bundle bundle;		/* there can be only one */
4207a6f8720SBrian Somers 
4217a6f8720SBrian Somers   if (bundle.ifname != NULL) {	/* Already allocated ! */
4227a6f8720SBrian Somers     LogPrintf(LogERROR, "bundle_Create:  There's only one BUNDLE !\n");
4237a6f8720SBrian Somers     return NULL;
4247a6f8720SBrian Somers   }
4257a6f8720SBrian Somers 
4267a6f8720SBrian Somers   err = ENOENT;
4277a6f8720SBrian Somers   enoentcount = 0;
4287a6f8720SBrian Somers   for (bundle.unit = 0; bundle.unit <= MAX_TUN; bundle.unit++) {
4297a6f8720SBrian Somers     snprintf(bundle.dev, sizeof bundle.dev, "%s%d", prefix, bundle.unit);
4307a6f8720SBrian Somers     bundle.tun_fd = ID0open(bundle.dev, O_RDWR);
4317a6f8720SBrian Somers     if (bundle.tun_fd >= 0)
4327a6f8720SBrian Somers       break;
4337a6f8720SBrian Somers     if (errno == ENXIO) {
4347a6f8720SBrian Somers       bundle.unit = MAX_TUN;
4357a6f8720SBrian Somers       err = errno;
4367a6f8720SBrian Somers     } else if (errno == ENOENT) {
4377a6f8720SBrian Somers       if (++enoentcount > 2)
4387a6f8720SBrian Somers 	bundle.unit = MAX_TUN;
4397a6f8720SBrian Somers     } else
4407a6f8720SBrian Somers       err = errno;
4417a6f8720SBrian Somers   }
4427a6f8720SBrian Somers 
4437a6f8720SBrian Somers   if (bundle.unit > MAX_TUN) {
44485b542cfSBrian Somers     prompt_Printf(&prompt, "No tunnel device is available (%s).\n",
44585b542cfSBrian Somers                   strerror(err));
4467a6f8720SBrian Somers     return NULL;
4477a6f8720SBrian Somers   }
4487a6f8720SBrian Somers 
4497a6f8720SBrian Somers   LogSetTun(bundle.unit);
4507a6f8720SBrian Somers 
4517a6f8720SBrian Somers   s = socket(AF_INET, SOCK_DGRAM, 0);
4527a6f8720SBrian Somers   if (s < 0) {
4537a6f8720SBrian Somers     LogPrintf(LogERROR, "bundle_Create: socket(): %s\n", strerror(errno));
4547a6f8720SBrian Somers     close(bundle.tun_fd);
4557a6f8720SBrian Somers     return NULL;
4567a6f8720SBrian Somers   }
4577a6f8720SBrian Somers 
4587a6f8720SBrian Somers   bundle.ifname = strrchr(bundle.dev, '/');
4597a6f8720SBrian Somers   if (bundle.ifname == NULL)
4607a6f8720SBrian Somers     bundle.ifname = bundle.dev;
4617a6f8720SBrian Somers   else
4627a6f8720SBrian Somers     bundle.ifname++;
4637a6f8720SBrian Somers 
4647a6f8720SBrian Somers   /*
4657a6f8720SBrian Somers    * Now, bring up the interface.
4667a6f8720SBrian Somers    */
4677a6f8720SBrian Somers   memset(&ifrq, '\0', sizeof ifrq);
4687a6f8720SBrian Somers   strncpy(ifrq.ifr_name, bundle.ifname, sizeof ifrq.ifr_name - 1);
4697a6f8720SBrian Somers   ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
4707a6f8720SBrian Somers   if (ID0ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) {
4717a6f8720SBrian Somers     LogPrintf(LogERROR, "OpenTunnel: ioctl(SIOCGIFFLAGS): %s\n",
4727a6f8720SBrian Somers 	      strerror(errno));
4737a6f8720SBrian Somers     close(s);
4747a6f8720SBrian Somers     close(bundle.tun_fd);
4757a6f8720SBrian Somers     bundle.ifname = NULL;
4767a6f8720SBrian Somers     return NULL;
4777a6f8720SBrian Somers   }
4787a6f8720SBrian Somers   ifrq.ifr_flags |= IFF_UP;
4797a6f8720SBrian Somers   if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
4807a6f8720SBrian Somers     LogPrintf(LogERROR, "OpenTunnel: ioctl(SIOCSIFFLAGS): %s\n",
4817a6f8720SBrian Somers 	      strerror(errno));
4827a6f8720SBrian Somers     close(s);
4837a6f8720SBrian Somers     close(bundle.tun_fd);
4847a6f8720SBrian Somers     bundle.ifname = NULL;
4857a6f8720SBrian Somers     return NULL;
4867a6f8720SBrian Somers   }
4877a6f8720SBrian Somers 
4887a6f8720SBrian Somers   close(s);
4897a6f8720SBrian Somers 
4907a6f8720SBrian Somers   if ((bundle.ifIndex = GetIfIndex(bundle.ifname)) < 0) {
4917a6f8720SBrian Somers     LogPrintf(LogERROR, "OpenTunnel: Can't find ifindex.\n");
4927a6f8720SBrian Somers     close(bundle.tun_fd);
4937a6f8720SBrian Somers     bundle.ifname = NULL;
4947a6f8720SBrian Somers     return NULL;
4957a6f8720SBrian Somers   }
4967a6f8720SBrian Somers 
49785b542cfSBrian Somers   prompt_Printf(&prompt, "Using interface: %s\n", bundle.ifname);
4987a6f8720SBrian Somers   LogPrintf(LogPHASE, "Using interface: %s\n", bundle.ifname);
4997a6f8720SBrian Somers 
500820de6ebSBrian Somers   bundle.routing_seq = 0;
501a0cbd833SBrian Somers   bundle.phase = PHASE_DEAD;
502a0cbd833SBrian Somers   bundle.CleaningUp = 0;
5037a6f8720SBrian Somers 
5046d666775SBrian Somers   bundle.fsm.LayerStart = bundle_LayerStart;
5053b0f8d2eSBrian Somers   bundle.fsm.LayerUp = bundle_vLayerUp;
5066d666775SBrian Somers   bundle.fsm.LayerDown = bundle_LayerDown;
5076d666775SBrian Somers   bundle.fsm.LayerFinish = bundle_LayerFinish;
5086d666775SBrian Somers   bundle.fsm.object = &bundle;
5097a6f8720SBrian Somers 
510ab886ad0SBrian Somers   bundle.cfg.idle_timeout = NCP_IDLE_TIMEOUT;
511ab886ad0SBrian Somers 
5126d666775SBrian Somers   bundle.links = datalink_Create("Modem", &bundle, &bundle.fsm);
5133006ec67SBrian Somers   if (bundle.links == NULL) {
5143006ec67SBrian Somers     LogPrintf(LogERROR, "Cannot create data link: %s\n", strerror(errno));
5156d666775SBrian Somers     close(bundle.tun_fd);
5166d666775SBrian Somers     bundle.ifname = NULL;
5172289f246SBrian Somers     return NULL;
5182289f246SBrian Somers   }
5192289f246SBrian Somers 
5202f786681SBrian Somers   bundle.desc.type = BUNDLE_DESCRIPTOR;
5212f786681SBrian Somers   bundle.desc.next = NULL;
5222f786681SBrian Somers   bundle.desc.UpdateSet = bundle_UpdateSet;
5232f786681SBrian Somers   bundle.desc.IsSet = bundle_IsSet;
5242f786681SBrian Somers   bundle.desc.Read = bundle_DescriptorRead;
5252f786681SBrian Somers   bundle.desc.Write = bundle_DescriptorWrite;
5262f786681SBrian Somers 
5275828db6dSBrian Somers   ipcp_Init(&bundle.ncp.ipcp, &bundle, &bundle.links->physical->link,
5285828db6dSBrian Somers             &bundle.fsm);
5296d666775SBrian Somers 
5305ca5389aSBrian Somers   memset(&bundle.filter, '\0', sizeof bundle.filter);
5315ca5389aSBrian Somers   bundle.filter.in.fragok = bundle.filter.in.logok = 1;
5325ca5389aSBrian Somers   bundle.filter.in.name = "IN";
5335ca5389aSBrian Somers   bundle.filter.out.fragok = bundle.filter.out.logok = 1;
5345ca5389aSBrian Somers   bundle.filter.out.name = "OUT";
5355ca5389aSBrian Somers   bundle.filter.dial.name = "DIAL";
5368390b576SBrian Somers   bundle.filter.dial.logok = 1;
5375ca5389aSBrian Somers   bundle.filter.alive.name = "ALIVE";
5385ca5389aSBrian Somers   bundle.filter.alive.logok = 1;
53993ee0ff2SBrian Somers   memset(&bundle.idle.timer, '\0', sizeof bundle.idle.timer);
54093ee0ff2SBrian Somers   bundle.idle.done = 0;
5415cf4388bSBrian Somers   bundle.notify.fd = -1;
54293ee0ff2SBrian Somers 
5436d666775SBrian Somers   /* Clean out any leftover crud */
5446d666775SBrian Somers   bundle_CleanInterface(&bundle);
5456d666775SBrian Somers 
5467a6f8720SBrian Somers   return &bundle;
5477a6f8720SBrian Somers }
5487a6f8720SBrian Somers 
54968a0f0ccSBrian Somers static void
55068a0f0ccSBrian Somers bundle_DownInterface(struct bundle *bundle)
55168a0f0ccSBrian Somers {
55268a0f0ccSBrian Somers   struct ifreq ifrq;
55368a0f0ccSBrian Somers   int s;
55468a0f0ccSBrian Somers 
55568a0f0ccSBrian Somers   DeleteIfRoutes(bundle, 1);
55668a0f0ccSBrian Somers 
55768a0f0ccSBrian Somers   s = ID0socket(AF_INET, SOCK_DGRAM, 0);
55868a0f0ccSBrian Somers   if (s < 0) {
55968a0f0ccSBrian Somers     LogPrintf(LogERROR, "bundle_DownInterface: socket: %s\n", strerror(errno));
56068a0f0ccSBrian Somers     return;
56168a0f0ccSBrian Somers   }
56268a0f0ccSBrian Somers 
56368a0f0ccSBrian Somers   memset(&ifrq, '\0', sizeof ifrq);
56468a0f0ccSBrian Somers   strncpy(ifrq.ifr_name, bundle->ifname, sizeof ifrq.ifr_name - 1);
56568a0f0ccSBrian Somers   ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
56668a0f0ccSBrian Somers   if (ID0ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) {
56768a0f0ccSBrian Somers     LogPrintf(LogERROR, "bundle_DownInterface: ioctl(SIOCGIFFLAGS): %s\n",
56868a0f0ccSBrian Somers        strerror(errno));
56968a0f0ccSBrian Somers     close(s);
57068a0f0ccSBrian Somers     return;
57168a0f0ccSBrian Somers   }
57268a0f0ccSBrian Somers   ifrq.ifr_flags &= ~IFF_UP;
57368a0f0ccSBrian Somers   if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
57468a0f0ccSBrian Somers     LogPrintf(LogERROR, "bundle_DownInterface: ioctl(SIOCSIFFLAGS): %s\n",
57568a0f0ccSBrian Somers        strerror(errno));
57668a0f0ccSBrian Somers     close(s);
57768a0f0ccSBrian Somers     return;
57868a0f0ccSBrian Somers   }
57968a0f0ccSBrian Somers   close(s);
58068a0f0ccSBrian Somers }
58168a0f0ccSBrian Somers 
58268a0f0ccSBrian Somers void
58368a0f0ccSBrian Somers bundle_Destroy(struct bundle *bundle)
58468a0f0ccSBrian Somers {
5853006ec67SBrian Somers   struct datalink *dl;
5863006ec67SBrian Somers 
58768a0f0ccSBrian Somers   if (mode & MODE_AUTO) {
5885828db6dSBrian Somers     IpcpCleanInterface(&bundle->ncp.ipcp.fsm);
58968a0f0ccSBrian Somers     bundle_DownInterface(bundle);
59068a0f0ccSBrian Somers   }
5913006ec67SBrian Somers 
5923006ec67SBrian Somers   dl = bundle->links;
5933006ec67SBrian Somers   while (dl)
5943006ec67SBrian Somers     dl = datalink_Destroy(dl);
5953006ec67SBrian Somers 
5965cf4388bSBrian Somers   bundle_Notify(bundle, EX_ERRDEAD);
59768a0f0ccSBrian Somers   bundle->ifname = NULL;
59868a0f0ccSBrian Somers }
59968a0f0ccSBrian Somers 
6007a6f8720SBrian Somers struct rtmsg {
6017a6f8720SBrian Somers   struct rt_msghdr m_rtm;
6027a6f8720SBrian Somers   char m_space[64];
6037a6f8720SBrian Somers };
6047a6f8720SBrian Somers 
6057a6f8720SBrian Somers void
606820de6ebSBrian Somers bundle_SetRoute(struct bundle *bundle, int cmd, struct in_addr dst,
6077a6f8720SBrian Somers                 struct in_addr gateway, struct in_addr mask, int bang)
6087a6f8720SBrian Somers {
6097a6f8720SBrian Somers   struct rtmsg rtmes;
6107a6f8720SBrian Somers   int s, nb, wb;
6117a6f8720SBrian Somers   char *cp;
6127a6f8720SBrian Somers   const char *cmdstr;
6137a6f8720SBrian Somers   struct sockaddr_in rtdata;
6147a6f8720SBrian Somers 
6157a6f8720SBrian Somers   if (bang)
6167a6f8720SBrian Somers     cmdstr = (cmd == RTM_ADD ? "Add!" : "Delete!");
6177a6f8720SBrian Somers   else
6187a6f8720SBrian Somers     cmdstr = (cmd == RTM_ADD ? "Add" : "Delete");
6197a6f8720SBrian Somers   s = ID0socket(PF_ROUTE, SOCK_RAW, 0);
6207a6f8720SBrian Somers   if (s < 0) {
62168a0f0ccSBrian Somers     LogPrintf(LogERROR, "bundle_SetRoute: socket(): %s\n", strerror(errno));
6227a6f8720SBrian Somers     return;
6237a6f8720SBrian Somers   }
6247a6f8720SBrian Somers   memset(&rtmes, '\0', sizeof rtmes);
6257a6f8720SBrian Somers   rtmes.m_rtm.rtm_version = RTM_VERSION;
6267a6f8720SBrian Somers   rtmes.m_rtm.rtm_type = cmd;
6277a6f8720SBrian Somers   rtmes.m_rtm.rtm_addrs = RTA_DST;
628820de6ebSBrian Somers   rtmes.m_rtm.rtm_seq = ++bundle->routing_seq;
6297a6f8720SBrian Somers   rtmes.m_rtm.rtm_pid = getpid();
6307a6f8720SBrian Somers   rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
6317a6f8720SBrian Somers 
6327a6f8720SBrian Somers   memset(&rtdata, '\0', sizeof rtdata);
6337a6f8720SBrian Somers   rtdata.sin_len = 16;
6347a6f8720SBrian Somers   rtdata.sin_family = AF_INET;
6357a6f8720SBrian Somers   rtdata.sin_port = 0;
6367a6f8720SBrian Somers   rtdata.sin_addr = dst;
6377a6f8720SBrian Somers 
6387a6f8720SBrian Somers   cp = rtmes.m_space;
6397a6f8720SBrian Somers   memcpy(cp, &rtdata, 16);
6407a6f8720SBrian Somers   cp += 16;
6417a6f8720SBrian Somers   if (cmd == RTM_ADD)
6427a6f8720SBrian Somers     if (gateway.s_addr == INADDR_ANY) {
6437a6f8720SBrian Somers       /* Add a route through the interface */
6447a6f8720SBrian Somers       struct sockaddr_dl dl;
6457a6f8720SBrian Somers       const char *iname;
6467a6f8720SBrian Somers       int ilen;
6477a6f8720SBrian Somers 
6487a6f8720SBrian Somers       iname = Index2Nam(bundle->ifIndex);
6497a6f8720SBrian Somers       ilen = strlen(iname);
6507a6f8720SBrian Somers       dl.sdl_len = sizeof dl - sizeof dl.sdl_data + ilen;
6517a6f8720SBrian Somers       dl.sdl_family = AF_LINK;
6527a6f8720SBrian Somers       dl.sdl_index = bundle->ifIndex;
6537a6f8720SBrian Somers       dl.sdl_type = 0;
6547a6f8720SBrian Somers       dl.sdl_nlen = ilen;
6557a6f8720SBrian Somers       dl.sdl_alen = 0;
6567a6f8720SBrian Somers       dl.sdl_slen = 0;
6577a6f8720SBrian Somers       strncpy(dl.sdl_data, iname, sizeof dl.sdl_data);
6587a6f8720SBrian Somers       memcpy(cp, &dl, dl.sdl_len);
6597a6f8720SBrian Somers       cp += dl.sdl_len;
6607a6f8720SBrian Somers       rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
6617a6f8720SBrian Somers     } else {
6627a6f8720SBrian Somers       rtdata.sin_addr = gateway;
6637a6f8720SBrian Somers       memcpy(cp, &rtdata, 16);
6647a6f8720SBrian Somers       cp += 16;
6657a6f8720SBrian Somers       rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
6667a6f8720SBrian Somers     }
6677a6f8720SBrian Somers 
6687a6f8720SBrian Somers   if (dst.s_addr == INADDR_ANY)
6697a6f8720SBrian Somers     mask.s_addr = INADDR_ANY;
6707a6f8720SBrian Somers 
6717a6f8720SBrian Somers   if (cmd == RTM_ADD || dst.s_addr == INADDR_ANY) {
6727a6f8720SBrian Somers     rtdata.sin_addr = mask;
6737a6f8720SBrian Somers     memcpy(cp, &rtdata, 16);
6747a6f8720SBrian Somers     cp += 16;
6757a6f8720SBrian Somers     rtmes.m_rtm.rtm_addrs |= RTA_NETMASK;
6767a6f8720SBrian Somers   }
6777a6f8720SBrian Somers 
6787a6f8720SBrian Somers   nb = cp - (char *) &rtmes;
6797a6f8720SBrian Somers   rtmes.m_rtm.rtm_msglen = nb;
6807a6f8720SBrian Somers   wb = ID0write(s, &rtmes, nb);
6817a6f8720SBrian Somers   if (wb < 0) {
68268a0f0ccSBrian Somers     LogPrintf(LogTCPIP, "bundle_SetRoute failure:\n");
68368a0f0ccSBrian Somers     LogPrintf(LogTCPIP, "bundle_SetRoute:  Cmd = %s\n", cmd);
68468a0f0ccSBrian Somers     LogPrintf(LogTCPIP, "bundle_SetRoute:  Dst = %s\n", inet_ntoa(dst));
68568a0f0ccSBrian Somers     LogPrintf(LogTCPIP, "bundle_SetRoute:  Gateway = %s\n", inet_ntoa(gateway));
68668a0f0ccSBrian Somers     LogPrintf(LogTCPIP, "bundle_SetRoute:  Mask = %s\n", inet_ntoa(mask));
6877a6f8720SBrian Somers failed:
6887a6f8720SBrian Somers     if (cmd == RTM_ADD && (rtmes.m_rtm.rtm_errno == EEXIST ||
6897a6f8720SBrian Somers                            (rtmes.m_rtm.rtm_errno == 0 && errno == EEXIST)))
6907a6f8720SBrian Somers       if (!bang)
6917a6f8720SBrian Somers         LogPrintf(LogWARN, "Add route failed: %s already exists\n",
6927a6f8720SBrian Somers                   inet_ntoa(dst));
6937a6f8720SBrian Somers       else {
6947a6f8720SBrian Somers         rtmes.m_rtm.rtm_type = cmd = RTM_CHANGE;
6957a6f8720SBrian Somers         if ((wb = ID0write(s, &rtmes, nb)) < 0)
6967a6f8720SBrian Somers           goto failed;
6977a6f8720SBrian Somers       }
6987a6f8720SBrian Somers     else if (cmd == RTM_DELETE &&
6997a6f8720SBrian Somers              (rtmes.m_rtm.rtm_errno == ESRCH ||
7007a6f8720SBrian Somers               (rtmes.m_rtm.rtm_errno == 0 && errno == ESRCH))) {
7017a6f8720SBrian Somers       if (!bang)
7027a6f8720SBrian Somers         LogPrintf(LogWARN, "Del route failed: %s: Non-existent\n",
7037a6f8720SBrian Somers                   inet_ntoa(dst));
7047a6f8720SBrian Somers     } else if (rtmes.m_rtm.rtm_errno == 0)
7057a6f8720SBrian Somers       LogPrintf(LogWARN, "%s route failed: %s: errno: %s\n", cmdstr,
7067a6f8720SBrian Somers                 inet_ntoa(dst), strerror(errno));
7077a6f8720SBrian Somers     else
7087a6f8720SBrian Somers       LogPrintf(LogWARN, "%s route failed: %s: %s\n",
7097a6f8720SBrian Somers 		cmdstr, inet_ntoa(dst), strerror(rtmes.m_rtm.rtm_errno));
7107a6f8720SBrian Somers   }
7117a6f8720SBrian Somers   LogPrintf(LogDEBUG, "wrote %d: cmd = %s, dst = %x, gateway = %x\n",
7127a6f8720SBrian Somers             wb, cmdstr, dst.s_addr, gateway.s_addr);
7137a6f8720SBrian Somers   close(s);
7147a6f8720SBrian Somers }
71583d1af55SBrian Somers 
71683d1af55SBrian Somers void
7173006ec67SBrian Somers bundle_LinkLost(struct bundle *bundle, struct link *link, int staydown)
71883d1af55SBrian Somers {
719455aabc3SBrian Somers   /*
7203006ec67SBrian Somers    * Locate the appropriate datalink, and Down it.
7213006ec67SBrian Somers    *
7223006ec67SBrian Somers    * The LayerFinish() called from the datalinks LCP will
7233006ec67SBrian Somers    * potentially Down our NCPs (if it's the last link).
7243006ec67SBrian Somers    *
7253006ec67SBrian Somers    * The LinkClosed() called when the datalink is finally in
7263006ec67SBrian Somers    * the CLOSED state MAY cause the entire datalink to be deleted
7273006ec67SBrian Somers    * and MAY cause a program exit.
728455aabc3SBrian Somers    */
72983d1af55SBrian Somers 
730a0cbd833SBrian Somers   if ((mode & MODE_DIRECT) || bundle->CleaningUp)
7315b8b8060SBrian Somers     staydown = 1;
7323006ec67SBrian Somers   datalink_Down(bundle->links, staydown);
7333006ec67SBrian Somers }
7343006ec67SBrian Somers 
7353006ec67SBrian Somers void
7363006ec67SBrian Somers bundle_LinkClosed(struct bundle *bundle, struct datalink *dl)
7373006ec67SBrian Somers {
7383006ec67SBrian Somers   /*
7393006ec67SBrian Somers    * Our datalink has closed.
7405b8b8060SBrian Somers    * If it's DIRECT or BACKGROUND, delete it.
7413b0f8d2eSBrian Somers    * If it's the last data link, enter phase DEAD.
7423006ec67SBrian Somers    */
7435b8b8060SBrian Somers 
7443b0f8d2eSBrian Somers   struct datalink *odl;
7453b0f8d2eSBrian Somers   int other_links;
7465b8b8060SBrian Somers 
7473b0f8d2eSBrian Somers   if (mode & (MODE_DIRECT|MODE_BACKGROUND)) {
7483b0f8d2eSBrian Somers     struct datalink **dlp;
7493b0f8d2eSBrian Somers     for (dlp = &bundle->links; *dlp; dlp = &(*dlp)->next)
7503b0f8d2eSBrian Somers       if (*dlp == dl)
7513b0f8d2eSBrian Somers         *dlp = datalink_Destroy(*dlp);
7523b0f8d2eSBrian Somers   }
7533b0f8d2eSBrian Somers 
7543b0f8d2eSBrian Somers   other_links = 0;
7553b0f8d2eSBrian Somers   for (odl = bundle->links; odl; odl = odl->next)
7563b0f8d2eSBrian Somers     if (odl != dl && odl->state != DATALINK_CLOSED)
7573b0f8d2eSBrian Somers       other_links++;
7583b0f8d2eSBrian Somers 
7593b0f8d2eSBrian Somers   if (!other_links) {
7603006ec67SBrian Somers     if (!(mode & MODE_AUTO))
7613006ec67SBrian Somers       bundle_DownInterface(bundle);
7625563ebdeSBrian Somers     bundle_NewPhase(bundle, PHASE_DEAD);
763c5a5a6caSBrian Somers     prompt_Display(&prompt, bundle);
7643b0f8d2eSBrian Somers   }
765455aabc3SBrian Somers }
766455aabc3SBrian Somers 
767455aabc3SBrian Somers void
7683006ec67SBrian Somers bundle_Open(struct bundle *bundle, const char *name)
7693006ec67SBrian Somers {
7703006ec67SBrian Somers   /*
7713006ec67SBrian Somers    * Please open the given datalink, or all if name == NULL
7723006ec67SBrian Somers    */
7733006ec67SBrian Somers   struct datalink *dl;
774c5a5a6caSBrian Somers   int runscripts;
7753006ec67SBrian Somers 
776c5a5a6caSBrian Somers   runscripts = (mode & (MODE_DIRECT|MODE_DEDICATED)) ? 0 : 1;
7773006ec67SBrian Somers   for (dl = bundle->links; dl; dl = dl->next)
7783006ec67SBrian Somers     if (name == NULL || !strcasecmp(dl->name, name)) {
779c5a5a6caSBrian Somers       datalink_Up(dl, runscripts, 1);
7803006ec67SBrian Somers       if (name != NULL)
7813006ec67SBrian Somers         break;
7823006ec67SBrian Somers     }
7833006ec67SBrian Somers }
7843006ec67SBrian Somers 
7853006ec67SBrian Somers struct datalink *
7863006ec67SBrian Somers bundle2datalink(struct bundle *bundle, const char *name)
7873006ec67SBrian Somers {
7883006ec67SBrian Somers   struct datalink *dl;
7893006ec67SBrian Somers 
7903006ec67SBrian Somers   if (name != NULL) {
7913006ec67SBrian Somers     for (dl = bundle->links; dl; dl = dl->next)
7923006ec67SBrian Somers       if (!strcasecmp(dl->name, name))
7933006ec67SBrian Somers         return dl;
7943006ec67SBrian Somers   } else if (bundle->links && !bundle->links->next)
7953006ec67SBrian Somers     return bundle->links;
7963006ec67SBrian Somers 
7973006ec67SBrian Somers   return NULL;
7983006ec67SBrian Somers }
7993006ec67SBrian Somers 
8003006ec67SBrian Somers struct physical *
8013006ec67SBrian Somers bundle2physical(struct bundle *bundle, const char *name)
8023006ec67SBrian Somers {
8033006ec67SBrian Somers   struct datalink *dl = bundle2datalink(bundle, name);
8043006ec67SBrian Somers   return dl ? dl->physical : NULL;
8053006ec67SBrian Somers }
8063006ec67SBrian Somers 
807e2ebb036SBrian Somers struct authinfo *
808e2ebb036SBrian Somers bundle2pap(struct bundle *bundle, const char *name)
809e2ebb036SBrian Somers {
810e2ebb036SBrian Somers   struct datalink *dl = bundle2datalink(bundle, name);
811e2ebb036SBrian Somers   if (dl)
812e2ebb036SBrian Somers     return &dl->pap;
813e2ebb036SBrian Somers   return NULL;
814e2ebb036SBrian Somers }
815e2ebb036SBrian Somers 
816e2ebb036SBrian Somers struct chap *
817e2ebb036SBrian Somers bundle2chap(struct bundle *bundle, const char *name)
818e2ebb036SBrian Somers {
819e2ebb036SBrian Somers   struct datalink *dl = bundle2datalink(bundle, name);
820e2ebb036SBrian Somers   if (dl)
821e2ebb036SBrian Somers     return &dl->chap;
822e2ebb036SBrian Somers   return NULL;
823e2ebb036SBrian Somers }
824e2ebb036SBrian Somers 
8253006ec67SBrian Somers int
8263006ec67SBrian Somers bundle_FillQueues(struct bundle *bundle)
8273006ec67SBrian Somers {
8283b0f8d2eSBrian Somers   int total;
8293b0f8d2eSBrian Somers 
8303b0f8d2eSBrian Somers   if (bundle->ncp.mp.active) {
8313b0f8d2eSBrian Somers     total = mp_FillQueues(bundle);
8323b0f8d2eSBrian Somers   } else {
8333006ec67SBrian Somers     struct datalink *dl;
8343006ec67SBrian Somers 
8353b0f8d2eSBrian Somers     dl = bundle2datalink(bundle, NULL);
8363b0f8d2eSBrian Somers     total = link_QueueLen(&dl->physical->link);
8373b0f8d2eSBrian Somers     if (total == 0 && dl->physical->out == NULL)
8383b0f8d2eSBrian Somers       total = IpFlushPacket(&dl->physical->link, bundle);
8393006ec67SBrian Somers   }
8403006ec67SBrian Somers 
8413b0f8d2eSBrian Somers   return total + ip_QueueLen();
8423006ec67SBrian Somers }
843aef795ccSBrian Somers 
844aef795ccSBrian Somers int
845aef795ccSBrian Somers bundle_ShowLinks(struct cmdargs const *arg)
846aef795ccSBrian Somers {
847c7cc5030SBrian Somers   if (arg->cx)
848c7cc5030SBrian Somers     datalink_Show(arg->cx);
849c7cc5030SBrian Somers   else {
850aef795ccSBrian Somers     struct datalink *dl;
851aef795ccSBrian Somers 
852aef795ccSBrian Somers     for (dl = arg->bundle->links; dl; dl = dl->next)
853c7cc5030SBrian Somers       datalink_Show(dl);
854c7cc5030SBrian Somers   }
855aef795ccSBrian Somers 
856aef795ccSBrian Somers   return 0;
857aef795ccSBrian Somers }
858ab886ad0SBrian Somers 
859ab886ad0SBrian Somers static void
860ab886ad0SBrian Somers bundle_IdleTimeout(void *v)
861ab886ad0SBrian Somers {
862ab886ad0SBrian Somers   struct bundle *bundle = (struct bundle *)v;
863ab886ad0SBrian Somers 
86493ee0ff2SBrian Somers   bundle->idle.done = 0;
865ab886ad0SBrian Somers   LogPrintf(LogPHASE, "IPCP Idle timer expired.\n");
866ab886ad0SBrian Somers   bundle_Close(bundle, NULL, 1);
867ab886ad0SBrian Somers }
868ab886ad0SBrian Somers 
869ab886ad0SBrian Somers /*
870ab886ad0SBrian Somers  *  Start Idle timer. If timeout is reached, we call bundle_Close() to
871ab886ad0SBrian Somers  *  close LCP and link.
872ab886ad0SBrian Somers  */
873ab886ad0SBrian Somers void
874ab886ad0SBrian Somers bundle_StartIdleTimer(struct bundle *bundle)
875ab886ad0SBrian Somers {
8761a56baf6SBrian Somers   if (!(mode & (MODE_DEDICATED | MODE_DDIAL)) && bundle->cfg.idle_timeout) {
87793ee0ff2SBrian Somers     StopTimer(&bundle->idle.timer);
87893ee0ff2SBrian Somers     bundle->idle.timer.func = bundle_IdleTimeout;
8793b0f8d2eSBrian Somers     bundle->idle.timer.name = "idle";
88093ee0ff2SBrian Somers     bundle->idle.timer.load = bundle->cfg.idle_timeout * SECTICKS;
88193ee0ff2SBrian Somers     bundle->idle.timer.state = TIMER_STOPPED;
88293ee0ff2SBrian Somers     bundle->idle.timer.arg = bundle;
88393ee0ff2SBrian Somers     StartTimer(&bundle->idle.timer);
88493ee0ff2SBrian Somers     bundle->idle.done = time(NULL) + bundle->cfg.idle_timeout;
885ab886ad0SBrian Somers   }
886ab886ad0SBrian Somers }
887ab886ad0SBrian Somers 
888ab886ad0SBrian Somers void
889ab886ad0SBrian Somers bundle_SetIdleTimer(struct bundle *bundle, int value)
890ab886ad0SBrian Somers {
891ab886ad0SBrian Somers   bundle->cfg.idle_timeout = value;
892ab886ad0SBrian Somers   if (bundle_LinkIsUp(bundle))
893ab886ad0SBrian Somers     bundle_StartIdleTimer(bundle);
894ab886ad0SBrian Somers }
895ab886ad0SBrian Somers 
896ab886ad0SBrian Somers void
897ab886ad0SBrian Somers bundle_StopIdleTimer(struct bundle *bundle)
898ab886ad0SBrian Somers {
89993ee0ff2SBrian Somers   StopTimer(&bundle->idle.timer);
900ab886ad0SBrian Somers }
901ab886ad0SBrian Somers 
902ab886ad0SBrian Somers int
903ab886ad0SBrian Somers bundle_RemainingIdleTime(struct bundle *bundle)
904ab886ad0SBrian Somers {
90593ee0ff2SBrian Somers   if (bundle->idle.done)
90693ee0ff2SBrian Somers     return bundle->idle.done - time(NULL);
907ab886ad0SBrian Somers   return -1;
908ab886ad0SBrian Somers }
9093b0f8d2eSBrian Somers 
9103b0f8d2eSBrian Somers int
9113b0f8d2eSBrian Somers bundle_IsDead(struct bundle *bundle)
9123b0f8d2eSBrian Somers {
9133b0f8d2eSBrian Somers   return !bundle->links || (bundle->phase == PHASE_DEAD && bundle->CleaningUp);
9143b0f8d2eSBrian Somers }
915