livoxProto1: detectSmoIp should be based on target dev IP

We previously used the smoIp provided by the user, but this
function is intended to enable us to figure out the correct
IP to send to the target device in the Handshake message.
This commit is contained in:
2025-09-06 20:44:28 -04:00
parent a0d577bf81
commit d2bf5aceee
2 changed files with 35 additions and 24 deletions
+33 -22
View File
@@ -204,7 +204,9 @@ bool Device::executeHandshake(
boost::asio::ip::udp::socket socket(io_context); boost::asio::ip::udp::socket socket(io_context);
socket.open(boost::asio::ip::udp::v4()); socket.open(boost::asio::ip::udp::v4());
std::string smoIp = getSmoIp(); // Get the IP addr of the SMO machine's iface that is facing the device.
std::string smoIp = getSmoIp(deviceIP);
comms::HandshakeRequest handshakeReq(smoIp, dataPort, cmdPort, imuPort); comms::HandshakeRequest handshakeReq(smoIp, dataPort, cmdPort, imuPort);
handshakeReq.swapContentsToProtocolEndianness(); handshakeReq.swapContentsToProtocolEndianness();
handshakeReq.header.setCrc16FromRawBytes(); handshakeReq.header.setCrc16FromRawBytes();
@@ -501,33 +503,40 @@ uint32_t Device::getSubnetMaskFor(uint8_t nbits)
} }
} }
std::optional<std::string> Device::detectSmoIp() std::optional<std::string> Device::detectSmoIp(const std::string& deviceIP)
{ {
/** EXPLANATION:
* This function detects the SMO IP address of the interface that's facing
* the device by iterating through all network interfaces and checking for
* the interface that has the IP address in the same subnet as the device's
* IP address.
*/
try { try {
// Parse the smoIp to get the network prefix // Parse the device IP to get the network prefix
auto smoIpOctets = comms::parseIPv4Address(smoIp); auto deviceIpOctets = comms::parseIPv4Address(deviceIP);
if (!smoIpOctets.has_value()) { if (!deviceIpOctets.has_value()) {
return std::nullopt; return std::nullopt;
} }
// Convert smoIp octets to integers for bitwise operations // Convert device IP octets to integers for bitwise operations
uint32_t smoIpAddr = (std::stoi(smoIpOctets->octet1) << 24) | uint32_t deviceIpAddr = (std::stoi(deviceIpOctets->octet1) << 24) |
(std::stoi(smoIpOctets->octet2) << 16) | (std::stoi(deviceIpOctets->octet2) << 16) |
(std::stoi(smoIpOctets->octet3) << 8) | (std::stoi(deviceIpOctets->octet3) << 8) |
std::stoi(smoIpOctets->octet4); std::stoi(deviceIpOctets->octet4);
// Generate subnet mask based on nbits // Generate subnet mask based on nbits
uint32_t subnetMask = getSubnetMaskFor(smoSubnetNbits); uint32_t subnetMask = getSubnetMaskFor(smoSubnetNbits);
// Get all network interfaces using getifaddrs (Linux/Unix specific) /* Get all network interfaces using getifaddrs (Linux/Unix specific)
// TODO: Add Windows support using GetAdaptersAddresses when porting *
* FIXME: Add Windows support using GetAdaptersAddresses when porting
*/
struct ifaddrs *ifaddr; struct ifaddrs *ifaddr;
if (getifaddrs(&ifaddr) == -1) { if (getifaddrs(&ifaddr) == -1) {
return std::nullopt; return std::nullopt;
} }
// Use unique_ptr for automatic cleanup (RAII) // Use unique_ptr for automatic cleanup (RAII) to free ifaddrs
// This ensures freeifaddrs is called even if we break out of the loop or throw an exception
auto ifaddr_deleter = [](struct ifaddrs* ptr) { freeifaddrs(ptr); }; auto ifaddr_deleter = [](struct ifaddrs* ptr) { freeifaddrs(ptr); };
std::unique_ptr<struct ifaddrs, decltype(ifaddr_deleter)> ifaddr_ptr( std::unique_ptr<struct ifaddrs, decltype(ifaddr_deleter)> ifaddr_ptr(
ifaddr, ifaddr_deleter); ifaddr, ifaddr_deleter);
@@ -564,9 +573,12 @@ std::optional<std::string> Device::detectSmoIp()
(std::stoi(ipOctets->octet3) << 8) | (std::stoi(ipOctets->octet3) << 8) |
std::stoi(ipOctets->octet4); std::stoi(ipOctets->octet4);
// Check if IP matches the subnet using the calculated mask /* Check if this iface's IP is in the same subnet as the device's IP
// Only compare the bits that are set in the subnet mask * using the calculated mask. Only compare the bits that are set in
if ((ipAddr & subnetMask) == (smoIpAddr & subnetMask)) { * the subnet mask.
*/
if ((ipAddr & subnetMask) == (deviceIpAddr & subnetMask))
{
found_ip = ip; found_ip = ip;
break; // Exit loop, let unique_ptr handle cleanup break; // Exit loop, let unique_ptr handle cleanup
} }
@@ -584,23 +596,22 @@ std::optional<std::string> Device::detectSmoIp()
} }
} }
std::string Device::getSmoIp() std::string Device::getSmoIp(const std::string& deviceIP)
{ {
// If smo-ip was provided, return it // If smo-ip was provided, return it
if (!smoIp.empty()) { if (!smoIp.empty()) {
return smoIp; return smoIp;
} }
// Otherwise, try to detect it auto detectedIp = detectSmoIp(deviceIP);
auto detectedIp = detectSmoIp();
if (detectedIp.has_value()) { if (detectedIp.has_value()) {
return detectedIp.value(); return detectedIp.value();
} }
// If detection failed, throw an exception // If detection failed, throw an exception
throw std::runtime_error( throw std::runtime_error(
std::string(__func__) + ": Failed to detect SMO IP address for smoIp " std::string(__func__) + ": Failed to detect SMO IP address for device "
+ smoIp + " with subnet mask /" + std::to_string(smoSubnetNbits)); + deviceIP + " with subnet mask /" + std::to_string(smoSubnetNbits));
} }
} // namespace livoxProto1 } // namespace livoxProto1
+2 -2
View File
@@ -83,8 +83,8 @@ private:
const std::string& broadcastCode); const std::string& broadcastCode);
// IP detection methods // IP detection methods
std::optional<std::string> detectSmoIp(); std::optional<std::string> detectSmoIp(const std::string& deviceIP);
std::string getSmoIp(); std::string getSmoIp(const std::string& deviceIP);
uint32_t getSubnetMaskFor(uint8_t nbits); uint32_t getSubnetMaskFor(uint8_t nbits);
// Heartbeat mechanism // Heartbeat mechanism