2
* Copyright (C) 2015 Canonical, Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 3, as published
6
* by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranties of
10
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
* PURPOSE. See the GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License along
14
* with this program. If not, see <http://www.gnu.org/licenses/>.
18
#include <asm/types.h>
19
#include <sys/socket.h>
25
#include <netinet/in.h>
26
#include <linux/netlink.h>
27
#include <linux/rtnetlink.h>
30
#include <sys/types.h>
31
#include <arpa/inet.h>
33
#include <mcs/logger.h>
34
#include <mcs/keep_alive.h>
35
#include <mcs/networkutils.h>
37
#include "netlinklistener.h"
40
NetlinkListener::Ptr NetlinkListener::Create(const std::weak_ptr<Delegate> &delegate) {
41
return std::shared_ptr<NetlinkListener>(new NetlinkListener(delegate))->FinalizeConstruction();
44
NetlinkListener::Ptr NetlinkListener::FinalizeConstruction() {
45
auto sp = shared_from_this();
47
auto fd = ::socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
49
MCS_ERROR("Could not connect with netlink");
53
struct sockaddr_nl addr = {};
54
memset(&addr, 0, sizeof(addr));
55
addr.nl_family = AF_NETLINK;
56
addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
58
if (::bind(fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
59
MCS_ERROR("Failed to bind netlink socket");
64
channel_ = g_io_channel_unix_new(fd);
65
g_io_channel_set_encoding(channel_, nullptr, nullptr);
66
g_io_channel_set_buffered(channel_, FALSE);
67
g_io_channel_set_close_on_unref(channel_, TRUE);
69
g_io_add_watch_full(channel_, 0, GIOCondition(G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP),
70
NetlinkListener::OnDataAvailable,
71
new mcs::WeakKeepAlive<NetlinkListener>(sp),
72
[](gpointer data) { delete static_cast<mcs::WeakKeepAlive<NetlinkListener>*>(data); });
77
NetlinkListener::NetlinkListener(const std::weak_ptr<Delegate> &delegate) :
80
interface_index_filter_(0) {
83
NetlinkListener::~NetlinkListener() {
85
g_io_channel_unref(channel_);
88
void NetlinkListener::SetInterfaceFilter(const std::string &interface_name) {
89
interface_index_filter_ = mcs::NetworkUtils::RetrieveInterfaceIndex(interface_name.c_str());
92
std::string TypeToString(uint16_t type) {
120
case RTM_NEWNDUSEROPT:
121
return "NEWNDUSEROPT";
128
void NetlinkListener::ProcessNewAddress(struct nlmsghdr *hdr) {
129
auto msg = (struct ifaddrmsg*) NLMSG_DATA(hdr);
130
auto bytes = IFA_PAYLOAD(hdr);
132
if (interface_index_filter_ > 0 && msg->ifa_index != interface_index_filter_)
135
MCS_DEBUG("family %d index %d", (unsigned int) msg->ifa_family, msg->ifa_index);
137
for (auto attr = IFA_RTA(msg); RTA_OK(attr, bytes); attr = RTA_NEXT(attr, bytes)) {
138
switch (attr->rta_type) {
140
auto len = static_cast<int>(RTA_PAYLOAD(attr));
142
if (msg->ifa_family == AF_INET && len == sizeof(struct in_addr)) {
144
addr = *((struct in_addr*) RTA_DATA(attr));
145
MCS_DEBUG(" attr address (len %d) %s", len, inet_ntoa(addr));
147
if (auto sp = delegate_.lock())
148
sp->OnInterfaceAddressChanged(mcs::NetworkUtils::RetrieveInterfaceName(msg->ifa_index),
149
std::string(inet_ntoa(addr)));
156
gboolean NetlinkListener::OnDataAvailable(GIOChannel *channel, GIOCondition condition, gpointer user_data) {
157
auto thiz = static_cast<mcs::WeakKeepAlive<NetlinkListener>*>(user_data)->GetInstance().lock();
163
auto fd = g_io_channel_unix_get_fd(thiz->channel_);
166
struct iovec iov = { buf, sizeof buf };
167
struct sockaddr_nl snl;
168
struct msghdr msg = { static_cast<void*>(&snl), sizeof(snl), &iov, 1, nullptr, 0, 0 };
170
auto ret = ::recvmsg(fd, &msg, 0);
177
auto hdr = (struct nlmsghdr*) ptr;
179
if (!NLMSG_OK(hdr, len))
182
MCS_DEBUG("%s len %d type %d flags %d seq %d pid %d",
183
TypeToString(hdr->nlmsg_type),
190
switch (hdr->nlmsg_type) {
192
thiz->ProcessNewAddress(hdr);
198
len -= hdr->nlmsg_len;
199
ptr += hdr->nlmsg_len;
205
} // namespace w11tng