monitor_netlink.c revision a3c8390d19593b1e5277d95bfb4ab206d4785150
/*
SSSD - Service monitor - netlink support
Authors:
Jakub Hrozek <jhrozek@redhat.com>
Parts of this code were borrowed from NetworkManager
Copyright (C) 2010 Red Hat
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <talloc.h>
#include <tevent.h>
#define __USE_GNU /* needed for struct ucred */
#include <unistd.h>
#include <fcntl.h>
#ifdef HAVE_LIBNL
#include <linux/rtnetlink.h>
#include <linux/wireless.h>
#include <netlink/handlers.h>
#endif
/* Linux header file confusion causes this to be undefined. */
#ifndef SOL_NETLINK
#define SOL_NETLINK 270
#endif
#define SYSFS_IFACE_TEMPLATE "/sys/class/net/%s"
#define PHY_80211_SUBDIR "phy80211"
/* 9 = strlen(PHY_80211_SUBDIR)+1, 1 = path delimeter */
#define TYPE_FILE "type"
/* 5 = strlen(TYPE_FILE)+1, 1 = path delimeter */
#define BUFSIZE 8
#ifdef HAVE_LIBNL
/* Wrappers determining use of libnl version 1 or 3 */
#ifdef HAVE_LIBNL3
#define nlw_destroy_handle nl_socket_free
#define nlw_alloc nl_socket_alloc
#define nlw_handle nl_sock
#define nlw_destroy_handle nl_handle_destroy
#define nlw_alloc nl_handle_alloc
#define nlw_handle nl_handle
#endif /* HAVE_LIBNL3 */
#endif /* HAVE_LIBNL */
enum nlw_msg_type {
};
struct netlink_ctx {
#ifdef HAVE_LIBNL
struct nlw_handle *nlp;
#endif
void *cb_data;
};
#ifdef HAVE_LIBNL
static int netlink_ctx_destructor(void *ptr)
{
struct netlink_ctx *nlctx;
return 0;
}
/*******************************************************************
* Utility functions
*******************************************************************/
/* rtnl_route_get_oif removed from libnl3 */
int
{
#ifndef HAVE_RTNL_ROUTE_GET_OIF
struct rtnl_nexthop * nh;
int hops;
if (hops <= 0) {
return 0;
}
return rtnl_route_nh_get_ifindex(nh);
#else
return rtnl_route_get_oif(route);
#endif
}
static bool has_wireless_extension(const char *ifname)
{
int s;
if (s == -1) {
return false;
}
/* Does the interface support a wireless extension? */
close(s);
return ret == 0;
}
static bool has_ethernet_encapsulation(const char *sysfs_path)
{
char type_path[SYSFS_TYPE_PATH_MAX];
int fd = -1;
if (ret < 0) {
return false;
} else if (ret >= SYSFS_TYPE_PATH_MAX) {
return false;
}
errno = 0;
if (fd == -1) {
return false;
}
errno = 0;
if (ret == -1) {
return false;
}
}
static bool has_phy_80211_subdir(const char *sysfs_path)
{
if (ret < 0) {
return false;
} else if (ret >= SYSFS_SUBDIR_PATH_MAX) {
return false;
}
errno = 0;
if (ret == -1) {
"not a wireless interface\n", PHY_80211_SUBDIR);
} else {
}
return false;
}
"a wireless iface\n", PHY_80211_SUBDIR);
return true;
}
return false;
}
static bool discard_iff_up(const char *ifname)
{
char path[SYSFS_IFACE_PATH_MAX];
/* This catches most of the new 80211 drivers */
if (has_wireless_extension(ifname)) {
return true;
}
if (ret < 0) {
return false;
} else if (ret >= SYSFS_IFACE_PATH_MAX) {
return false;
}
/* This will filter PPP and such. Both wired and wireless
* interfaces have the encapsulation. */
if (!has_ethernet_encapsulation(path)) {
"filtering out\n", ifname);
return true;
}
/* This captures old WEXT drivers, the new mac8011 would
* be caught by the ioctl check */
if (has_phy_80211_subdir(path)) {
ifname);
return true;
}
return false;
}
{
int addr_family;
void *addr;
return;
}
if (!addr) return;
}
}
/*******************************************************************
* Wrappers for different capabilities of different libnl versions
*******************************************************************/
const struct sockaddr_nl *snl,
{
bool accept_msg = false;
return false;
}
/* Accept any messages from the kernel */
accept_msg = true;
}
/* And any multicast message directed to our netlink PID, since multicast
* currently requires CAP_ADMIN to use.
*/
accept_msg = true;
}
if (accept_msg == false) {
}
return accept_msg;
}
{
bool is_addr_object = true;
filter = rtnl_addr_alloc();
if (!filter) {
is_addr_object = false;
}
/* Ensure it's an addr object */
is_addr_object = false;
}
return is_addr_object;
}
{
bool is_route_object = true;
struct rtnl_route *filter;
filter = rtnl_route_alloc();
if (!filter) {
is_route_object = false;
}
/* Ensure it's a route object */
is_route_object = false;
}
return is_route_object;
}
{
bool is_link_object = true;
filter = rtnl_link_alloc();
if (!filter) {
DEBUG(0, "Allocation error!\n");
is_link_object = false;
}
/* Ensure it's a link object */
is_link_object = false;
}
return is_link_object;
}
{
#ifdef HAVE_NL_SET_PASSCRED
#else
return EOK; /* not available in this version */
#endif
}
{
int ret;
if (ret != 0) {
return ret;
}
#else
errno = 0;
if (ret < 0) {
return ret;
}
#endif
return 0;
}
{
int ret;
int i;
for (i=0; groups[i]; i++) {
}
return EOK;
}
/*******************************************************************
* Callbacks for validating and receiving messages
*******************************************************************/
{
const struct sockaddr_nl *snl;
"Ignoring netlink message from UID %"SPRIuid,
return NL_SKIP;
}
return NL_SKIP;
}
return NL_OK;
}
{
switch (hdr->nlmsg_type) {
/* network interface added */
case RTM_NEWLINK:
return NLW_LINK;
/* routing table changed */
case RTM_NEWROUTE:
case RTM_DELROUTE:
return NLW_ROUTE;
/* IP address added or deleted */
case RTM_NEWADDR:
case RTM_DELADDR:
return NLW_ADDR;
/* Something else happened, but we don't care (typically RTM_GET* ) */
default:
return NLW_OTHER;
}
return NLW_OTHER;
}
{
switch (message_type(hdr)) {
case NLW_LINK:
break;
case NLW_ROUTE:
break;
case NLW_ADDR:
break;
default:
return EOK; /* Don't care */
}
return NL_OK;
}
{
#ifdef HAVE_NL_SOCKET_MODIFY_CB
data);
#else
#endif
if (ret != 0) {
return ret;
}
#ifdef HAVE_NL_SOCKET_MODIFY_CB
data);
#else
#endif
if (ret != 0) {
return ret;
}
return ret;
}
{
int prefixlen;
char buf[INET6_ADDRSTRLEN];
if (nl) {
} else {
prefixlen = 0;
}
}
/*
* If a bridge interface is configured it sets up a timer to requery for
* multicast group memberships periodically. We need to discard such
* messages.
*/
{
if (!nl) {
return false;
}
if (!addr4) {
return false;
}
if (!addr6) {
return false;
}
return IN6_IS_ADDR_MULTICAST(addr6);
}
return false;
}
{
struct rtnl_route *route_obj;
if (!nlw_is_route_object(obj)) return;
if (route_is_multicast(route_obj)) {
"Discarding multicast route message\n");
return;
}
if (debug_level & SSSDBG_TRACE_LIBS) {
}
}
{
unsigned int flags;
char str_flags[512];
int ifidx;
struct nl_addr *local_addr;
char buf[INET6_ADDRSTRLEN];
}
{
if (!nlw_is_addr_object(obj)) return;
if (debug_level & SSSDBG_TRACE_LIBS) {
}
}
{
unsigned int flags;
char str_flags[512];
int ifidx;
const char *ifname;
if (!nlw_is_link_object(obj)) return;
/* IFF_LOWER_UP is the indicator of carrier status */
!discard_iff_up(ifname)) {
}
}
{
int ret;
return;
}
return;
}
}
/*******************************************************************
* Set up the netlink library
*******************************************************************/
struct netlink_ctx **_nlctx)
{
struct netlink_ctx *nlctx;
int ret;
int nlfd;
unsigned flags;
goto fail;
}
/* Register our custom message validation filter */
if (ret != 0) {
goto fail;
}
/* Try to start talking to netlink */
if (ret != 0) {
goto fail;
}
if (ret != 0) {
goto fail;
}
/* Subscribe to the LINK group for internal carrier signals */
if (ret != 0) {
goto fail;
}
errno = 0;
if (ret < 0) {
goto fail;
}
goto fail;
}
return EOK;
fail:
return ret;
}
#else /* HAVE_LIBNL not defined */
struct netlink_ctx **_nlctx)
{
return EOK;
}
#endif