Browse code

get_default_gateway_ipv6(): *BSD / MacOS / Solaris PF_ROUTE implementation

As for IPv4, a common implementation for all (supported) BSD families
and Solaris. Supporting the latter requires separate implementations
for IPv4 and IPv6, unfortunately, so it's quite a bit of duplicate
code.

Further, extend add_route_ipv6() and delete_route_ipv6() to handle
link-local gateway addresses that require "gateway + interface" in
scoped notation ("fe80::1%em0").

Tested on FreeBSD 7.4/amd64, 9.3/sparc64, 10.0/amd64, NetBSD 5.1/amd64,
OpenBSD 4.9/i386, MacOS X 10.5/32 and 10.10/64 and OpenSolaris 10.11/i386.

Signed-off-by: Gert Doering <gert@greenie.muc.de>
Acked-by: Arne Schwabe <arne@rfc2549.org>
Message-Id: <1441985627-14822-9-git-send-email-gert@greenie.muc.de>
URL: http://article.gmane.org/gmane.network.openvpn.devel/10082

Gert Doering authored on 2015/09/12 00:33:45
Showing 1 changed files
... ...
@@ -1687,6 +1687,24 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla
1687 1687
   network = print_in6_addr_netbits_only( r6->network, r6->netbits, &gc);
1688 1688
   gateway = print_in6_addr( r6->gateway, 0, &gc);
1689 1689
 
1690
+#if defined(TARGET_DARWIN) || \
1691
+	defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) || \
1692
+	defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
1693
+
1694
+  /* the BSD platforms cannot specify gateway and interface independently,
1695
+   * but for link-local destinations, we MUST specify the interface, so
1696
+   * we build a combined "$gateway%$interface" gateway string
1697
+   */
1698
+  if ( r6->iface != NULL && gateway_needed &&
1699
+       IN6_IS_ADDR_LINKLOCAL(&r6->gateway) )		/* fe80::...%intf */
1700
+    {
1701
+      int len = strlen(gateway) + 1 + strlen(r6->iface)+1;
1702
+      char * tmp = gc_malloc( len, true, &gc );
1703
+      snprintf( tmp, len, "%s%%%s", gateway, r6->iface );
1704
+      gateway = tmp;
1705
+    }
1706
+#endif
1707
+
1690 1708
   if ( !tt->ipv6 )
1691 1709
     {
1692 1710
       msg( M_INFO, "add_route_ipv6(): not adding %s/%d, no IPv6 on if %s",
... ...
@@ -2069,6 +2087,24 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne
2069 2069
   network = print_in6_addr_netbits_only( r6->network, r6->netbits, &gc);
2070 2070
   gateway = print_in6_addr( r6->gateway, 0, &gc);
2071 2071
 
2072
+#if defined(TARGET_DARWIN) || \
2073
+	defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) || \
2074
+	defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
2075
+
2076
+  /* the BSD platforms cannot specify gateway and interface independently,
2077
+   * but for link-local destinations, we MUST specify the interface, so
2078
+   * we build a combined "$gateway%$interface" gateway string
2079
+   */
2080
+  if ( r6->iface != NULL && gateway_needed &&
2081
+       IN6_IS_ADDR_LINKLOCAL(&r6->gateway) )		/* fe80::...%intf */
2082
+    {
2083
+      int len = strlen(gateway) + 1 + strlen(r6->iface)+1;
2084
+      char * tmp = gc_malloc( len, true, &gc );
2085
+      snprintf( tmp, len, "%s%%%s", gateway, r6->iface );
2086
+      gateway = tmp;
2087
+    }
2088
+#endif
2089
+
2072 2090
   if ( !tt->ipv6 )
2073 2091
     {
2074 2092
       msg( M_INFO, "delete_route_ipv6(): not deleting %s/%d, no IPv6 on if %s",
... ...
@@ -2973,14 +3009,14 @@ struct rtmsg {
2973 2973
 #if defined(TARGET_SOLARIS)
2974 2974
 #define NEXTADDR(w, u) \
2975 2975
         if (rtm_addrs & (w)) {\
2976
-            l = ROUNDUP(sizeof(struct sockaddr_in)); memmove(cp, &(u), l); cp += l;\
2976
+            l = ROUNDUP(sizeof(u)); memmove(cp, &(u), l); cp += l;\
2977 2977
         }
2978 2978
 
2979 2979
 #define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr_in)))
2980 2980
 #else
2981 2981
 #define NEXTADDR(w, u) \
2982 2982
         if (rtm_addrs & (w)) {\
2983
-            l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\
2983
+            l = ROUNDUP( ((struct sockaddr *)&(u))->sa_len); memmove(cp, &(u), l); cp += l;\
2984 2984
         }
2985 2985
 
2986 2986
 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
... ...
@@ -3177,14 +3213,157 @@ get_default_gateway (struct route_gateway_info *rgi)
3177 3177
   gc_free (&gc);
3178 3178
 }
3179 3179
 
3180
-/* BSD implementation using routing socket (as does IPv4) (TBD)
3180
+/* BSD implementation using routing socket (as does IPv4)
3181
+ * (the code duplication is somewhat unavoidable if we want this to
3182
+ * work on OpenSolaris as well.  *sigh*)
3181 3183
  */
3184
+
3185
+/* Solaris has no length field - this is ugly, but less #ifdef in total
3186
+ */
3187
+#if defined(TARGET_SOLARIS)
3188
+# undef ADVANCE
3189
+# define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr_in6)))
3190
+#endif
3191
+
3182 3192
 void
3183 3193
 get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
3184 3194
 			 const struct in6_addr *dest)
3185 3195
 {
3186
-    msg(D_ROUTE, "no support for get_default_gateway_ipv6() on this system (BSD and OSX)");
3196
+
3197
+    struct rtmsg m_rtmsg;
3198
+    int sockfd = -1;
3199
+    int seq, l, pid, rtm_addrs, i;
3200
+    struct sockaddr_in6 so_dst, so_mask;
3201
+    char *cp = m_rtmsg.m_space;
3202
+    struct sockaddr *gate = NULL, *ifp = NULL, *sa;
3203
+    struct rt_msghdr *rtm_aux;
3204
+
3187 3205
     CLEAR(*rgi6);
3206
+
3207
+    /* setup data to send to routing socket */
3208
+    pid = getpid();
3209
+    seq = 0;
3210
+    rtm_addrs = RTA_DST | RTA_NETMASK | RTA_IFP;
3211
+
3212
+    bzero(&m_rtmsg, sizeof(m_rtmsg));
3213
+    bzero(&so_dst, sizeof(so_dst));
3214
+    bzero(&so_mask, sizeof(so_mask));
3215
+    bzero(&rtm, sizeof(struct rt_msghdr));
3216
+
3217
+    rtm.rtm_type = RTM_GET;
3218
+    rtm.rtm_flags = RTF_UP;
3219
+    rtm.rtm_version = RTM_VERSION;
3220
+    rtm.rtm_seq = ++seq;
3221
+
3222
+    so_dst.sin6_family = AF_INET6;
3223
+    so_mask.sin6_family = AF_INET6;
3224
+
3225
+    if ( dest != NULL &&		/* specific host? */
3226
+	 !IN6_IS_ADDR_UNSPECIFIED(dest) )
3227
+    {
3228
+	so_dst.sin6_addr = *dest;
3229
+	/* :: needs /0 "netmask", host route wants "no netmask */
3230
+	rtm_addrs &= ~RTA_NETMASK;
3231
+    }
3232
+
3233
+    rtm.rtm_addrs = rtm_addrs;
3234
+
3235
+#ifndef TARGET_SOLARIS
3236
+    so_dst.sin6_len = sizeof(struct sockaddr_in6);
3237
+    so_mask.sin6_len = sizeof(struct sockaddr_in6);
3238
+#endif
3239
+
3240
+    NEXTADDR(RTA_DST, so_dst);
3241
+    NEXTADDR(RTA_NETMASK, so_mask);
3242
+
3243
+    rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
3244
+
3245
+    /* transact with routing socket */
3246
+    sockfd = socket(PF_ROUTE, SOCK_RAW, 0);
3247
+    if (sockfd < 0)
3248
+    {
3249
+	msg (M_WARN, "GDG6: socket #1 failed");
3250
+	goto done;
3251
+    }
3252
+    if (write(sockfd, (char *)&m_rtmsg, l) < 0)
3253
+    {
3254
+	msg (M_WARN, "GDG6: problem writing to routing socket");
3255
+	goto done;
3256
+    }
3257
+
3258
+    do
3259
+    {
3260
+	l = read(sockfd, (char *)&m_rtmsg, sizeof(m_rtmsg));
3261
+    }
3262
+    while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
3263
+
3264
+    close(sockfd);
3265
+    sockfd = -1;
3266
+
3267
+    /* extract return data from routing socket */
3268
+    rtm_aux = &rtm;
3269
+    cp = ((char *)(rtm_aux + 1));
3270
+    if (rtm_aux->rtm_addrs)
3271
+    {
3272
+	for (i = 1; i; i <<= 1)
3273
+	{
3274
+	    if (i & rtm_aux->rtm_addrs)
3275
+	    {
3276
+		sa = (struct sockaddr *)cp;
3277
+		if (i == RTA_GATEWAY )
3278
+		    gate = sa;
3279
+		else if (i == RTA_IFP)
3280
+		    ifp = sa;
3281
+		ADVANCE(cp, sa);
3282
+	    }
3283
+	}
3284
+    }
3285
+    else
3286
+      goto done;
3287
+
3288
+    /* get gateway addr and interface name */
3289
+    if (gate != NULL )
3290
+    {
3291
+	struct sockaddr_in6 * s6 = (struct sockaddr_in6 *)gate;
3292
+	struct in6_addr gw = s6->sin6_addr;
3293
+
3294
+#ifndef TARGET_SOLARIS
3295
+	/* You do not really want to know... from FreeBSD's route.c
3296
+	 * (KAME encodes the 16 bit scope_id in s6_addr[2] + [3],
3297
+	 * but for a correct link-local address these must be :0000: )
3298
+	 */
3299
+	if ( gate->sa_len == sizeof(struct sockaddr_in6) &&
3300
+	     IN6_IS_ADDR_LINKLOCAL(&gw) )
3301
+        {
3302
+	    gw.s6_addr[2] = gw.s6_addr[3] = 0;
3303
+	}
3304
+
3305
+	if ( gate->sa_len != sizeof(struct sockaddr_in6) ||
3306
+	     IN6_IS_ADDR_UNSPECIFIED(&gw) )
3307
+	{
3308
+	    rgi6->flags |= RGI_ON_LINK;
3309
+	}
3310
+	else
3311
+#endif
3312
+
3313
+	rgi6->gateway.addr_ipv6 = gw;
3314
+	rgi6->flags |= RGI_ADDR_DEFINED;
3315
+
3316
+	if (ifp)
3317
+	{
3318
+	    /* get interface name */
3319
+	    const struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp;
3320
+	    if (adl->sdl_nlen && adl->sdl_nlen < sizeof(rgi6->iface))
3321
+	    {
3322
+		memcpy (rgi6->iface, adl->sdl_data, adl->sdl_nlen);
3323
+		rgi6->flags |= RGI_IFACE_DEFINED;
3324
+	    }
3325
+	}
3326
+    }
3327
+
3328
+  done:
3329
+    if (sockfd >= 0)
3330
+	close(sockfd);
3188 3331
 }
3189 3332
 
3190 3333
 #undef max