diff options
Diffstat (limited to 'lib/sockopt.c')
-rw-r--r-- | lib/sockopt.c | 85 |
1 files changed, 60 insertions, 25 deletions
diff --git a/lib/sockopt.c b/lib/sockopt.c index e0027e88..2f01199a 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -22,6 +22,7 @@ #include <zebra.h> #include "log.h" #include "sockopt.h" +#include "sockunion.h" int setsockopt_so_recvbuf (int sock, int size) @@ -494,35 +495,69 @@ sockopt_iphdrincl_swab_systoh (struct ip *iph) iph->ip_id = ntohs(iph->ip_id); } -#if defined(HAVE_TCP_MD5SIG) int -sockopt_tcp_signature (int sock, struct sockaddr_in *sin, const char *password) +sockopt_tcp_signature (int sock, union sockunion *su, const char *password) { - int keylen = password ? strlen(password) : 0; - -#if defined(GNU_LINUX) - - struct tcp_md5sig md5sig; - - bzero ((char *)&md5sig, sizeof(md5sig)); - memcpy (&md5sig.tcpm_addr, sin, sizeof(*sin)); - md5sig.tcpm_keylen = keylen; - if (keylen) - memcpy (md5sig.tcpm_key, password, keylen); - - return setsockopt (sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig); - -#else /* !GNU_LINUX */ - - int enable = keylen ? (TCP_SIG_SPI_BASE + sin->sin_port) : 0; - +#if HAVE_DECL_TCP_MD5SIG +#ifndef GNU_LINUX /* * XXX Need to do PF_KEY operation here to add/remove an SA entry, * and add/remove an SP entry for this peer's packet flows also. */ - return setsockopt (sock, IPPROTO_TCP, TCP_MD5SIG, &enable, - sizeof(enable)); - -#endif /* !GNU_LINUX */ -} + int md5sig = password && *password ? 1 : 0; +#else + int keylen = password ? strlen (password) : 0; + struct tcp_md5sig md5sig; + union sockunion *su2, *susock; + int ret; + + /* Figure out whether the socket and the sockunion are the same family.. + * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think.. + */ + if (!(susock = sockunion_getsockname (sock))) + return -1; + + if (susock->sa.sa_family == su->sa.sa_family) + su2 = su; + else + { + /* oops.. */ + su2 = susock; + + if (su2->sa.sa_family == AF_INET) + { + sockunion_free (susock); + return -1; + }; + + /* If this does not work, then all users of this sockopt will need to + * differentiate between IPv4 and IPv6, and keep seperate sockets for + * each. + * + * Sadly, it doesn't seem to work at present. It's unknown whether + * this is a bug or not. + */ + if (su2->sa.sa_family == AF_INET6 + && su->sa.sa_family == AF_INET) + { + su2->sin6.sin6_family = AF_INET6; + /* V4Map the address */ + memset (&su2->sin6.sin6_addr, 0, sizeof (struct in6_addr)); + su2->sin6.sin6_addr.s6_addr32[2] = htonl(0xffff); + memcpy (&su2->sin6.sin6_addr.s6_addr32[3], &su->sin.sin_addr, 4); + } + } + + memset (&md5sig, 0, sizeof (md5sig)); + memcpy (&md5sig.tcpm_addr, su2, sizeof (*su2)); + md5sig.tcpm_keylen = keylen; + if (keylen) + memcpy (md5sig.tcpm_key, password, keylen); +#endif /* GNU_LINUX */ + ret = setsockopt (sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig); + sockunion_free (susock); + return ret; +#else /* HAVE_TCP_MD5SIG */ + return -2; #endif /* HAVE_TCP_MD5SIG */ +} |