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
... | ... |
@@ -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 |