blob: f81e0abaf48bf844f3fab651fe75a9aecb084ff3 [file] [log] [blame]
// Copyright (C) 2014-2021 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#if defined(__linux__) || defined(ANDROID)
#include <thread>
#include <boost/asio/write.hpp>
#include <boost/asio/read.hpp>
#include<sstream>
#include <vsomeip/internal/logger.hpp>
#include "../include/netlink_connector.hpp"
namespace vsomeip_v3 {
void netlink_connector::register_net_if_changes_handler(const net_if_changed_handler_t& _handler) {
handler_ = _handler;
}
void netlink_connector::unregister_net_if_changes_handler() {
handler_ = nullptr;
}
void netlink_connector::stop() {
std::lock_guard<std::mutex> its_lock(socket_mutex_);
boost::system::error_code its_error;
socket_.shutdown(socket_.shutdown_both, its_error);
socket_.close(its_error);
if (its_error) {
VSOMEIP_WARNING << "Error closing NETLINK socket!";
}
}
void netlink_connector::start() {
std::lock_guard<std::mutex> its_lock(socket_mutex_);
boost::system::error_code ec;
if (socket_.is_open()) {
socket_.close(ec);
if (ec) {
VSOMEIP_WARNING << "Error closing NETLINK socket: " << ec.message();
}
}
socket_.open(nl_protocol(NETLINK_ROUTE), ec);
if (ec) {
VSOMEIP_WARNING << "Error opening NETLINK socket: " << ec.message();
if (handler_) {
handler_(true, "n/a", true);
handler_(false, "n/a", true);
}
return;
}
if (socket_.is_open()) {
socket_.bind(nl_endpoint<nl_protocol>(
RTMGRP_LINK |
RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR |
RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE |
RTMGRP_IPV4_MROUTE | RTMGRP_IPV6_MROUTE), ec);
if (ec && ec != boost::asio::error::address_in_use) {
VSOMEIP_WARNING << "Error binding NETLINK socket: " << ec.message();
if (handler_) {
handler_(true, "n/a", true);
handler_(false, "n/a", true);
}
return;
}
send_ifa_request();
socket_.async_receive(
boost::asio::buffer(&recv_buffer_[0], recv_buffer_size),
std::bind(
&netlink_connector::receive_cbk,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2
)
);
} else {
VSOMEIP_WARNING << "Error opening NETLINK socket!";
if (handler_) {
handler_(true, "n/a", true);
handler_(false, "n/a", true);
}
}
}
void netlink_connector::receive_cbk(boost::system::error_code const &_error,
std::size_t _bytes) {
if (!_error) {
size_t len = _bytes;
unsigned int address(0);
if (address_.is_v4()) {
inet_pton(AF_INET, address_.to_string().c_str(), &address);
} else {
inet_pton(AF_INET6, address_.to_string().c_str(), &address);
}
struct nlmsghdr *nlh = (struct nlmsghdr *)&recv_buffer_[0];
while ((NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE)) {
char ifname[IF_NAMESIZE];
switch (nlh->nlmsg_type) {
case RTM_NEWADDR: {
// New Address information
struct ifaddrmsg *ifa = (ifaddrmsg *)NLMSG_DATA(nlh);
if (has_address(ifa, IFA_PAYLOAD(nlh), address)) {
net_if_index_for_address_ = static_cast<int>(ifa->ifa_index);
auto its_if = net_if_flags_.find(static_cast<int>(ifa->ifa_index));
if (its_if != net_if_flags_.end()) {
if ((its_if->second & IFF_UP) &&
(is_requiring_link_ ? (its_if->second & IFF_RUNNING) : true)) {
if (handler_) {
if_indextoname(ifa->ifa_index,ifname);
handler_(true, ifname, true);
send_rt_request();
}
} else {
if (handler_) {
if_indextoname(ifa->ifa_index,ifname);
handler_(true, ifname, false);
}
}
} else {
// Request interface information
// as we don't know about up/running state!
send_ifi_request();
}
}
break;
}
case RTM_NEWLINK: {
// New Interface information
struct ifinfomsg *ifi = (ifinfomsg *)NLMSG_DATA(nlh);
net_if_flags_[ifi->ifi_index] = ifi->ifi_flags;
if (net_if_index_for_address_ == ifi->ifi_index) {
if ((ifi->ifi_flags & IFF_UP) &&
(is_requiring_link_ ? (ifi->ifi_flags & IFF_RUNNING) : true)) {
if (handler_) {
if_indextoname(static_cast<unsigned int>(ifi->ifi_index),ifname);
handler_(true, ifname, true);
send_rt_request();
}
} else {
if (handler_) {
if_indextoname(static_cast<unsigned int>(ifi->ifi_index),ifname);
handler_(true, ifname, false);
}
}
}
break;
}
case RTM_NEWROUTE: {
struct rtmsg *routemsg = (rtmsg *)NLMSG_DATA(nlh);
std::string its_route_name;
if (check_sd_multicast_route_match(routemsg, RTM_PAYLOAD(nlh),
&its_route_name)) {
if (handler_) {
handler_(false, its_route_name, true);
}
}
break;
}
case RTM_DELROUTE: {
struct rtmsg *routemsg = (rtmsg *)NLMSG_DATA(nlh);
std::string its_route_name;
if (check_sd_multicast_route_match(routemsg, RTM_PAYLOAD(nlh),
&its_route_name)) {
if (handler_) {
handler_(false, its_route_name, false);
}
}
break;
}
case NLMSG_ERROR: {
struct nlmsgerr *errmsg = (nlmsgerr *)NLMSG_DATA(nlh);
if (errmsg->error != 0) {
handle_netlink_error(errmsg);
}
break;
}
case NLMSG_DONE:
case NLMSG_NOOP:
default:
break;
}
nlh = NLMSG_NEXT(nlh, len);
}
{
std::lock_guard<std::mutex> its_lock(socket_mutex_);
if (socket_.is_open()) {
socket_.async_receive(
boost::asio::buffer(&recv_buffer_[0], recv_buffer_size),
std::bind(
&netlink_connector::receive_cbk,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2
)
);
}
}
} else {
if (_error != boost::asio::error::operation_aborted) {
VSOMEIP_WARNING << "Error receive_cbk NETLINK socket!" << _error.message();
boost::system::error_code its_error;
{
std::lock_guard<std::mutex> its_lock(socket_mutex_);
if (socket_.is_open()) {
socket_.shutdown(socket_.shutdown_both, its_error);
socket_.close(its_error);
if (its_error) {
VSOMEIP_WARNING << "Error closing NETLINK socket!"
<< its_error.message();
}
}
}
if (handler_) {
handler_(true, "n/a", true);
handler_(false, "n/a", true);
}
}
}
}
void netlink_connector::send_cbk(boost::system::error_code const &_error, std::size_t _bytes) {
(void)_bytes;
if (_error) {
VSOMEIP_WARNING << "Netlink send error : " << _error.message();
if (handler_) {
handler_(true, "n/a", true);
handler_(false, "n/a", true);
}
}
}
void netlink_connector::send_ifa_request(std::uint32_t _retry) {
typedef struct {
struct nlmsghdr nlhdr;
struct ifaddrmsg addrmsg;
} netlink_address_msg;
netlink_address_msg get_address_msg;
memset(&get_address_msg, 0, sizeof(get_address_msg));
get_address_msg.nlhdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
get_address_msg.nlhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
get_address_msg.nlhdr.nlmsg_type = RTM_GETADDR;
// the sequence number has stored the request sequence and the retry count.
// request sequece is stored in the LSB (least significant byte) and
// retry is stored in the 2nd LSB.
get_address_msg.nlhdr.nlmsg_seq = ifa_request_sequence_ | (_retry << retry_bit_shift_);
if (address_.is_v4()) {
get_address_msg.addrmsg.ifa_family = AF_INET;
} else {
get_address_msg.addrmsg.ifa_family = AF_INET6;
}
socket_.async_send(
boost::asio::buffer(&get_address_msg, get_address_msg.nlhdr.nlmsg_len),
std::bind(
&netlink_connector::send_cbk,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2
)
);
}
void netlink_connector::send_ifi_request(std::uint32_t _retry) {
typedef struct {
struct nlmsghdr nlhdr;
struct ifinfomsg infomsg;
} netlink_link_msg;
netlink_link_msg get_link_msg;
memset(&get_link_msg, 0, sizeof(get_link_msg));
get_link_msg.nlhdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
get_link_msg.nlhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
get_link_msg.nlhdr.nlmsg_type = RTM_GETLINK;
get_link_msg.infomsg.ifi_family = AF_UNSPEC;
// the sequence number has stored the request sequence and the retry count.
// request sequece is stored in the LSB (least significant byte) and
// retry is stored in the 2nd LSB.
get_link_msg.nlhdr.nlmsg_seq = ifi_request_sequence_ | (_retry << retry_bit_shift_);
{
std::lock_guard<std::mutex> its_lock(socket_mutex_);
socket_.async_send(
boost::asio::buffer(&get_link_msg, get_link_msg.nlhdr.nlmsg_len),
std::bind(
&netlink_connector::send_cbk,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2
)
);
}
}
void netlink_connector::send_rt_request(std::uint32_t _retry) {
typedef struct {
struct nlmsghdr nlhdr;
struct rtgenmsg routemsg;
} netlink_route_msg;
netlink_route_msg get_route_msg;
memset(&get_route_msg, 0, sizeof(get_route_msg));
get_route_msg.nlhdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
get_route_msg.nlhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
get_route_msg.nlhdr.nlmsg_type = RTM_GETROUTE;
// the sequence number has stored the request sequence and the retry count.
// request sequece is stored in the LSB (least significant byte) and
// retry is stored in the 2nd LSB.
get_route_msg.nlhdr.nlmsg_seq = rt_request_sequence_ | (_retry << retry_bit_shift_);
if (multicast_address_.is_v6()) {
get_route_msg.routemsg.rtgen_family = AF_INET6;
} else {
get_route_msg.routemsg.rtgen_family = AF_INET;
}
{
std::lock_guard<std::mutex> its_lock(socket_mutex_);
socket_.async_send(
boost::asio::buffer(&get_route_msg, get_route_msg.nlhdr.nlmsg_len),
std::bind(
&netlink_connector::send_cbk,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2
)
);
}
}
void netlink_connector::handle_netlink_error(struct nlmsgerr *_error_msg) {
// the sequence number has stored the request sequence and the retry count.
// retry is stored in the 2nd LSB.
std::uint32_t retry = _error_msg->msg.nlmsg_seq >> retry_bit_shift_;
if (retry >= max_retries_) {
VSOMEIP_ERROR << "netlink_connector::receive_cbk received "
"error message: " << strerror(-_error_msg->error)
<< " type " << std::dec << _error_msg->msg.nlmsg_type
<< " seq " << _error_msg->msg.nlmsg_seq;
return;
}
// the sequence number has stored the request sequence and the retry count.
// request sequece is stored in the LSB.
std::uint32_t request_sequence = _error_msg->msg.nlmsg_seq & request_sequence_bitmask_;
std::string request_type{};
if (_error_msg->msg.nlmsg_type == RTM_GETADDR && request_sequence == ifa_request_sequence_) {
request_type = "address request";
send_ifa_request(retry + 1);
} else if (_error_msg->msg.nlmsg_type == RTM_GETLINK && request_sequence == ifi_request_sequence_) {
request_type = "link request";
send_ifi_request(retry + 1);
} else if (_error_msg->msg.nlmsg_type == RTM_GETROUTE && request_sequence == rt_request_sequence_) {
request_type = "route request";
send_rt_request(retry + 1);
}
if (!request_type.empty()) {
VSOMEIP_INFO << "Retrying netlink " << request_type;
}
}
bool netlink_connector::has_address(struct ifaddrmsg * ifa_struct,
size_t length,
const unsigned int address) {
struct rtattr *retrta;
retrta = static_cast<struct rtattr *>(IFA_RTA(ifa_struct));
while RTA_OK(retrta, length) {
if (retrta->rta_type == IFA_ADDRESS) {
char pradd[128];
unsigned int * tmp_address = (unsigned int *)RTA_DATA(retrta);
if (address_.is_v4()) {
inet_ntop(AF_INET, tmp_address, pradd, sizeof(pradd));
} else {
inet_ntop(AF_INET6, tmp_address, pradd, sizeof(pradd));
}
if (address == *tmp_address) {
return true;
}
}
retrta = RTA_NEXT(retrta, length);
}
return false;
}
bool netlink_connector::check_sd_multicast_route_match(struct rtmsg* _routemsg,
size_t _length,
std::string* _routename) const {
struct rtattr *retrta;
retrta = static_cast<struct rtattr *>(RTM_RTA(_routemsg));
int if_index(0);
char if_name[IF_NAMESIZE] = "n/a";
char address[INET6_ADDRSTRLEN] = "n/a";
char gateway[INET6_ADDRSTRLEN] = "n/a";
bool matches_sd_multicast(false);
while (RTA_OK(retrta, _length)) {
if (retrta->rta_type == RTA_DST) {
// check if added/removed route matches on configured SD multicast address
size_t rtattr_length = RTA_PAYLOAD(retrta);
if (rtattr_length == 4 && multicast_address_.is_v4()) { // IPv4 route
inet_ntop(AF_INET, RTA_DATA(retrta), address, sizeof(address));
std::uint32_t netmask(0);
for (int i = 31; i > 31 - _routemsg->rtm_dst_len; i--) {
netmask |= static_cast<std::uint32_t>(1 << i);
}
const std::uint32_t dst_addr = ntohl(*((std::uint32_t *)RTA_DATA(retrta)));
const std::uint32_t dst_net = (dst_addr & netmask);
const std::uint32_t sd_addr = static_cast<std::uint32_t>(multicast_address_.to_v4().to_ulong());
const std::uint32_t sd_net = (sd_addr & netmask);
matches_sd_multicast = !(dst_net ^ sd_net);
} else if (rtattr_length == 16 && multicast_address_.is_v6()) { // IPv6 route
inet_ntop(AF_INET6, RTA_DATA(retrta), address, sizeof(address));
std::uint32_t netmask2[4] = {0,0,0,0};
for (int i = 127; i > 127 - _routemsg->rtm_dst_len; i--) {
if (i > 95) {
netmask2[0] |= static_cast<std::uint32_t>(1 << (i-96));
} else if (i > 63) {
netmask2[1] |= static_cast<std::uint32_t>(1 << (i-64));
} else if (i > 31) {
netmask2[2] |= static_cast<std::uint32_t>(1 << (i-32));
} else {
netmask2[3] |= static_cast<std::uint32_t>(1 << i);
}
}
for (int i = 0; i < 4; i++) {
#ifndef ANDROID
const std::uint32_t dst = ntohl((*(struct in6_addr*)RTA_DATA(retrta)).__in6_u.__u6_addr32[i]);
#else
const std::uint32_t dst = ntohl((*(struct in6_addr*)RTA_DATA(retrta)).in6_u.u6_addr32[i]);
#endif
const std::uint32_t sd = ntohl(reinterpret_cast<std::uint32_t*>(multicast_address_.to_v6().to_bytes().data())[i]);
const std::uint32_t dst_net = dst & netmask2[i];
const std::uint32_t sd_net = sd & netmask2[i];
matches_sd_multicast = !(dst_net ^ sd_net);
if (!matches_sd_multicast) {
break;
}
}
}
} else if (retrta->rta_type == RTA_OIF) {
if_index = *(int *)(RTA_DATA(retrta));
if_indextoname(static_cast<unsigned int>(if_index),if_name);
} else if (retrta->rta_type == RTA_GATEWAY) {
size_t rtattr_length = RTA_PAYLOAD(retrta);
if (rtattr_length == 4) {
inet_ntop(AF_INET, RTA_DATA(retrta), gateway, sizeof(gateway));
} else if (rtattr_length == 16) {
inet_ntop(AF_INET6, RTA_DATA(retrta), gateway, sizeof(gateway));
}
}
retrta = RTA_NEXT(retrta, _length);
}
if (matches_sd_multicast && net_if_index_for_address_ == if_index) {
std::stringstream stream;
stream << address << "/" << (static_cast<uint32_t>(_routemsg->rtm_dst_len))
<< " if: " << if_name << " gw: " << gateway;
*_routename = stream.str();
return true;
} else if (if_index > 0 && net_if_index_for_address_ == if_index &&
_routemsg->rtm_dst_len == 0) {
// the default route is set to the interface on which the SD will listen
// therefore no explicit multicast route is required.
std::stringstream stream;
stream << "default route (0.0.0.0/0) if: " << if_name << " gw: " << gateway;
*_routename = stream.str();
return true;
}
return false;
}
} // namespace vsomeip_v3
#endif // __linux__ or ANDROID