forked from vitalif/vitastor
Implement OSD address selection from a specified subnet
parent
655a2c871d
commit
6e0e172e15
|
@ -85,6 +85,7 @@ const etcd_tree = {
|
||||||
// osd
|
// osd
|
||||||
etcd_report_interval: 5,
|
etcd_report_interval: 5,
|
||||||
run_primary: true,
|
run_primary: true,
|
||||||
|
osd_network: null, // "192.168.7.0/24" or an array of masks
|
||||||
bind_address: "0.0.0.0",
|
bind_address: "0.0.0.0",
|
||||||
bind_port: 0,
|
bind_port: 0,
|
||||||
autosync_interval: 5,
|
autosync_interval: 5,
|
||||||
|
|
|
@ -621,8 +621,82 @@ static bool ws_parse_frame(std::string & buf, int & type, std::string & res)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> getifaddr_list(bool include_v6)
|
static bool cidr_match(const in_addr &addr, const in_addr &net, uint8_t bits)
|
||||||
{
|
{
|
||||||
|
if (bits == 0)
|
||||||
|
{
|
||||||
|
// C99 6.5.7 (3): u32 << 32 is undefined behaviour
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !((addr.s_addr ^ net.s_addr) & htonl(0xFFFFFFFFu << (32 - bits)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool cidr6_match(const in6_addr &address, const in6_addr &network, uint8_t bits)
|
||||||
|
{
|
||||||
|
const uint32_t *a = address.s6_addr32;
|
||||||
|
const uint32_t *n = network.s6_addr32;
|
||||||
|
int bits_whole, bits_incomplete;
|
||||||
|
bits_whole = bits >> 5; // number of whole u32
|
||||||
|
bits_incomplete = bits & 0x1F; // number of bits in incomplete u32
|
||||||
|
if (bits_whole && memcmp(a, n, bits_whole << 2))
|
||||||
|
return false;
|
||||||
|
if (bits_incomplete)
|
||||||
|
{
|
||||||
|
uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete));
|
||||||
|
if ((a[bits_whole] ^ n[bits_whole]) & mask)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct addr_mask_t
|
||||||
|
{
|
||||||
|
sa_family_t family;
|
||||||
|
in_addr ipv4;
|
||||||
|
in6_addr ipv6;
|
||||||
|
uint8_t bits;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::string> getifaddr_list(json11::Json mask_cfg, bool include_v6)
|
||||||
|
{
|
||||||
|
std::vector<addr_mask_t> masks;
|
||||||
|
if (mask_cfg.is_string())
|
||||||
|
{
|
||||||
|
mask_cfg = json11::Json::array{ mask_cfg };
|
||||||
|
}
|
||||||
|
for (auto mask_json: mask_cfg.array_items())
|
||||||
|
{
|
||||||
|
std::string mask = mask_json.string_value();
|
||||||
|
unsigned bits = 0;
|
||||||
|
int p = mask.find('/');
|
||||||
|
if (p != std::string::npos)
|
||||||
|
{
|
||||||
|
char null_byte = 0;
|
||||||
|
if (sscanf(mask.c_str()+p+1, "%u%c", &bits, &null_byte) != 1 || bits > 128)
|
||||||
|
{
|
||||||
|
throw std::runtime_error((include_v6 ? "Invalid IPv4 address mask: " : "Invalid IP address mask: ") + mask);
|
||||||
|
}
|
||||||
|
mask = mask.substr(0, p);
|
||||||
|
}
|
||||||
|
in_addr ipv4;
|
||||||
|
in6_addr ipv6;
|
||||||
|
if (inet_pton(AF_INET, mask.c_str(), &ipv4) == 1)
|
||||||
|
{
|
||||||
|
if (bits > 32)
|
||||||
|
{
|
||||||
|
throw std::runtime_error((include_v6 ? "Invalid IPv4 address mask: " : "Invalid IP address mask: ") + mask);
|
||||||
|
}
|
||||||
|
masks.push_back((addr_mask_t){ .family = AF_INET, .ipv4 = ipv4, .bits = (uint8_t)bits });
|
||||||
|
}
|
||||||
|
else if (include_v6 && inet_pton(AF_INET6, mask.c_str(), &ipv6) == 1)
|
||||||
|
{
|
||||||
|
masks.push_back((addr_mask_t){ .family = AF_INET6, .ipv6 = ipv6, .bits = (uint8_t)bits });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error((include_v6 ? "Invalid IPv4 address mask: " : "Invalid IP address mask: ") + mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
std::vector<std::string> addresses;
|
std::vector<std::string> addresses;
|
||||||
ifaddrs *list, *ifa;
|
ifaddrs *list, *ifa;
|
||||||
if (getifaddrs(&list) == -1)
|
if (getifaddrs(&list) == -1)
|
||||||
|
@ -641,9 +715,30 @@ std::vector<std::string> getifaddr_list(bool include_v6)
|
||||||
{
|
{
|
||||||
void *addr_ptr;
|
void *addr_ptr;
|
||||||
if (family == AF_INET)
|
if (family == AF_INET)
|
||||||
|
{
|
||||||
addr_ptr = &((sockaddr_in *)ifa->ifa_addr)->sin_addr;
|
addr_ptr = &((sockaddr_in *)ifa->ifa_addr)->sin_addr;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
addr_ptr = &((sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
|
addr_ptr = &((sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
|
||||||
|
}
|
||||||
|
if (masks.size() > 0)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < masks.size(); i++)
|
||||||
|
{
|
||||||
|
if (masks[i].family == family && (family == AF_INET
|
||||||
|
? cidr_match(*(in_addr*)addr_ptr, masks[i].ipv4, masks[i].bits)
|
||||||
|
: cidr6_match(*(in6_addr*)addr_ptr, masks[i].ipv6, masks[i].bits)))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i >= masks.size())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
char addr[INET6_ADDRSTRLEN];
|
char addr[INET6_ADDRSTRLEN];
|
||||||
if (!inet_ntop(family, addr_ptr, addr, INET6_ADDRSTRLEN))
|
if (!inet_ntop(family, addr_ptr, addr, INET6_ADDRSTRLEN))
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,7 +45,7 @@ struct websocket_t
|
||||||
|
|
||||||
void parse_http_headers(std::string & res, http_response_t *parsed);
|
void parse_http_headers(std::string & res, http_response_t *parsed);
|
||||||
|
|
||||||
std::vector<std::string> getifaddr_list(bool include_v6 = false);
|
std::vector<std::string> getifaddr_list(json11::Json mask_cfg = json11::Json(), bool include_v6 = false);
|
||||||
|
|
||||||
uint64_t stoull_full(const std::string & str, int base = 10);
|
uint64_t stoull_full(const std::string & str, int base = 10);
|
||||||
|
|
||||||
|
|
20
src/osd.cpp
20
src/osd.cpp
|
@ -164,6 +164,26 @@ void osd_t::bind_socket()
|
||||||
int enable = 1;
|
int enable = 1;
|
||||||
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
||||||
|
|
||||||
|
if (config["osd_network"].is_string() ||
|
||||||
|
config["osd_network"].is_array())
|
||||||
|
{
|
||||||
|
std::vector<std::string> mask;
|
||||||
|
if (config["osd_network"].is_string())
|
||||||
|
mask.push_back(config["osd_network"].string_value());
|
||||||
|
else
|
||||||
|
for (auto v: config["osd_network"].array_items())
|
||||||
|
mask.push_back(v.string_value());
|
||||||
|
auto matched_addrs = getifaddr_list(mask, false);
|
||||||
|
if (matched_addrs.size() > 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "More than 1 address matches requested network(s): %s\n", json11::Json(matched_addrs).dump().c_str());
|
||||||
|
force_stop(1);
|
||||||
|
}
|
||||||
|
bind_address = matched_addrs[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME Support multiple listening sockets
|
||||||
|
|
||||||
sockaddr_in addr;
|
sockaddr_in addr;
|
||||||
int r;
|
int r;
|
||||||
if ((r = inet_pton(AF_INET, bind_address.c_str(), &addr.sin_addr)) != 1)
|
if ((r = inet_pton(AF_INET, bind_address.c_str(), &addr.sin_addr)) != 1)
|
||||||
|
|
Loading…
Reference in New Issue