#include "memdbg.h"
- static void delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
+ static void delete_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, const struct env_set *es);
++
static void get_bypass_addresses (struct route_bypass *rb, const unsigned int flags);
#ifdef ENABLE_DEBUG
rl->capacity = capacity;
}
+void
+clear_route_ipv6_list (struct route_ipv6_list *rl6)
+{
+ const int capacity = rl6->capacity;
+ const size_t rl6_size = array_mult_safe (sizeof(struct route_ipv6), capacity, sizeof(struct route_ipv6_list));
+ memset(rl6, 0, rl6_size);
+ rl6->capacity = capacity;
+}
+
void
- route_list_add_default_gateway (struct route_list *rl,
- struct env_set *es,
- const in_addr_t addr)
+ route_list_add_vpn_gateway (struct route_list *rl,
+ struct env_set *es,
+ const in_addr_t addr)
{
rl->spec.remote_endpoint = addr;
- rl->spec.remote_endpoint_defined = true;
+ rl->spec.flags |= RTSA_REMOTE_ENDPOINT;
setenv_route_addr (es, "vpn_gateway", rl->spec.remote_endpoint, -1);
}
if (!local)
{
/* route remote host to original default gateway */
- add_route3 (rl->spec.remote_host,
- ~0,
- rl->rgi.gateway.addr,
- tt,
- flags,
- &rl->rgi,
- es);
- rl->iflags |= RL_DID_LOCAL;
+#ifdef USE_PF_INET6
+ /* if remote_host is not ipv4 (ie: ipv6), just skip
+ * adding this special /32 route */
+ if (rl->spec.remote_host != IPV4_INVALID_ADDR) {
+#endif
+ add_route3 (rl->spec.remote_host,
+ ~0,
- rl->spec.net_gateway,
++ rl->rgi.gateway.addr,
+ tt,
+ flags,
++ &rl->rgi,
+ es);
- rl->did_local = true;
++ rl->iflags |= RL_DID_LOCAL;
+#ifdef USE_PF_INET6
+ } else {
+ dmsg (D_ROUTE, "ROUTE remote_host protocol differs from tunneled");
+ }
+#endif
}
/* route DHCP/DNS server traffic through original default gateway */
}
void
- add_routes (struct route_list *rl, struct route_ipv6_list *rl6,
- const struct tuntap *tt, unsigned int flags, const struct env_set *es)
-add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
++add_routes (struct route_list *rl, struct route_ipv6_list *rl6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
- if (rl)
- redirect_default_route_to_vpn (rl, tt, flags, es);
-
- if (rl && !rl->routes_added)
+ redirect_default_route_to_vpn (rl, tt, flags, es);
+ if (!(rl->iflags & RL_ROUTES_ADDED))
{
int i;
struct route *r = &rl->routes[i];
check_subnet_conflict (r->network, r->netmask, "route");
if (flags & ROUTE_DELETE_FIRST)
- delete_route (r, tt, flags, es);
- add_route (r, tt, flags, es);
+ delete_route (r, tt, flags, &rl->rgi, es);
+ add_route (r, tt, flags, &rl->rgi, es);
}
- rl->routes_added = true;
+ rl->iflags |= RL_ROUTES_ADDED;
}
-
+ if (rl6 && !rl6->routes_added)
+ {
+ int i;
+
+ for (i = 0; i < rl6->n; ++i)
+ {
+ struct route_ipv6 *r = &rl6->routes_ipv6[i];
+ if (flags & ROUTE_DELETE_FIRST)
+ delete_route_ipv6 (r, tt, flags, es);
+ add_route_ipv6 (r, tt, flags, es);
+ }
+ rl6->routes_added = true;
+ }
}
void
-delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+delete_routes (struct route_list *rl, struct route_ipv6_list *rl6,
+ const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
- if (rl && rl->routes_added)
+ if (rl->iflags & RL_ROUTES_ADDED)
{
int i;
for (i = rl->n - 1; i >= 0; --i)
{
- struct route *r = &rl->routes[i];
+ const struct route *r = &rl->routes[i];
- delete_route (r, tt, flags, es);
+ delete_route (r, tt, flags, &rl->rgi, es);
}
- rl->routes_added = false;
+ rl->iflags &= ~RL_ROUTES_ADDED;
}
- undo_redirect_default_route_to_vpn (rl, tt, flags, es);
- if ( rl )
- {
- undo_redirect_default_route_to_vpn (rl, tt, flags, es);
- clear_route_list (rl);
- }
- clear_route_list (rl);
++ undo_redirect_default_route_to_vpn (rl, tt, flags, es);
++ clear_route_list (rl);
+
+ if ( rl6 && rl6->routes_added )
+ {
+ int i;
+ for (i = rl6->n - 1; i >= 0; --i)
+ {
+ const struct route_ipv6 *r6 = &rl6->routes_ipv6[i];
+ delete_route_ipv6 (r6, tt, flags, es);
+ }
+ rl6->routes_added = false;
+ }
+
+ if ( rl6 )
+ {
+ clear_route_ipv6_list (rl6);
+ }
}
#ifdef ENABLE_DEBUG
setenv_route (es, &rl->routes[i], i + 1);
}
- * route add -net 10.10.0.1 netmask 255.255.255.255 dev eth0
+static void
+setenv_route_ipv6 (struct env_set *es, const struct route_ipv6 *r6, int i)
+{
+ struct gc_arena gc = gc_new ();
+ if (r6->defined)
+ {
+ struct buffer name1 = alloc_buf_gc( 256, &gc );
+ struct buffer val = alloc_buf_gc( 256, &gc );
+ struct buffer name2 = alloc_buf_gc( 256, &gc );
+
+ buf_printf( &name1, "route_ipv6_network_%d", i );
+ buf_printf( &val, "%s/%d", print_in6_addr( r6->network, 0, &gc ),
+ r6->netbits );
+ setenv_str( es, BSTR(&name1), BSTR(&val) );
+
+ buf_printf( &name2, "route_ipv6_gateway_%d", i );
+ setenv_str( es, BSTR(&name2), print_in6_addr( r6->gateway, 0, &gc ));
+ }
+ gc_free (&gc);
+}
+void
+setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6)
+{
+ int i;
+ for (i = 0; i < rl6->n; ++i)
+ setenv_route_ipv6 (es, &rl6->routes_ipv6[i], i + 1);
+}
+
+ /*
+ * local_route() determines whether the gateway of a provided host
+ * route is on the same interface that owns the default gateway.
+ * It uses the data structure
+ * returned by get_default_gateway() (struct route_gateway_info)
+ * to determine this. If the route is local, LR_MATCH is returned.
+ * When adding routes into the kernel, if LR_MATCH is defined for
+ * a given route, the route should explicitly reference the default
+ * gateway interface as the route destination. For example, here
+ * is an example on Linux that uses LR_MATCH:
+ *
++ * route add -net 10.10.0.1 netmask 255.255.255.255 dev eth0
+ *
+ * This capability is needed by the "default-gateway block-local"
+ * directive, to allow client access to the local subnet to be
+ * blocked but still allow access to the local default gateway.
+ */
+
+ /* local_route() return values */
+ #define LR_NOMATCH 0 /* route is not local */
+ #define LR_MATCH 1 /* route is local */
+ #define LR_ERROR 2 /* caller should abort adding route */
+
+ static int
+ local_route (in_addr_t network,
+ in_addr_t netmask,
+ in_addr_t gateway,
+ const struct route_gateway_info *rgi)
+ {
+ /* set LR_MATCH on local host routes */
+ const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED|RGI_IFACE_DEFINED);
+ if (rgi
+ && (rgi->flags & rgi_needed) == rgi_needed
+ && gateway == rgi->gateway.addr
+ && netmask == 0xFFFFFFFF)
+ {
+ if (((network ^ rgi->gateway.addr) & rgi->gateway.netmask) == 0)
+ return LR_MATCH;
+ else
+ {
+ /* examine additional subnets on gateway interface */
+ size_t i;
+ for (i = 0; i < rgi->n_addrs; ++i)
+ {
+ const struct route_gateway_address *gwa = &rgi->addrs[i];
+ if (((network ^ gwa->addr) & gwa->netmask) == 0)
+ return LR_MATCH;
+ }
+ }
+ }
+ return LR_NOMATCH;
+ }
+
void
- add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+ add_route (struct route *r,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct route_gateway_info *rgi, /* may be NULL */
+ const struct env_set *es)
{
struct gc_arena gc;
struct argv argv;
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
- argv_printf_cat (&argv, "-net %s %s %s",
- network,
- gateway,
- netmask);
+ if (rgi && is_local_route == LR_MATCH)
+ {
- /* Mac OS X route syntax for LR_MATCH:
++ /* Mac OS X route syntax for LR_MATCH:
+ route add -cloning -net 10.10.0.1 -netmask 255.255.255.255 -interface en0 */
+ argv_printf_cat (&argv, "-cloning -net %s -netmask %s -interface %s",
+ network,
+ netmask,
+ rgi->iface);
+ }
+ else
+ {
+ argv_printf_cat (&argv, "-net %s %s %s",
+ network,
+ gateway,
+ netmask);
+ }
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: OS X route add command failed");
gc_free (&gc);
}
+
+static const char *
+print_in6_addr_netbits_only( struct in6_addr network_copy, int netbits,
+ struct gc_arena * gc)
+{
+ /* clear host bit parts of route
+ * (needed if routes are specified improperly, or if we need to
+ * explicitely setup/clear the "connected" network routes on some OSes)
+ */
+ int byte = 15;
+ int bits_to_clear = 128 - netbits;
+
+ while( byte >= 0 && bits_to_clear > 0 )
+ {
+ if ( bits_to_clear >= 8 )
+ { network_copy.s6_addr[byte--] = 0; bits_to_clear -= 8; }
+ else
+ { network_copy.s6_addr[byte--] &= (~0 << bits_to_clear); bits_to_clear = 0; }
+ }
+
+ return print_in6_addr( network_copy, 0, gc);
+}
+
+void
+add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+{
+ struct gc_arena gc;
+ struct argv argv;
+
+ const char *network;
+ const char *gateway;
+ bool status = false;
+ const char *device = tt->actual_name;
+
+ if (!r6->defined)
+ return;
+
+ gc_init (&gc);
+ argv_init (&argv);
+
+ network = print_in6_addr_netbits_only( r6->network, r6->netbits, &gc);
+ gateway = print_in6_addr( r6->gateway, 0, &gc);
+
+ if ( !tt->ipv6 )
+ {
+ msg( M_INFO, "add_route_ipv6(): not adding %s/%d, no IPv6 on if %s",
+ network, r6->netbits, device );
+ return;
+ }
+
+ msg( M_INFO, "add_route_ipv6(%s/%d -> %s metric %d) dev %s",
+ network, r6->netbits, gateway, r6->metric, device );
+
+ /*
+ * Filter out routes which are essentially no-ops
+ * (not currently done for IPv6)
+ */
+
+#if defined(TARGET_LINUX)
+#ifdef CONFIG_FEATURE_IPROUTE
+ argv_printf (&argv, "%s -6 route add %s/%d dev %s",
+ iproute_path,
+ network,
+ r6->netbits,
+ device);
+ if (r6->metric_defined)
+ argv_printf_cat (&argv, " metric %d", r6->metric);
+
+#else
+ argv_printf (&argv, "%s -A inet6 add %s/%d dev %s",
+ ROUTE_PATH,
+ network,
+ r6->netbits,
+ device);
+ if (r6->metric_defined)
+ argv_printf_cat (&argv, " metric %d", r6->metric);
+#endif /*CONFIG_FEATURE_IPROUTE*/
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 add command failed");
+
+#elif defined (WIN32)
+
+ /* netsh interface ipv6 add route 2001:db8::/32 MyTunDevice */
+ argv_printf (&argv, "%s%sc interface ipv6 add route %s/%d %s",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ network,
+ r6->netbits,
+ device);
+
+ /* next-hop depends on TUN or TAP mode:
+ * - in TAP mode, we use the "real" next-hop
+ * - in TUN mode we use a special-case link-local address that the tapdrvr
+ * knows about and will answer ND (neighbor discovery) packets for
+ */
+ if ( tt->type == DEV_TYPE_TUN )
+ argv_printf_cat( &argv, " %s", "fe80::8" );
+ else
+ argv_printf_cat( &argv, " %s", gateway );
+
+#if 0
+ if (r->metric_defined)
+ argv_printf_cat (&argv, " METRIC %d", r->metric);
+#endif
+
+ /* in some versions of Windows, routes are persistent across reboots by
+ * default, unless "store=active" is set (pointed out by Tony Lim, thanks)
+ */
+ argv_printf_cat( &argv, " store=active" );
+
+ argv_msg (D_ROUTE, &argv);
+
+ netcmd_semaphore_lock ();
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add ipv6 command failed");
+ netcmd_semaphore_release ();
+
+#elif defined (TARGET_SOLARIS)
+
+ /* example: route add -inet6 2001:db8::/32 somegateway 0 */
+
+ /* for some weird reason, this does not work for me unless I set
+ * "metric 0" - otherwise, the routes will be nicely installed, but
+ * packets will just disappear somewhere. So we use "0" now...
+ */
+
+ argv_printf (&argv, "%s add -inet6 %s/%d %s 0",
+ ROUTE_PATH,
+ network,
+ r6->netbits,
+ gateway );
+
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add -inet6 command failed");
+
+#elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
+
+ argv_printf (&argv, "%s add -inet6 %s/%d -iface %s",
+ ROUTE_PATH,
+ network,
+ r6->netbits,
+ device );
+
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route add -inet6 command failed");
+
+#elif defined(TARGET_DARWIN)
+
+ argv_printf (&argv, "%s add -inet6 %s -prefixlen %d -iface %s",
+ ROUTE_PATH,
+ network, r6->netbits, device );
+
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: MacOS X route add -inet6 command failed");
+
+#elif defined(TARGET_OPENBSD)
+
+ argv_printf (&argv, "%s add -inet6 %s -prefixlen %d %s",
+ ROUTE_PATH,
+ network, r6->netbits, gateway );
+
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD route add -inet6 command failed");
+
+#elif defined(TARGET_NETBSD)
+
+ argv_printf (&argv, "%s add -inet6 %s/%d %s",
+ ROUTE_PATH,
+ network, r6->netbits, gateway );
+
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD route add -inet6 command failed");
+
+#else
+ msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-up script");
+#endif
+
+ r6->defined = status;
+ argv_reset (&argv);
+ gc_free (&gc);
+}
+
static void
- delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+ delete_route (struct route *r,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct route_gateway_info *rgi,
+ const struct env_set *es)
{
struct gc_arena gc;
struct argv argv;
#elif defined(TARGET_LINUX)
- bool
- get_default_gateway (in_addr_t *gateway, in_addr_t *netmask)
+ void
+ get_default_gateway (struct route_gateway_info *rgi)
{
struct gc_arena gc = gc_new ();
- bool ret = false;
- FILE *fp = fopen ("/proc/net/route", "r");
- if (fp)
- {
- char line[256];
- int count = 0;
- int best_count = 0;
- unsigned int lowest_metric = ~0;
- in_addr_t best_gw = 0;
- while (fgets (line, sizeof (line), fp) != NULL)
+ int sd = -1;
+
+ CLEAR(*rgi);
+
+ /* get default gateway IP addr */
+ {
+ FILE *fp = fopen ("/proc/net/route", "r");
+ if (fp)
+ {
+ char line[256];
+ int count = 0;
+ unsigned int lowest_metric = ~0;
+ in_addr_t best_gw = 0;
+ while (fgets (line, sizeof (line), fp) != NULL)
+ {
+ if (count)
+ {
+ unsigned int net_x = 0;
+ unsigned int mask_x = 0;
+ unsigned int gw_x = 0;
+ unsigned int metric = 0;
+ unsigned int flags = 0;
+ const int np = sscanf (line, "%*s\t%x\t%x\t%x\t%*s\t%*s\t%d\t%x",
+ &net_x,
+ &gw_x,
+ &flags,
+ &metric,
+ &mask_x);
+ if (np == 5 && (flags & IFF_UP))
+ {
+ const in_addr_t net = ntohl (net_x);
+ const in_addr_t mask = ntohl (mask_x);
+ const in_addr_t gw = ntohl (gw_x);
+
+ if (!net && !mask && metric < lowest_metric)
+ {
+ best_gw = gw;
+ lowest_metric = metric;
+ }
+ }
+ }
+ ++count;
+ }
+ fclose (fp);
+
+ if (best_gw)
+ {
+ rgi->gateway.addr = best_gw;
+ rgi->flags |= RGI_ADDR_DEFINED;
+ }
+ }
+ }
+
+ /* scan adapter list */
+ if (rgi->flags & RGI_ADDR_DEFINED)
+ {
+ struct ifreq *ifr, *ifend;
+ in_addr_t addr, netmask;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[20]; /* Maximum number of interfaces to scan */
+
+ if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
{
- if (count)
- {
- unsigned int net_x = 0;
- unsigned int mask_x = 0;
- unsigned int gw_x = 0;
- unsigned int metric = 0;
- const int np = sscanf (line, "%*s\t%x\t%x\t%*s\t%*s\t%*s\t%d\t%x",
- &net_x,
- &gw_x,
- &metric,
- &mask_x);
- if (np == 4)
- {
- const in_addr_t net = ntohl (net_x);
- const in_addr_t mask = ntohl (mask_x);
- const in_addr_t gw = ntohl (gw_x);
-
- dmsg (D_ROUTE_DEBUG, "GDG: route[%d] %s/%s/%s m=%u",
- count,
- print_in_addr_t ((in_addr_t) net, 0, &gc),
- print_in_addr_t ((in_addr_t) mask, 0, &gc),
- print_in_addr_t ((in_addr_t) gw, 0, &gc),
- metric);
-
- if (!net && !mask && metric < lowest_metric)
- {
- best_gw = gw;
- lowest_metric = metric;
- best_count = count;
- }
- }
- }
- ++count;
+ msg (M_WARN, "GDG: socket() failed");
+ goto done;
+ }
+ ifc.ifc_len = sizeof (ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl (sd, SIOCGIFCONF, &ifc) < 0)
+ {
+ msg (M_WARN, "GDG: ioctl(SIOCGIFCONF) failed");
+ goto done;
}
- fclose (fp);
- if (best_gw)
+ /* scan through interface list */
+ ifend = ifs + (ifc.ifc_len / sizeof (struct ifreq));
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr++)
{
- *gateway = best_gw;
- if (netmask)
+ if (ifr->ifr_addr.sa_family == AF_INET)
{
- *netmask = 0xFFFFFF00; /* FIXME -- get the real netmask of the adapter containing the default gateway */
+ /* get interface addr */
+ addr = ntohl(((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr);
+
+ /* get interface name */
+ strncpynt (ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
+
+ /* check that the interface is up, and not point-to-point or loopback */
+ if (ioctl (sd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if (!(ifreq.ifr_flags & IFF_UP))
+ continue;
+
+ /* get interface netmask */
+ if (ioctl (sd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ netmask = ntohl(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr);
+
+ /* check that interface matches default route */
+ if (((rgi->gateway.addr ^ addr) & netmask) != 0)
+ continue;
+
+ /* save iface name and netmask */
+ strncpynt (rgi->iface, ifreq.ifr_name, sizeof(rgi->iface));
+ rgi->gateway.netmask = netmask;
+ rgi->flags |= (RGI_IFACE_DEFINED|RGI_NETMASK_DEFINED);
-
++
+ /* now get the hardware address. */
+ memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
+ if (ioctl (sd, SIOCGIFHWADDR, &ifreq) < 0)
+ {
+ msg (M_WARN, "GDG: SIOCGIFHWADDR(%s) failed", ifreq.ifr_name);
+ goto done;
+ }
+ memcpy (rgi->hwaddr, &ifreq.ifr_hwaddr.sa_data, 6);
+ rgi->flags |= RGI_HWADDR_DEFINED;
+
+ break;
}
- ret = true;
}
-
- dmsg (D_ROUTE_DEBUG, "GDG: best=%s[%d] lm=%u",
- print_in_addr_t ((in_addr_t) best_gw, 0, &gc),
- best_count,
- (unsigned int)lowest_metric);
}
+ done:
+ if (sd >= 0)
+ close (sd);
gc_free (&gc);
- return ret;
}
#elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
- s = socket(PF_ROUTE, SOCK_RAW, 0);
-
- if (write(s, (char *)&m_rtmsg, l) < 0)
+ /* transact with routing socket */
+ sockfd = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (sockfd < 0)
{
- msg (M_WARN, "ROUTE: problem writing to routing socket");
- gc_free (&gc);
- close(s);
- return false;
+ msg (M_WARN, "GDG: socket #1 failed");
+ goto done;
+ }
+ if (write(sockfd, (char *)&m_rtmsg, l) < 0)
+ {
+ msg (M_WARN, "GDG: problem writing to routing socket");
+ goto done;
}
-
do {
- l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ l = read(sockfd, (char *)&m_rtmsg, sizeof(m_rtmsg));
- } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+ } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
-
- close(s);
+ close(sockfd);
+ sockfd = -1;
+ /* extract return data from routing socket */
rtm_aux = &rtm;
-
cp = ((char *)(rtm_aux + 1));
- if (rtm_aux->rtm_addrs) {
- for (i = 1; i; i <<= 1)
- {
- if (i & rtm_aux->rtm_addrs)
- {
- sa = (struct sockaddr *)cp;
- if (i == RTA_GATEWAY )
- gate = sa;
- else if (i == RTA_IFP)
- ifp = sa;
- ADVANCE(cp, sa);
- }
- }
- }
+ if (rtm_aux->rtm_addrs)
+ {
+ for (i = 1; i; i <<= 1)
+ {
+ if (i & rtm_aux->rtm_addrs)
+ {
+ sa = (struct sockaddr *)cp;
+ if (i == RTA_GATEWAY )
+ gate = sa;
+ else if (i == RTA_IFP)
+ ifp = sa;
+ ADVANCE(cp, sa);
+ }
+ }
+ }
else
+ goto done;
+
+ /* get gateway addr and interface name */
+ if (gate != NULL )
{
- gc_free (&gc);
- return false;
+ /* get default gateway addr */
+ rgi->gateway.addr = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
+ if (rgi->gateway.addr)
+ rgi->flags |= RGI_ADDR_DEFINED;
+
+ if (ifp)
+ {
+ /* get interface name */
+ const struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp;
+ int len = adl->sdl_nlen;
+ if (adl->sdl_nlen && adl->sdl_nlen < sizeof(rgi->iface))
+ {
+ memcpy (rgi->iface, adl->sdl_data, adl->sdl_nlen);
+ rgi->iface[adl->sdl_nlen] = '\0';
+ rgi->flags |= RGI_IFACE_DEFINED;
+ }
+ }
}
+ /* get netmask of interface that owns default gateway */
+ if (rgi->flags & RGI_IFACE_DEFINED) {
+ struct ifreq ifr;
- if (gate != NULL )
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
+ {
+ msg (M_WARN, "GDG: socket #2 failed");
+ goto done;
+ }
+
+ CLEAR(ifr);
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpynt(ifr.ifr_name, rgi->iface, IFNAMSIZ);
+
+ if (ioctl(sockfd, SIOCGIFNETMASK, (char *)&ifr) < 0)
+ {
+ msg (M_WARN, "GDG: ioctl #1 failed");
+ goto done;
+ }
+ close(sockfd);
+ sockfd = -1;
+
+ rgi->gateway.netmask = ntohl(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr);
+ rgi->flags |= RGI_NETMASK_DEFINED;
+ }
+
+ /* try to read MAC addr associated with interface that owns default gateway */
+ if (rgi->flags & RGI_IFACE_DEFINED)
{
- *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
- #if 0
- msg (M_INFO, "gw %s",
- print_in_addr_t ((in_addr_t) *ret, 0, &gc));
- #endif
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ const int bufsize = 4096;
+ char *buffer;
- if (netmask)
+ buffer = (char *) gc_malloc (bufsize, true, &gc);
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
{
- *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
+ msg (M_WARN, "GDG: socket #3 failed");
+ goto done;
}
- if (ifp && ifname)
+ ifc.ifc_len = bufsize;
+ ifc.ifc_buf = buffer;
+
+ if (ioctl(sockfd, SIOCGIFCONF, (char *)&ifc) < 0)
{
- struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp;
- char *name = malloc(adl->sdl_nlen+1);
- check_malloc_return(name);
- memcpy(name, adl->sdl_data, adl->sdl_nlen);
- name[adl->sdl_nlen] = '\0';
- *ifname = name;
+ msg (M_WARN, "GDG: ioctl #2 failed");
+ goto done;
}
+ close(sockfd);
+ sockfd = -1;
- gc_free (&gc);
- return true;
- }
- else
- {
- gc_free (&gc);
- return false;
+ for (cp = buffer; cp <= buffer + ifc.ifc_len - sizeof(struct ifreq); )
+ {
+ ifr = (struct ifreq *)cp;
+ const size_t len = sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
+ if (!ifr->ifr_addr.sa_family)
+ break;
+ if (!strncmp(ifr->ifr_name, rgi->iface, IFNAMSIZ))
+ {
+ if (ifr->ifr_addr.sa_family == AF_LINK)
+ {
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
+ memcpy(rgi->hwaddr, LLADDR(sdl), 6);
+ rgi->flags |= RGI_HWADDR_DEFINED;
+ }
+ }
+ cp += len;
+ }
}
- }
- bool
- get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
- {
- return get_default_gateway_ex(ret, netmask, NULL);
+ done:
+ if (sockfd >= 0)
+ close(sockfd);
+ gc_free (&gc);
}
+ #undef max
+
#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
#include <sys/types.h>