diff --git a/usr.sbin/rpcbind/tests/addrmerge_test.c b/usr.sbin/rpcbind/tests/addrmerge_test.c index e1600f1a4837..a29dc30fdde8 100644 --- a/usr.sbin/rpcbind/tests/addrmerge_test.c +++ b/usr.sbin/rpcbind/tests/addrmerge_test.c @@ -1,870 +1,904 @@ /*- * 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 "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); } +static void +mock_mlxen0(void) +{ + mock_ifaddr4("mlxen0", "192.0.3.1", "255.255.255.128", "192.0.3.127", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + false); + /* Setting link local address before ipv6 address*/ + mock_ifaddr6("mlxen0", "fe80::4", "ffff:ffff:ffff:ffff::", + "fe80::ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + 3, false); + mock_ifaddr6("mlxen0", "2001:db8::7", "ffff:ffff:ffff:ffff::", + "2001:db8::ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | 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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(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); free(maddr); } + +ATF_TC_WITHOUT_HEAD(addrmerge_ipv6_other_subnet); +ATF_TC_BODY(addrmerge_ipv6_other_subnet, tc) +{ + char *maddr; + + /* getifaddrs will return link local before normal ipv6 */ + mock_lo0(); + mock_mlxen0(); + + maddr = do_addrmerge6("2001:db8:1::1.3.46"); + + /* We must return the closest ipv6 address*/ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::7.3.46", maddr); + free(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); + ATF_TP_ADD_TC(tp, addrmerge_ipv6_other_subnet); #endif return (atf_no_error()); } diff --git a/usr.sbin/rpcbind/util.c b/usr.sbin/rpcbind/util.c index 455578b657fb..e497e9227690 100644 --- a/usr.sbin/rpcbind/util.c +++ b/usr.sbin/rpcbind/util.c @@ -1,417 +1,419 @@ /* * $NetBSD: util.c,v 1.4 2000/08/03 00:04:30 fvdl Exp $ * $FreeBSD$ */ /*- * SPDX-License-Identifier: BSD-2-Clause-NetBSD * * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Frank van der Linden. * * 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. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rpcbind.h" static struct sockaddr_in *local_in4; #ifdef INET6 static struct sockaddr_in6 *local_in6; #endif static int bitmaskcmp(struct sockaddr *, struct sockaddr *, struct sockaddr *); /* * For all bits set in "mask", compare the corresponding bits in * "dst" and "src", and see if they match. Returns 0 if the addresses * match. */ static int bitmaskcmp(struct sockaddr *dst, struct sockaddr *src, struct sockaddr *mask) { int i; 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); break; #ifdef INET6 case AF_INET6: p1 = (uint8_t*) &SA2SIN6ADDR(dst); p2 = (uint8_t*) &SA2SIN6ADDR(src); netmask = (uint8_t*) &SA2SIN6ADDR(mask); bytelen = sizeof(struct in6_addr); break; #endif default: return (1); } for (i = 0; i < bytelen; i++) if ((p1[i] & netmask[i]) != (p2[i] & netmask[i])) return (1); return (0); } /* * Find a server address that can be used by `caller' to contact * the local service specified by `serv_uaddr'. If `clnt_uaddr' is * non-NULL, it is used instead of `caller' as a hint suggesting * the best address (e.g. the `r_addr' field of an rpc, which * contains the rpcbind server address that the caller used). * * Returns the best server address as a malloc'd "universal address" * string which should be freed by the caller. On error, returns NULL. */ char * addrmerge(struct netbuf *caller, const char *serv_uaddr, const char *clnt_uaddr, const char *netid) { struct ifaddrs *ifap, *ifp = NULL, *bestif; struct netbuf *serv_nbp = NULL, *hint_nbp = NULL, tbuf; struct sockaddr *caller_sa, *hint_sa, *ifsa, *ifmasksa, *serv_sa; struct sockaddr_storage ss; struct netconfig *nconf; char *caller_uaddr = NULL; #ifdef ND_DEBUG const char *hint_uaddr = NULL; #endif char *ret = NULL; int bestif_goodness; #ifdef ND_DEBUG if (debugging) fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr, clnt_uaddr == NULL ? "NULL" : clnt_uaddr, netid); #endif caller_sa = caller->buf; if ((nconf = rpcbind_get_conf(netid)) == NULL) goto freeit; if ((caller_uaddr = taddr2uaddr(nconf, caller)) == NULL) goto freeit; /* * Use `clnt_uaddr' as the hint if non-NULL, but ignore it if its * address family is different from that of the caller. */ hint_sa = NULL; if (clnt_uaddr != NULL) { #ifdef ND_DEBUG hint_uaddr = clnt_uaddr; #endif if ((hint_nbp = uaddr2taddr(nconf, clnt_uaddr)) == NULL) goto freeit; hint_sa = hint_nbp->buf; } if (hint_sa == NULL || hint_sa->sa_family != caller_sa->sa_family) { #ifdef ND_DEBUG hint_uaddr = caller_uaddr; #endif hint_sa = caller->buf; } #ifdef ND_DEBUG if (debugging) fprintf(stderr, "addrmerge: hint %s\n", hint_uaddr); #endif /* Local caller, just return the server address. */ if (strncmp(caller_uaddr, "0.0.0.0.", 8) == 0 || strncmp(caller_uaddr, "::.", 3) == 0 || caller_uaddr[0] == '/') { ret = strdup(serv_uaddr); goto freeit; } if (getifaddrs(&ifp) < 0) goto freeit; /* * Loop through all interface addresses. We are listening to an address * if any of the following are true: * a) It's a loopback address * b) It was specified with the -h command line option * c) There were no -h command line options. * * Among addresses on which we are listening, choose in order of * preference an address that is: * * a) Equal to the hint * b) A link local address with the same scope ID as the client's * address, if the client's address is also link local * c) An address on the same subnet as the client's address * d) A non-localhost, non-p2p address * e) Any usable address */ bestif = NULL; bestif_goodness = 0; for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { ifsa = ifap->ifa_addr; ifmasksa = ifap->ifa_netmask; /* Skip addresses where we don't listen */ if (ifsa == NULL || ifsa->sa_family != hint_sa->sa_family || !(ifap->ifa_flags & IFF_UP)) continue; if (!(ifap->ifa_flags & IFF_LOOPBACK) && !listen_addr(ifsa)) continue; if ((hint_sa->sa_family == AF_INET) && ((((struct sockaddr_in*)hint_sa)->sin_addr.s_addr == ((struct sockaddr_in*)ifsa)->sin_addr.s_addr))) { const int goodness = 4; bestif_goodness = goodness; bestif = ifap; goto found; } #ifdef INET6 if ((hint_sa->sa_family == AF_INET6) && (0 == memcmp(&((struct sockaddr_in6*)hint_sa)->sin6_addr, &((struct sockaddr_in6*)ifsa)->sin6_addr, sizeof(struct in6_addr))) && (((struct sockaddr_in6*)hint_sa)->sin6_scope_id == (((struct sockaddr_in6*)ifsa)->sin6_scope_id))) { const int goodness = 4; bestif_goodness = goodness; bestif = ifap; goto found; } if (hint_sa->sa_family == AF_INET6) { /* * For v6 link local addresses, if the caller is on * a link-local address then use the scope id to see * which one. */ - if (IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(ifsa)) && - IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(caller_sa)) && - IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(hint_sa))) { - if (SA2SIN6(ifsa)->sin6_scope_id == - SA2SIN6(caller_sa)->sin6_scope_id) { + if (IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(ifsa))) { + if (IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(caller_sa)) && + IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(hint_sa)) && + (SA2SIN6(ifsa)->sin6_scope_id == + SA2SIN6(caller_sa)->sin6_scope_id)) { const int goodness = 3; if (bestif_goodness < goodness) { bestif = ifap; bestif_goodness = goodness; } + } else { + continue; } } } #endif /* INET6 */ if (0 == bitmaskcmp(hint_sa, ifsa, ifmasksa)) { const int goodness = 2; if (bestif_goodness < goodness) { bestif = ifap; bestif_goodness = goodness; } } if (!(ifap->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) { const int goodness = 1; if (bestif_goodness < goodness) { bestif = ifap; bestif_goodness = goodness; } } if (bestif == NULL) bestif = ifap; } if (bestif == NULL) goto freeit; found: /* * Construct the new address using the address from * `bestif', and the port number from `serv_uaddr'. */ serv_nbp = uaddr2taddr(nconf, serv_uaddr); if (serv_nbp == NULL) goto freeit; serv_sa = serv_nbp->buf; memcpy(&ss, bestif->ifa_addr, bestif->ifa_addr->sa_len); switch (ss.ss_family) { case AF_INET: SA2SIN(&ss)->sin_port = SA2SIN(serv_sa)->sin_port; break; #ifdef INET6 case AF_INET6: SA2SIN6(&ss)->sin6_port = SA2SIN6(serv_sa)->sin6_port; break; #endif } tbuf.len = ss.ss_len; tbuf.maxlen = sizeof(ss); tbuf.buf = &ss; ret = taddr2uaddr(nconf, &tbuf); freeit: free(caller_uaddr); if (hint_nbp != NULL) { free(hint_nbp->buf); free(hint_nbp); } if (serv_nbp != NULL) { free(serv_nbp->buf); free(serv_nbp); } if (ifp != NULL) freeifaddrs(ifp); #ifdef ND_DEBUG if (debugging) fprintf(stderr, "addrmerge: returning %s\n", ret); #endif return ret; } void network_init(void) { #ifdef INET6 struct ifaddrs *ifap, *ifp; struct ipv6_mreq mreq6; unsigned int ifindex; int s; #endif int ecode; struct addrinfo hints, *res; memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) { if (debugging) fprintf(stderr, "can't get local ip4 address: %s\n", gai_strerror(ecode)); } else { local_in4 = (struct sockaddr_in *)malloc(sizeof *local_in4); if (local_in4 == NULL) { if (debugging) fprintf(stderr, "can't alloc local ip4 addr\n"); exit(1); } memcpy(local_in4, res->ai_addr, sizeof *local_in4); freeaddrinfo(res); } #ifdef INET6 hints.ai_family = AF_INET6; if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) { if (debugging) fprintf(stderr, "can't get local ip6 address: %s\n", gai_strerror(ecode)); } else { local_in6 = (struct sockaddr_in6 *)malloc(sizeof *local_in6); if (local_in6 == NULL) { if (debugging) fprintf(stderr, "can't alloc local ip6 addr\n"); exit(1); } memcpy(local_in6, res->ai_addr, sizeof *local_in6); freeaddrinfo(res); } /* * Now join the RPC ipv6 multicast group on all interfaces. */ if (getifaddrs(&ifp) < 0) return; mreq6.ipv6mr_interface = 0; inet_pton(AF_INET6, RPCB_MULTICAST_ADDR, &mreq6.ipv6mr_multiaddr); s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (s == -1) { if (debugging) fprintf(stderr, "couldn't create ip6 socket"); goto done_inet6; } /* * Loop through all interfaces. For each IPv6 multicast-capable * interface, join the RPC multicast group on that interface. */ for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { if (ifap->ifa_addr->sa_family != AF_INET6 || !(ifap->ifa_flags & IFF_MULTICAST)) continue; ifindex = if_nametoindex(ifap->ifa_name); if (ifindex == mreq6.ipv6mr_interface) /* * Already did this one. */ continue; mreq6.ipv6mr_interface = ifindex; if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof mreq6) < 0) if (debugging) perror("setsockopt v6 multicast"); } done_inet6: freeifaddrs(ifp); #endif /* close(s); */ } struct sockaddr * local_sa(int af) { switch (af) { case AF_INET: return (struct sockaddr *)local_in4; #ifdef INET6 case AF_INET6: return (struct sockaddr *)local_in6; #endif default: return NULL; } }