Merge /home/kaber/src/repos/libnl.git
This commit is contained in:
commit
6ae22c1b39
24
configure.ac
24
configure.ac
|
@ -13,7 +13,7 @@
|
||||||
# copied from glib
|
# copied from glib
|
||||||
m4_define([libnl_major_version], [3])
|
m4_define([libnl_major_version], [3])
|
||||||
m4_define([libnl_minor_version], [2])
|
m4_define([libnl_minor_version], [2])
|
||||||
m4_define([libnl_micro_version], [21])
|
m4_define([libnl_micro_version], [22])
|
||||||
|
|
||||||
|
|
||||||
# The following explanation may help to understand the above rules a bit
|
# The following explanation may help to understand the above rules a bit
|
||||||
|
@ -34,9 +34,9 @@ m4_define([libnl_micro_version], [21])
|
||||||
# 3. Programs may need to be changed, recompiled, relinked in order to use
|
# 3. Programs may need to be changed, recompiled, relinked in order to use
|
||||||
# the new version. Bump current, set revision and age to 0.
|
# the new version. Bump current, set revision and age to 0.
|
||||||
|
|
||||||
m4_define([libnl_lt_current], [216])
|
m4_define([libnl_lt_current], [217])
|
||||||
m4_define([libnl_lt_revision], [1])
|
m4_define([libnl_lt_revision], [0])
|
||||||
m4_define([libnl_lt_age], [16])
|
m4_define([libnl_lt_age], [17])
|
||||||
|
|
||||||
m4_define([libnl_version],
|
m4_define([libnl_version],
|
||||||
[libnl_major_version.libnl_minor_version.libnl_micro_version])
|
[libnl_major_version.libnl_minor_version.libnl_micro_version])
|
||||||
|
@ -75,7 +75,12 @@ AC_CHECK_PROGS(YACC, 'bison -y')
|
||||||
AC_C_CONST
|
AC_C_CONST
|
||||||
AC_C_INLINE
|
AC_C_INLINE
|
||||||
|
|
||||||
AM_PATH_CHECK()
|
PKG_CHECK_MODULES([CHECK], [check >= 0.9.0],
|
||||||
|
[enable_unit_tests="yes"],
|
||||||
|
[AC_MSG_WARN([*** Disabling building of unit tests])
|
||||||
|
enable_unit_tests="no"])
|
||||||
|
|
||||||
|
AM_CONDITIONAL([ENABLE_UNIT_TESTS], [test "$enable_unit_tests" = "yes"])
|
||||||
|
|
||||||
AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH],
|
AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH],
|
||||||
[Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]),
|
[Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]),
|
||||||
|
@ -92,6 +97,11 @@ AC_ARG_ENABLE([pthreads],
|
||||||
[enable_pthreads="$enableval"], [enable_pthreads="yes"])
|
[enable_pthreads="$enableval"], [enable_pthreads="yes"])
|
||||||
AM_CONDITIONAL([DISABLE_PTHREADS], [test "$enable_pthreads" = "no"])
|
AM_CONDITIONAL([DISABLE_PTHREADS], [test "$enable_pthreads" = "no"])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([debug],
|
||||||
|
AS_HELP_STRING([--disable-debug], [Do not include debugging statements]),
|
||||||
|
[enable_debug="$enableval"], [enable_debug="yes"])
|
||||||
|
AM_CONDITIONAL([ENABLE_DEBUG], [test "$enable_debug" = "no" ])
|
||||||
|
|
||||||
AC_CHECK_LIB([m], [pow], [], AC_MSG_ERROR([libm is required]))
|
AC_CHECK_LIB([m], [pow], [], AC_MSG_ERROR([libm is required]))
|
||||||
|
|
||||||
if test "x$enable_pthreads" = "xno"; then
|
if test "x$enable_pthreads" = "xno"; then
|
||||||
|
@ -100,6 +110,10 @@ else
|
||||||
AC_CHECK_LIB([pthread], [pthread_mutex_lock], [], AC_MSG_ERROR([libpthread is required]))
|
AC_CHECK_LIB([pthread], [pthread_mutex_lock], [], AC_MSG_ERROR([libpthread is required]))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "x$enable_debug" = "xyes"; then
|
||||||
|
AC_DEFINE([NL_DEBUG], [1], [Define to 1 to enable debugging])
|
||||||
|
fi
|
||||||
|
|
||||||
AC_CONFIG_SUBDIRS([doc])
|
AC_CONFIG_SUBDIRS([doc])
|
||||||
|
|
||||||
AC_CONFIG_FILES([
|
AC_CONFIG_FILES([
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
# Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
|
# Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
|
||||||
#
|
#
|
||||||
|
|
||||||
AC_INIT(libnl-doc, [3.2.21], [http://www.infradead.org/~tgr/libnl/])
|
AC_INIT(libnl-doc, [3.2.22], [http://www.infradead.org/~tgr/libnl/])
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
AC_CONFIG_AUX_DIR([build-aux])
|
AC_CONFIG_AUX_DIR([build-aux])
|
||||||
AM_INIT_AUTOMAKE([foreign])
|
AM_INIT_AUTOMAKE([foreign])
|
||||||
|
|
|
@ -706,6 +706,63 @@ if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
|
||||||
rtnl_link_put(link);
|
rtnl_link_put(link);
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
[[link_macvlan]]
|
||||||
|
==== MACVLAN
|
||||||
|
|
||||||
|
[source,c]
|
||||||
|
-----
|
||||||
|
extern struct rtnl_link *rtnl_link_macvlan_alloc(void);
|
||||||
|
|
||||||
|
extern int rtnl_link_is_macvlan(struct rtnl_link *);
|
||||||
|
|
||||||
|
extern char * rtnl_link_macvlan_mode2str(int, char *, size_t);
|
||||||
|
extern int rtnl_link_macvlan_str2mode(const char *);
|
||||||
|
|
||||||
|
extern char * rtnl_link_macvlan_flags2str(int, char *, size_t);
|
||||||
|
extern int rtnl_link_macvlan_str2flags(const char *);
|
||||||
|
|
||||||
|
extern int rtnl_link_macvlan_set_mode(struct rtnl_link *,
|
||||||
|
uint32_t);
|
||||||
|
extern uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *);
|
||||||
|
|
||||||
|
extern int rtnl_link_macvlan_set_flags(struct rtnl_link *,
|
||||||
|
uint16_t);
|
||||||
|
extern int rtnl_link_macvlan_unset_flags(struct rtnl_link *,
|
||||||
|
uint16_t);
|
||||||
|
extern uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *);
|
||||||
|
-----
|
||||||
|
|
||||||
|
.Example: Add a MACVLAN device
|
||||||
|
[source,c]
|
||||||
|
-----
|
||||||
|
struct rtnl_link *link;
|
||||||
|
int master_index;
|
||||||
|
struct nl_addr* addr;
|
||||||
|
|
||||||
|
/* lookup interface index of eth0 */
|
||||||
|
if (!(master_index = rtnl_link_name2i(link_cache, "eth0")))
|
||||||
|
/* error */
|
||||||
|
|
||||||
|
/* allocate new link object of type macvlan */
|
||||||
|
link = rtnl_link_macvlan_alloc();
|
||||||
|
|
||||||
|
/* set eth0 to be our master device */
|
||||||
|
rtnl_link_set_link(link, master_index);
|
||||||
|
|
||||||
|
/* set address of virtual interface */
|
||||||
|
addr = nl_addr_build(AF_LLC, ether_aton("00:11:22:33:44:55"), ETH_ALEN);
|
||||||
|
rtnl_link_set_addr(link, addr);
|
||||||
|
nl_addr_put(addr);
|
||||||
|
|
||||||
|
/* set mode of virtual interface */
|
||||||
|
rtnl_link_macvlan_set_mode(link, rtnl_link_macvlan_str2mode("bridge"));
|
||||||
|
|
||||||
|
if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
|
||||||
|
/* error */
|
||||||
|
|
||||||
|
rtnl_link_put(link);
|
||||||
|
-----
|
||||||
|
|
||||||
== Neighbouring
|
== Neighbouring
|
||||||
|
|
||||||
== Routing
|
== Routing
|
||||||
|
|
|
@ -52,6 +52,7 @@ nobase_libnlinclude_HEADERS = \
|
||||||
netlink/route/link/can.h \
|
netlink/route/link/can.h \
|
||||||
netlink/route/link/inet.h \
|
netlink/route/link/inet.h \
|
||||||
netlink/route/link/vlan.h \
|
netlink/route/link/vlan.h \
|
||||||
|
netlink/route/link/macvlan.h \
|
||||||
netlink/route/qdisc/cbq.h \
|
netlink/route/qdisc/cbq.h \
|
||||||
netlink/route/qdisc/dsmark.h \
|
netlink/route/qdisc/dsmark.h \
|
||||||
netlink/route/qdisc/fifo.h \
|
netlink/route/qdisc/fifo.h \
|
||||||
|
|
|
@ -17,4 +17,6 @@
|
||||||
|
|
||||||
#define GENL_HDRSIZE(hdrlen) (GENL_HDRLEN + (hdrlen))
|
#define GENL_HDRSIZE(hdrlen) (GENL_HDRLEN + (hdrlen))
|
||||||
|
|
||||||
|
extern int genl_resolve_id(struct genl_ops *ops);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -80,24 +80,29 @@ struct trans_list {
|
||||||
struct nl_list_head list;
|
struct nl_list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NL_DEBUG 1
|
#ifdef NL_DEBUG
|
||||||
|
|
||||||
#define NL_DBG(LVL,FMT,ARG...) \
|
#define NL_DBG(LVL,FMT,ARG...) \
|
||||||
do { \
|
do { \
|
||||||
if (LVL <= nl_debug) \
|
if (LVL <= nl_debug) \
|
||||||
fprintf(stderr, "DBG<" #LVL ">: " FMT, ##ARG); \
|
fprintf(stderr, \
|
||||||
|
"DBG<" #LVL ">%20s:%-4u %s: " FMT, \
|
||||||
|
__FILE__, __LINE__, \
|
||||||
|
__PRETTY_FUNCTION__, ##ARG); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
#else /* NL_DEBUG */
|
||||||
|
#define NL_DBG(LVL,FMT,ARG...) do { } while(0)
|
||||||
|
#endif /* NL_DEBUG */
|
||||||
|
|
||||||
#define BUG() \
|
#define BUG() \
|
||||||
do { \
|
do { \
|
||||||
NL_DBG(1, "BUG: %s:%d\n", \
|
fprintf(stderr, "BUG at file position %s:%d:%s\n", \
|
||||||
__FILE__, __LINE__); \
|
__FILE__, __LINE__, __PRETTY_FUNCTION__); \
|
||||||
assert(0); \
|
assert(0); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define APPBUG(msg) \
|
#define APPBUG(msg) \
|
||||||
do { \
|
do { \
|
||||||
NL_DBG(1, "APPLICATION BUG: %s:%d:%s: %s\n", \
|
fprintf(stderr, "APPLICATION BUG: %s:%d:%s: %s\n", \
|
||||||
__FILE__, __LINE__, __PRETTY_FUNCTION__, msg); \
|
__FILE__, __LINE__, __PRETTY_FUNCTION__, msg); \
|
||||||
assert(0); \
|
assert(0); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
|
@ -93,6 +93,8 @@ extern unsigned int nfnl_queue_msg_get_verdict(const struct nfnl_queue_msg *);
|
||||||
extern struct nl_msg * nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *);
|
extern struct nl_msg * nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *);
|
||||||
extern int nfnl_queue_msg_send_verdict(struct nl_sock *,
|
extern int nfnl_queue_msg_send_verdict(struct nl_sock *,
|
||||||
const struct nfnl_queue_msg *);
|
const struct nfnl_queue_msg *);
|
||||||
|
extern int nfnl_queue_msg_send_verdict_batch(struct nl_sock *,
|
||||||
|
const struct nfnl_queue_msg *);
|
||||||
extern int nfnl_queue_msg_send_verdict_payload(struct nl_sock *,
|
extern int nfnl_queue_msg_send_verdict_payload(struct nl_sock *,
|
||||||
const struct nfnl_queue_msg *,
|
const struct nfnl_queue_msg *,
|
||||||
const void *, unsigned );
|
const void *, unsigned );
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* netlink/route/link/macvlan.h MACVLAN interface
|
||||||
|
*
|
||||||
|
* 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 version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 Michael Braun <michael-dev@fami-braun.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_LINK_MACVLAN_H_
|
||||||
|
#define NETLINK_LINK_MACVLAN_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/route/link.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern struct rtnl_link *rtnl_link_macvlan_alloc(void);
|
||||||
|
|
||||||
|
extern int rtnl_link_is_macvlan(struct rtnl_link *);
|
||||||
|
|
||||||
|
extern char * rtnl_link_macvlan_mode2str(int, char *, size_t);
|
||||||
|
extern int rtnl_link_macvlan_str2mode(const char *);
|
||||||
|
|
||||||
|
extern char * rtnl_link_macvlan_flags2str(int, char *, size_t);
|
||||||
|
extern int rtnl_link_macvlan_str2flags(const char *);
|
||||||
|
|
||||||
|
extern int rtnl_link_macvlan_set_mode(struct rtnl_link *,
|
||||||
|
uint32_t);
|
||||||
|
extern uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *);
|
||||||
|
|
||||||
|
extern int rtnl_link_macvlan_set_flags(struct rtnl_link *,
|
||||||
|
uint16_t);
|
||||||
|
extern int rtnl_link_macvlan_unset_flags(struct rtnl_link *,
|
||||||
|
uint16_t);
|
||||||
|
extern uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -75,7 +75,7 @@ libnl_route_3_la_SOURCES = \
|
||||||
\
|
\
|
||||||
route/link/api.c route/link/vlan.c route/link/dummy.c \
|
route/link/api.c route/link/vlan.c route/link/dummy.c \
|
||||||
route/link/bridge.c route/link/inet6.c route/link/inet.c \
|
route/link/bridge.c route/link/inet6.c route/link/inet.c \
|
||||||
route/link/bonding.c route/link/can.c \
|
route/link/bonding.c route/link/can.c route/link/macvlan.c \
|
||||||
\
|
\
|
||||||
route/qdisc/blackhole.c route/qdisc/cbq.c route/qdisc/dsmark.c \
|
route/qdisc/blackhole.c route/qdisc/cbq.c route/qdisc/dsmark.c \
|
||||||
route/qdisc/fifo.c route/qdisc/htb.c route/qdisc/netem.c \
|
route/qdisc/fifo.c route/qdisc/htb.c route/qdisc/netem.c \
|
||||||
|
|
|
@ -1037,10 +1037,18 @@ static const struct trans_tbl afs[] = {
|
||||||
__ADD(AF_RXRPC,rxrpc)
|
__ADD(AF_RXRPC,rxrpc)
|
||||||
__ADD(AF_ISDN,isdn)
|
__ADD(AF_ISDN,isdn)
|
||||||
__ADD(AF_PHONET,phonet)
|
__ADD(AF_PHONET,phonet)
|
||||||
|
#ifdef AF_IEEE802154
|
||||||
__ADD(AF_IEEE802154,ieee802154)
|
__ADD(AF_IEEE802154,ieee802154)
|
||||||
|
#endif
|
||||||
|
#ifdef AF_CAIF
|
||||||
__ADD(AF_CAIF,caif)
|
__ADD(AF_CAIF,caif)
|
||||||
|
#endif
|
||||||
|
#ifdef AF_ALG
|
||||||
__ADD(AF_ALG,alg)
|
__ADD(AF_ALG,alg)
|
||||||
|
#endif
|
||||||
|
#ifdef AF_NFC
|
||||||
__ADD(AF_NFC,nfc)
|
__ADD(AF_NFC,nfc)
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
char *nl_af2str(int family, char *buf, size_t size)
|
char *nl_af2str(int family, char *buf, size_t size)
|
||||||
|
|
|
@ -464,7 +464,7 @@ struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int attrlen)
|
||||||
|
|
||||||
tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen);
|
tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen);
|
||||||
|
|
||||||
if ((tlen + msg->nm_nlh->nlmsg_len) > msg->nm_size)
|
if (tlen > msg->nm_size)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh);
|
nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh);
|
||||||
|
@ -899,7 +899,7 @@ int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
|
||||||
*/
|
*/
|
||||||
int nla_is_nested(struct nlattr *attr)
|
int nla_is_nested(struct nlattr *attr)
|
||||||
{
|
{
|
||||||
return !!(nla_type(attr) & NLA_F_NESTED);
|
return !!(attr->nla_type & NLA_F_NESTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
58
lib/cache.c
58
lib/cache.c
|
@ -307,6 +307,9 @@ struct nl_cache *nl_cache_subset(struct nl_cache *orig,
|
||||||
if (!cache)
|
if (!cache)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
NL_DBG(2, "Filling subset of cache %p <%s> with filter %p into %p\n",
|
||||||
|
orig, nl_cache_name(orig), filter, cache);
|
||||||
|
|
||||||
nl_list_for_each_entry(obj, &orig->c_items, ce_list) {
|
nl_list_for_each_entry(obj, &orig->c_items, ce_list) {
|
||||||
if (!nl_object_match_filter(obj, filter))
|
if (!nl_object_match_filter(obj, filter))
|
||||||
continue;
|
continue;
|
||||||
|
@ -341,6 +344,8 @@ struct nl_cache *nl_cache_clone(struct nl_cache *cache)
|
||||||
if (!clone)
|
if (!clone)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
NL_DBG(2, "Cloning %p into %p\n", cache, clone);
|
||||||
|
|
||||||
nl_list_for_each_entry(obj, &cache->c_items, ce_list)
|
nl_list_for_each_entry(obj, &cache->c_items, ce_list)
|
||||||
nl_cache_add(clone, obj);
|
nl_cache_add(clone, obj);
|
||||||
|
|
||||||
|
@ -362,7 +367,7 @@ void nl_cache_clear(struct nl_cache *cache)
|
||||||
{
|
{
|
||||||
struct nl_object *obj, *tmp;
|
struct nl_object *obj, *tmp;
|
||||||
|
|
||||||
NL_DBG(1, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache));
|
NL_DBG(2, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache));
|
||||||
|
|
||||||
nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list)
|
nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list)
|
||||||
nl_cache_remove(obj);
|
nl_cache_remove(obj);
|
||||||
|
@ -375,7 +380,7 @@ static void __nl_cache_free(struct nl_cache *cache)
|
||||||
if (cache->hashtable)
|
if (cache->hashtable)
|
||||||
nl_hash_table_free(cache->hashtable);
|
nl_hash_table_free(cache->hashtable);
|
||||||
|
|
||||||
NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache));
|
NL_DBG(2, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache));
|
||||||
free(cache);
|
free(cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,6 +391,9 @@ static void __nl_cache_free(struct nl_cache *cache)
|
||||||
void nl_cache_get(struct nl_cache *cache)
|
void nl_cache_get(struct nl_cache *cache)
|
||||||
{
|
{
|
||||||
cache->c_refcnt++;
|
cache->c_refcnt++;
|
||||||
|
|
||||||
|
NL_DBG(3, "Incremented cache %p <%s> reference count to %d\n",
|
||||||
|
cache, nl_cache_name(cache), cache->c_refcnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -403,8 +411,9 @@ void nl_cache_free(struct nl_cache *cache)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cache->c_refcnt--;
|
cache->c_refcnt--;
|
||||||
NL_DBG(4, "Returned cache reference %p, %d remaining\n",
|
|
||||||
cache, cache->c_refcnt);
|
NL_DBG(3, "Decremented cache %p <%s> reference count, %d remaining\n",
|
||||||
|
cache, nl_cache_name(cache), cache->c_refcnt);
|
||||||
|
|
||||||
if (cache->c_refcnt <= 0)
|
if (cache->c_refcnt <= 0)
|
||||||
__nl_cache_free(cache);
|
__nl_cache_free(cache);
|
||||||
|
@ -439,8 +448,8 @@ static int __cache_add(struct nl_cache *cache, struct nl_object *obj)
|
||||||
nl_list_add_tail(&obj->ce_list, &cache->c_items);
|
nl_list_add_tail(&obj->ce_list, &cache->c_items);
|
||||||
cache->c_nitems++;
|
cache->c_nitems++;
|
||||||
|
|
||||||
NL_DBG(1, "Added %p to cache %p <%s>.\n",
|
NL_DBG(3, "Added object %p to cache %p <%s>, nitems %d\n",
|
||||||
obj, cache, nl_cache_name(cache));
|
obj, cache, nl_cache_name(cache), cache->c_nitems);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -476,6 +485,8 @@ int nl_cache_add(struct nl_cache *cache, struct nl_object *obj)
|
||||||
return -NLE_OBJ_MISMATCH;
|
return -NLE_OBJ_MISMATCH;
|
||||||
|
|
||||||
if (!nl_list_empty(&obj->ce_list)) {
|
if (!nl_list_empty(&obj->ce_list)) {
|
||||||
|
NL_DBG(3, "Object %p already in cache, cloning new object\n", obj);
|
||||||
|
|
||||||
new = nl_object_clone(obj);
|
new = nl_object_clone(obj);
|
||||||
if (!new)
|
if (!new)
|
||||||
return -NLE_NOMEM;
|
return -NLE_NOMEM;
|
||||||
|
@ -514,7 +525,8 @@ int nl_cache_move(struct nl_cache *cache, struct nl_object *obj)
|
||||||
if (cache->c_ops->co_obj_ops != obj->ce_ops)
|
if (cache->c_ops->co_obj_ops != obj->ce_ops)
|
||||||
return -NLE_OBJ_MISMATCH;
|
return -NLE_OBJ_MISMATCH;
|
||||||
|
|
||||||
NL_DBG(3, "Moving object %p to cache %p\n", obj, cache);
|
NL_DBG(3, "Moving object %p from cache %p to cache %p\n",
|
||||||
|
obj, obj->ce_cache, cache);
|
||||||
|
|
||||||
/* Acquire reference, if already in a cache this will be
|
/* Acquire reference, if already in a cache this will be
|
||||||
* reverted during removal */
|
* reverted during removal */
|
||||||
|
@ -547,7 +559,7 @@ void nl_cache_remove(struct nl_object *obj)
|
||||||
if (cache->hashtable) {
|
if (cache->hashtable) {
|
||||||
ret = nl_hash_table_del(cache->hashtable, obj);
|
ret = nl_hash_table_del(cache->hashtable, obj);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
NL_DBG(3, "Failed to delete %p from cache %p <%s>.\n",
|
NL_DBG(2, "Failed to delete %p from cache %p <%s>.\n",
|
||||||
obj, cache, nl_cache_name(cache));
|
obj, cache, nl_cache_name(cache));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,7 +568,7 @@ void nl_cache_remove(struct nl_object *obj)
|
||||||
nl_object_put(obj);
|
nl_object_put(obj);
|
||||||
cache->c_nitems--;
|
cache->c_nitems--;
|
||||||
|
|
||||||
NL_DBG(1, "Deleted %p from cache %p <%s>.\n",
|
NL_DBG(2, "Deleted object %p from cache %p <%s>.\n",
|
||||||
obj, cache, nl_cache_name(cache));
|
obj, cache, nl_cache_name(cache));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -630,12 +642,12 @@ void nl_cache_set_flags(struct nl_cache *cache, unsigned int flags)
|
||||||
static int nl_cache_request_full_dump(struct nl_sock *sk,
|
static int nl_cache_request_full_dump(struct nl_sock *sk,
|
||||||
struct nl_cache *cache)
|
struct nl_cache *cache)
|
||||||
{
|
{
|
||||||
NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n",
|
|
||||||
cache, nl_cache_name(cache));
|
|
||||||
|
|
||||||
if (cache->c_ops->co_request_update == NULL)
|
if (cache->c_ops->co_request_update == NULL)
|
||||||
return -NLE_OPNOTSUPP;
|
return -NLE_OPNOTSUPP;
|
||||||
|
|
||||||
|
NL_DBG(2, "Requesting update from kernel for cache %p <%s>\n",
|
||||||
|
cache, nl_cache_name(cache));
|
||||||
|
|
||||||
return cache->c_ops->co_request_update(cache, sk);
|
return cache->c_ops->co_request_update(cache, sk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,7 +686,7 @@ static int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
|
||||||
.params = param,
|
.params = param,
|
||||||
};
|
};
|
||||||
|
|
||||||
NL_DBG(1, "Picking up answer for cache %p <%s>...\n",
|
NL_DBG(2, "Picking up answer for cache %p <%s>\n",
|
||||||
cache, nl_cache_name(cache));
|
cache, nl_cache_name(cache));
|
||||||
|
|
||||||
cb = nl_cb_clone(sk->s_cb);
|
cb = nl_cb_clone(sk->s_cb);
|
||||||
|
@ -685,9 +697,8 @@ static int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
|
||||||
|
|
||||||
err = nl_recvmsgs(sk, cb);
|
err = nl_recvmsgs(sk, cb);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned " \
|
NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned %d: %s\n",
|
||||||
"%d: %s", cache, nl_cache_name(cache),
|
cache, nl_cache_name(cache), err, nl_geterror(err));
|
||||||
err, nl_geterror(err));
|
|
||||||
|
|
||||||
nl_cb_put(cb);
|
nl_cb_put(cb);
|
||||||
|
|
||||||
|
@ -800,6 +811,9 @@ int nl_cache_include(struct nl_cache *cache, struct nl_object *obj,
|
||||||
return cache_include(cache, obj, &ops->co_msgtypes[i],
|
return cache_include(cache, obj, &ops->co_msgtypes[i],
|
||||||
change_cb, data);
|
change_cb, data);
|
||||||
|
|
||||||
|
NL_DBG(3, "Object %p does not seem to belong to cache %p <%s>\n",
|
||||||
|
obj, cache, nl_cache_name(cache));
|
||||||
|
|
||||||
return -NLE_MSGTYPE_NOSUPPORT;
|
return -NLE_MSGTYPE_NOSUPPORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -951,9 +965,12 @@ restart:
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
NL_DBG(2, "Updating cache %p <%s> for family %u, request sent, waiting for reply\n",
|
||||||
|
cache, nl_cache_name(cache), grp ? grp->ag_family : AF_UNSPEC);
|
||||||
|
|
||||||
err = nl_cache_pickup(sk, cache);
|
err = nl_cache_pickup(sk, cache);
|
||||||
if (err == -NLE_DUMP_INTR) {
|
if (err == -NLE_DUMP_INTR) {
|
||||||
NL_DBG(1, "dump interrupted, restarting!\n");
|
NL_DBG(2, "Dump interrupted, restarting!\n");
|
||||||
goto restart;
|
goto restart;
|
||||||
} else if (err < 0)
|
} else if (err < 0)
|
||||||
break;
|
break;
|
||||||
|
@ -963,9 +980,6 @@ restart:
|
||||||
} while (grp && grp->ag_group &&
|
} while (grp && grp->ag_group &&
|
||||||
(cache->c_flags & NL_CACHE_AF_ITER));
|
(cache->c_flags & NL_CACHE_AF_ITER));
|
||||||
|
|
||||||
NL_DBG(2, "Upading cache %p <%s>, request sent, waiting for dump...\n",
|
|
||||||
cache, nl_cache_name(cache));
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1072,7 +1086,7 @@ void nl_cache_mark_all(struct nl_cache *cache)
|
||||||
{
|
{
|
||||||
struct nl_object *obj;
|
struct nl_object *obj;
|
||||||
|
|
||||||
NL_DBG(2, "Marking all objects in cache %p <%s>...\n",
|
NL_DBG(2, "Marking all objects in cache %p <%s>\n",
|
||||||
cache, nl_cache_name(cache));
|
cache, nl_cache_name(cache));
|
||||||
|
|
||||||
nl_list_for_each_entry(obj, &cache->c_items, ce_list)
|
nl_list_for_each_entry(obj, &cache->c_items, ce_list)
|
||||||
|
@ -1115,7 +1129,7 @@ void nl_cache_dump_filter(struct nl_cache *cache,
|
||||||
struct nl_object_ops *ops;
|
struct nl_object_ops *ops;
|
||||||
struct nl_object *obj;
|
struct nl_object *obj;
|
||||||
|
|
||||||
NL_DBG(2, "Dumping cache %p <%s> filter %p\n",
|
NL_DBG(2, "Dumping cache %p <%s> with filter %p\n",
|
||||||
cache, nl_cache_name(cache), filter);
|
cache, nl_cache_name(cache), filter);
|
||||||
|
|
||||||
if (type > NL_DUMP_MAX || type < 0)
|
if (type > NL_DUMP_MAX || type < 0)
|
||||||
|
|
|
@ -435,7 +435,7 @@ int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
|
||||||
}
|
}
|
||||||
|
|
||||||
nl_cb_put(cb);
|
nl_cb_put(cb);
|
||||||
if (err < 0)
|
if (err < 0 && err != -NLE_AGAIN)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
return nread;
|
return nread;
|
||||||
|
|
|
@ -239,7 +239,7 @@ static struct genl_family *genl_ctrl_probe_by_name(struct nl_sock *sk,
|
||||||
{
|
{
|
||||||
struct nl_msg *msg;
|
struct nl_msg *msg;
|
||||||
struct genl_family *ret;
|
struct genl_family *ret;
|
||||||
struct nl_cb *cb;
|
struct nl_cb *cb, *orig;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
ret = genl_family_alloc();
|
ret = genl_family_alloc();
|
||||||
|
@ -252,7 +252,12 @@ static struct genl_family *genl_ctrl_probe_by_name(struct nl_sock *sk,
|
||||||
if (!msg)
|
if (!msg)
|
||||||
goto out_fam_free;
|
goto out_fam_free;
|
||||||
|
|
||||||
if (!(cb = nl_cb_clone(nl_socket_get_cb(sk))))
|
if (!(orig = nl_socket_get_cb(sk)))
|
||||||
|
goto out_msg_free;
|
||||||
|
|
||||||
|
cb = nl_cb_clone(orig);
|
||||||
|
nl_cb_put(orig);
|
||||||
|
if (!cb)
|
||||||
goto out_msg_free;
|
goto out_msg_free;
|
||||||
|
|
||||||
if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL,
|
if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL,
|
||||||
|
|
|
@ -295,6 +295,10 @@ static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops)
|
||||||
family = genl_ctrl_search_by_name(ctrl, ops->o_name);
|
family = genl_ctrl_search_by_name(ctrl, ops->o_name);
|
||||||
if (family != NULL) {
|
if (family != NULL) {
|
||||||
ops->o_id = genl_family_get_id(family);
|
ops->o_id = genl_family_get_id(family);
|
||||||
|
|
||||||
|
if (ops->o_cache_ops)
|
||||||
|
ops->o_cache_ops->co_msgtypes[0].mt_id = ops->o_id;
|
||||||
|
|
||||||
genl_family_put(family);
|
genl_family_put(family);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -302,6 +306,32 @@ static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops)
|
||||||
|
|
||||||
return -NLE_OBJ_NOTFOUND;
|
return -NLE_OBJ_NOTFOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int genl_resolve_id(struct genl_ops *ops)
|
||||||
|
{
|
||||||
|
struct nl_sock *sk;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/* Check if resolved already */
|
||||||
|
if (ops->o_id != GENL_ID_GENERATE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!ops->o_name)
|
||||||
|
return -NLE_INVAL;
|
||||||
|
|
||||||
|
if (!(sk = nl_socket_alloc()))
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
if ((err = genl_connect(sk)) < 0)
|
||||||
|
goto errout_free;
|
||||||
|
|
||||||
|
err = genl_ops_resolve(sk, ops);
|
||||||
|
|
||||||
|
errout_free:
|
||||||
|
nl_socket_free(sk);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
/** @endcond */
|
/** @endcond */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
122
lib/msg.c
122
lib/msg.c
|
@ -153,7 +153,7 @@ struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen)
|
||||||
*/
|
*/
|
||||||
int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen)
|
int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen)
|
||||||
{
|
{
|
||||||
return nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen);
|
return max_t(int, nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
@ -767,7 +767,7 @@ static inline void dump_hex(FILE *ofd, char *start, int len, int prefix)
|
||||||
int i, a, c, limit;
|
int i, a, c, limit;
|
||||||
char ascii[21] = {0};
|
char ascii[21] = {0};
|
||||||
|
|
||||||
limit = 18 - (prefix * 2);
|
limit = 16 - (prefix * 2);
|
||||||
prefix_line(ofd, prefix);
|
prefix_line(ofd, prefix);
|
||||||
fprintf(ofd, " ");
|
fprintf(ofd, " ");
|
||||||
|
|
||||||
|
@ -777,7 +777,7 @@ static inline void dump_hex(FILE *ofd, char *start, int len, int prefix)
|
||||||
fprintf(ofd, "%02x ", v);
|
fprintf(ofd, "%02x ", v);
|
||||||
ascii[a++] = isprint(v) ? v : '.';
|
ascii[a++] = isprint(v) ? v : '.';
|
||||||
|
|
||||||
if (c == limit-1) {
|
if (++c >= limit) {
|
||||||
fprintf(ofd, "%s\n", ascii);
|
fprintf(ofd, "%s\n", ascii);
|
||||||
if (i < (len - 1)) {
|
if (i < (len - 1)) {
|
||||||
prefix_line(ofd, prefix);
|
prefix_line(ofd, prefix);
|
||||||
|
@ -785,8 +785,7 @@ static inline void dump_hex(FILE *ofd, char *start, int len, int prefix)
|
||||||
}
|
}
|
||||||
a = c = 0;
|
a = c = 0;
|
||||||
memset(ascii, 0, sizeof(ascii));
|
memset(ascii, 0, sizeof(ascii));
|
||||||
} else
|
}
|
||||||
c++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c != 0) {
|
if (c != 0) {
|
||||||
|
@ -816,14 +815,62 @@ static void print_hdr(FILE *ofd, struct nl_msg *msg)
|
||||||
} else
|
} else
|
||||||
nl_nlmsgtype2str(nlh->nlmsg_type, buf, sizeof(buf));
|
nl_nlmsgtype2str(nlh->nlmsg_type, buf, sizeof(buf));
|
||||||
|
|
||||||
fprintf(ofd, " .nlmsg_type = %d <%s>\n", nlh->nlmsg_type, buf);
|
fprintf(ofd, " .type = %d <%s>\n", nlh->nlmsg_type, buf);
|
||||||
fprintf(ofd, " .nlmsg_flags = %d <%s>\n", nlh->nlmsg_flags,
|
fprintf(ofd, " .flags = %d <%s>\n", nlh->nlmsg_flags,
|
||||||
nl_nlmsg_flags2str(nlh->nlmsg_flags, buf, sizeof(buf)));
|
nl_nlmsg_flags2str(nlh->nlmsg_flags, buf, sizeof(buf)));
|
||||||
fprintf(ofd, " .nlmsg_seq = %d\n", nlh->nlmsg_seq);
|
fprintf(ofd, " .seq = %d\n", nlh->nlmsg_seq);
|
||||||
fprintf(ofd, " .nlmsg_pid = %d\n", nlh->nlmsg_pid);
|
fprintf(ofd, " .port = %d\n", nlh->nlmsg_pid);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void print_genl_hdr(FILE *ofd, void *start)
|
||||||
|
{
|
||||||
|
struct genlmsghdr *ghdr = start;
|
||||||
|
|
||||||
|
fprintf(ofd, " [GENERIC NETLINK HEADER] %zu octets\n", GENL_HDRLEN);
|
||||||
|
fprintf(ofd, " .cmd = %u\n", ghdr->cmd);
|
||||||
|
fprintf(ofd, " .version = %u\n", ghdr->version);
|
||||||
|
fprintf(ofd, " .unused = %#x\n", ghdr->reserved);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *print_genl_msg(struct nl_msg *msg, FILE *ofd, struct nlmsghdr *hdr,
|
||||||
|
struct nl_cache_ops *ops, int *payloadlen)
|
||||||
|
{
|
||||||
|
void *data = nlmsg_data(hdr);
|
||||||
|
|
||||||
|
if (*payloadlen < GENL_HDRLEN)
|
||||||
|
return data;
|
||||||
|
|
||||||
|
print_genl_hdr(ofd, data);
|
||||||
|
|
||||||
|
*payloadlen -= GENL_HDRLEN;
|
||||||
|
data += GENL_HDRLEN;
|
||||||
|
|
||||||
|
if (ops) {
|
||||||
|
int hdrsize = ops->co_hdrsize - GENL_HDRLEN;
|
||||||
|
|
||||||
|
if (hdrsize > 0) {
|
||||||
|
if (*payloadlen < hdrsize)
|
||||||
|
return data;
|
||||||
|
|
||||||
|
fprintf(ofd, " [HEADER] %d octets\n", hdrsize);
|
||||||
|
dump_hex(ofd, data, hdrsize, 0);
|
||||||
|
|
||||||
|
*payloadlen -= hdrsize;
|
||||||
|
data += hdrsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_attr(FILE *ofd, struct nlattr *attr, int prefix)
|
||||||
|
{
|
||||||
|
int len = nla_len(attr);
|
||||||
|
|
||||||
|
dump_hex(ofd, nla_data(attr), len, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen,
|
static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen,
|
||||||
int prefix)
|
int prefix)
|
||||||
{
|
{
|
||||||
|
@ -839,13 +886,13 @@ static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen,
|
||||||
fprintf(ofd, " [ATTR PADDING] %d octets\n", alen);
|
fprintf(ofd, " [ATTR PADDING] %d octets\n", alen);
|
||||||
else
|
else
|
||||||
fprintf(ofd, " [ATTR %02d%s] %d octets\n", nla_type(nla),
|
fprintf(ofd, " [ATTR %02d%s] %d octets\n", nla_type(nla),
|
||||||
nla->nla_type & NLA_F_NESTED ? " NESTED" : "",
|
nla_is_nested(nla) ? " NESTED" : "",
|
||||||
alen);
|
alen);
|
||||||
|
|
||||||
if (nla->nla_type & NLA_F_NESTED)
|
if (nla_is_nested(nla))
|
||||||
dump_attrs(ofd, nla_data(nla), alen, prefix+1);
|
dump_attrs(ofd, nla_data(nla), alen, prefix+1);
|
||||||
else
|
else
|
||||||
dump_hex(ofd, nla_data(nla), alen, prefix);
|
dump_attr(ofd, nla, prefix);
|
||||||
|
|
||||||
padlen = nla_padlen(alen);
|
padlen = nla_padlen(alen);
|
||||||
if (padlen > 0) {
|
if (padlen > 0) {
|
||||||
|
@ -884,29 +931,14 @@ static void dump_error_msg(struct nl_msg *msg, FILE *ofd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void print_msg(struct nl_msg *msg, FILE *ofd, struct nlmsghdr *hdr)
|
||||||
* Dump message in human readable format to file descriptor
|
|
||||||
* @arg msg Message to print
|
|
||||||
* @arg ofd File descriptor.
|
|
||||||
*/
|
|
||||||
void nl_msg_dump(struct nl_msg *msg, FILE *ofd)
|
|
||||||
{
|
{
|
||||||
struct nlmsghdr *hdr = nlmsg_hdr(msg);
|
|
||||||
|
|
||||||
fprintf(ofd,
|
|
||||||
"-------------------------- BEGIN NETLINK MESSAGE "
|
|
||||||
"---------------------------\n");
|
|
||||||
|
|
||||||
fprintf(ofd, " [HEADER] %zu octets\n", sizeof(struct nlmsghdr));
|
|
||||||
print_hdr(ofd, msg);
|
|
||||||
|
|
||||||
if (hdr->nlmsg_type == NLMSG_ERROR)
|
|
||||||
dump_error_msg(msg, ofd);
|
|
||||||
else if (nlmsg_len(hdr) > 0) {
|
|
||||||
struct nl_cache_ops *ops;
|
struct nl_cache_ops *ops;
|
||||||
int payloadlen = nlmsg_len(hdr);
|
int payloadlen = nlmsg_len(hdr);
|
||||||
int attrlen = 0;
|
int attrlen = 0;
|
||||||
|
void *data;
|
||||||
|
|
||||||
|
data = nlmsg_data(hdr);
|
||||||
ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg),
|
ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg),
|
||||||
hdr->nlmsg_type);
|
hdr->nlmsg_type);
|
||||||
if (ops) {
|
if (ops) {
|
||||||
|
@ -914,8 +946,13 @@ void nl_msg_dump(struct nl_msg *msg, FILE *ofd)
|
||||||
payloadlen -= attrlen;
|
payloadlen -= attrlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg->nm_protocol == NETLINK_GENERIC)
|
||||||
|
data = print_genl_msg(msg, ofd, hdr, ops, &payloadlen);
|
||||||
|
|
||||||
|
if (payloadlen) {
|
||||||
fprintf(ofd, " [PAYLOAD] %d octets\n", payloadlen);
|
fprintf(ofd, " [PAYLOAD] %d octets\n", payloadlen);
|
||||||
dump_hex(ofd, nlmsg_data(hdr), payloadlen, 0);
|
dump_hex(ofd, data, payloadlen, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (attrlen) {
|
if (attrlen) {
|
||||||
struct nlattr *attrs;
|
struct nlattr *attrs;
|
||||||
|
@ -930,9 +967,28 @@ void nl_msg_dump(struct nl_msg *msg, FILE *ofd)
|
||||||
nl_cache_ops_put(ops);
|
nl_cache_ops_put(ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump message in human readable format to file descriptor
|
||||||
|
* @arg msg Message to print
|
||||||
|
* @arg ofd File descriptor.
|
||||||
|
*/
|
||||||
|
void nl_msg_dump(struct nl_msg *msg, FILE *ofd)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *hdr = nlmsg_hdr(msg);
|
||||||
|
|
||||||
fprintf(ofd,
|
fprintf(ofd,
|
||||||
"--------------------------- END NETLINK MESSAGE "
|
"-------------------------- BEGIN NETLINK MESSAGE ---------------------------\n");
|
||||||
"---------------------------\n");
|
|
||||||
|
fprintf(ofd, " [NETLINK HEADER] %zu octets\n", sizeof(struct nlmsghdr));
|
||||||
|
print_hdr(ofd, msg);
|
||||||
|
|
||||||
|
if (hdr->nlmsg_type == NLMSG_ERROR)
|
||||||
|
dump_error_msg(msg, ofd);
|
||||||
|
else if (nlmsg_len(hdr) > 0)
|
||||||
|
print_msg(msg, ofd, hdr);
|
||||||
|
|
||||||
|
fprintf(ofd,
|
||||||
|
"--------------------------- END NETLINK MESSAGE ---------------------------\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
|
@ -174,6 +174,8 @@ static int ct_parse_proto(struct nfnl_ct *ct, int repl, struct nlattr *attr)
|
||||||
if (tb[CTA_PROTO_DST_PORT])
|
if (tb[CTA_PROTO_DST_PORT])
|
||||||
nfnl_ct_set_dst_port(ct, repl,
|
nfnl_ct_set_dst_port(ct, repl,
|
||||||
ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT])));
|
ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT])));
|
||||||
|
|
||||||
|
if (ct->ct_family == AF_INET) {
|
||||||
if (tb[CTA_PROTO_ICMP_ID])
|
if (tb[CTA_PROTO_ICMP_ID])
|
||||||
nfnl_ct_set_icmp_id(ct, repl,
|
nfnl_ct_set_icmp_id(ct, repl,
|
||||||
ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID])));
|
ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID])));
|
||||||
|
@ -183,6 +185,17 @@ static int ct_parse_proto(struct nfnl_ct *ct, int repl, struct nlattr *attr)
|
||||||
if (tb[CTA_PROTO_ICMP_CODE])
|
if (tb[CTA_PROTO_ICMP_CODE])
|
||||||
nfnl_ct_set_icmp_code(ct, repl,
|
nfnl_ct_set_icmp_code(ct, repl,
|
||||||
nla_get_u8(tb[CTA_PROTO_ICMP_CODE]));
|
nla_get_u8(tb[CTA_PROTO_ICMP_CODE]));
|
||||||
|
} else if (ct->ct_family == AF_INET6) {
|
||||||
|
if (tb[CTA_PROTO_ICMPV6_ID])
|
||||||
|
nfnl_ct_set_icmp_id(ct, repl,
|
||||||
|
ntohs(nla_get_u16(tb[CTA_PROTO_ICMPV6_ID])));
|
||||||
|
if (tb[CTA_PROTO_ICMPV6_TYPE])
|
||||||
|
nfnl_ct_set_icmp_type(ct, repl,
|
||||||
|
nla_get_u8(tb[CTA_PROTO_ICMPV6_TYPE]));
|
||||||
|
if (tb[CTA_PROTO_ICMPV6_CODE])
|
||||||
|
nfnl_ct_set_icmp_code(ct, repl,
|
||||||
|
nla_get_u8(tb[CTA_PROTO_ICMPV6_CODE]));
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -426,6 +439,7 @@ static int nfnl_ct_build_tuple(struct nl_msg *msg, const struct nfnl_ct *ct,
|
||||||
NLA_PUT_U16(msg, CTA_PROTO_DST_PORT,
|
NLA_PUT_U16(msg, CTA_PROTO_DST_PORT,
|
||||||
htons(nfnl_ct_get_dst_port(ct, repl)));
|
htons(nfnl_ct_get_dst_port(ct, repl)));
|
||||||
|
|
||||||
|
if (family == AF_INET) {
|
||||||
if (nfnl_ct_test_icmp_id(ct, repl))
|
if (nfnl_ct_test_icmp_id(ct, repl))
|
||||||
NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID,
|
NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID,
|
||||||
htons(nfnl_ct_get_icmp_id(ct, repl)));
|
htons(nfnl_ct_get_icmp_id(ct, repl)));
|
||||||
|
@ -437,6 +451,19 @@ static int nfnl_ct_build_tuple(struct nl_msg *msg, const struct nfnl_ct *ct,
|
||||||
if (nfnl_ct_test_icmp_code(ct, repl))
|
if (nfnl_ct_test_icmp_code(ct, repl))
|
||||||
NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE,
|
NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE,
|
||||||
nfnl_ct_get_icmp_code(ct, repl));
|
nfnl_ct_get_icmp_code(ct, repl));
|
||||||
|
} else if (family == AF_INET6) {
|
||||||
|
if (nfnl_ct_test_icmp_id(ct, repl))
|
||||||
|
NLA_PUT_U16(msg, CTA_PROTO_ICMPV6_ID,
|
||||||
|
htons(nfnl_ct_get_icmp_id(ct, repl)));
|
||||||
|
|
||||||
|
if (nfnl_ct_test_icmp_type(ct, repl))
|
||||||
|
NLA_PUT_U8(msg, CTA_PROTO_ICMPV6_TYPE,
|
||||||
|
nfnl_ct_get_icmp_type(ct, repl));
|
||||||
|
|
||||||
|
if (nfnl_ct_test_icmp_code(ct, repl))
|
||||||
|
NLA_PUT_U8(msg, CTA_PROTO_ICMPV6_CODE,
|
||||||
|
nfnl_ct_get_icmp_code(ct, repl));
|
||||||
|
}
|
||||||
|
|
||||||
nla_nest_end(msg, proto);
|
nla_nest_end(msg, proto);
|
||||||
|
|
||||||
|
|
|
@ -163,12 +163,14 @@ errout:
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
struct nl_msg *nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *msg)
|
static struct nl_msg *
|
||||||
|
__nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *msg,
|
||||||
|
uint8_t type)
|
||||||
{
|
{
|
||||||
struct nl_msg *nlmsg;
|
struct nl_msg *nlmsg;
|
||||||
struct nfqnl_msg_verdict_hdr verdict;
|
struct nfqnl_msg_verdict_hdr verdict;
|
||||||
|
|
||||||
nlmsg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_VERDICT, 0,
|
nlmsg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, type, 0,
|
||||||
nfnl_queue_msg_get_family(msg),
|
nfnl_queue_msg_get_family(msg),
|
||||||
nfnl_queue_msg_get_group(msg));
|
nfnl_queue_msg_get_group(msg));
|
||||||
if (nlmsg == NULL)
|
if (nlmsg == NULL)
|
||||||
|
@ -191,6 +193,18 @@ nla_put_failure:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct nl_msg *
|
||||||
|
nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *msg)
|
||||||
|
{
|
||||||
|
return __nfnl_queue_msg_build_verdict(msg, NFQNL_MSG_VERDICT);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nl_msg *
|
||||||
|
nfnl_queue_msg_build_verdict_batch(const struct nfnl_queue_msg *msg)
|
||||||
|
{
|
||||||
|
return __nfnl_queue_msg_build_verdict(msg, NFQNL_MSG_VERDICT_BATCH);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a message verdict/mark
|
* Send a message verdict/mark
|
||||||
* @arg nlh netlink messsage header
|
* @arg nlh netlink messsage header
|
||||||
|
@ -214,6 +228,29 @@ int nfnl_queue_msg_send_verdict(struct nl_sock *nlh,
|
||||||
return wait_for_ack(nlh);
|
return wait_for_ack(nlh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message batched verdict/mark
|
||||||
|
* @arg nlh netlink messsage header
|
||||||
|
* @arg msg queue msg
|
||||||
|
* @return 0 on OK or error code
|
||||||
|
*/
|
||||||
|
int nfnl_queue_msg_send_verdict_batch(struct nl_sock *nlh,
|
||||||
|
const struct nfnl_queue_msg *msg)
|
||||||
|
{
|
||||||
|
struct nl_msg *nlmsg;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
nlmsg = nfnl_queue_msg_build_verdict_batch(msg);
|
||||||
|
if (nlmsg == NULL)
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
err = nl_send_auto_complete(nlh, nlmsg);
|
||||||
|
nlmsg_free(nlmsg);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
return wait_for_ack(nlh);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a message verdict including the payload
|
* Send a message verdict including the payload
|
||||||
* @arg nlh netlink messsage header
|
* @arg nlh netlink messsage header
|
||||||
|
|
7
lib/nl.c
7
lib/nl.c
|
@ -597,7 +597,7 @@ int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla,
|
||||||
flags |= MSG_PEEK | MSG_TRUNC;
|
flags |= MSG_PEEK | MSG_TRUNC;
|
||||||
|
|
||||||
if (page_size == 0)
|
if (page_size == 0)
|
||||||
page_size = getpagesize();
|
page_size = getpagesize() * 4;
|
||||||
|
|
||||||
iov.iov_len = sk->s_bufsize ? : page_size;
|
iov.iov_len = sk->s_bufsize ? : page_size;
|
||||||
iov.iov_base = malloc(iov.iov_len);
|
iov.iov_base = malloc(iov.iov_len);
|
||||||
|
@ -627,11 +627,6 @@ retry:
|
||||||
NL_DBG(3, "recvmsg() returned EINTR, retrying\n");
|
NL_DBG(3, "recvmsg() returned EINTR, retrying\n");
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
||||||
NL_DBG(3, "recvmsg() returned EAGAIN||EWOULDBLOCK, aborting\n");
|
|
||||||
retval = 0;
|
|
||||||
goto abort;
|
|
||||||
}
|
|
||||||
retval = -nl_syserr2nlerr(errno);
|
retval = -nl_syserr2nlerr(errno);
|
||||||
goto abort;
|
goto abort;
|
||||||
}
|
}
|
||||||
|
|
11
lib/object.c
11
lib/object.c
|
@ -165,7 +165,12 @@ int nl_object_update(struct nl_object *dst, struct nl_object *src)
|
||||||
*/
|
*/
|
||||||
void nl_object_free(struct nl_object *obj)
|
void nl_object_free(struct nl_object *obj)
|
||||||
{
|
{
|
||||||
struct nl_object_ops *ops = obj_ops(obj);
|
struct nl_object_ops *ops;
|
||||||
|
|
||||||
|
if (!obj)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ops = obj_ops(obj);
|
||||||
|
|
||||||
if (obj->ce_refcnt > 0)
|
if (obj->ce_refcnt > 0)
|
||||||
NL_DBG(1, "Warning: Freeing object in use...\n");
|
NL_DBG(1, "Warning: Freeing object in use...\n");
|
||||||
|
@ -316,8 +321,10 @@ int nl_object_identical(struct nl_object *a, struct nl_object *b)
|
||||||
if (req_attrs_a != req_attrs_b)
|
if (req_attrs_a != req_attrs_b)
|
||||||
return 0;
|
return 0;
|
||||||
req_attrs = req_attrs_a;
|
req_attrs = req_attrs_a;
|
||||||
} else {
|
} else if (ops->oo_id_attrs) {
|
||||||
req_attrs = ops->oo_id_attrs;
|
req_attrs = ops->oo_id_attrs;
|
||||||
|
} else {
|
||||||
|
req_attrs = 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
if (req_attrs == 0xFFFFFFFF)
|
if (req_attrs == 0xFFFFFFFF)
|
||||||
req_attrs = a->ce_mask & b->ce_mask;
|
req_attrs = a->ce_mask & b->ce_mask;
|
||||||
|
|
|
@ -326,7 +326,7 @@ int rtnl_link_af_unregister(struct rtnl_link_af_ops *ops)
|
||||||
int err = -NLE_INVAL;
|
int err = -NLE_INVAL;
|
||||||
|
|
||||||
if (!ops)
|
if (!ops)
|
||||||
goto errout;
|
return err;
|
||||||
|
|
||||||
nl_write_lock(&info_lock);
|
nl_write_lock(&info_lock);
|
||||||
if (!af_ops[ops->ao_family]) {
|
if (!af_ops[ops->ao_family]) {
|
||||||
|
@ -345,7 +345,7 @@ int rtnl_link_af_unregister(struct rtnl_link_af_ops *ops)
|
||||||
ops->ao_family);
|
ops->ao_family);
|
||||||
|
|
||||||
errout:
|
errout:
|
||||||
nl_write_lock(&info_lock);
|
nl_write_unlock(&info_lock);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,367 @@
|
||||||
|
/*
|
||||||
|
* lib/route/link/macvlan.c MACVLAN Link Info
|
||||||
|
*
|
||||||
|
* 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 version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 Michael Braun <michael-dev@fami-braun.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup link
|
||||||
|
* @defgroup macvlan MACVLAN
|
||||||
|
* MAC-based Virtual LAN link module
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* \b Link Type Name: "macvlan"
|
||||||
|
*
|
||||||
|
* @route_doc{link_macvlan, MACVLAN Documentation}
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-private/netlink.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/attr.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
#include <netlink/object.h>
|
||||||
|
#include <netlink/route/rtnl.h>
|
||||||
|
#include <netlink-private/route/link/api.h>
|
||||||
|
#include <netlink/route/link/macvlan.h>
|
||||||
|
|
||||||
|
#include <linux/if_link.h>
|
||||||
|
|
||||||
|
/** @cond SKIP */
|
||||||
|
#define MACVLAN_HAS_MODE (1<<0)
|
||||||
|
#define MACVLAN_HAS_FLAGS (1<<1)
|
||||||
|
|
||||||
|
struct macvlan_info
|
||||||
|
{
|
||||||
|
uint32_t mvi_mode;
|
||||||
|
uint16_t mvi_flags; // there currently is only one flag and kernel has no flags_mask yet
|
||||||
|
uint32_t mvi_mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
static struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX+1] = {
|
||||||
|
[IFLA_MACVLAN_MODE] = { .type = NLA_U32 },
|
||||||
|
[IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int macvlan_alloc(struct rtnl_link *link)
|
||||||
|
{
|
||||||
|
struct macvlan_info *mvi;
|
||||||
|
|
||||||
|
if ((mvi = calloc(1, sizeof(*mvi))) == NULL)
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
link->l_info = mvi;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int macvlan_parse(struct rtnl_link *link, struct nlattr *data,
|
||||||
|
struct nlattr *xstats)
|
||||||
|
{
|
||||||
|
struct nlattr *tb[IFLA_MACVLAN_MAX+1];
|
||||||
|
struct macvlan_info *mvi;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
NL_DBG(3, "Parsing MACVLAN link info");
|
||||||
|
|
||||||
|
if ((err = nla_parse_nested(tb, IFLA_MACVLAN_MAX, data, macvlan_policy)) < 0)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
if ((err = macvlan_alloc(link)) < 0)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
mvi = link->l_info;
|
||||||
|
|
||||||
|
if (tb[IFLA_MACVLAN_MODE]) {
|
||||||
|
mvi->mvi_mode = nla_get_u32(tb[IFLA_MACVLAN_MODE]);
|
||||||
|
mvi->mvi_mask |= MACVLAN_HAS_MODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tb[IFLA_MACVLAN_FLAGS]) {
|
||||||
|
mvi->mvi_mode = nla_get_u16(tb[IFLA_MACVLAN_FLAGS]);
|
||||||
|
mvi->mvi_mask |= MACVLAN_HAS_FLAGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
errout:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void macvlan_free(struct rtnl_link *link)
|
||||||
|
{
|
||||||
|
free(link->l_info);
|
||||||
|
link->l_info = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void macvlan_dump(struct rtnl_link *link, struct nl_dump_params *p)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
struct macvlan_info *mvi = link->l_info;
|
||||||
|
|
||||||
|
if (mvi->mvi_mask & MACVLAN_HAS_MODE) {
|
||||||
|
rtnl_link_macvlan_mode2str(mvi->mvi_mode, buf, sizeof(buf));
|
||||||
|
nl_dump(p, "macvlan-mode %s", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mvi->mvi_mask & MACVLAN_HAS_FLAGS) {
|
||||||
|
rtnl_link_macvlan_flags2str(mvi->mvi_flags, buf, sizeof(buf));
|
||||||
|
nl_dump(p, "macvlan-flags %s", buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int macvlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
|
||||||
|
{
|
||||||
|
struct macvlan_info *vdst, *vsrc = src->l_info;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
dst->l_info = NULL;
|
||||||
|
if ((err = rtnl_link_set_type(dst, "macvlan")) < 0)
|
||||||
|
return err;
|
||||||
|
vdst = dst->l_info;
|
||||||
|
|
||||||
|
if (!vdst || !vsrc)
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
memcpy(vdst, vsrc, sizeof(struct macvlan_info));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int macvlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
|
||||||
|
{
|
||||||
|
struct macvlan_info *mvi = link->l_info;
|
||||||
|
struct nlattr *data;
|
||||||
|
|
||||||
|
if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
|
||||||
|
return -NLE_MSGSIZE;
|
||||||
|
|
||||||
|
if (mvi->mvi_mask & MACVLAN_HAS_MODE)
|
||||||
|
NLA_PUT_U32(msg, IFLA_MACVLAN_MODE, mvi->mvi_mode);
|
||||||
|
|
||||||
|
if (mvi->mvi_mask & MACVLAN_HAS_FLAGS)
|
||||||
|
NLA_PUT_U16(msg, IFLA_MACVLAN_FLAGS, mvi->mvi_flags);
|
||||||
|
|
||||||
|
nla_nest_end(msg, data);
|
||||||
|
|
||||||
|
nla_put_failure:
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct rtnl_link_info_ops macvlan_info_ops = {
|
||||||
|
.io_name = "macvlan",
|
||||||
|
.io_alloc = macvlan_alloc,
|
||||||
|
.io_parse = macvlan_parse,
|
||||||
|
.io_dump = {
|
||||||
|
[NL_DUMP_LINE] = macvlan_dump,
|
||||||
|
[NL_DUMP_DETAILS] = macvlan_dump,
|
||||||
|
},
|
||||||
|
.io_clone = macvlan_clone,
|
||||||
|
.io_put_attrs = macvlan_put_attrs,
|
||||||
|
.io_free = macvlan_free,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @cond SKIP */
|
||||||
|
#define IS_MACVLAN_LINK_ASSERT(link) \
|
||||||
|
if ((link)->l_info_ops != &macvlan_info_ops) { \
|
||||||
|
APPBUG("Link is not a macvlan link. set type \"macvlan\" first."); \
|
||||||
|
return -NLE_OPNOTSUPP; \
|
||||||
|
}
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name MACVLAN Object
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate link object of type MACVLAN
|
||||||
|
*
|
||||||
|
* @return Allocated link object or NULL.
|
||||||
|
*/
|
||||||
|
struct rtnl_link *rtnl_link_macvlan_alloc(void)
|
||||||
|
{
|
||||||
|
struct rtnl_link *link;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!(link = rtnl_link_alloc()))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if ((err = rtnl_link_set_type(link, "macvlan")) < 0) {
|
||||||
|
rtnl_link_put(link);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if link is a MACVLAN link
|
||||||
|
* @arg link Link object
|
||||||
|
*
|
||||||
|
* @return True if link is a MACVLAN link, otherwise false is returned.
|
||||||
|
*/
|
||||||
|
int rtnl_link_is_macvlan(struct rtnl_link *link)
|
||||||
|
{
|
||||||
|
return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "macvlan");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set MACVLAN MODE
|
||||||
|
* @arg link Link object
|
||||||
|
* @arg mode MACVLAN mode
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code
|
||||||
|
*/
|
||||||
|
int rtnl_link_macvlan_set_mode(struct rtnl_link *link, uint32_t mode)
|
||||||
|
{
|
||||||
|
struct macvlan_info *mvi = link->l_info;
|
||||||
|
|
||||||
|
IS_MACVLAN_LINK_ASSERT(link);
|
||||||
|
|
||||||
|
mvi->mvi_mode = mode;
|
||||||
|
mvi->mvi_mask |= MACVLAN_HAS_MODE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get MACVLAN Mode
|
||||||
|
* @arg link Link object
|
||||||
|
*
|
||||||
|
* @return MACVLAN mode, 0 if not set or a negative error code.
|
||||||
|
*/
|
||||||
|
uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *link)
|
||||||
|
{
|
||||||
|
struct macvlan_info *mvi = link->l_info;
|
||||||
|
|
||||||
|
IS_MACVLAN_LINK_ASSERT(link);
|
||||||
|
|
||||||
|
if (mvi->mvi_mask & MACVLAN_HAS_MODE)
|
||||||
|
return mvi->mvi_mode;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set MACVLAN flags
|
||||||
|
* @arg link Link object
|
||||||
|
* @arg flags MACVLAN flags
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int rtnl_link_macvlan_set_flags(struct rtnl_link *link, uint16_t flags)
|
||||||
|
{
|
||||||
|
struct macvlan_info *mvi = link->l_info;
|
||||||
|
|
||||||
|
IS_MACVLAN_LINK_ASSERT(link);
|
||||||
|
|
||||||
|
mvi->mvi_flags |= flags;
|
||||||
|
mvi->mvi_mask |= MACVLAN_HAS_FLAGS;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unset MACVLAN flags
|
||||||
|
* @arg link Link object
|
||||||
|
* @arg flags MACVLAN flags
|
||||||
|
*
|
||||||
|
* Note: kernel currently only has a single flag and lacks flags_mask to
|
||||||
|
* indicate which flags shall be changed (it always all).
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int rtnl_link_macvlan_unset_flags(struct rtnl_link *link, uint16_t flags)
|
||||||
|
{
|
||||||
|
struct macvlan_info *mvi = link->l_info;
|
||||||
|
|
||||||
|
IS_MACVLAN_LINK_ASSERT(link);
|
||||||
|
|
||||||
|
mvi->mvi_flags &= ~flags;
|
||||||
|
mvi->mvi_mask |= MACVLAN_HAS_FLAGS;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get MACVLAN flags
|
||||||
|
* @arg link Link object
|
||||||
|
*
|
||||||
|
* @return MACVLAN flags, 0 if none set, or a negative error code.
|
||||||
|
*/
|
||||||
|
uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *link)
|
||||||
|
{
|
||||||
|
struct macvlan_info *mvi = link->l_info;
|
||||||
|
|
||||||
|
IS_MACVLAN_LINK_ASSERT(link);
|
||||||
|
|
||||||
|
return mvi->mvi_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
static const struct trans_tbl macvlan_flags[] = {
|
||||||
|
__ADD(MACVLAN_FLAG_NOPROMISC, nopromisc)
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct trans_tbl macvlan_modes[] = {
|
||||||
|
__ADD(MACVLAN_MODE_PRIVATE, private)
|
||||||
|
__ADD(MACVLAN_MODE_VEPA, vepa)
|
||||||
|
__ADD(MACVLAN_MODE_BRIDGE, bridge)
|
||||||
|
__ADD(MACVLAN_MODE_PASSTHRU, passthru)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Flag Translation
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *rtnl_link_macvlan_flags2str(int flags, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
return __flags2str(flags, buf, len, macvlan_flags, ARRAY_SIZE(macvlan_flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
int rtnl_link_macvlan_str2flags(const char *name)
|
||||||
|
{
|
||||||
|
return __str2flags(name, macvlan_flags, ARRAY_SIZE(macvlan_flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Mode Translation
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *rtnl_link_macvlan_mode2str(int mode, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
return __type2str(mode, buf, len, macvlan_modes, ARRAY_SIZE(macvlan_modes));
|
||||||
|
}
|
||||||
|
|
||||||
|
int rtnl_link_macvlan_str2mode(const char *name)
|
||||||
|
{
|
||||||
|
return __str2type(name, macvlan_modes, ARRAY_SIZE(macvlan_modes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
static void __init macvlan_init(void)
|
||||||
|
{
|
||||||
|
rtnl_link_register_info(&macvlan_info_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit macvlan_exit(void)
|
||||||
|
{
|
||||||
|
rtnl_link_unregister_info(&macvlan_info_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
|
@ -211,7 +211,9 @@ static void neigh_keygen(struct nl_object *obj, uint32_t *hashkey,
|
||||||
uint32_t n_ifindex;
|
uint32_t n_ifindex;
|
||||||
char n_addr[0];
|
char n_addr[0];
|
||||||
} __attribute__((packed)) *nkey;
|
} __attribute__((packed)) *nkey;
|
||||||
|
#ifdef NL_DEBUG
|
||||||
char buf[INET6_ADDRSTRLEN+5];
|
char buf[INET6_ADDRSTRLEN+5];
|
||||||
|
#endif
|
||||||
|
|
||||||
if (neigh->n_family == AF_BRIDGE) {
|
if (neigh->n_family == AF_BRIDGE) {
|
||||||
if (neigh->n_lladdr)
|
if (neigh->n_lladdr)
|
||||||
|
|
|
@ -307,7 +307,9 @@ static void route_keygen(struct nl_object *obj, uint32_t *hashkey,
|
||||||
uint32_t rt_prio;
|
uint32_t rt_prio;
|
||||||
char rt_addr[0];
|
char rt_addr[0];
|
||||||
} __attribute__((packed)) *rkey;
|
} __attribute__((packed)) *rkey;
|
||||||
|
#ifdef NL_DEBUG
|
||||||
char buf[INET6_ADDRSTRLEN+5];
|
char buf[INET6_ADDRSTRLEN+5];
|
||||||
|
#endif
|
||||||
|
|
||||||
if (route->rt_dst)
|
if (route->rt_dst)
|
||||||
addr = route->rt_dst;
|
addr = route->rt_dst;
|
||||||
|
@ -449,8 +451,10 @@ static int route_update(struct nl_object *old_obj, struct nl_object *new_obj)
|
||||||
struct rtnl_route *new_route = (struct rtnl_route *) new_obj;
|
struct rtnl_route *new_route = (struct rtnl_route *) new_obj;
|
||||||
struct rtnl_route *old_route = (struct rtnl_route *) old_obj;
|
struct rtnl_route *old_route = (struct rtnl_route *) old_obj;
|
||||||
struct rtnl_nexthop *new_nh;
|
struct rtnl_nexthop *new_nh;
|
||||||
char buf[INET6_ADDRSTRLEN+5];
|
|
||||||
int action = new_obj->ce_msgtype;
|
int action = new_obj->ce_msgtype;
|
||||||
|
#ifdef NL_DEBUG
|
||||||
|
char buf[INET6_ADDRSTRLEN+5];
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ipv6 ECMP route notifications from the kernel come as
|
* ipv6 ECMP route notifications from the kernel come as
|
||||||
|
|
15
lib/socket.c
15
lib/socket.c
|
@ -120,7 +120,7 @@ static struct nl_sock *__alloc_socket(struct nl_cb *cb)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
sk->s_fd = -1;
|
sk->s_fd = -1;
|
||||||
sk->s_cb = cb;
|
sk->s_cb = nl_cb_get(cb);
|
||||||
sk->s_local.nl_family = AF_NETLINK;
|
sk->s_local.nl_family = AF_NETLINK;
|
||||||
sk->s_peer.nl_family = AF_NETLINK;
|
sk->s_peer.nl_family = AF_NETLINK;
|
||||||
sk->s_seq_expect = sk->s_seq_next = time(0);
|
sk->s_seq_expect = sk->s_seq_next = time(0);
|
||||||
|
@ -141,12 +141,18 @@ static struct nl_sock *__alloc_socket(struct nl_cb *cb)
|
||||||
struct nl_sock *nl_socket_alloc(void)
|
struct nl_sock *nl_socket_alloc(void)
|
||||||
{
|
{
|
||||||
struct nl_cb *cb;
|
struct nl_cb *cb;
|
||||||
|
struct nl_sock *sk;
|
||||||
|
|
||||||
cb = nl_cb_alloc(default_cb);
|
cb = nl_cb_alloc(default_cb);
|
||||||
if (!cb)
|
if (!cb)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return __alloc_socket(cb);
|
/* will increment cb reference count on success */
|
||||||
|
sk = __alloc_socket(cb);
|
||||||
|
|
||||||
|
nl_cb_put(cb);
|
||||||
|
|
||||||
|
return sk;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -163,7 +169,7 @@ struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb)
|
||||||
if (cb == NULL)
|
if (cb == NULL)
|
||||||
BUG();
|
BUG();
|
||||||
|
|
||||||
return __alloc_socket(nl_cb_get(cb));
|
return __alloc_socket(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -519,6 +525,9 @@ struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk)
|
||||||
|
|
||||||
void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb)
|
void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb)
|
||||||
{
|
{
|
||||||
|
if (cb == NULL)
|
||||||
|
BUG();
|
||||||
|
|
||||||
nl_cb_put(sk->s_cb);
|
nl_cb_put(sk->s_cb);
|
||||||
sk->s_cb = nl_cb_get(cb);
|
sk->s_cb = nl_cb_get(cb);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
int nl_debug = 0;
|
int nl_debug = 0;
|
||||||
|
|
||||||
/** @cond SKIP */
|
/** @cond SKIP */
|
||||||
|
#ifdef NL_DEBUG
|
||||||
struct nl_dump_params nl_debug_dp = {
|
struct nl_dump_params nl_debug_dp = {
|
||||||
.dp_type = NL_DUMP_DETAILS,
|
.dp_type = NL_DUMP_DETAILS,
|
||||||
};
|
};
|
||||||
|
@ -65,6 +66,7 @@ static void __init nl_debug_init(void)
|
||||||
|
|
||||||
nl_debug_dp.dp_fd = stderr;
|
nl_debug_dp.dp_fd = stderr;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *))
|
int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *))
|
||||||
{
|
{
|
||||||
|
@ -683,7 +685,9 @@ static const struct trans_tbl llprotos[] = {
|
||||||
__ADD(ARPHRD_IEEE802_TR,tr)
|
__ADD(ARPHRD_IEEE802_TR,tr)
|
||||||
__ADD(ARPHRD_IEEE80211,ieee802.11)
|
__ADD(ARPHRD_IEEE80211,ieee802.11)
|
||||||
__ADD(ARPHRD_PHONET,phonet)
|
__ADD(ARPHRD_PHONET,phonet)
|
||||||
|
#ifdef ARPHRD_CAIF
|
||||||
__ADD(ARPHRD_CAIF, caif)
|
__ADD(ARPHRD_CAIF, caif)
|
||||||
|
#endif
|
||||||
#ifdef ARPHRD_IEEE80211_PRISM
|
#ifdef ARPHRD_IEEE80211_PRISM
|
||||||
__ADD(ARPHRD_IEEE80211_PRISM, ieee802.11_prism)
|
__ADD(ARPHRD_IEEE80211_PRISM, ieee802.11_prism)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <netlink/route/rtnl.h>
|
#include <netlink/route/rtnl.h>
|
||||||
#include <netlink/route/link.h>
|
#include <netlink/route/link.h>
|
||||||
#include <netlink/route/link/vlan.h>
|
#include <netlink/route/link/vlan.h>
|
||||||
|
#include <netlink/route/link/macvlan.h>
|
||||||
#include <netlink/route/link/inet.h>
|
#include <netlink/route/link/inet.h>
|
||||||
|
|
||||||
#include <netlink/route/tc.h>
|
#include <netlink/route/tc.h>
|
||||||
|
@ -169,6 +170,21 @@ extern uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *);
|
||||||
extern int rtnl_link_vlan_set_egress_map(struct rtnl_link *, uint32_t, int);
|
extern int rtnl_link_vlan_set_egress_map(struct rtnl_link *, uint32_t, int);
|
||||||
extern struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *, int *);
|
extern struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *, int *);
|
||||||
|
|
||||||
|
/* <netlink/route/link/macvlan.h> */
|
||||||
|
|
||||||
|
%cstring_output_maxsize(char *buf, size_t len)
|
||||||
|
extern struct rtnl_link *rtnl_link_macvlan_alloc(void);
|
||||||
|
extern int rtnl_link_is_macvlan(struct rtnl_link *);
|
||||||
|
extern char * rtnl_link_macvlan_mode2str(int, char *, size_t);
|
||||||
|
extern int rtnl_link_macvlan_str2mode(const char *);
|
||||||
|
extern char * rtnl_link_macvlan_flags2str(int, char *, size_t);
|
||||||
|
extern int rtnl_link_macvlan_str2flags(const char *);
|
||||||
|
extern int rtnl_link_macvlan_set_mode(struct rtnl_link *, uint32_t);
|
||||||
|
extern uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *);
|
||||||
|
extern int rtnl_link_macvlan_set_flags(struct rtnl_link *, uint16_t);
|
||||||
|
extern int rtnl_link_macvlan_unset_flags(struct rtnl_link *, uint16_t);
|
||||||
|
extern uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *);
|
||||||
|
|
||||||
/* <netlink/route/link/inet.h> */
|
/* <netlink/route/link/inet.h> */
|
||||||
%cstring_output_maxsize(char *buf, size_t len)
|
%cstring_output_maxsize(char *buf, size_t len)
|
||||||
extern const char *rtnl_link_inet_devconf2str(int, char *buf, size_t len);
|
extern const char *rtnl_link_inet_devconf2str(int, char *buf, size_t len);
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
# -*- Makefile -*-
|
# -*- Makefile -*-
|
||||||
|
|
||||||
|
EXTRA_DIST = \
|
||||||
|
util.h
|
||||||
|
|
||||||
|
if ENABLE_UNIT_TESTS
|
||||||
|
|
||||||
AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\"
|
AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\"
|
||||||
|
|
||||||
LDADD = \
|
LDADD = \
|
||||||
|
@ -44,4 +49,6 @@ test_complex_HTB_with_hash_filters_SOURCES = test-complex-HTB-with-hash-filters.
|
||||||
# Unit tests
|
# Unit tests
|
||||||
check_all_SOURCES = \
|
check_all_SOURCES = \
|
||||||
check-all.c \
|
check-all.c \
|
||||||
check-addr.c
|
check-addr.c \
|
||||||
|
check-attr.c
|
||||||
|
endif
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <check.h>
|
#include <check.h>
|
||||||
|
|
||||||
extern Suite *make_nl_addr_suite(void);
|
extern Suite *make_nl_addr_suite(void);
|
||||||
|
extern Suite *make_nl_attr_suite(void);
|
||||||
|
|
||||||
static Suite *main_suite(void)
|
static Suite *main_suite(void)
|
||||||
{
|
{
|
||||||
|
@ -30,6 +31,7 @@ int main(int argc, char *argv[])
|
||||||
/* Add testsuites below */
|
/* Add testsuites below */
|
||||||
|
|
||||||
srunner_add_suite(runner, make_nl_addr_suite());
|
srunner_add_suite(runner, make_nl_addr_suite());
|
||||||
|
srunner_add_suite(runner, make_nl_attr_suite());
|
||||||
|
|
||||||
/* Do not add testsuites below this line */
|
/* Do not add testsuites below this line */
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* tests/check-attr.c nla_attr unit tests
|
||||||
|
*
|
||||||
|
* 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 version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include <netlink/attr.h>
|
||||||
|
#include <netlink/msg.h>
|
||||||
|
|
||||||
|
START_TEST(attr_size)
|
||||||
|
{
|
||||||
|
fail_if(nla_attr_size(0) != NLA_HDRLEN,
|
||||||
|
"Length of empty attribute should match header size");
|
||||||
|
fail_if(nla_attr_size(1) != NLA_HDRLEN + 1,
|
||||||
|
"Length of 1 bytes payload should be NLA_HDRLEN + 1");
|
||||||
|
fail_if(nla_attr_size(2) != NLA_HDRLEN + 2,
|
||||||
|
"Length of 2 bytes payload should be NLA_HDRLEN + 2");
|
||||||
|
fail_if(nla_attr_size(3) != NLA_HDRLEN + 3,
|
||||||
|
"Length of 3 bytes payload should be NLA_HDRLEN + 3");
|
||||||
|
fail_if(nla_attr_size(4) != NLA_HDRLEN + 4,
|
||||||
|
"Length of 4 bytes payload should be NLA_HDRLEN + 4");
|
||||||
|
|
||||||
|
fail_if(nla_total_size(1) != NLA_HDRLEN + 4,
|
||||||
|
"Total size of 1 bytes payload should result in 8 bytes");
|
||||||
|
fail_if(nla_total_size(2) != NLA_HDRLEN + 4,
|
||||||
|
"Total size of 2 bytes payload should result in 8 bytes");
|
||||||
|
fail_if(nla_total_size(3) != NLA_HDRLEN + 4,
|
||||||
|
"Total size of 3 bytes payload should result in 8 bytes");
|
||||||
|
fail_if(nla_total_size(4) != NLA_HDRLEN + 4,
|
||||||
|
"Total size of 4 bytes payload should result in 8 bytes");
|
||||||
|
|
||||||
|
fail_if(nla_padlen(1) != 3,
|
||||||
|
"2 bytes of payload should result in 3 padding bytes");
|
||||||
|
fail_if(nla_padlen(2) != 2,
|
||||||
|
"2 bytes of payload should result in 2 padding bytes");
|
||||||
|
fail_if(nla_padlen(3) != 1,
|
||||||
|
"3 bytes of payload should result in 1 padding bytes");
|
||||||
|
fail_if(nla_padlen(4) != 0,
|
||||||
|
"4 bytes of payload should result in 0 padding bytes");
|
||||||
|
fail_if(nla_padlen(5) != 3,
|
||||||
|
"5 bytes of payload should result in 3 padding bytes");
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(msg_construct)
|
||||||
|
{
|
||||||
|
struct nl_msg *msg;
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
struct nlattr *a;
|
||||||
|
int i, rem;
|
||||||
|
|
||||||
|
msg = nlmsg_alloc();
|
||||||
|
fail_if(!msg, "Unable to allocate netlink message");
|
||||||
|
|
||||||
|
for (i = 1; i < 256; i++) {
|
||||||
|
fail_if(nla_put_u32(msg, i, i+1) != 0,
|
||||||
|
"Unable to add attribute %d", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
nlh = nlmsg_hdr(msg);
|
||||||
|
i = 1;
|
||||||
|
nlmsg_for_each_attr(a, nlh, 0, rem) {
|
||||||
|
fail_if(nla_type(a) != i, "Expected attribute %d", i);
|
||||||
|
i++;
|
||||||
|
fail_if(nla_get_u32(a) != i, "Expected attribute value %d", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
nlmsg_free(msg);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
Suite *make_nl_attr_suite(void)
|
||||||
|
{
|
||||||
|
Suite *suite = suite_create("Netlink attributes");
|
||||||
|
|
||||||
|
TCase *nl_attr = tcase_create("Core");
|
||||||
|
tcase_add_test(nl_attr, attr_size);
|
||||||
|
tcase_add_test(nl_attr, msg_construct);
|
||||||
|
suite_add_tcase(suite, nl_attr);
|
||||||
|
|
||||||
|
return suite;
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/route/link.h>
|
||||||
|
#include <netlink/route/link/macvlan.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct rtnl_link *link;
|
||||||
|
struct nl_cache *link_cache;
|
||||||
|
struct nl_sock *sk;
|
||||||
|
struct nl_addr* addr;
|
||||||
|
int err, master_index;
|
||||||
|
|
||||||
|
sk = nl_socket_alloc();
|
||||||
|
if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
|
||||||
|
nl_perror(err, "Unable to connect socket");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) {
|
||||||
|
nl_perror(err, "Unable to allocate cache");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) {
|
||||||
|
fprintf(stderr, "Unable to lookup eth0");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
link = rtnl_link_macvlan_alloc();
|
||||||
|
|
||||||
|
rtnl_link_set_link(link, master_index);
|
||||||
|
|
||||||
|
addr = nl_addr_build(AF_LLC, ether_aton("00:11:22:33:44:55"), ETH_ALEN);
|
||||||
|
rtnl_link_set_addr(link, addr);
|
||||||
|
nl_addr_put(addr);
|
||||||
|
|
||||||
|
rtnl_link_macvlan_set_mode(link, rtnl_link_macvlan_str2mode("bridge"));
|
||||||
|
|
||||||
|
if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
|
||||||
|
nl_perror(err, "Unable to add link");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtnl_link_put(link);
|
||||||
|
nl_close(sk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
#include <check.h>
|
||||||
|
|
||||||
|
#define nl_fail_if(condition, error, message) \
|
||||||
|
fail_if((condition), "nlerr=%d (%s): %s", \
|
||||||
|
(error), nl_geterror(error), (message))
|
Reference in New Issue