/*
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 "config.h"
#include <talloc.h>
#include <tevent.h>
#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
#endif
/* 9 = strlen(PHY_80211_SUBDIR)+1, 1 = path delimiter */
/* 5 = strlen(TYPE_FILE)+1, 1 = path delimiter */
#ifdef HAVE_LIBNL
/* Wrappers determining use of libnl version 1 or 3 */
#ifdef HAVE_LIBNL3
#elif defined(HAVE_LIBNL1)
#endif /* HAVE_LIBNL3 */
#endif /* HAVE_LIBNL */
enum nlw_msg_type {
};
struct netlink_ctx {
#ifdef HAVE_LIBNL
#endif
void *cb_data;
};
#ifdef HAVE_LIBNL
{
return 0;
}
/*******************************************************************
* Utility functions
*******************************************************************/
/* rtnl_route_get_oif removed from libnl3 */
int
{
#ifndef HAVE_RTNL_ROUTE_GET_OIF
int hops;
if (hops <= 0) {
return 0;
}
return rtnl_route_nh_get_ifindex(nh);
#else
return rtnl_route_get_oif(route);
#endif
}
{
int s;
if (s == -1) {
return false;
}
/* Does the interface support a wireless extension? */
close(s);
return ret == 0;
}
{
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;
}
}
{
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;
}
{
/* 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;
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) {
is_link_object = false;
}
/* Ensure it's a link object */
is_link_object = false;
}
return is_link_object;
}
{
#ifdef HAVE_NL_SET_PASSCRED
#elif defined(HAVE_NL_SOCKET_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
*******************************************************************/
{
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;
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;
}
{
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;
int ifidx;
}
{
int err;
if (!nlw_is_addr_object(obj)) return;
if (debug_level & SSSDBG_TRACE_LIBS) {
}
if (local_addr == NULL) {
"Received RTM_NEWADDR with no address\n");
return;
}
switch (nl_addr_get_family(local_addr)) {
case AF_INET6:
salen = sizeof(struct sockaddr_in6);
&salen);
if (err < 0) {
"Unknown error in nl_addr_fill_sockaddr\n");
return;
}
return;
}
break;
case AF_INET:
salen = sizeof(struct sockaddr_in);
&salen);
if (err < 0) {
"Unknown error in nl_addr_fill_sockaddr\n");
return;
}
return;
}
break;
default:
return;
}
}
{
unsigned int flags;
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;
"Invalid netlink handle, this is most likely a bug!\n");
return;
}
return;
}
}
/*******************************************************************
* Set up the netlink library
*******************************************************************/
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) {
"Cannot set the netlink fd to nonblocking\n");
goto fail;
}
goto fail;
}
return EOK;
fail:
return ret;
}
#else /* HAVE_LIBNL not defined */
struct netlink_ctx **_nlctx)
{
return EOK;
}
#endif