Skip to content

Commit d68857c

Browse files
committed
common/pick_address: Add IPv6 support to is_addr_in_subnet
Updated the is_addr_in_subnet function to work with both IPv4 and IPv6 addresses. Previously, it only supported IPv4, which caused failures when IPv6 addresses were passed in. Changes: - Use inet_pton to detect IPv4 (AF_INET) or IPv6 (AF_INET6). - Added sockaddr_in6 for IPv6 handling while keeping sockaddr_in for IPv4. - Adjust the family and ifa_addr dynamically based on the address type. Fixes: https://tracker.ceph.com/issues/67517 Signed-off-by: Nitzan Mordechai <[email protected]>
1 parent d0d4ae9 commit d68857c

File tree

4 files changed

+179
-13
lines changed

4 files changed

+179
-13
lines changed

src/common/pick_address.cc

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -642,28 +642,41 @@ int get_iface_numa_node(
642642
bool is_addr_in_subnet(
643643
CephContext *cct,
644644
const std::string &networks,
645-
const std::string &addr)
645+
const entity_addr_t &addr)
646646
{
647647
const auto nets = get_str_list(networks);
648648
ceph_assert(!nets.empty());
649-
650649
unsigned ipv = CEPH_PICK_ADDRESS_IPV4;
651-
struct sockaddr_in public_addr;
652-
public_addr.sin_family = AF_INET;
653-
654-
if(inet_pton(AF_INET, addr.c_str(), &public_addr.sin_addr) != 1) {
655-
lderr(cct) << "unable to convert chosen address to string: " << addr << dendl;
650+
struct sockaddr_in6 public_addr6;
651+
struct sockaddr_in public_addr4;
652+
653+
if (addr.is_ipv4() &&
654+
inet_pton(AF_INET, addr.ip_only_to_str().c_str(), &public_addr4.sin_addr) == 1) {
655+
public_addr4.sin_family = AF_INET;
656+
} else if (addr.is_ipv6() &&
657+
inet_pton(AF_INET6, addr.ip_only_to_str().c_str(), &public_addr6.sin6_addr) == 1) {
658+
public_addr6.sin6_family = AF_INET6;
659+
ipv = CEPH_PICK_ADDRESS_IPV6;
660+
} else {
661+
std::string_view addr_type = addr.is_ipv4() ? "IPv4" : "IPv6";
662+
lderr(cct) << "IP address " << addr << " is not parseable as " << addr_type << dendl;
656663
return false;
657664
}
658665

659666
for (const auto &net : nets) {
660667
struct ifaddrs ifa;
661668
memset(&ifa, 0, sizeof(ifa));
662669
ifa.ifa_next = nullptr;
663-
ifa.ifa_addr = (struct sockaddr*)&public_addr;
670+
if (addr.is_ipv4()) {
671+
ifa.ifa_addr = (struct sockaddr*)&public_addr4;
672+
} else if (addr.is_ipv6()) {
673+
ifa.ifa_addr = (struct sockaddr*)&public_addr6;
674+
}
675+
664676
if(matches_with_net(cct, ifa, net, ipv)) {
665677
return true;
666678
}
667679
}
680+
lderr(cct) << "address " << addr << " is not in networks '" << networks << "'" << dendl;
668681
return false;
669682
}

src/common/pick_address.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,6 @@ int get_iface_numa_node(
9898
bool is_addr_in_subnet(
9999
CephContext *cct,
100100
const std::string &networks,
101-
const std::string &addr);
101+
const entity_addr_t &addr);
102102

103103
#endif

src/osd/OSDMap.cc

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1642,12 +1642,10 @@ void OSDMap::get_out_of_subnet_osd_counts(CephContext *cct,
16421642
for (int i = 0; i < max_osd; i++) {
16431643
if (exists(i) && is_up(i)) {
16441644
if (const auto& addrs = get_addrs(i).v; addrs.size() >= 2) {
1645-
auto v1_addr = addrs[0].ip_only_to_str();
1646-
if (!is_addr_in_subnet(cct, public_network, v1_addr)) {
1645+
if (!is_addr_in_subnet(cct, public_network, addrs[0])) {
16471646
unreachable->emplace(i);
16481647
}
1649-
auto v2_addr = addrs[1].ip_only_to_str();
1650-
if (!is_addr_in_subnet(cct, public_network, v2_addr)) {
1648+
if (!is_addr_in_subnet(cct, public_network, addrs[1])) {
16511649
unreachable->emplace(i);
16521650
}
16531651
}

src/test/test_ipaddr.cc

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -995,3 +995,158 @@ TEST(pick_address, ipv4_ipv6_enabled2)
995995
ASSERT_EQ(-1, r);
996996
}
997997
}
998+
999+
// Test for IPv4 address
1000+
TEST(is_addr_in_subnet, ipv4)
1001+
{
1002+
std::string public_network = "10.1.1.0/24";
1003+
entity_addr_t addr;
1004+
addr.parse("10.1.1.2", nullptr);
1005+
1006+
boost::intrusive_ptr<CephContext> cct(new CephContext(CEPH_ENTITY_TYPE_OSD), false);
1007+
cct->_conf._clear_safe_to_start_threads();
1008+
cct->_conf.set_val("ms_bind_ipv4", "true");
1009+
cct->_conf.set_val("ms_bind_ipv6", "false");
1010+
1011+
bool r = is_addr_in_subnet(cct.get(), public_network, addr);
1012+
ASSERT_EQ(true, r);
1013+
}
1014+
1015+
// Test for IPv6 address
1016+
TEST(is_addr_in_subnet, ipv6)
1017+
{
1018+
std::string public_network = "2001:db8::/64";
1019+
entity_addr_t addr;
1020+
addr.parse("2001:db8::1", nullptr);
1021+
1022+
boost::intrusive_ptr<CephContext> cct(new CephContext(CEPH_ENTITY_TYPE_OSD), false);
1023+
cct->_conf._clear_safe_to_start_threads();
1024+
cct->_conf.set_val("ms_bind_ipv6", "true");
1025+
cct->_conf.set_val("ms_bind_ipv4", "false");
1026+
1027+
bool r = is_addr_in_subnet(cct.get(), public_network, addr);
1028+
ASSERT_EQ(true, r);
1029+
}
1030+
1031+
// Test for invalid address
1032+
TEST(is_addr_in_subnet, invalid_address)
1033+
{
1034+
std::string public_network = "10.1.1.0/24";
1035+
entity_addr_t addr;
1036+
addr.parse("192.168.1.1", nullptr);
1037+
1038+
boost::intrusive_ptr<CephContext> cct(new CephContext(CEPH_ENTITY_TYPE_OSD), false);
1039+
cct->_conf._clear_safe_to_start_threads();
1040+
cct->_conf.set_val("ms_bind_ipv4", "true");
1041+
cct->_conf.set_val("ms_bind_ipv6", "false");
1042+
1043+
bool r = is_addr_in_subnet(cct.get(), public_network, addr);
1044+
ASSERT_EQ(false, r);
1045+
}
1046+
1047+
// Test for malformed address
1048+
TEST(is_addr_in_subnet, malformed_address)
1049+
{
1050+
std::string public_network = "10.1.1.0/24";
1051+
entity_addr_t addr;
1052+
addr.parse("invalid_address", nullptr);
1053+
1054+
boost::intrusive_ptr<CephContext> cct(new CephContext(CEPH_ENTITY_TYPE_OSD), false);
1055+
cct->_conf._clear_safe_to_start_threads();
1056+
cct->_conf.set_val("ms_bind_ipv4", "true");
1057+
cct->_conf.set_val("ms_bind_ipv6", "false");
1058+
1059+
// Test with a malformed address
1060+
bool r = is_addr_in_subnet(cct.get(), public_network, addr);
1061+
ASSERT_EQ(false, r);
1062+
}
1063+
1064+
TEST(is_addr_in_subnet, boundary_ipv4)
1065+
{
1066+
std::string public_network = "10.1.1.0/24";
1067+
entity_addr_t addr_low;
1068+
addr_low.parse("10.1.1.0", nullptr);
1069+
entity_addr_t addr_high;
1070+
addr_high.parse("10.1.1.255", nullptr);
1071+
entity_addr_t addr_out;
1072+
addr_out.parse("10.1.2.0", nullptr);
1073+
1074+
boost::intrusive_ptr<CephContext> cct(new CephContext(CEPH_ENTITY_TYPE_OSD), false);
1075+
cct->_conf._clear_safe_to_start_threads();
1076+
cct->_conf.set_val("ms_bind_ipv4", "true");
1077+
cct->_conf.set_val("ms_bind_ipv6", "false");
1078+
1079+
ASSERT_TRUE(is_addr_in_subnet(cct.get(), public_network, addr_low));
1080+
ASSERT_TRUE(is_addr_in_subnet(cct.get(), public_network, addr_high));
1081+
ASSERT_FALSE(is_addr_in_subnet(cct.get(), public_network, addr_out));
1082+
}
1083+
1084+
TEST(is_addr_in_subnet, boundary_ipv6)
1085+
{
1086+
std::string public_network = "2001:db8::/64";
1087+
entity_addr_t addr_low;
1088+
addr_low.parse("2001:db8::", nullptr);
1089+
entity_addr_t addr_high;
1090+
addr_high.parse("2001:db8:0:0:ffff:ffff:ffff:ffff", nullptr);
1091+
entity_addr_t addr_out;
1092+
addr_out.parse("2001:db9::", nullptr);
1093+
1094+
boost::intrusive_ptr<CephContext> cct(new CephContext(CEPH_ENTITY_TYPE_OSD), false);
1095+
cct->_conf._clear_safe_to_start_threads();
1096+
cct->_conf.set_val("ms_bind_ipv6", "true");
1097+
cct->_conf.set_val("ms_bind_ipv4", "false");
1098+
1099+
ASSERT_TRUE(is_addr_in_subnet(cct.get(), public_network, addr_low));
1100+
ASSERT_TRUE(is_addr_in_subnet(cct.get(), public_network, addr_high));
1101+
ASSERT_FALSE(is_addr_in_subnet(cct.get(), public_network, addr_out));
1102+
}
1103+
1104+
TEST(is_addr_in_subnet, overlapping_subnets)
1105+
{
1106+
std::string public_network_1 = "10.1.1.0/24";
1107+
std::string public_network_2 = "10.1.2.0/24";
1108+
entity_addr_t addr;
1109+
addr.parse("10.1.1.5", nullptr);
1110+
1111+
boost::intrusive_ptr<CephContext> cct(new CephContext(CEPH_ENTITY_TYPE_OSD), false);
1112+
cct->_conf._clear_safe_to_start_threads();
1113+
cct->_conf.set_val("ms_bind_ipv4", "true");
1114+
cct->_conf.set_val("ms_bind_ipv6", "false");
1115+
1116+
ASSERT_TRUE(is_addr_in_subnet(cct.get(), public_network_1, addr));
1117+
ASSERT_FALSE(is_addr_in_subnet(cct.get(), public_network_2, addr));
1118+
}
1119+
1120+
TEST(is_addr_in_subnet, mismatched_family)
1121+
{
1122+
std::string public_network_1 = "2001:db8::/64";
1123+
entity_addr_t addr_1;
1124+
addr_1.parse("10.1.1.5", nullptr);
1125+
1126+
std::string public_network_2 = "10.1.1.0/24";
1127+
entity_addr_t addr_2;
1128+
addr_2.parse("2001:db8::1", nullptr);
1129+
1130+
boost::intrusive_ptr<CephContext> cct(new CephContext(CEPH_ENTITY_TYPE_OSD), false);
1131+
cct->_conf._clear_safe_to_start_threads();
1132+
cct->_conf.set_val("ms_bind_ipv4", "true");
1133+
cct->_conf.set_val("ms_bind_ipv6", "true");
1134+
1135+
ASSERT_FALSE(is_addr_in_subnet(cct.get(), public_network_1, addr_1));
1136+
ASSERT_FALSE(is_addr_in_subnet(cct.get(), public_network_2, addr_2));
1137+
}
1138+
1139+
TEST(is_addr_in_subnet, invalid_subnets)
1140+
{
1141+
std::string public_network_1 = "10.1.1.0/33";
1142+
std::string public_network_2 = "25.0.0.99/10";
1143+
entity_addr_t addr;
1144+
addr.parse("10.1.1.2", nullptr);
1145+
1146+
boost::intrusive_ptr<CephContext> cct(new CephContext(CEPH_ENTITY_TYPE_OSD), false);
1147+
cct->_conf._clear_safe_to_start_threads();
1148+
1149+
ASSERT_FALSE(is_addr_in_subnet(cct.get(), public_network_1, addr)); // Invalid prefix
1150+
ASSERT_FALSE(is_addr_in_subnet(cct.get(), public_network_2, addr)); // Invalid subnet string
1151+
}
1152+

0 commit comments

Comments
 (0)