From owner-svn-src-head@freebsd.org Wed Jan 6 00:00:13 2016 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 2682AA63C9A; Wed, 6 Jan 2016 00:00:13 +0000 (UTC) (envelope-from asomers@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id B5CAA110A; Wed, 6 Jan 2016 00:00:12 +0000 (UTC) (envelope-from asomers@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u0600Ba4021623; Wed, 6 Jan 2016 00:00:11 GMT (envelope-from asomers@FreeBSD.org) Received: (from asomers@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u0600Bat021616; Wed, 6 Jan 2016 00:00:11 GMT (envelope-from asomers@FreeBSD.org) Message-Id: <201601060000.u0600Bat021616@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: asomers set sender to asomers@FreeBSD.org using -f From: Alan Somers Date: Wed, 6 Jan 2016 00:00:11 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r293229 - in head: etc/mtree usr.sbin/rpcbind usr.sbin/rpcbind/tests X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 06 Jan 2016 00:00:13 -0000 Author: asomers Date: Wed Jan 6 00:00:11 2016 New Revision: 293229 URL: https://svnweb.freebsd.org/changeset/base/293229 Log: "source routing" in rpcbind Fix a bug in rpcbind for multihomed hosts. If the server had interfaces on two separate subnets, and a client on the first subnet contacted rpcbind at the address on the second subnet, rpcbind would advertise addresses on the first subnet. This is a bug, because it should prefer to advertise the address where it was contacted. The requested service might be firewalled off from the address on the first subnet, for example. usr.sbin/rpcbind/check_bound.c If the address on which a request was received is known, pass that to addrmerge as the clnt_uaddr parameter. That is what addrmerge's comment indicates the parameter is supposed to mean. The previous behavior is that clnt_uaddr would contain the address from which the client sent the request. usr.sbin/rpcbind/util.c Modify addrmerge to prefer to use an IP that is equal to clnt_uaddr, if one is found. Refactor the relevant portion of the function for clarity, and to reduce the number of ifdefs. etc/mtree/BSD.tests.dist usr.sbin/rpcbind/tests/Makefile usr.sbin/rpcbind/tests/addrmerge_test.c Add unit tests for usr.sbin/rpcbind/util.c:addrmerge. usr.sbin/rpcbind/check_bound.c usr.sbin/rpcbind/rpcbind.h usr.sbin/rpcbind/util.c Constify some function arguments Reviewed by: imp MFC after: 4 weeks Sponsored by: Spectra Logic Corp Differential Revision: https://reviews.freebsd.org/D4690 Added: head/usr.sbin/rpcbind/tests/ head/usr.sbin/rpcbind/tests/Makefile (contents, props changed) head/usr.sbin/rpcbind/tests/addrmerge_test.c (contents, props changed) Modified: head/etc/mtree/BSD.tests.dist head/usr.sbin/rpcbind/Makefile head/usr.sbin/rpcbind/check_bound.c head/usr.sbin/rpcbind/rpcbind.h head/usr.sbin/rpcbind/util.c Modified: head/etc/mtree/BSD.tests.dist ============================================================================== --- head/etc/mtree/BSD.tests.dist Tue Jan 5 22:27:34 2016 (r293228) +++ head/etc/mtree/BSD.tests.dist Wed Jan 6 00:00:11 2016 (r293229) @@ -622,6 +622,8 @@ .. pw .. + rpcbind + .. sa .. .. Modified: head/usr.sbin/rpcbind/Makefile ============================================================================== --- head/usr.sbin/rpcbind/Makefile Tue Jan 5 22:27:34 2016 (r293228) +++ head/usr.sbin/rpcbind/Makefile Wed Jan 6 00:00:11 2016 (r293229) @@ -14,6 +14,10 @@ CFLAGS+= -DPORTMAP -DLIBWRAP CFLAGS+= -DINET6 .endif +.if ${MK_TESTS} != "no" +SUBDIR+= tests +.endif + WARNS?= 1 LIBADD= wrap Modified: head/usr.sbin/rpcbind/check_bound.c ============================================================================== --- head/usr.sbin/rpcbind/check_bound.c Tue Jan 5 22:27:34 2016 (r293228) +++ head/usr.sbin/rpcbind/check_bound.c Wed Jan 6 00:00:11 2016 (r293229) @@ -50,6 +50,7 @@ static char sccsid[] = "@(#)check_bound. #include #include #include +#include #include #include #include @@ -159,6 +160,7 @@ char * mergeaddr(SVCXPRT *xprt, char *netid, char *uaddr, char *saddr) { struct fdlist *fdl; + struct svc_dg_data *dg_data; char *c_uaddr, *s_uaddr, *m_uaddr, *allocated_uaddr = NULL; for (fdl = fdhead; fdl; fdl = fdl->next) @@ -170,11 +172,20 @@ mergeaddr(SVCXPRT *xprt, char *netid, ch /* that server died */ return (nullstring); /* + * Try to determine the local address on which the client contacted us, + * so we can send a reply from the same address. If it's unknown, then + * try to determine which address the client used, and pick a nearby + * local address. + * * If saddr is not NULL, the remote client may have included the * address by which it contacted us. Use that for the "client" uaddr, * otherwise use the info from the SVCXPRT. */ - if (saddr != NULL) { + dg_data = (struct svc_dg_data*)xprt->xp_p2; + if (dg_data != NULL && dg_data->su_srcaddr.buf != NULL) { + c_uaddr = taddr2uaddr(fdl->nconf, &dg_data->su_srcaddr); + } + else if (saddr != NULL) { c_uaddr = saddr; } else { c_uaddr = taddr2uaddr(fdl->nconf, svc_getrpccaller(xprt)); @@ -217,7 +228,7 @@ mergeaddr(SVCXPRT *xprt, char *netid, ch * structure should not be freed. */ struct netconfig * -rpcbind_get_conf(char *netid) +rpcbind_get_conf(const char *netid) { struct fdlist *fdl; Modified: head/usr.sbin/rpcbind/rpcbind.h ============================================================================== --- head/usr.sbin/rpcbind/rpcbind.h Tue Jan 5 22:27:34 2016 (r293228) +++ head/usr.sbin/rpcbind/rpcbind.h Wed Jan 6 00:00:11 2016 (r293229) @@ -85,7 +85,7 @@ extern char *tcp_uaddr; /* Universal TC int add_bndlist(struct netconfig *, struct netbuf *); bool_t is_bound(char *, char *); char *mergeaddr(SVCXPRT *, char *, char *, char *); -struct netconfig *rpcbind_get_conf(char *); +struct netconfig *rpcbind_get_conf(const char *); void rpcbs_init(void); void rpcbs_procinfo(rpcvers_t, rpcproc_t); @@ -134,8 +134,8 @@ extern void pmap_service(struct svc_req void write_warmstart(void); void read_warmstart(void); -char *addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr, - char *netid); +char *addrmerge(struct netbuf *caller, const char *serv_uaddr, + const char *clnt_uaddr, char const *netid); int listen_addr(const struct sockaddr *sa); void network_init(void); struct sockaddr *local_sa(int); Added: head/usr.sbin/rpcbind/tests/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/rpcbind/tests/Makefile Wed Jan 6 00:00:11 2016 (r293229) @@ -0,0 +1,17 @@ +# $FreeBSD$ + +.include + +.PATH: ${.CURDIR}/.. + +ATF_TESTS_C= addrmerge_test +CFLAGS+= -I${.CURDIR}/.. -Wno-cast-qual +SRCS.addrmerge_test= addrmerge_test.c util.c + +.if ${MK_INET6_SUPPORT} != "no" +CFLAGS+= -DINET6 +.endif + +WARNS?= 3 + +.include Added: head/usr.sbin/rpcbind/tests/addrmerge_test.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/rpcbind/tests/addrmerge_test.c Wed Jan 6 00:00:11 2016 (r293229) @@ -0,0 +1,849 @@ +/*- + * Copyright (c) 2014 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * $FreeBSD$ + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "rpcbind.h" + +#define MAX_IFADDRS 16 + +int debugging = false; + +/* Data for mocking getifaddrs */ +struct ifaddr_storage { + struct ifaddrs ifaddr; + struct sockaddr_storage addr; + struct sockaddr_storage mask; + struct sockaddr_storage bcast; +} mock_ifaddr_storage[MAX_IFADDRS]; +struct ifaddrs *mock_ifaddrs = NULL; +int ifaddr_count = 0; + +/* Data for mocking listen_addr */ +int bind_address_count = 0; +struct sockaddr* bind_addresses[MAX_IFADDRS]; + +/* Stub library functions */ +void +freeifaddrs(struct ifaddrs *ifp __unused) +{ + return ; +} + +int +getifaddrs(struct ifaddrs **ifap) +{ + *ifap = mock_ifaddrs; + return (0); +} + +static void +mock_ifaddr4(const char* name, const char* addr, const char* mask, + const char* bcast, unsigned int flags, bool bind) +{ + struct ifaddrs *ifaddr = &mock_ifaddr_storage[ifaddr_count].ifaddr; + struct sockaddr_in *in = (struct sockaddr_in*) + &mock_ifaddr_storage[ifaddr_count].addr; + struct sockaddr_in *mask_in = (struct sockaddr_in*) + &mock_ifaddr_storage[ifaddr_count].mask; + struct sockaddr_in *bcast_in = (struct sockaddr_in*) + &mock_ifaddr_storage[ifaddr_count].bcast; + + in->sin_family = AF_INET; + in->sin_port = 0; + in->sin_len = sizeof(in); + in->sin_addr.s_addr = inet_addr(addr); + mask_in->sin_family = AF_INET; + mask_in->sin_port = 0; + mask_in->sin_len = sizeof(mask_in); + mask_in->sin_addr.s_addr = inet_addr(mask); + bcast_in->sin_family = AF_INET; + bcast_in->sin_port = 0; + bcast_in->sin_len = sizeof(bcast_in); + bcast_in->sin_addr.s_addr = inet_addr(bcast); + *ifaddr = (struct ifaddrs) { + .ifa_next = NULL, + .ifa_name = (char*) name, + .ifa_flags = flags, + .ifa_addr = (struct sockaddr*) in, + .ifa_netmask = (struct sockaddr*) mask_in, + .ifa_broadaddr = (struct sockaddr*) bcast_in, + .ifa_data = NULL, /* addrmerge doesn't care*/ + }; + + if (ifaddr_count > 0) + mock_ifaddr_storage[ifaddr_count - 1].ifaddr.ifa_next = ifaddr; + ifaddr_count++; + mock_ifaddrs = &mock_ifaddr_storage[0].ifaddr; + + /* Optionally simulate binding an ip ala "rpcbind -h foo" */ + if (bind) { + bind_addresses[bind_address_count] = (struct sockaddr*)in; + bind_address_count++; + } +} + +#ifdef INET6 +static void +mock_ifaddr6(const char* name, const char* addr, const char* mask, + const char* bcast, unsigned int flags, uint32_t scope_id, bool bind) +{ + struct ifaddrs *ifaddr = &mock_ifaddr_storage[ifaddr_count].ifaddr; + struct sockaddr_in6 *in6 = (struct sockaddr_in6*) + &mock_ifaddr_storage[ifaddr_count].addr; + struct sockaddr_in6 *mask_in6 = (struct sockaddr_in6*) + &mock_ifaddr_storage[ifaddr_count].mask; + struct sockaddr_in6 *bcast_in6 = (struct sockaddr_in6*) + &mock_ifaddr_storage[ifaddr_count].bcast; + + in6->sin6_family = AF_INET6; + in6->sin6_port = 0; + in6->sin6_len = sizeof(*in6); + in6->sin6_scope_id = scope_id; + ATF_REQUIRE_EQ(1, inet_pton(AF_INET6, addr, (void*)&in6->sin6_addr)); + mask_in6->sin6_family = AF_INET6; + mask_in6->sin6_port = 0; + mask_in6->sin6_len = sizeof(*mask_in6); + mask_in6->sin6_scope_id = scope_id; + ATF_REQUIRE_EQ(1, inet_pton(AF_INET6, mask, + (void*)&mask_in6->sin6_addr)); + bcast_in6->sin6_family = AF_INET6; + bcast_in6->sin6_port = 0; + bcast_in6->sin6_len = sizeof(*bcast_in6); + bcast_in6->sin6_scope_id = scope_id; + ATF_REQUIRE_EQ(1, inet_pton(AF_INET6, bcast, + (void*)&bcast_in6->sin6_addr)); + *ifaddr = (struct ifaddrs) { + .ifa_next = NULL, + .ifa_name = (char*) name, + .ifa_flags = flags, + .ifa_addr = (struct sockaddr*) in6, + .ifa_netmask = (struct sockaddr*) mask_in6, + .ifa_broadaddr = (struct sockaddr*) bcast_in6, + .ifa_data = NULL, /* addrmerge doesn't care*/ + }; + + if (ifaddr_count > 0) + mock_ifaddr_storage[ifaddr_count - 1].ifaddr.ifa_next = ifaddr; + ifaddr_count++; + mock_ifaddrs = &mock_ifaddr_storage[0].ifaddr; + + /* Optionally simulate binding an ip ala "rpcbind -h foo" */ + if (bind) { + bind_addresses[bind_address_count] = (struct sockaddr*)in6; + bind_address_count++; + } +} +#else +static void +mock_ifaddr6(const char* name __unused, const char* addr __unused, + const char* mask __unused, const char* bcast __unused, + unsigned int flags __unused, uint32_t scope_id __unused, bool bind __unused) +{ +} +#endif /*INET6 */ + +static void +mock_lo0(void) +{ + /* + * This broadcast address looks wrong, but it's what getifaddrs(2) + * actually returns. It's invalid because IFF_BROADCAST is not set + */ + mock_ifaddr4("lo0", "127.0.0.1", "255.0.0.0", "127.0.0.1", + IFF_LOOPBACK | IFF_UP | IFF_RUNNING | IFF_MULTICAST, false); + mock_ifaddr6("lo0", "::1", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + "::1", + IFF_LOOPBACK | IFF_UP | IFF_RUNNING | IFF_MULTICAST, 0, false); +} + +static void +mock_igb0(void) +{ + mock_ifaddr4("igb0", "192.0.2.2", "255.255.255.128", "192.0.2.127", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + false); + mock_ifaddr6("igb0", "2001:db8::2", "ffff:ffff:ffff:ffff::", + "2001:db8::ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + 0, false); + /* Link local address */ + mock_ifaddr6("igb0", "fe80::2", "ffff:ffff:ffff:ffff::", + "fe80::ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + 2, false); +} + +/* On the same subnet as igb0 */ +static void +mock_igb1(bool bind) +{ + mock_ifaddr4("igb1", "192.0.2.3", "255.255.255.128", "192.0.2.127", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + bind); + mock_ifaddr6("igb1", "2001:db8::3", "ffff:ffff:ffff:ffff::", + "2001:db8::ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + 0, bind); + /* Link local address */ + mock_ifaddr6("igb1", "fe80::3", "ffff:ffff:ffff:ffff::", + "fe80::ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + 3, bind); +} + +/* igb2 is on a different subnet than igb0 */ +static void +mock_igb2(void) +{ + mock_ifaddr4("igb2", "192.0.2.130", "255.255.255.128", "192.0.2.255", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + false); + mock_ifaddr6("igb2", "2001:db8:1::2", "ffff:ffff:ffff:ffff::", + "2001:db8:1:0:ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + 0, false); +} + +/* tun0 is a P2P interface */ +static void +mock_tun0(void) +{ + mock_ifaddr4("tun0", "192.0.2.5", "255.255.255.255", "192.0.2.6", + IFF_UP | IFF_RUNNING | IFF_POINTOPOINT | IFF_MULTICAST, false); + mock_ifaddr6("tun0", "2001:db8::5", + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + "2001:db8::6", + IFF_UP | IFF_RUNNING | IFF_POINTOPOINT | IFF_MULTICAST, 0, false); +} + + +/* Stub rpcbind functions */ +int +listen_addr(const struct sockaddr *sa) +{ + int i; + + if (bind_address_count == 0) + return (1); + + for (i = 0; i < bind_address_count; i++) { + if (bind_addresses[i]->sa_family != sa->sa_family) + continue; + + if (0 == memcmp(bind_addresses[i]->sa_data, sa->sa_data, + sa->sa_len)) + return (1); + } + return (0); +} + +struct netconfig* +rpcbind_get_conf(const char* netid __unused) +{ + /* Use static variables so we can return pointers to them */ + static char* lookups = NULL; + static struct netconfig nconf_udp; +#ifdef INET6 + static struct netconfig nconf_udp6; +#endif /* INET6 */ + + nconf_udp.nc_netid = "udp"; //netid_storage; + nconf_udp.nc_semantics = NC_TPI_CLTS; + nconf_udp.nc_flag = NC_VISIBLE; + nconf_udp.nc_protofmly = (char*)"inet"; + nconf_udp.nc_proto = (char*)"udp"; + nconf_udp.nc_device = (char*)"-"; + nconf_udp.nc_nlookups = 0; + nconf_udp.nc_lookups = &lookups; + +#ifdef INET6 + nconf_udp6.nc_netid = "udp6"; //netid_storage; + nconf_udp6.nc_semantics = NC_TPI_CLTS; + nconf_udp6.nc_flag = NC_VISIBLE; + nconf_udp6.nc_protofmly = (char*)"inet6"; + nconf_udp6.nc_proto = (char*)"udp6"; + nconf_udp6.nc_device = (char*)"-"; + nconf_udp6.nc_nlookups = 0; + nconf_udp6.nc_lookups = &lookups; +#endif /* INET6 */ + + if (0 == strncmp("udp", netid, sizeof("udp"))) + return (&nconf_udp); +#ifdef INET6 + else if (0 == strncmp("udp6", netid, sizeof("udp6"))) + return (&nconf_udp6); +#endif /* INET6 */ + else + return (NULL); +} + +/* + * Helper function used by most test cases + * param recvdstaddr If non-null, the uaddr on which the request was received + */ +static char* +do_addrmerge4(const char* recvdstaddr) +{ + struct netbuf caller; + struct sockaddr_in caller_in; + const char *serv_uaddr, *clnt_uaddr, *netid; + + /* caller contains the client's IP address */ + caller.maxlen = sizeof(struct sockaddr_storage); + caller.len = sizeof(caller_in); + caller_in.sin_family = AF_INET; + caller_in.sin_len = sizeof(caller_in); + caller_in.sin_port = 1234; + caller_in.sin_addr.s_addr = inet_addr("192.0.2.1"); + caller.buf = (void*)&caller_in; + if (recvdstaddr != NULL) + clnt_uaddr = recvdstaddr; + else + clnt_uaddr = "192.0.2.1.3.46"; + + /* assume server is bound in INADDR_ANY port 814 */ + serv_uaddr = "0.0.0.0.3.46"; + + netid = "udp"; + return (addrmerge(&caller, serv_uaddr, clnt_uaddr, netid)); +} + +#ifdef INET6 +/* + * Variant of do_addrmerge4 where the caller has an IPv6 address + * param recvdstaddr If non-null, the uaddr on which the request was received + */ +static char* +do_addrmerge6(const char* recvdstaddr) +{ + struct netbuf caller; + struct sockaddr_in6 caller_in6; + const char *serv_uaddr, *clnt_uaddr, *netid; + + /* caller contains the client's IP address */ + caller.maxlen = sizeof(struct sockaddr_storage); + caller.len = sizeof(caller_in6); + caller_in6.sin6_family = AF_INET6; + caller_in6.sin6_len = sizeof(caller_in6); + caller_in6.sin6_port = 1234; + ATF_REQUIRE_EQ(1, inet_pton(AF_INET6, "2001:db8::1", + (void*)&caller_in6.sin6_addr)); + caller.buf = (void*)&caller_in6; + if (recvdstaddr != NULL) + clnt_uaddr = recvdstaddr; + else + clnt_uaddr = "2001:db8::1.3.46"; + + /* assume server is bound in INADDR_ANY port 814 */ + serv_uaddr = "::1.3.46"; + + netid = "udp6"; + return (addrmerge(&caller, serv_uaddr, clnt_uaddr, netid)); +} + +/* Variant of do_addrmerge6 where the caller uses a link local address */ +static char* +do_addrmerge6_ll(void) +{ + struct netbuf caller; + struct sockaddr_in6 caller_in6; + const char *serv_uaddr, *clnt_uaddr, *netid; + + /* caller contains the client's IP address */ + caller.maxlen = sizeof(struct sockaddr_storage); + caller.len = sizeof(caller_in6); + caller_in6.sin6_family = AF_INET6; + caller_in6.sin6_len = sizeof(caller_in6); + caller_in6.sin6_port = 1234; + caller_in6.sin6_scope_id = 2; /* same as igb0 */ + ATF_REQUIRE_EQ(1, inet_pton(AF_INET6, "fe80::beef", + (void*)&caller_in6.sin6_addr)); + caller.buf = (void*)&caller_in6; + clnt_uaddr = "fe80::beef.3.46"; + + /* assume server is bound in INADDR_ANY port 814 */ + serv_uaddr = "::1.3.46"; + + netid = "udp6"; + return (addrmerge(&caller, serv_uaddr, clnt_uaddr, netid)); +} +#endif /* INET6 */ + +ATF_TC_WITHOUT_HEAD(addrmerge_noifaddrs); +ATF_TC_BODY(addrmerge_noifaddrs, tc) +{ + char* maddr; + + maddr = do_addrmerge4(NULL); + + /* Since getifaddrs returns null, addrmerge must too */ + ATF_CHECK_EQ(NULL, maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_localhost_only); +ATF_TC_BODY(addrmerge_localhost_only, tc) +{ + char *maddr; + + /* getifaddrs will return localhost only */ + mock_lo0(); + + maddr = do_addrmerge4(NULL); + + /* We must return localhost if there is nothing better */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("127.0.0.1.3.46", maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_singlehomed); +ATF_TC_BODY(addrmerge_singlehomed, tc) +{ + char *maddr; + + /* getifaddrs will return one public address */ + mock_lo0(); + mock_igb0(); + + maddr = do_addrmerge4(NULL); + + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.2.3.46", maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_one_addr_on_each_subnet); +ATF_TC_BODY(addrmerge_one_addr_on_each_subnet, tc) +{ + char *maddr; + + mock_lo0(); + mock_igb0(); + mock_igb2(); + + maddr = do_addrmerge4(NULL); + + /* We must return the address on the caller's subnet */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.2.3.46", maddr); +} + + +/* + * Like addrmerge_one_addr_on_each_subnet, but getifaddrs returns a different + * order + */ +ATF_TC_WITHOUT_HEAD(addrmerge_one_addr_on_each_subnet_rev); +ATF_TC_BODY(addrmerge_one_addr_on_each_subnet_rev, tc) +{ + char *maddr; + + /* getifaddrs will return one public address on each of two subnets */ + mock_igb2(); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge4(NULL); + + /* We must return the address on the caller's subnet */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.2.3.46", maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_point2point); +ATF_TC_BODY(addrmerge_point2point, tc) +{ + char *maddr; + + /* getifaddrs will return one normal and one p2p address */ + mock_lo0(); + mock_igb2(); + mock_tun0(); + + maddr = do_addrmerge4(NULL); + + /* addrmerge should disprefer P2P interfaces */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.130.3.46", maddr); +} + +/* Like addrerge_point2point, but getifaddrs returns a different order */ +ATF_TC_WITHOUT_HEAD(addrmerge_point2point_rev); +ATF_TC_BODY(addrmerge_point2point_rev, tc) +{ + char *maddr; + + /* getifaddrs will return one normal and one p2p address */ + mock_tun0(); + mock_igb2(); + mock_lo0(); + + maddr = do_addrmerge4(NULL); + + /* addrmerge should disprefer P2P interfaces */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.130.3.46", maddr); +} + +/* + * Simulate using rpcbind -h to select just one ip when the subnet has + * multiple + */ +ATF_TC_WITHOUT_HEAD(addrmerge_bindip); +ATF_TC_BODY(addrmerge_bindip, tc) +{ + char *maddr; + + /* getifaddrs will return one public address on each of two subnets */ + mock_lo0(); + mock_igb0(); + mock_igb1(true); + + maddr = do_addrmerge4(NULL); + + /* We must return the address to which we are bound */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.3.3.46", maddr); +} + +/* Like addrmerge_bindip, but getifaddrs returns a different order */ +ATF_TC_WITHOUT_HEAD(addrmerge_bindip_rev); +ATF_TC_BODY(addrmerge_bindip_rev, tc) +{ + char *maddr; + + /* getifaddrs will return one public address on each of two subnets */ + mock_igb1(true); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge4(NULL); + + /* We must return the address to which we are bound */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.3.3.46", maddr); +} + +/* + * The address on which the request was received is known, and is provided as + * the hint. + */ +ATF_TC_WITHOUT_HEAD(addrmerge_recvdstaddr); +ATF_TC_BODY(addrmerge_recvdstaddr, tc) +{ + char *maddr; + + mock_lo0(); + mock_igb0(); + mock_igb1(false); + + maddr = do_addrmerge4("192.0.2.2.3.46"); + + /* We must return the address on which the request was received */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.2.3.46", maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_recvdstaddr_rev); +ATF_TC_BODY(addrmerge_recvdstaddr_rev, tc) +{ + char *maddr; + + mock_igb1(false); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge4("192.0.2.2.3.46"); + + /* We must return the address on which the request was received */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.2.3.46", maddr); +} + +#ifdef INET6 +ATF_TC_WITHOUT_HEAD(addrmerge_localhost_only6); +ATF_TC_BODY(addrmerge_localhost_only6, tc) +{ + char *maddr; + + /* getifaddrs will return localhost only */ + mock_lo0(); + + maddr = do_addrmerge6(NULL); + + /* We must return localhost if there is nothing better */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("::1.3.46", maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_singlehomed6); +ATF_TC_BODY(addrmerge_singlehomed6, tc) +{ + char *maddr; + + /* getifaddrs will return one public address */ + mock_lo0(); + mock_igb0(); + + maddr = do_addrmerge6(NULL); + + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::2.3.46", maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_one_addr_on_each_subnet6); +ATF_TC_BODY(addrmerge_one_addr_on_each_subnet6, tc) +{ + char *maddr; + + mock_lo0(); + mock_igb0(); + mock_igb2(); + + maddr = do_addrmerge6(NULL); + + /* We must return the address on the caller's subnet */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::2.3.46", maddr); +} + + +/* + * Like addrmerge_one_addr_on_each_subnet6, but getifaddrs returns a different + * order + */ +ATF_TC_WITHOUT_HEAD(addrmerge_one_addr_on_each_subnet6_rev); +ATF_TC_BODY(addrmerge_one_addr_on_each_subnet6_rev, tc) +{ + char *maddr; + + /* getifaddrs will return one public address on each of two subnets */ + mock_igb2(); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge6(NULL); + + /* We must return the address on the caller's subnet */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::2.3.46", maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_point2point6); +ATF_TC_BODY(addrmerge_point2point6, tc) +{ + char *maddr; + + /* getifaddrs will return one normal and one p2p address */ + mock_lo0(); + mock_igb2(); + mock_tun0(); + + maddr = do_addrmerge6(NULL); + + /* addrmerge should disprefer P2P interfaces */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8:1::2.3.46", maddr); +} + +/* Like addrerge_point2point, but getifaddrs returns a different order */ +ATF_TC_WITHOUT_HEAD(addrmerge_point2point6_rev); +ATF_TC_BODY(addrmerge_point2point6_rev, tc) +{ + char *maddr; + + /* getifaddrs will return one normal and one p2p address */ + mock_tun0(); + mock_igb2(); + mock_lo0(); + + maddr = do_addrmerge6(NULL); + + /* addrmerge should disprefer P2P interfaces */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8:1::2.3.46", maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_bindip6); +ATF_TC_BODY(addrmerge_bindip6, tc) +{ + char *maddr; + + /* getifaddrs will return one public address on each of two subnets */ + mock_lo0(); + mock_igb0(); + mock_igb1(true); + + maddr = do_addrmerge6(NULL); + + /* We must return the address to which we are bound */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::3.3.46", maddr); +} + +/* Like addrerge_bindip, but getifaddrs returns a different order */ +ATF_TC_WITHOUT_HEAD(addrmerge_bindip6_rev); +ATF_TC_BODY(addrmerge_bindip6_rev, tc) +{ + char *maddr; + + /* getifaddrs will return one public address on each of two subnets */ + mock_igb1(true); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge6(NULL); + + /* We must return the address to which we are bound */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::3.3.46", maddr); +} + +/* + * IPv6 Link Local addresses with the same scope id as the caller, if the caller + * is also a link local address, should be preferred + */ +ATF_TC_WITHOUT_HEAD(addrmerge_ipv6_linklocal); +ATF_TC_BODY(addrmerge_ipv6_linklocal, tc) +{ + char *maddr; + + /* + * getifaddrs will return two link local addresses with the same netmask + * and prefix but different scope IDs + */ + mock_igb1(false); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge6_ll(); + + /* We must return the address to which we are bound */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("fe80::2.3.46", maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_ipv6_linklocal_rev); +ATF_TC_BODY(addrmerge_ipv6_linklocal_rev, tc) +{ + char *maddr; + + /* + * getifaddrs will return two link local addresses with the same netmask + * and prefix but different scope IDs + */ + mock_lo0(); + mock_igb0(); + mock_igb1(false); + + maddr = do_addrmerge6_ll(); + + /* We must return the address to which we are bound */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("fe80::2.3.46", maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_recvdstaddr6); +ATF_TC_BODY(addrmerge_recvdstaddr6, tc) +{ + char *maddr; + + mock_lo0(); + mock_igb0(); + mock_igb1(false); + + maddr = do_addrmerge6("2001:db8::2.3.46"); + + /* We must return the address on which the request was received */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::2.3.46", maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_recvdstaddr6_rev); +ATF_TC_BODY(addrmerge_recvdstaddr6_rev, tc) +{ + char *maddr; + + mock_igb1(false); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge6("2001:db8::2.3.46"); + + /* We must return the address on which the request was received */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::2.3.46", maddr); +} +#endif /* INET6 */ + + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, addrmerge_noifaddrs); + ATF_TP_ADD_TC(tp, addrmerge_localhost_only); + ATF_TP_ADD_TC(tp, addrmerge_singlehomed); + ATF_TP_ADD_TC(tp, addrmerge_one_addr_on_each_subnet); + ATF_TP_ADD_TC(tp, addrmerge_one_addr_on_each_subnet_rev); + ATF_TP_ADD_TC(tp, addrmerge_point2point); + ATF_TP_ADD_TC(tp, addrmerge_point2point_rev); + ATF_TP_ADD_TC(tp, addrmerge_bindip); + ATF_TP_ADD_TC(tp, addrmerge_bindip_rev); + ATF_TP_ADD_TC(tp, addrmerge_recvdstaddr); + ATF_TP_ADD_TC(tp, addrmerge_recvdstaddr_rev); +#ifdef INET6 + ATF_TP_ADD_TC(tp, addrmerge_localhost_only6); + ATF_TP_ADD_TC(tp, addrmerge_singlehomed6); + ATF_TP_ADD_TC(tp, addrmerge_one_addr_on_each_subnet6); + ATF_TP_ADD_TC(tp, addrmerge_one_addr_on_each_subnet6_rev); + ATF_TP_ADD_TC(tp, addrmerge_point2point6); + ATF_TP_ADD_TC(tp, addrmerge_point2point6_rev); + ATF_TP_ADD_TC(tp, addrmerge_bindip6); + ATF_TP_ADD_TC(tp, addrmerge_bindip6_rev); + ATF_TP_ADD_TC(tp, addrmerge_ipv6_linklocal); + ATF_TP_ADD_TC(tp, addrmerge_ipv6_linklocal_rev); + ATF_TP_ADD_TC(tp, addrmerge_recvdstaddr6); + ATF_TP_ADD_TC(tp, addrmerge_recvdstaddr6_rev); +#endif + + return (atf_no_error()); +} Modified: head/usr.sbin/rpcbind/util.c ============================================================================== --- head/usr.sbin/rpcbind/util.c Tue Jan 5 22:27:34 2016 (r293228) +++ head/usr.sbin/rpcbind/util.c Wed Jan 6 00:00:11 2016 (r293229) @@ -56,7 +56,7 @@ static struct sockaddr_in *local_in4; static struct sockaddr_in6 *local_in6; #endif -static int bitmaskcmp(void *, void *, void *, int); +static int bitmaskcmp(struct sockaddr *, struct sockaddr *, struct sockaddr *); /* * For all bits set in "mask", compare the corresponding bits in @@ -64,10 +64,34 @@ static int bitmaskcmp(void *, void *, vo * match. */ static int -bitmaskcmp(void *dst, void *src, void *mask, int bytelen) +bitmaskcmp(struct sockaddr *dst, struct sockaddr *src, struct sockaddr *mask) { int i; - u_int8_t *p1 = dst, *p2 = src, *netmask = mask; + u_int8_t *p1, *p2, *netmask; + int bytelen; + + if (dst->sa_family != src->sa_family || + dst->sa_family != mask->sa_family) + return (1); + + switch (dst->sa_family) { + case AF_INET: + p1 = (uint8_t*) &SA2SINADDR(dst); + p2 = (uint8_t*) &SA2SINADDR(src); + netmask = (uint8_t*) &SA2SINADDR(mask); + bytelen = sizeof(struct in_addr); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***