3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-10-06 11:28:38 +02:00
iwd/src/netdev.c
Denis Kenzior f3fc0ea1f9 device: Refactor netdev watches
Turn netdev watches into device watches.  The intent is to refactor out
netdev specific details into its own class and move device specific
logic into device.c away from wiphy.c
2016-05-05 12:14:59 -05:00

247 lines
5.2 KiB
C

/*
*
* Wireless daemon for Linux
*
* Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <linux/rtnetlink.h>
#include <net/if_arp.h>
#include <net/if.h>
#include <ell/ell.h>
#include "src/wiphy.h"
#include "src/netdev.h"
struct netdev_data {
uint32_t index;
uint32_t flags;
char ifname[IF_NAMESIZE];
};
static struct l_netlink *rtnl = NULL;
static struct l_hashmap *netdev_list = NULL;
static void do_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
l_info("%s%s", prefix, str);
}
struct cb_data {
netdev_command_func_t callback;
void *user_data;
};
static void netlink_result(int error, uint16_t type, const void *data,
uint32_t len, void *user_data)
{
struct cb_data *cb_data = user_data;
if (!cb_data)
return;
cb_data->callback(error < 0 ? false : true, cb_data->user_data);
l_free(cb_data);
}
static size_t rta_add_u8(void *rta_buf, unsigned short type, uint8_t value)
{
struct rtattr *rta = rta_buf;
rta->rta_len = RTA_LENGTH(sizeof(uint8_t));
rta->rta_type = type;
*((uint8_t *) RTA_DATA(rta)) = value;
return RTA_SPACE(sizeof(uint8_t));
}
void netdev_set_linkmode_and_operstate(uint32_t ifindex,
uint8_t linkmode, uint8_t operstate,
netdev_command_func_t callback, void *user_data)
{
struct ifinfomsg *rtmmsg;
void *rta_buf;
size_t bufsize;
struct cb_data *cb_data = NULL;
bufsize = NLMSG_LENGTH(sizeof(struct ifinfomsg)) +
RTA_SPACE(sizeof(uint8_t)) + RTA_SPACE(sizeof(uint8_t));
rtmmsg = l_malloc(bufsize);
memset(rtmmsg, 0, bufsize);
rtmmsg->ifi_family = AF_UNSPEC;
rtmmsg->ifi_index = ifindex;
rta_buf = rtmmsg + 1;
rta_buf += rta_add_u8(rta_buf, IFLA_LINKMODE, linkmode);
rta_buf += rta_add_u8(rta_buf, IFLA_OPERSTATE, operstate);
if (callback) {
cb_data = l_new(struct cb_data, 1);
cb_data->callback = callback;
cb_data->user_data = user_data;
}
l_netlink_send(rtnl, RTM_SETLINK, 0, rtmmsg,
rta_buf - (void *) rtmmsg,
netlink_result, cb_data, NULL);
l_free(rtmmsg);
}
static void free_netdev_data(void *user_data)
{
struct netdev_data *netdev = user_data;
l_free(netdev);
}
static void newlink_notify(const struct ifinfomsg *ifi, int bytes)
{
struct netdev_data *netdev;
uint32_t index = ifi->ifi_index;
struct rtattr *rta;
netdev = l_hashmap_lookup(netdev_list, L_UINT_TO_PTR(index));
if (!netdev) {
netdev = l_new(struct netdev_data, 1);
netdev->index = index;
if (!l_hashmap_insert(netdev_list,
L_UINT_TO_PTR(index), netdev)) {
free_netdev_data(netdev);
return;
}
}
netdev->flags = ifi->ifi_flags;
for (rta = IFLA_RTA(ifi); RTA_OK(rta, bytes);
rta = RTA_NEXT(rta, bytes)) {
switch (rta->rta_type) {
case IFLA_IFNAME:
if (RTA_PAYLOAD(rta) <= IF_NAMESIZE)
strcpy(netdev->ifname, RTA_DATA(rta));
break;
}
}
}
static void dellink_notify(const struct ifinfomsg *ifi, int bytes)
{
struct netdev_data *netdev;
uint32_t index = ifi->ifi_index;
wiphy_notify_dellink(index);
netdev = l_hashmap_remove(netdev_list, L_UINT_TO_PTR(index));
if (!netdev)
return;
free_netdev_data(netdev);
}
static void link_notify(uint16_t type, const void *data, uint32_t len,
void *user_data)
{
const struct ifinfomsg *ifi = data;
int bytes;
if (ifi->ifi_type != ARPHRD_ETHER)
return;
bytes = len - NLMSG_ALIGN(sizeof(struct ifinfomsg));
switch (type) {
case RTM_NEWLINK:
newlink_notify(ifi, bytes);
break;
case RTM_DELLINK:
dellink_notify(ifi, bytes);
break;
}
}
static void netdev_destroy(void)
{
/*
* The netlink object keeps track of the registered notification
* callbacks and their multicast memberships. When destroying the
* netlink object, all resources will be freed.
*/
l_netlink_destroy(rtnl);
rtnl = NULL;
l_hashmap_destroy(netdev_list, free_netdev_data);
netdev_list = NULL;
}
bool netdev_init(void)
{
if (rtnl)
return false;
l_debug("Opening route netlink socket");
rtnl = l_netlink_new(NETLINK_ROUTE);
if (!rtnl) {
l_error("Failed to open route netlink socket");
return false;
}
if (getenv("IWD_RTNL_DEBUG"))
l_netlink_set_debug(rtnl, do_debug, "[RTNL] ", NULL);
netdev_list = l_hashmap_new();
if (!l_netlink_register(rtnl, RTNLGRP_LINK, link_notify, NULL, NULL)) {
l_error("Failed to register link notification");
goto destroy;
}
return true;
destroy:
netdev_destroy();
return false;
}
bool netdev_exit(void)
{
if (!rtnl)
return false;
l_debug("Closing route netlink socket");
netdev_destroy();
return true;
}