+ if (proc_path_len == 0) {
+ /* Default the mount location of /proc */
+ strlcpy (proc_path, "/proc", sizeof(proc_path));
+ proc_path_len = 5;
+ fp = fopen(MOUNTED, "r");
+ if (fp != NULL) {
+ while ((mntent = getmntent(fp)) != NULL) {
+ if (strcmp(mntent->mnt_type, MNTTYPE_IGNORE) == 0)
+ continue;
+ if (strcmp(mntent->mnt_type, "proc") == 0) {
+ strlcpy(proc_path, mntent->mnt_dir, sizeof(proc_path));
+ proc_path_len = strlen(proc_path);
+ break;
+ }
+ }
+ fclose (fp);
+ }
+ }
+
+ strlcpy(proc_path + proc_path_len, tail,
+ sizeof(proc_path) - proc_path_len);
+ return proc_path;
+}
+
+/*
+ * /proc/net/route parsing stuff.
+ */
+#define ROUTE_MAX_COLS 12
+FILE *route_fd = (FILE *) 0;
+static char route_buffer[512];
+static int route_dev_col, route_dest_col, route_gw_col;
+static int route_flags_col, route_mask_col;
+static int route_num_cols;
+
+static int open_route_table (void);
+static void close_route_table (void);
+static int read_route_table (struct rtentry *rt);
+
+/********************************************************************
+ *
+ * close_route_table - close the interface to the route table
+ */
+
+static void close_route_table (void)
+{
+ if (route_fd != (FILE *) 0) {
+ fclose (route_fd);
+ route_fd = (FILE *) 0;
+ }
+}
+
+/********************************************************************
+ *
+ * open_route_table - open the interface to the route table
+ */
+static char route_delims[] = " \t\n";
+
+static int open_route_table (void)
+{
+ char *path;
+
+ close_route_table();
+
+ path = path_to_procfs("/net/route");
+ route_fd = fopen (path, "r");
+ if (route_fd == NULL) {
+ error("can't open routing table %s: %m", path);
+ return 0;
+ }
+
+ route_dev_col = 0; /* default to usual columns */
+ route_dest_col = 1;
+ route_gw_col = 2;
+ route_flags_col = 3;
+ route_mask_col = 7;
+ route_num_cols = 8;
+
+ /* parse header line */
+ if (fgets(route_buffer, sizeof(route_buffer), route_fd) != 0) {
+ char *p = route_buffer, *q;
+ int col;
+ for (col = 0; col < ROUTE_MAX_COLS; ++col) {
+ int used = 1;
+ if ((q = strtok(p, route_delims)) == 0)
+ break;
+ if (strcasecmp(q, "iface") == 0)
+ route_dev_col = col;
+ else if (strcasecmp(q, "destination") == 0)
+ route_dest_col = col;
+ else if (strcasecmp(q, "gateway") == 0)
+ route_gw_col = col;
+ else if (strcasecmp(q, "flags") == 0)
+ route_flags_col = col;
+ else if (strcasecmp(q, "mask") == 0)
+ route_mask_col = col;
+ else
+ used = 0;
+ if (used && col >= route_num_cols)
+ route_num_cols = col + 1;
+ p = NULL;
+ }
+ }
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * read_route_table - read the next entry from the route table
+ */
+
+static int read_route_table(struct rtentry *rt)
+{
+ char *cols[ROUTE_MAX_COLS], *p;
+ int col;
+
+ memset (rt, '\0', sizeof (struct rtentry));
+
+ if (fgets (route_buffer, sizeof (route_buffer), route_fd) == (char *) 0)
+ return 0;
+
+ p = route_buffer;
+ for (col = 0; col < route_num_cols; ++col) {
+ cols[col] = strtok(p, route_delims);
+ if (cols[col] == NULL)
+ return 0; /* didn't get enough columns */
+ p = NULL;
+ }
+
+ SIN_ADDR(rt->rt_dst) = strtoul(cols[route_dest_col], NULL, 16);
+ SIN_ADDR(rt->rt_gateway) = strtoul(cols[route_gw_col], NULL, 16);
+ SIN_ADDR(rt->rt_genmask) = strtoul(cols[route_mask_col], NULL, 16);
+
+ rt->rt_flags = (short) strtoul(cols[route_flags_col], NULL, 16);
+ rt->rt_dev = cols[route_dev_col];
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * defaultroute_exists - determine if there is a default route
+ */
+
+static int defaultroute_exists (struct rtentry *rt)
+{
+ int result = 0;
+
+ if (!open_route_table())
+ return 0;
+
+ while (read_route_table(rt) != 0) {
+ if ((rt->rt_flags & RTF_UP) == 0)
+ continue;
+
+ if (kernel_version > KVERSION(2,1,0) && SIN_ADDR(rt->rt_genmask) != 0)
+ continue;
+ if (SIN_ADDR(rt->rt_dst) == 0L) {
+ result = 1;
+ break;
+ }
+ }
+
+ close_route_table();
+ return result;
+}
+
+/*
+ * have_route_to - determine if the system has any route to
+ * a given IP address. `addr' is in network byte order.
+ * Return value is 1 if yes, 0 if no, -1 if don't know.
+ * For demand mode to work properly, we have to ignore routes
+ * through our own interface.
+ */
+int have_route_to(u_int32_t addr)
+{
+ struct rtentry rt;
+ int result = 0;
+
+ if (!open_route_table())
+ return -1; /* don't know */
+
+ while (read_route_table(&rt)) {
+ if ((rt.rt_flags & RTF_UP) == 0 || strcmp(rt.rt_dev, ifname) == 0)
+ continue;
+ if ((addr & SIN_ADDR(rt.rt_genmask)) == SIN_ADDR(rt.rt_dst)) {
+ result = 1;
+ break;
+ }
+ }
+
+ close_route_table();
+ return result;
+}
+
+/********************************************************************
+ *
+ * sifdefaultroute - assign a default route through the address given.
+ */
+
+int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
+{
+ struct rtentry rt;
+
+ if (defaultroute_exists(&rt) && strcmp(rt.rt_dev, ifname) != 0) {
+ u_int32_t old_gateway = SIN_ADDR(rt.rt_gateway);
+
+ if (old_gateway != gateway)
+ error("not replacing existing default route to %s [%I]",
+ rt.rt_dev, old_gateway);
+ return 0;
+ }
+
+ memset (&rt, '\0', sizeof (rt));
+ SET_SA_FAMILY (rt.rt_dst, AF_INET);
+ SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+
+ if (kernel_version > KVERSION(2,1,0)) {
+ SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+ SIN_ADDR(rt.rt_genmask) = 0L;
+ }
+
+ SIN_ADDR(rt.rt_gateway) = gateway;
+
+ rt.rt_flags = RTF_UP | RTF_GATEWAY;
+ if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
+ if ( ! ok_error ( errno ))
+ error("default route ioctl(SIOCADDRT): %m(%d)", errno);
+ return 0;
+ }
+
+ default_route_gateway = gateway;
+ return 1;
+}
+
+/********************************************************************
+ *
+ * cifdefaultroute - delete a default route through the address given.
+ */
+
+int cifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
+{
+ struct rtentry rt;
+
+ default_route_gateway = 0;
+
+ memset (&rt, '\0', sizeof (rt));
+ SET_SA_FAMILY (rt.rt_dst, AF_INET);
+ SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+
+ if (kernel_version > KVERSION(2,1,0)) {
+ SET_SA_FAMILY (rt.rt_genmask, AF_INET);
+ SIN_ADDR(rt.rt_genmask) = 0L;
+ }
+
+ SIN_ADDR(rt.rt_gateway) = gateway;
+
+ rt.rt_flags = RTF_UP | RTF_GATEWAY;
+ if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
+ if (still_ppp()) {
+ if ( ! ok_error ( errno ))
+ error("default route ioctl(SIOCDELRT): %m (%d)", errno);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+
+int sifproxyarp (int unit, u_int32_t his_adr)
+{
+ struct arpreq arpreq;
+ char *forw_path;
+
+ if (has_proxy_arp == 0) {
+ memset (&arpreq, '\0', sizeof(arpreq));
+
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ SIN_ADDR(arpreq.arp_pa) = his_adr;
+ arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+/*
+ * Get the hardware address of an interface on the same subnet
+ * as our local address.
+ */
+ if (!get_ether_addr(his_adr, &arpreq.arp_ha, proxy_arp_dev,
+ sizeof(proxy_arp_dev))) {
+ error("Cannot determine ethernet address for proxy ARP");
+ return 0;
+ }
+ strlcpy(arpreq.arp_dev, proxy_arp_dev, sizeof(arpreq.arp_dev));
+
+ if (ioctl(sock_fd, SIOCSARP, (caddr_t)&arpreq) < 0) {
+ if ( ! ok_error ( errno ))
+ error("ioctl(SIOCSARP): %m(%d)", errno);
+ return 0;
+ }
+ proxy_arp_addr = his_adr;
+ has_proxy_arp = 1;
+
+ if (tune_kernel) {
+ forw_path = path_to_procfs("/sys/net/ipv4/ip_forward");
+ if (forw_path != 0) {
+ int fd = open(forw_path, O_WRONLY);
+ if (fd >= 0) {
+ if (write(fd, "1", 1) != 1)
+ error("Couldn't enable IP forwarding: %m");
+ close(fd);
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+/********************************************************************
+ *
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+
+int cifproxyarp (int unit, u_int32_t his_adr)
+{
+ struct arpreq arpreq;
+
+ if (has_proxy_arp) {
+ has_proxy_arp = 0;
+ memset (&arpreq, '\0', sizeof(arpreq));
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ SIN_ADDR(arpreq.arp_pa) = his_adr;
+ arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+ strlcpy(arpreq.arp_dev, proxy_arp_dev, sizeof(arpreq.arp_dev));
+
+ if (ioctl(sock_fd, SIOCDARP, (caddr_t)&arpreq) < 0) {
+ if ( ! ok_error ( errno ))
+ warn("ioctl(SIOCDARP): %m(%d)", errno);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/********************************************************************
+ *
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+
+static int get_ether_addr (u_int32_t ipaddr,
+ struct sockaddr *hwaddr,
+ char *name, int namelen)
+{
+ struct ifreq *ifr, *ifend;
+ u_int32_t ina, mask;
+ char *aliasp;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) {
+ if ( ! ok_error ( errno ))
+ error("ioctl(SIOCGIFCONF): %m(%d)", errno);
+ return 0;
+ }
+
+ SYSDEBUG ((LOG_DEBUG, "proxy arp: scanning %d interfaces for IP %s",
+ ifc.ifc_len / sizeof(struct ifreq), ip_ntoa(ipaddr)));
+/*
+ * Scan through looking for an interface with an Internet
+ * address on the same subnet as `ipaddr'.
+ */
+ ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ ina = SIN_ADDR(ifr->ifr_addr);
+ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+ SYSDEBUG ((LOG_DEBUG, "proxy arp: examining interface %s",
+ ifreq.ifr_name));
+/*
+ * Check that the interface is up, and not point-to-point
+ * nor loopback.
+ */
+ if (ioctl(sock_fd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+
+ if (((ifreq.ifr_flags ^ FLAGS_GOOD) & FLAGS_MASK) != 0)
+ continue;
+/*
+ * Get its netmask and check that it's on the right subnet.
+ */
+ if (ioctl(sock_fd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+
+ mask = SIN_ADDR(ifreq.ifr_addr);
+ SYSDEBUG ((LOG_DEBUG, "proxy arp: interface addr %s mask %lx",
+ ip_ntoa(ina), ntohl(mask)));
+
+ if (((ipaddr ^ ina) & mask) != 0)
+ continue;
+ break;
+ }
+ }
+
+ if (ifr >= ifend)
+ return 0;
+
+ strlcpy(name, ifreq.ifr_name, namelen);
+
+ /* trim off the :1 in eth0:1 */
+ aliasp = strchr(name, ':');
+ if (aliasp != 0)
+ *aliasp = 0;
+
+ info("found interface %s for proxy arp", name);
+/*
+ * Now get the hardware address.
+ */
+ memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
+ if (ioctl (sock_fd, SIOCGIFHWADDR, &ifreq) < 0) {
+ error("SIOCGIFHWADDR(%s): %m(%d)", ifreq.ifr_name, errno);
+ return 0;
+ }
+
+ memcpy (hwaddr,
+ &ifreq.ifr_hwaddr,
+ sizeof (struct sockaddr));
+
+ SYSDEBUG ((LOG_DEBUG,
+ "proxy arp: found hwaddr %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ (int) ((unsigned char *) &hwaddr->sa_data)[0],
+ (int) ((unsigned char *) &hwaddr->sa_data)[1],
+ (int) ((unsigned char *) &hwaddr->sa_data)[2],
+ (int) ((unsigned char *) &hwaddr->sa_data)[3],
+ (int) ((unsigned char *) &hwaddr->sa_data)[4],
+ (int) ((unsigned char *) &hwaddr->sa_data)[5],
+ (int) ((unsigned char *) &hwaddr->sa_data)[6],
+ (int) ((unsigned char *) &hwaddr->sa_data)[7]));
+ return 1;
+}
+
+/*
+ * get_if_hwaddr - get the hardware address for the specified
+ * network interface device.
+ */
+int
+get_if_hwaddr(u_char *addr, char *name)
+{
+ struct ifreq ifreq;
+ int ret, sock_fd;
+
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd < 0)
+ return 0;
+ memset(&ifreq.ifr_hwaddr, 0, sizeof(struct sockaddr));
+ strlcpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name));
+ ret = ioctl(sock_fd, SIOCGIFHWADDR, &ifreq);
+ close(sock_fd);
+ if (ret >= 0)
+ memcpy(addr, ifreq.ifr_hwaddr.sa_data, 6);
+ return ret;
+}
+
+/*
+ * get_first_ethernet - return the name of the first ethernet-style
+ * interface on this system.
+ */
+char *
+get_first_ethernet()
+{
+ return "eth0";
+}
+
+/********************************************************************
+ *
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'. If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */