xref: /freebsd/usr.sbin/ppp/bundle.c (revision 2289f246989477469d41233bb9688b22956f8563)
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  *
262289f246SBrian Somers  *	$Id: bundle.c,v 1.1.2.2 1998/02/02 19:33:00 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>
377a6f8720SBrian Somers 
387a6f8720SBrian Somers #include <errno.h>
397a6f8720SBrian Somers #include <fcntl.h>
407a6f8720SBrian Somers #include <stdio.h>
417a6f8720SBrian Somers #include <string.h>
427a6f8720SBrian Somers #include <sys/ioctl.h>
437a6f8720SBrian Somers #include <termios.h>
447a6f8720SBrian Somers #include <unistd.h>
457a6f8720SBrian Somers 
467a6f8720SBrian Somers #include "command.h"
477a6f8720SBrian Somers #include "mbuf.h"
487a6f8720SBrian Somers #include "log.h"
497a6f8720SBrian Somers #include "id.h"
507a6f8720SBrian Somers #include "defs.h"
517a6f8720SBrian Somers #include "timer.h"
527a6f8720SBrian Somers #include "fsm.h"
537a6f8720SBrian Somers #include "iplist.h"
547a6f8720SBrian Somers #include "throughput.h"
557a6f8720SBrian Somers #include "ipcp.h"
567a6f8720SBrian Somers #include "bundle.h"
577a6f8720SBrian Somers #include "loadalias.h"
587a6f8720SBrian Somers #include "vars.h"
597a6f8720SBrian Somers #include "arp.h"
607a6f8720SBrian Somers #include "systems.h"
617a6f8720SBrian Somers #include "route.h"
627a6f8720SBrian Somers #include "lcp.h"
637a6f8720SBrian Somers #include "ccp.h"
642289f246SBrian Somers #include "modem.h"
657a6f8720SBrian Somers 
667a6f8720SBrian Somers static int
677a6f8720SBrian Somers bundle_SetIpDevice(struct bundle *bundle, struct in_addr myaddr,
687a6f8720SBrian Somers                    struct in_addr hisaddr, struct in_addr netmask, int silent)
697a6f8720SBrian Somers {
707a6f8720SBrian Somers   struct sockaddr_in *sock_in;
717a6f8720SBrian Somers   int s;
727a6f8720SBrian Somers   u_long mask, addr;
737a6f8720SBrian Somers   struct ifaliasreq ifra;
747a6f8720SBrian Somers 
757a6f8720SBrian Somers   /* If given addresses are alreay set, then ignore this request */
767a6f8720SBrian Somers   if (bundle->if_mine.s_addr == myaddr.s_addr &&
777a6f8720SBrian Somers       bundle->if_peer.s_addr == hisaddr.s_addr)
787a6f8720SBrian Somers     return 0;
797a6f8720SBrian Somers 
807a6f8720SBrian Somers   s = ID0socket(AF_INET, SOCK_DGRAM, 0);
817a6f8720SBrian Somers   if (s < 0) {
827a6f8720SBrian Somers     LogPrintf(LogERROR, "SetIpDevice: socket(): %s\n", strerror(errno));
837a6f8720SBrian Somers     return (-1);
847a6f8720SBrian Somers   }
857a6f8720SBrian Somers 
867a6f8720SBrian Somers   memset(&ifra, '\0', sizeof ifra);
877a6f8720SBrian Somers   strncpy(ifra.ifra_name, bundle->ifname, sizeof ifra.ifra_name - 1);
887a6f8720SBrian Somers   ifra.ifra_name[sizeof ifra.ifra_name - 1] = '\0';
897a6f8720SBrian Somers 
907a6f8720SBrian Somers   /* If different address has been set, then delete it first */
917a6f8720SBrian Somers   if (bundle->if_mine.s_addr != INADDR_ANY ||
927a6f8720SBrian Somers       bundle->if_peer.s_addr != INADDR_ANY)
937a6f8720SBrian Somers     if (ID0ioctl(s, SIOCDIFADDR, &ifra) < 0) {
947a6f8720SBrian Somers       LogPrintf(LogERROR, "SetIpDevice: ioctl(SIOCDIFADDR): %s\n",
957a6f8720SBrian Somers 		strerror(errno));
967a6f8720SBrian Somers       close(s);
977a6f8720SBrian Somers       return (-1);
987a6f8720SBrian Somers     }
997a6f8720SBrian Somers 
1007a6f8720SBrian Somers   /* Set interface address */
1017a6f8720SBrian Somers   sock_in = (struct sockaddr_in *)&ifra.ifra_addr;
1027a6f8720SBrian Somers   sock_in->sin_family = AF_INET;
1037a6f8720SBrian Somers   sock_in->sin_addr = myaddr;
1047a6f8720SBrian Somers   sock_in->sin_len = sizeof *sock_in;
1057a6f8720SBrian Somers 
1067a6f8720SBrian Somers   /* Set destination address */
1077a6f8720SBrian Somers   sock_in = (struct sockaddr_in *)&ifra.ifra_broadaddr;
1087a6f8720SBrian Somers   sock_in->sin_family = AF_INET;
1097a6f8720SBrian Somers   sock_in->sin_addr = hisaddr;
1107a6f8720SBrian Somers   sock_in->sin_len = sizeof *sock_in;
1117a6f8720SBrian Somers 
1127a6f8720SBrian Somers   addr = ntohl(myaddr.s_addr);
1137a6f8720SBrian Somers   if (IN_CLASSA(addr))
1147a6f8720SBrian Somers     mask = IN_CLASSA_NET;
1157a6f8720SBrian Somers   else if (IN_CLASSB(addr))
1167a6f8720SBrian Somers     mask = IN_CLASSB_NET;
1177a6f8720SBrian Somers   else
1187a6f8720SBrian Somers     mask = IN_CLASSC_NET;
1197a6f8720SBrian Somers 
1207a6f8720SBrian Somers   /* if subnet mask is given, use it instead of class mask */
1217a6f8720SBrian Somers   if (netmask.s_addr != INADDR_ANY && (ntohl(netmask.s_addr) & mask) == mask)
1227a6f8720SBrian Somers     mask = ntohl(netmask.s_addr);
1237a6f8720SBrian Somers 
1247a6f8720SBrian Somers   sock_in = (struct sockaddr_in *)&ifra.ifra_mask;
1257a6f8720SBrian Somers   sock_in->sin_family = AF_INET;
1267a6f8720SBrian Somers   sock_in->sin_addr.s_addr = htonl(mask);
1277a6f8720SBrian Somers   sock_in->sin_len = sizeof *sock_in;
1287a6f8720SBrian Somers 
1297a6f8720SBrian Somers   if (ID0ioctl(s, SIOCAIFADDR, &ifra) < 0) {
1307a6f8720SBrian Somers     if (!silent)
1317a6f8720SBrian Somers       LogPrintf(LogERROR, "SetIpDevice: ioctl(SIOCAIFADDR): %s\n",
1327a6f8720SBrian Somers 		strerror(errno));
1337a6f8720SBrian Somers     close(s);
1347a6f8720SBrian Somers     return (-1);
1357a6f8720SBrian Somers   }
1367a6f8720SBrian Somers 
1377a6f8720SBrian Somers   bundle->if_peer.s_addr = hisaddr.s_addr;
1387a6f8720SBrian Somers   bundle->if_mine.s_addr = myaddr.s_addr;
1397a6f8720SBrian Somers 
1407a6f8720SBrian Somers   if (Enabled(ConfProxy))
141820de6ebSBrian Somers     sifproxyarp(bundle, s);
1427a6f8720SBrian Somers 
1437a6f8720SBrian Somers   close(s);
1447a6f8720SBrian Somers   return (0);
1457a6f8720SBrian Somers }
1467a6f8720SBrian Somers 
1477a6f8720SBrian Somers static int
1487a6f8720SBrian Somers bundle_CleanInterface(const struct bundle *bundle)
1497a6f8720SBrian Somers {
1507a6f8720SBrian Somers   int s;
1517a6f8720SBrian Somers   struct ifreq ifrq;
1527a6f8720SBrian Somers   struct ifaliasreq ifra;
1537a6f8720SBrian Somers 
1547a6f8720SBrian Somers   s = ID0socket(AF_INET, SOCK_DGRAM, 0);
1557a6f8720SBrian Somers   if (s < 0) {
1567a6f8720SBrian Somers     LogPrintf(LogERROR, "bundle_CleanInterface: socket(): %s\n",
1577a6f8720SBrian Somers               strerror(errno));
1587a6f8720SBrian Somers     return (-1);
1597a6f8720SBrian Somers   }
1607a6f8720SBrian Somers   strncpy(ifrq.ifr_name, bundle->ifname, sizeof ifrq.ifr_name - 1);
1617a6f8720SBrian Somers   ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
1627a6f8720SBrian Somers   while (ID0ioctl(s, SIOCGIFADDR, &ifrq) == 0) {
1637a6f8720SBrian Somers     memset(&ifra.ifra_mask, '\0', sizeof ifra.ifra_mask);
1647a6f8720SBrian Somers     strncpy(ifra.ifra_name, bundle->ifname, sizeof ifra.ifra_name - 1);
1657a6f8720SBrian Somers     ifra.ifra_name[sizeof ifra.ifra_name - 1] = '\0';
1667a6f8720SBrian Somers     ifra.ifra_addr = ifrq.ifr_addr;
1677a6f8720SBrian Somers     if (ID0ioctl(s, SIOCGIFDSTADDR, &ifrq) < 0) {
1687a6f8720SBrian Somers       if (ifra.ifra_addr.sa_family == AF_INET)
1697a6f8720SBrian Somers         LogPrintf(LogERROR,
1707a6f8720SBrian Somers                   "bundle_CleanInterface: Can't get dst for %s on %s !\n",
1717a6f8720SBrian Somers                   inet_ntoa(((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr),
1727a6f8720SBrian Somers                   bundle->ifname);
1737a6f8720SBrian Somers       return 0;
1747a6f8720SBrian Somers     }
1757a6f8720SBrian Somers     ifra.ifra_broadaddr = ifrq.ifr_dstaddr;
1767a6f8720SBrian Somers     if (ID0ioctl(s, SIOCDIFADDR, &ifra) < 0) {
1777a6f8720SBrian Somers       if (ifra.ifra_addr.sa_family == AF_INET)
1787a6f8720SBrian Somers         LogPrintf(LogERROR,
1797a6f8720SBrian Somers                   "bundle_CleanInterface: Can't delete %s address on %s !\n",
1807a6f8720SBrian Somers                   inet_ntoa(((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr),
1817a6f8720SBrian Somers                   bundle->ifname);
1827a6f8720SBrian Somers       return 0;
1837a6f8720SBrian Somers     }
1847a6f8720SBrian Somers   }
1857a6f8720SBrian Somers 
1867a6f8720SBrian Somers   return 1;
1877a6f8720SBrian Somers }
1887a6f8720SBrian Somers 
1897a6f8720SBrian Somers int
1907a6f8720SBrian Somers bundle_TrySetIPaddress(struct bundle *bundle, struct in_addr myaddr,
1917a6f8720SBrian Somers                        struct in_addr hisaddr)
1927a6f8720SBrian Somers {
1937a6f8720SBrian Somers   return bundle_SetIpDevice(bundle, myaddr, hisaddr, ifnetmask, 1);
1947a6f8720SBrian Somers }
1957a6f8720SBrian Somers 
1967a6f8720SBrian Somers int
1977a6f8720SBrian Somers bundle_SetIPaddress(struct bundle *bundle, struct in_addr myaddr,
1987a6f8720SBrian Somers                     struct in_addr hisaddr)
1997a6f8720SBrian Somers {
2007a6f8720SBrian Somers   return bundle_SetIpDevice(bundle, myaddr, hisaddr, ifnetmask, 0);
2017a6f8720SBrian Somers }
2027a6f8720SBrian Somers 
2037a6f8720SBrian Somers void
2047a6f8720SBrian Somers bundle_Linkup(struct bundle *bundle)
2057a6f8720SBrian Somers {
2067a6f8720SBrian Somers   if (bundle->linkup == 0) {
2077a6f8720SBrian Somers     char *s;
2087a6f8720SBrian Somers 
2097a6f8720SBrian Somers     reconnectState = RECON_UNKNOWN;
2107a6f8720SBrian Somers     if (mode & MODE_BACKGROUND && BGFiledes[1] != -1) {
2117a6f8720SBrian Somers       char c = EX_NORMAL;
2127a6f8720SBrian Somers 
2137a6f8720SBrian Somers       if (write(BGFiledes[1], &c, 1) == 1)
2147a6f8720SBrian Somers 	LogPrintf(LogPHASE, "Parent notified of success.\n");
2157a6f8720SBrian Somers       else
2167a6f8720SBrian Somers 	LogPrintf(LogPHASE, "Failed to notify parent of success.\n");
2177a6f8720SBrian Somers       close(BGFiledes[1]);
2187a6f8720SBrian Somers       BGFiledes[1] = -1;
2197a6f8720SBrian Somers     }
2207a6f8720SBrian Somers 
2217a6f8720SBrian Somers     s = inet_ntoa(bundle->if_peer);
2227a6f8720SBrian Somers     if (LogIsKept(LogLINK))
2237a6f8720SBrian Somers       LogPrintf(LogLINK, "OsLinkup: %s\n", s);
2247a6f8720SBrian Somers     else
2257a6f8720SBrian Somers       LogPrintf(LogLCP, "OsLinkup: %s\n", s);
2267a6f8720SBrian Somers 
2277a6f8720SBrian Somers     /*
2287a6f8720SBrian Somers      * XXX this stuff should really live in the FSM.  Our config should
2297a6f8720SBrian Somers      * associate executable sections in files with events.
2307a6f8720SBrian Somers      */
2317a6f8720SBrian Somers     if (SelectSystem(bundle, inet_ntoa(bundle->if_mine), LINKUPFILE) < 0) {
2327a6f8720SBrian Somers       if (GetLabel()) {
2337a6f8720SBrian Somers 	if (SelectSystem(bundle, GetLabel(), LINKUPFILE) < 0)
2347a6f8720SBrian Somers 	  SelectSystem(bundle, "MYADDR", LINKUPFILE);
2357a6f8720SBrian Somers       } else
2367a6f8720SBrian Somers 	SelectSystem(bundle, "MYADDR", LINKUPFILE);
2377a6f8720SBrian Somers     }
2387a6f8720SBrian Somers     bundle->linkup = 1;
2397a6f8720SBrian Somers   }
2407a6f8720SBrian Somers }
2417a6f8720SBrian Somers 
2427a6f8720SBrian Somers int
2437a6f8720SBrian Somers bundle_LinkIsUp(const struct bundle *bundle)
2447a6f8720SBrian Somers {
2457a6f8720SBrian Somers   return bundle->linkup;
2467a6f8720SBrian Somers }
2477a6f8720SBrian Somers 
2487a6f8720SBrian Somers void
2497a6f8720SBrian Somers bundle_Linkdown(struct bundle *bundle)
2507a6f8720SBrian Somers {
2517a6f8720SBrian Somers   char *s = NULL;
2527a6f8720SBrian Somers   int Level;
2537a6f8720SBrian Somers 
2547a6f8720SBrian Somers   if (bundle->linkup) {
2557a6f8720SBrian Somers     s = inet_ntoa(bundle->if_peer);
2567a6f8720SBrian Somers     Level = LogIsKept(LogLINK) ? LogLINK : LogIPCP;
2577a6f8720SBrian Somers     LogPrintf(Level, "OsLinkdown: %s\n", s);
2587a6f8720SBrian Somers   }
2597a6f8720SBrian Somers 
2607a6f8720SBrian Somers   FsmClose(&IpcpInfo.fsm);
2617a6f8720SBrian Somers   FsmClose(&CcpInfo.fsm);
2627a6f8720SBrian Somers 
2637a6f8720SBrian Somers   if (bundle->linkup) {
2647a6f8720SBrian Somers     /*
2657a6f8720SBrian Somers      * XXX this stuff should really live in the FSM.  Our config should
2667a6f8720SBrian Somers      * associate executable sections in files with events.
2677a6f8720SBrian Somers      */
2687a6f8720SBrian Somers     bundle->linkup = 0;
2697a6f8720SBrian Somers     if (SelectSystem(bundle, s, LINKDOWNFILE) < 0)
2707a6f8720SBrian Somers       if (GetLabel()) {
2717a6f8720SBrian Somers 	if (SelectSystem(bundle, GetLabel(), LINKDOWNFILE) < 0)
2727a6f8720SBrian Somers 	  SelectSystem(bundle, "MYADDR", LINKDOWNFILE);
2737a6f8720SBrian Somers       } else
2747a6f8720SBrian Somers 	SelectSystem(bundle, "MYADDR", LINKDOWNFILE);
2757a6f8720SBrian Somers   }
2767a6f8720SBrian Somers }
2777a6f8720SBrian Somers 
2787a6f8720SBrian Somers int
2797a6f8720SBrian Somers bundle_InterfaceDown(struct bundle *bundle)
2807a6f8720SBrian Somers {
2817a6f8720SBrian Somers   struct ifreq ifrq;
2827a6f8720SBrian Somers   struct ifaliasreq ifra;
2837a6f8720SBrian Somers   int s;
2847a6f8720SBrian Somers 
2857a6f8720SBrian Somers   s = ID0socket(AF_INET, SOCK_DGRAM, 0);
2867a6f8720SBrian Somers   if (s < 0) {
2877a6f8720SBrian Somers     LogPrintf(LogERROR, "OsInterfaceDown: socket: %s\n", strerror(errno));
2887a6f8720SBrian Somers     return -1;
2897a6f8720SBrian Somers   }
2907a6f8720SBrian Somers 
2917a6f8720SBrian Somers   if (Enabled(ConfProxy))
292820de6ebSBrian Somers     cifproxyarp(bundle, s);
2937a6f8720SBrian Somers 
2947a6f8720SBrian Somers   memset(&ifrq, '\0', sizeof ifrq);
2957a6f8720SBrian Somers   strncpy(ifrq.ifr_name, bundle->ifname, sizeof ifrq.ifr_name - 1);
2967a6f8720SBrian Somers   ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
2977a6f8720SBrian Somers   if (ID0ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) {
2987a6f8720SBrian Somers     LogPrintf(LogERROR, "OsInterfaceDown: ioctl(SIOCGIFFLAGS): %s\n",
2997a6f8720SBrian Somers 	      strerror(errno));
3007a6f8720SBrian Somers     close(s);
3017a6f8720SBrian Somers     return -1;
3027a6f8720SBrian Somers   }
3037a6f8720SBrian Somers   ifrq.ifr_flags &= ~IFF_UP;
3047a6f8720SBrian Somers   if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
3057a6f8720SBrian Somers     LogPrintf(LogERROR, "OsInterfaceDown: ioctl(SIOCSIFFLAGS): %s\n",
3067a6f8720SBrian Somers 	      strerror(errno));
3077a6f8720SBrian Somers     close(s);
3087a6f8720SBrian Somers     return -1;
3097a6f8720SBrian Somers   }
3107a6f8720SBrian Somers 
3117a6f8720SBrian Somers   if (bundle->if_mine.s_addr != INADDR_ANY ||
3127a6f8720SBrian Somers       bundle->if_peer.s_addr != INADDR_ANY) {
3137a6f8720SBrian Somers     memset(&ifra, '\0', sizeof ifra);
3147a6f8720SBrian Somers     strncpy(ifra.ifra_name, bundle->ifname, sizeof ifra.ifra_name - 1);
3157a6f8720SBrian Somers     ifra.ifra_name[sizeof ifra.ifra_name - 1] = '\0';
3167a6f8720SBrian Somers     if (ID0ioctl(s, SIOCDIFADDR, &ifra) < 0) {
3177a6f8720SBrian Somers       LogPrintf(LogERROR, "OsInterfaceDown: ioctl(SIOCDIFADDR): %s\n",
3187a6f8720SBrian Somers 		strerror(errno));
3197a6f8720SBrian Somers       close(s);
3207a6f8720SBrian Somers       return -1;
3217a6f8720SBrian Somers     }
3227a6f8720SBrian Somers     bundle->if_mine.s_addr = bundle->if_peer.s_addr = INADDR_ANY;
3237a6f8720SBrian Somers   }
3247a6f8720SBrian Somers 
3257a6f8720SBrian Somers   close(s);
3267a6f8720SBrian Somers   return 0;
3277a6f8720SBrian Somers }
3287a6f8720SBrian Somers 
3297a6f8720SBrian Somers /*
3307a6f8720SBrian Somers  *  Open tunnel device and returns its descriptor
3317a6f8720SBrian Somers  */
3327a6f8720SBrian Somers 
3337a6f8720SBrian Somers #define MAX_TUN 256
3347a6f8720SBrian Somers /*
3357a6f8720SBrian Somers  * MAX_TUN is set at 256 because that is the largest minor number
3367a6f8720SBrian Somers  * we can use (certainly with mknod(1) anyway.  The search for a
3377a6f8720SBrian Somers  * device aborts when it reaches the first `Device not configured'
3387a6f8720SBrian Somers  * (ENXIO) or the third `No such file or directory' (ENOENT) error.
3397a6f8720SBrian Somers  */
3407a6f8720SBrian Somers struct bundle *
3417a6f8720SBrian Somers bundle_Create(const char *prefix)
3427a6f8720SBrian Somers {
3437a6f8720SBrian Somers   int s, enoentcount, err;
3447a6f8720SBrian Somers   struct ifreq ifrq;
3457a6f8720SBrian Somers   static struct bundle bundle;		/* there can be only one */
3467a6f8720SBrian Somers 
3477a6f8720SBrian Somers   if (bundle.ifname != NULL) {	/* Already allocated ! */
3487a6f8720SBrian Somers     LogPrintf(LogERROR, "bundle_Create:  There's only one BUNDLE !\n");
3497a6f8720SBrian Somers     return NULL;
3507a6f8720SBrian Somers   }
3517a6f8720SBrian Somers 
3527a6f8720SBrian Somers   err = ENOENT;
3537a6f8720SBrian Somers   enoentcount = 0;
3547a6f8720SBrian Somers   for (bundle.unit = 0; bundle.unit <= MAX_TUN; bundle.unit++) {
3557a6f8720SBrian Somers     snprintf(bundle.dev, sizeof bundle.dev, "%s%d", prefix, bundle.unit);
3567a6f8720SBrian Somers     bundle.tun_fd = ID0open(bundle.dev, O_RDWR);
3577a6f8720SBrian Somers     if (bundle.tun_fd >= 0)
3587a6f8720SBrian Somers       break;
3597a6f8720SBrian Somers     if (errno == ENXIO) {
3607a6f8720SBrian Somers       bundle.unit = MAX_TUN;
3617a6f8720SBrian Somers       err = errno;
3627a6f8720SBrian Somers     } else if (errno == ENOENT) {
3637a6f8720SBrian Somers       if (++enoentcount > 2)
3647a6f8720SBrian Somers 	bundle.unit = MAX_TUN;
3657a6f8720SBrian Somers     } else
3667a6f8720SBrian Somers       err = errno;
3677a6f8720SBrian Somers   }
3687a6f8720SBrian Somers 
3697a6f8720SBrian Somers   if (bundle.unit > MAX_TUN) {
3707a6f8720SBrian Somers     if (VarTerm)
3717a6f8720SBrian Somers       fprintf(VarTerm, "No tunnel device is available (%s).\n", strerror(err));
3727a6f8720SBrian Somers     return NULL;
3737a6f8720SBrian Somers   }
3747a6f8720SBrian Somers 
3757a6f8720SBrian Somers   LogSetTun(bundle.unit);
3767a6f8720SBrian Somers 
3777a6f8720SBrian Somers   s = socket(AF_INET, SOCK_DGRAM, 0);
3787a6f8720SBrian Somers   if (s < 0) {
3797a6f8720SBrian Somers     LogPrintf(LogERROR, "bundle_Create: socket(): %s\n", strerror(errno));
3807a6f8720SBrian Somers     close(bundle.tun_fd);
3817a6f8720SBrian Somers     return NULL;
3827a6f8720SBrian Somers   }
3837a6f8720SBrian Somers 
3847a6f8720SBrian Somers   bundle.ifname = strrchr(bundle.dev, '/');
3857a6f8720SBrian Somers   if (bundle.ifname == NULL)
3867a6f8720SBrian Somers     bundle.ifname = bundle.dev;
3877a6f8720SBrian Somers   else
3887a6f8720SBrian Somers     bundle.ifname++;
3897a6f8720SBrian Somers 
3907a6f8720SBrian Somers   /*
3917a6f8720SBrian Somers    * Now, bring up the interface.
3927a6f8720SBrian Somers    */
3937a6f8720SBrian Somers   memset(&ifrq, '\0', sizeof ifrq);
3947a6f8720SBrian Somers   strncpy(ifrq.ifr_name, bundle.ifname, sizeof ifrq.ifr_name - 1);
3957a6f8720SBrian Somers   ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
3967a6f8720SBrian Somers   if (ID0ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) {
3977a6f8720SBrian Somers     LogPrintf(LogERROR, "OpenTunnel: ioctl(SIOCGIFFLAGS): %s\n",
3987a6f8720SBrian Somers 	      strerror(errno));
3997a6f8720SBrian Somers     close(s);
4007a6f8720SBrian Somers     close(bundle.tun_fd);
4017a6f8720SBrian Somers     bundle.ifname = NULL;
4027a6f8720SBrian Somers     return NULL;
4037a6f8720SBrian Somers   }
4047a6f8720SBrian Somers   ifrq.ifr_flags |= IFF_UP;
4057a6f8720SBrian Somers   if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
4067a6f8720SBrian Somers     LogPrintf(LogERROR, "OpenTunnel: ioctl(SIOCSIFFLAGS): %s\n",
4077a6f8720SBrian Somers 	      strerror(errno));
4087a6f8720SBrian Somers     close(s);
4097a6f8720SBrian Somers     close(bundle.tun_fd);
4107a6f8720SBrian Somers     bundle.ifname = NULL;
4117a6f8720SBrian Somers     return NULL;
4127a6f8720SBrian Somers   }
4137a6f8720SBrian Somers 
4147a6f8720SBrian Somers   close(s);
4157a6f8720SBrian Somers 
4167a6f8720SBrian Somers   if ((bundle.ifIndex = GetIfIndex(bundle.ifname)) < 0) {
4177a6f8720SBrian Somers     LogPrintf(LogERROR, "OpenTunnel: Can't find ifindex.\n");
4187a6f8720SBrian Somers     close(bundle.tun_fd);
4197a6f8720SBrian Somers     bundle.ifname = NULL;
4207a6f8720SBrian Somers     return NULL;
4217a6f8720SBrian Somers   }
4227a6f8720SBrian Somers 
4237a6f8720SBrian Somers   if (VarTerm)
4247a6f8720SBrian Somers     fprintf(VarTerm, "Using interface: %s\n", bundle.ifname);
4257a6f8720SBrian Somers   LogPrintf(LogPHASE, "Using interface: %s\n", bundle.ifname);
4267a6f8720SBrian Somers 
4277a6f8720SBrian Somers   bundle.linkup = 0;
4287a6f8720SBrian Somers   bundle.if_mine.s_addr = bundle.if_peer.s_addr = INADDR_ANY;
429820de6ebSBrian Somers   bundle.routing_seq = 0;
4307a6f8720SBrian Somers 
4317a6f8720SBrian Somers   /* Clean out any leftover crud */
4327a6f8720SBrian Somers   bundle_CleanInterface(&bundle);
4337a6f8720SBrian Somers 
4342289f246SBrian Somers   bundle.physical = modem_Create("default");
4352289f246SBrian Somers   if (bundle.physical == NULL) {
4362289f246SBrian Somers     LogPrintf(LogERROR, "Cannot create modem device: %s\n", strerror(errno));
4372289f246SBrian Somers     return NULL;
4382289f246SBrian Somers   }
4392289f246SBrian Somers 
4407a6f8720SBrian Somers   return &bundle;
4417a6f8720SBrian Somers }
4427a6f8720SBrian Somers 
4437a6f8720SBrian Somers struct rtmsg {
4447a6f8720SBrian Somers   struct rt_msghdr m_rtm;
4457a6f8720SBrian Somers   char m_space[64];
4467a6f8720SBrian Somers };
4477a6f8720SBrian Somers 
4487a6f8720SBrian Somers void
449820de6ebSBrian Somers bundle_SetRoute(struct bundle *bundle, int cmd, struct in_addr dst,
4507a6f8720SBrian Somers                 struct in_addr gateway, struct in_addr mask, int bang)
4517a6f8720SBrian Somers {
4527a6f8720SBrian Somers   struct rtmsg rtmes;
4537a6f8720SBrian Somers   int s, nb, wb;
4547a6f8720SBrian Somers   char *cp;
4557a6f8720SBrian Somers   const char *cmdstr;
4567a6f8720SBrian Somers   struct sockaddr_in rtdata;
4577a6f8720SBrian Somers 
4587a6f8720SBrian Somers   if (bang)
4597a6f8720SBrian Somers     cmdstr = (cmd == RTM_ADD ? "Add!" : "Delete!");
4607a6f8720SBrian Somers   else
4617a6f8720SBrian Somers     cmdstr = (cmd == RTM_ADD ? "Add" : "Delete");
4627a6f8720SBrian Somers   s = ID0socket(PF_ROUTE, SOCK_RAW, 0);
4637a6f8720SBrian Somers   if (s < 0) {
4647a6f8720SBrian Somers     LogPrintf(LogERROR, "OsSetRoute: socket(): %s\n", strerror(errno));
4657a6f8720SBrian Somers     return;
4667a6f8720SBrian Somers   }
4677a6f8720SBrian Somers   memset(&rtmes, '\0', sizeof rtmes);
4687a6f8720SBrian Somers   rtmes.m_rtm.rtm_version = RTM_VERSION;
4697a6f8720SBrian Somers   rtmes.m_rtm.rtm_type = cmd;
4707a6f8720SBrian Somers   rtmes.m_rtm.rtm_addrs = RTA_DST;
471820de6ebSBrian Somers   rtmes.m_rtm.rtm_seq = ++bundle->routing_seq;
4727a6f8720SBrian Somers   rtmes.m_rtm.rtm_pid = getpid();
4737a6f8720SBrian Somers   rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
4747a6f8720SBrian Somers 
4757a6f8720SBrian Somers   memset(&rtdata, '\0', sizeof rtdata);
4767a6f8720SBrian Somers   rtdata.sin_len = 16;
4777a6f8720SBrian Somers   rtdata.sin_family = AF_INET;
4787a6f8720SBrian Somers   rtdata.sin_port = 0;
4797a6f8720SBrian Somers   rtdata.sin_addr = dst;
4807a6f8720SBrian Somers 
4817a6f8720SBrian Somers   cp = rtmes.m_space;
4827a6f8720SBrian Somers   memcpy(cp, &rtdata, 16);
4837a6f8720SBrian Somers   cp += 16;
4847a6f8720SBrian Somers   if (cmd == RTM_ADD)
4857a6f8720SBrian Somers     if (gateway.s_addr == INADDR_ANY) {
4867a6f8720SBrian Somers       /* Add a route through the interface */
4877a6f8720SBrian Somers       struct sockaddr_dl dl;
4887a6f8720SBrian Somers       const char *iname;
4897a6f8720SBrian Somers       int ilen;
4907a6f8720SBrian Somers 
4917a6f8720SBrian Somers       iname = Index2Nam(bundle->ifIndex);
4927a6f8720SBrian Somers       ilen = strlen(iname);
4937a6f8720SBrian Somers       dl.sdl_len = sizeof dl - sizeof dl.sdl_data + ilen;
4947a6f8720SBrian Somers       dl.sdl_family = AF_LINK;
4957a6f8720SBrian Somers       dl.sdl_index = bundle->ifIndex;
4967a6f8720SBrian Somers       dl.sdl_type = 0;
4977a6f8720SBrian Somers       dl.sdl_nlen = ilen;
4987a6f8720SBrian Somers       dl.sdl_alen = 0;
4997a6f8720SBrian Somers       dl.sdl_slen = 0;
5007a6f8720SBrian Somers       strncpy(dl.sdl_data, iname, sizeof dl.sdl_data);
5017a6f8720SBrian Somers       memcpy(cp, &dl, dl.sdl_len);
5027a6f8720SBrian Somers       cp += dl.sdl_len;
5037a6f8720SBrian Somers       rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
5047a6f8720SBrian Somers     } else {
5057a6f8720SBrian Somers       rtdata.sin_addr = gateway;
5067a6f8720SBrian Somers       memcpy(cp, &rtdata, 16);
5077a6f8720SBrian Somers       cp += 16;
5087a6f8720SBrian Somers       rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
5097a6f8720SBrian Somers     }
5107a6f8720SBrian Somers 
5117a6f8720SBrian Somers   if (dst.s_addr == INADDR_ANY)
5127a6f8720SBrian Somers     mask.s_addr = INADDR_ANY;
5137a6f8720SBrian Somers 
5147a6f8720SBrian Somers   if (cmd == RTM_ADD || dst.s_addr == INADDR_ANY) {
5157a6f8720SBrian Somers     rtdata.sin_addr = mask;
5167a6f8720SBrian Somers     memcpy(cp, &rtdata, 16);
5177a6f8720SBrian Somers     cp += 16;
5187a6f8720SBrian Somers     rtmes.m_rtm.rtm_addrs |= RTA_NETMASK;
5197a6f8720SBrian Somers   }
5207a6f8720SBrian Somers 
5217a6f8720SBrian Somers   nb = cp - (char *) &rtmes;
5227a6f8720SBrian Somers   rtmes.m_rtm.rtm_msglen = nb;
5237a6f8720SBrian Somers   wb = ID0write(s, &rtmes, nb);
5247a6f8720SBrian Somers   if (wb < 0) {
5257a6f8720SBrian Somers     LogPrintf(LogTCPIP, "OsSetRoute failure:\n");
5267a6f8720SBrian Somers     LogPrintf(LogTCPIP, "OsSetRoute:  Cmd = %s\n", cmd);
5277a6f8720SBrian Somers     LogPrintf(LogTCPIP, "OsSetRoute:  Dst = %s\n", inet_ntoa(dst));
5287a6f8720SBrian Somers     LogPrintf(LogTCPIP, "OsSetRoute:  Gateway = %s\n", inet_ntoa(gateway));
5297a6f8720SBrian Somers     LogPrintf(LogTCPIP, "OsSetRoute:  Mask = %s\n", inet_ntoa(mask));
5307a6f8720SBrian Somers failed:
5317a6f8720SBrian Somers     if (cmd == RTM_ADD && (rtmes.m_rtm.rtm_errno == EEXIST ||
5327a6f8720SBrian Somers                            (rtmes.m_rtm.rtm_errno == 0 && errno == EEXIST)))
5337a6f8720SBrian Somers       if (!bang)
5347a6f8720SBrian Somers         LogPrintf(LogWARN, "Add route failed: %s already exists\n",
5357a6f8720SBrian Somers                   inet_ntoa(dst));
5367a6f8720SBrian Somers       else {
5377a6f8720SBrian Somers         rtmes.m_rtm.rtm_type = cmd = RTM_CHANGE;
5387a6f8720SBrian Somers         if ((wb = ID0write(s, &rtmes, nb)) < 0)
5397a6f8720SBrian Somers           goto failed;
5407a6f8720SBrian Somers       }
5417a6f8720SBrian Somers     else if (cmd == RTM_DELETE &&
5427a6f8720SBrian Somers              (rtmes.m_rtm.rtm_errno == ESRCH ||
5437a6f8720SBrian Somers               (rtmes.m_rtm.rtm_errno == 0 && errno == ESRCH))) {
5447a6f8720SBrian Somers       if (!bang)
5457a6f8720SBrian Somers         LogPrintf(LogWARN, "Del route failed: %s: Non-existent\n",
5467a6f8720SBrian Somers                   inet_ntoa(dst));
5477a6f8720SBrian Somers     } else if (rtmes.m_rtm.rtm_errno == 0)
5487a6f8720SBrian Somers       LogPrintf(LogWARN, "%s route failed: %s: errno: %s\n", cmdstr,
5497a6f8720SBrian Somers                 inet_ntoa(dst), strerror(errno));
5507a6f8720SBrian Somers     else
5517a6f8720SBrian Somers       LogPrintf(LogWARN, "%s route failed: %s: %s\n",
5527a6f8720SBrian Somers 		cmdstr, inet_ntoa(dst), strerror(rtmes.m_rtm.rtm_errno));
5537a6f8720SBrian Somers   }
5547a6f8720SBrian Somers   LogPrintf(LogDEBUG, "wrote %d: cmd = %s, dst = %x, gateway = %x\n",
5557a6f8720SBrian Somers             wb, cmdstr, dst.s_addr, gateway.s_addr);
5567a6f8720SBrian Somers   close(s);
5577a6f8720SBrian Somers }
558