diff --git a/configure.ac b/configure.ac index 7797c3d..f314e5c 100644 --- a/configure.ac +++ b/configure.ac @@ -13,7 +13,7 @@ # copied from glib m4_define([libnl_major_version], [3]) 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 @@ -34,9 +34,9 @@ m4_define([libnl_micro_version], [21]) # 3. Programs may need to be changed, recompiled, relinked in order to use # the new version. Bump current, set revision and age to 0. -m4_define([libnl_lt_current], [216]) -m4_define([libnl_lt_revision], [1]) -m4_define([libnl_lt_age], [16]) +m4_define([libnl_lt_current], [217]) +m4_define([libnl_lt_revision], [0]) +m4_define([libnl_lt_age], [17]) m4_define([libnl_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_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], [Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]), @@ -92,6 +97,11 @@ AC_ARG_ENABLE([pthreads], [enable_pthreads="$enableval"], [enable_pthreads="yes"]) 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])) 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])) 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_FILES([ diff --git a/doc/configure.ac b/doc/configure.ac index 4c3a52f..42cde8d 100644 --- a/doc/configure.ac +++ b/doc/configure.ac @@ -9,7 +9,7 @@ # Copyright (c) 2003-2013 Thomas Graf # -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_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([foreign]) diff --git a/doc/route.txt b/doc/route.txt index c8f1735..6c97f7b 100644 --- a/doc/route.txt +++ b/doc/route.txt @@ -706,6 +706,63 @@ if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) 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 == Routing diff --git a/include/Makefile.am b/include/Makefile.am index e4fe6a4..047f16b 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -52,6 +52,7 @@ nobase_libnlinclude_HEADERS = \ netlink/route/link/can.h \ netlink/route/link/inet.h \ netlink/route/link/vlan.h \ + netlink/route/link/macvlan.h \ netlink/route/qdisc/cbq.h \ netlink/route/qdisc/dsmark.h \ netlink/route/qdisc/fifo.h \ diff --git a/include/netlink-private/genl.h b/include/netlink-private/genl.h index 0aca6d7..5b93db3 100644 --- a/include/netlink-private/genl.h +++ b/include/netlink-private/genl.h @@ -17,4 +17,6 @@ #define GENL_HDRSIZE(hdrlen) (GENL_HDRLEN + (hdrlen)) +extern int genl_resolve_id(struct genl_ops *ops); + #endif diff --git a/include/netlink-private/netlink.h b/include/netlink-private/netlink.h index 6ae6d17..2e511bf 100644 --- a/include/netlink-private/netlink.h +++ b/include/netlink-private/netlink.h @@ -80,24 +80,29 @@ struct trans_list { struct nl_list_head list; }; -#define NL_DEBUG 1 - -#define NL_DBG(LVL,FMT,ARG...) \ - do { \ - if (LVL <= nl_debug) \ - fprintf(stderr, "DBG<" #LVL ">: " FMT, ##ARG); \ +#ifdef NL_DEBUG +#define NL_DBG(LVL,FMT,ARG...) \ + do { \ + if (LVL <= nl_debug) \ + fprintf(stderr, \ + "DBG<" #LVL ">%20s:%-4u %s: " FMT, \ + __FILE__, __LINE__, \ + __PRETTY_FUNCTION__, ##ARG); \ } while (0) +#else /* NL_DEBUG */ +#define NL_DBG(LVL,FMT,ARG...) do { } while(0) +#endif /* NL_DEBUG */ #define BUG() \ do { \ - NL_DBG(1, "BUG: %s:%d\n", \ - __FILE__, __LINE__); \ + fprintf(stderr, "BUG at file position %s:%d:%s\n", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__); \ assert(0); \ } while (0) #define APPBUG(msg) \ 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); \ assert(0); \ } while(0) diff --git a/include/netlink/netfilter/queue_msg.h b/include/netlink/netfilter/queue_msg.h index 24ed081..9befee7 100644 --- a/include/netlink/netfilter/queue_msg.h +++ b/include/netlink/netfilter/queue_msg.h @@ -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 int nfnl_queue_msg_send_verdict(struct nl_sock *, 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 *, const struct nfnl_queue_msg *, const void *, unsigned ); diff --git a/include/netlink/route/link/macvlan.h b/include/netlink/route/link/macvlan.h new file mode 100644 index 0000000..2207c53 --- /dev/null +++ b/include/netlink/route/link/macvlan.h @@ -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 + */ + +#ifndef NETLINK_LINK_MACVLAN_H_ +#define NETLINK_LINK_MACVLAN_H_ + +#include +#include + +#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 diff --git a/lib/Makefile.am b/lib/Makefile.am index 438b3eb..7203810 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -75,7 +75,7 @@ libnl_route_3_la_SOURCES = \ \ 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/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/fifo.c route/qdisc/htb.c route/qdisc/netem.c \ diff --git a/lib/addr.c b/lib/addr.c index 557b0ad..86e7e58 100644 --- a/lib/addr.c +++ b/lib/addr.c @@ -1037,10 +1037,18 @@ static const struct trans_tbl afs[] = { __ADD(AF_RXRPC,rxrpc) __ADD(AF_ISDN,isdn) __ADD(AF_PHONET,phonet) +#ifdef AF_IEEE802154 __ADD(AF_IEEE802154,ieee802154) +#endif +#ifdef AF_CAIF __ADD(AF_CAIF,caif) +#endif +#ifdef AF_ALG __ADD(AF_ALG,alg) +#endif +#ifdef AF_NFC __ADD(AF_NFC,nfc) +#endif }; char *nl_af2str(int family, char *buf, size_t size) diff --git a/lib/attr.c b/lib/attr.c index e6efe4e..535f10c 100644 --- a/lib/attr.c +++ b/lib/attr.c @@ -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); - if ((tlen + msg->nm_nlh->nlmsg_len) > msg->nm_size) + if (tlen > msg->nm_size) return NULL; 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) { - return !!(nla_type(attr) & NLA_F_NESTED); + return !!(attr->nla_type & NLA_F_NESTED); } /** @} */ diff --git a/lib/cache.c b/lib/cache.c index fafc023..e99e9d2 100644 --- a/lib/cache.c +++ b/lib/cache.c @@ -307,6 +307,9 @@ struct nl_cache *nl_cache_subset(struct nl_cache *orig, if (!cache) 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) { if (!nl_object_match_filter(obj, filter)) continue; @@ -341,6 +344,8 @@ struct nl_cache *nl_cache_clone(struct nl_cache *cache) if (!clone) return NULL; + NL_DBG(2, "Cloning %p into %p\n", cache, clone); + nl_list_for_each_entry(obj, &cache->c_items, ce_list) nl_cache_add(clone, obj); @@ -362,7 +367,7 @@ void nl_cache_clear(struct nl_cache *cache) { 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_cache_remove(obj); @@ -375,7 +380,7 @@ static void __nl_cache_free(struct nl_cache *cache) if (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); } @@ -386,6 +391,9 @@ static void __nl_cache_free(struct nl_cache *cache) void nl_cache_get(struct nl_cache *cache) { 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; 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) __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); cache->c_nitems++; - NL_DBG(1, "Added %p to cache %p <%s>.\n", - obj, cache, nl_cache_name(cache)); + NL_DBG(3, "Added object %p to cache %p <%s>, nitems %d\n", + obj, cache, nl_cache_name(cache), cache->c_nitems); return 0; } @@ -476,6 +485,8 @@ int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) return -NLE_OBJ_MISMATCH; 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); if (!new) 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) 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 * reverted during removal */ @@ -547,7 +559,7 @@ void nl_cache_remove(struct nl_object *obj) if (cache->hashtable) { ret = nl_hash_table_del(cache->hashtable, obj); 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)); } @@ -556,7 +568,7 @@ void nl_cache_remove(struct nl_object *obj) nl_object_put(obj); 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)); } @@ -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, 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) 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); } @@ -674,8 +686,8 @@ static int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache, .params = param, }; - NL_DBG(1, "Picking up answer for cache %p <%s>...\n", - cache, nl_cache_name(cache)); + NL_DBG(2, "Picking up answer for cache %p <%s>\n", + cache, nl_cache_name(cache)); cb = nl_cb_clone(sk->s_cb); if (cb == NULL) @@ -685,9 +697,8 @@ static int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache, err = nl_recvmsgs(sk, cb); if (err < 0) - NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned " \ - "%d: %s", cache, nl_cache_name(cache), - err, nl_geterror(err)); + NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned %d: %s\n", + cache, nl_cache_name(cache), err, nl_geterror(err)); 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], 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; } @@ -951,9 +965,12 @@ restart: if (err < 0) 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); if (err == -NLE_DUMP_INTR) { - NL_DBG(1, "dump interrupted, restarting!\n"); + NL_DBG(2, "Dump interrupted, restarting!\n"); goto restart; } else if (err < 0) break; @@ -963,9 +980,6 @@ restart: } while (grp && grp->ag_group && (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; } @@ -1072,8 +1086,8 @@ void nl_cache_mark_all(struct nl_cache *cache) { struct nl_object *obj; - NL_DBG(2, "Marking all objects in cache %p <%s>...\n", - cache, nl_cache_name(cache)); + NL_DBG(2, "Marking all objects in cache %p <%s>\n", + cache, nl_cache_name(cache)); nl_list_for_each_entry(obj, &cache->c_items, ce_list) nl_object_mark(obj); @@ -1115,7 +1129,7 @@ void nl_cache_dump_filter(struct nl_cache *cache, struct nl_object_ops *ops; 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); if (type > NL_DUMP_MAX || type < 0) diff --git a/lib/cache_mngr.c b/lib/cache_mngr.c index f8a65e3..7fb1ed0 100644 --- a/lib/cache_mngr.c +++ b/lib/cache_mngr.c @@ -435,7 +435,7 @@ int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr) } nl_cb_put(cb); - if (err < 0) + if (err < 0 && err != -NLE_AGAIN) return err; return nread; diff --git a/lib/genl/ctrl.c b/lib/genl/ctrl.c index c6db742..ce07f1d 100644 --- a/lib/genl/ctrl.c +++ b/lib/genl/ctrl.c @@ -239,7 +239,7 @@ static struct genl_family *genl_ctrl_probe_by_name(struct nl_sock *sk, { struct nl_msg *msg; struct genl_family *ret; - struct nl_cb *cb; + struct nl_cb *cb, *orig; int rc; ret = genl_family_alloc(); @@ -252,7 +252,12 @@ static struct genl_family *genl_ctrl_probe_by_name(struct nl_sock *sk, if (!msg) 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; if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, diff --git a/lib/genl/mngt.c b/lib/genl/mngt.c index a3faaf2..3648663 100644 --- a/lib/genl/mngt.c +++ b/lib/genl/mngt.c @@ -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); if (family != NULL) { 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); return 0; @@ -302,6 +306,32 @@ static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops) 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 */ /** diff --git a/lib/msg.c b/lib/msg.c index b07de63..3c58cb7 100644 --- a/lib/msg.c +++ b/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) { - 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; char ascii[21] = {0}; - limit = 18 - (prefix * 2); + limit = 16 - (prefix * 2); prefix_line(ofd, prefix); fprintf(ofd, " "); @@ -777,7 +777,7 @@ static inline void dump_hex(FILE *ofd, char *start, int len, int prefix) fprintf(ofd, "%02x ", v); ascii[a++] = isprint(v) ? v : '.'; - if (c == limit-1) { + if (++c >= limit) { fprintf(ofd, "%s\n", ascii); if (i < (len - 1)) { prefix_line(ofd, prefix); @@ -785,8 +785,7 @@ static inline void dump_hex(FILE *ofd, char *start, int len, int prefix) } a = c = 0; memset(ascii, 0, sizeof(ascii)); - } else - c++; + } } if (c != 0) { @@ -816,14 +815,62 @@ static void print_hdr(FILE *ofd, struct nl_msg *msg) } else nl_nlmsgtype2str(nlh->nlmsg_type, buf, sizeof(buf)); - fprintf(ofd, " .nlmsg_type = %d <%s>\n", nlh->nlmsg_type, buf); - fprintf(ofd, " .nlmsg_flags = %d <%s>\n", nlh->nlmsg_flags, + fprintf(ofd, " .type = %d <%s>\n", nlh->nlmsg_type, buf); + fprintf(ofd, " .flags = %d <%s>\n", nlh->nlmsg_flags, nl_nlmsg_flags2str(nlh->nlmsg_flags, buf, sizeof(buf))); - fprintf(ofd, " .nlmsg_seq = %d\n", nlh->nlmsg_seq); - fprintf(ofd, " .nlmsg_pid = %d\n", nlh->nlmsg_pid); + fprintf(ofd, " .seq = %d\n", nlh->nlmsg_seq); + 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, 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); else fprintf(ofd, " [ATTR %02d%s] %d octets\n", nla_type(nla), - nla->nla_type & NLA_F_NESTED ? " NESTED" : "", + nla_is_nested(nla) ? " NESTED" : "", alen); - if (nla->nla_type & NLA_F_NESTED) + if (nla_is_nested(nla)) dump_attrs(ofd, nla_data(nla), alen, prefix+1); else - dump_hex(ofd, nla_data(nla), alen, prefix); + dump_attr(ofd, nla, prefix); padlen = nla_padlen(alen); if (padlen > 0) { @@ -884,6 +931,42 @@ static void dump_error_msg(struct nl_msg *msg, FILE *ofd) } } +static void print_msg(struct nl_msg *msg, FILE *ofd, struct nlmsghdr *hdr) +{ + struct nl_cache_ops *ops; + int payloadlen = nlmsg_len(hdr); + int attrlen = 0; + void *data; + + data = nlmsg_data(hdr); + ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg), + hdr->nlmsg_type); + if (ops) { + attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); + 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); + dump_hex(ofd, data, payloadlen, 0); + } + + if (attrlen) { + struct nlattr *attrs; + int attrlen; + + attrs = nlmsg_attrdata(hdr, ops->co_hdrsize); + attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); + dump_attrs(ofd, attrs, attrlen, 0); + } + + if (ops) + nl_cache_ops_put(ops); +} + /** * Dump message in human readable format to file descriptor * @arg msg Message to print @@ -894,45 +977,18 @@ void nl_msg_dump(struct nl_msg *msg, FILE *ofd) struct nlmsghdr *hdr = nlmsg_hdr(msg); fprintf(ofd, - "-------------------------- BEGIN NETLINK MESSAGE " - "---------------------------\n"); + "-------------------------- BEGIN NETLINK MESSAGE ---------------------------\n"); - fprintf(ofd, " [HEADER] %zu octets\n", sizeof(struct nlmsghdr)); + 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) { - struct nl_cache_ops *ops; - int payloadlen = nlmsg_len(hdr); - int attrlen = 0; - - ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg), - hdr->nlmsg_type); - if (ops) { - attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); - payloadlen -= attrlen; - } - - fprintf(ofd, " [PAYLOAD] %d octets\n", payloadlen); - dump_hex(ofd, nlmsg_data(hdr), payloadlen, 0); - - if (attrlen) { - struct nlattr *attrs; - int attrlen; - - attrs = nlmsg_attrdata(hdr, ops->co_hdrsize); - attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); - dump_attrs(ofd, attrs, attrlen, 0); - } - - if (ops) - nl_cache_ops_put(ops); - } + else if (nlmsg_len(hdr) > 0) + print_msg(msg, ofd, hdr); fprintf(ofd, - "--------------------------- END NETLINK MESSAGE " - "---------------------------\n"); + "--------------------------- END NETLINK MESSAGE ---------------------------\n"); } /** @} */ diff --git a/lib/netfilter/ct.c b/lib/netfilter/ct.c index 5dde1d1..794932f 100644 --- a/lib/netfilter/ct.c +++ b/lib/netfilter/ct.c @@ -174,15 +174,28 @@ static int ct_parse_proto(struct nfnl_ct *ct, int repl, struct nlattr *attr) if (tb[CTA_PROTO_DST_PORT]) nfnl_ct_set_dst_port(ct, repl, ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT]))); - if (tb[CTA_PROTO_ICMP_ID]) - nfnl_ct_set_icmp_id(ct, repl, - ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID]))); - if (tb[CTA_PROTO_ICMP_TYPE]) - nfnl_ct_set_icmp_type(ct, repl, + + if (ct->ct_family == AF_INET) { + if (tb[CTA_PROTO_ICMP_ID]) + nfnl_ct_set_icmp_id(ct, repl, + ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID]))); + if (tb[CTA_PROTO_ICMP_TYPE]) + nfnl_ct_set_icmp_type(ct, repl, nla_get_u8(tb[CTA_PROTO_ICMP_TYPE])); - if (tb[CTA_PROTO_ICMP_CODE]) - nfnl_ct_set_icmp_code(ct, repl, + if (tb[CTA_PROTO_ICMP_CODE]) + nfnl_ct_set_icmp_code(ct, repl, 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; } @@ -426,17 +439,31 @@ static int nfnl_ct_build_tuple(struct nl_msg *msg, const struct nfnl_ct *ct, NLA_PUT_U16(msg, CTA_PROTO_DST_PORT, htons(nfnl_ct_get_dst_port(ct, repl))); - if (nfnl_ct_test_icmp_id(ct, repl)) - NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID, - htons(nfnl_ct_get_icmp_id(ct, repl))); + if (family == AF_INET) { + if (nfnl_ct_test_icmp_id(ct, repl)) + NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID, + htons(nfnl_ct_get_icmp_id(ct, repl))); - if (nfnl_ct_test_icmp_type(ct, repl)) - NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE, - nfnl_ct_get_icmp_type(ct, repl)); + if (nfnl_ct_test_icmp_type(ct, repl)) + NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE, + nfnl_ct_get_icmp_type(ct, repl)); - if (nfnl_ct_test_icmp_code(ct, repl)) - NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE, - nfnl_ct_get_icmp_code(ct, repl)); + if (nfnl_ct_test_icmp_code(ct, repl)) + NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE, + 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); diff --git a/lib/netfilter/queue_msg.c b/lib/netfilter/queue_msg.c index 95d8ad3..1425577 100644 --- a/lib/netfilter/queue_msg.c +++ b/lib/netfilter/queue_msg.c @@ -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 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_group(msg)); if (nlmsg == NULL) @@ -191,6 +193,18 @@ nla_put_failure: 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 * @arg nlh netlink messsage header @@ -214,6 +228,29 @@ int nfnl_queue_msg_send_verdict(struct nl_sock *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 * @arg nlh netlink messsage header diff --git a/lib/nl.c b/lib/nl.c index fa43c56..565747a 100644 --- a/lib/nl.c +++ b/lib/nl.c @@ -597,7 +597,7 @@ int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, flags |= MSG_PEEK | MSG_TRUNC; if (page_size == 0) - page_size = getpagesize(); + page_size = getpagesize() * 4; iov.iov_len = sk->s_bufsize ? : page_size; iov.iov_base = malloc(iov.iov_len); @@ -627,11 +627,6 @@ retry: NL_DBG(3, "recvmsg() returned EINTR, retrying\n"); goto retry; } - if (errno == EAGAIN || errno == EWOULDBLOCK) { - NL_DBG(3, "recvmsg() returned EAGAIN||EWOULDBLOCK, aborting\n"); - retval = 0; - goto abort; - } retval = -nl_syserr2nlerr(errno); goto abort; } diff --git a/lib/object.c b/lib/object.c index 17b98f6..9293df9 100644 --- a/lib/object.c +++ b/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) { - 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) 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) return 0; req_attrs = req_attrs_a; - } else { + } else if (ops->oo_id_attrs) { req_attrs = ops->oo_id_attrs; + } else { + req_attrs = 0xFFFFFFFF; } if (req_attrs == 0xFFFFFFFF) req_attrs = a->ce_mask & b->ce_mask; diff --git a/lib/route/link/api.c b/lib/route/link/api.c index 352bb83..6d1e12f 100644 --- a/lib/route/link/api.c +++ b/lib/route/link/api.c @@ -326,7 +326,7 @@ int rtnl_link_af_unregister(struct rtnl_link_af_ops *ops) int err = -NLE_INVAL; if (!ops) - goto errout; + return err; nl_write_lock(&info_lock); if (!af_ops[ops->ao_family]) { @@ -345,7 +345,7 @@ int rtnl_link_af_unregister(struct rtnl_link_af_ops *ops) ops->ao_family); errout: - nl_write_lock(&info_lock); + nl_write_unlock(&info_lock); return err; } diff --git a/lib/route/link/macvlan.c b/lib/route/link/macvlan.c new file mode 100644 index 0000000..2340903 --- /dev/null +++ b/lib/route/link/macvlan.c @@ -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 + */ + +/** + * @ingroup link + * @defgroup macvlan MACVLAN + * MAC-based Virtual LAN link module + * + * @details + * \b Link Type Name: "macvlan" + * + * @route_doc{link_macvlan, MACVLAN Documentation} + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** @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); +} + +/** @} */ diff --git a/lib/route/neigh.c b/lib/route/neigh.c index 288bb85..ad26b4d 100644 --- a/lib/route/neigh.c +++ b/lib/route/neigh.c @@ -211,7 +211,9 @@ static void neigh_keygen(struct nl_object *obj, uint32_t *hashkey, uint32_t n_ifindex; char n_addr[0]; } __attribute__((packed)) *nkey; +#ifdef NL_DEBUG char buf[INET6_ADDRSTRLEN+5]; +#endif if (neigh->n_family == AF_BRIDGE) { if (neigh->n_lladdr) diff --git a/lib/route/route_obj.c b/lib/route/route_obj.c index 795047f..f2de523 100644 --- a/lib/route/route_obj.c +++ b/lib/route/route_obj.c @@ -307,7 +307,9 @@ static void route_keygen(struct nl_object *obj, uint32_t *hashkey, uint32_t rt_prio; char rt_addr[0]; } __attribute__((packed)) *rkey; +#ifdef NL_DEBUG char buf[INET6_ADDRSTRLEN+5]; +#endif if (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 *old_route = (struct rtnl_route *) old_obj; struct rtnl_nexthop *new_nh; - char buf[INET6_ADDRSTRLEN+5]; int action = new_obj->ce_msgtype; +#ifdef NL_DEBUG + char buf[INET6_ADDRSTRLEN+5]; +#endif /* * ipv6 ECMP route notifications from the kernel come as diff --git a/lib/socket.c b/lib/socket.c index d3e636e..1ca7783 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -120,7 +120,7 @@ static struct nl_sock *__alloc_socket(struct nl_cb *cb) return NULL; sk->s_fd = -1; - sk->s_cb = cb; + sk->s_cb = nl_cb_get(cb); sk->s_local.nl_family = AF_NETLINK; sk->s_peer.nl_family = AF_NETLINK; 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_cb *cb; - + struct nl_sock *sk; + cb = nl_cb_alloc(default_cb); if (!cb) 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) 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) { + if (cb == NULL) + BUG(); + nl_cb_put(sk->s_cb); sk->s_cb = nl_cb_get(cb); } diff --git a/lib/utils.c b/lib/utils.c index 3bfa604..4457b1f 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -49,6 +49,7 @@ int nl_debug = 0; /** @cond SKIP */ +#ifdef NL_DEBUG struct nl_dump_params nl_debug_dp = { .dp_type = NL_DUMP_DETAILS, }; @@ -65,6 +66,7 @@ static void __init nl_debug_init(void) nl_debug_dp.dp_fd = stderr; } +#endif 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_IEEE80211,ieee802.11) __ADD(ARPHRD_PHONET,phonet) +#ifdef ARPHRD_CAIF __ADD(ARPHRD_CAIF, caif) +#endif #ifdef ARPHRD_IEEE80211_PRISM __ADD(ARPHRD_IEEE80211_PRISM, ieee802.11_prism) #endif diff --git a/python/netlink/route/capi.i b/python/netlink/route/capi.i index 8ac114b..949a5ed 100644 --- a/python/netlink/route/capi.i +++ b/python/netlink/route/capi.i @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -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 struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *, int *); +/* */ + +%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 *); + /* */ %cstring_output_maxsize(char *buf, size_t len) extern const char *rtnl_link_inet_devconf2str(int, char *buf, size_t len); diff --git a/tests/Makefile.am b/tests/Makefile.am index 6a5606a..7953176 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,10 @@ # -*- 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\" LDADD = \ @@ -44,4 +49,6 @@ test_complex_HTB_with_hash_filters_SOURCES = test-complex-HTB-with-hash-filters. # Unit tests check_all_SOURCES = \ check-all.c \ - check-addr.c + check-addr.c \ + check-attr.c +endif diff --git a/tests/check-all.c b/tests/check-all.c index e801003..e431802 100644 --- a/tests/check-all.c +++ b/tests/check-all.c @@ -12,6 +12,7 @@ #include extern Suite *make_nl_addr_suite(void); +extern Suite *make_nl_attr_suite(void); static Suite *main_suite(void) { @@ -30,6 +31,7 @@ int main(int argc, char *argv[]) /* Add testsuites below */ srunner_add_suite(runner, make_nl_addr_suite()); + srunner_add_suite(runner, make_nl_attr_suite()); /* Do not add testsuites below this line */ diff --git a/tests/check-attr.c b/tests/check-attr.c new file mode 100644 index 0000000..d862230 --- /dev/null +++ b/tests/check-attr.c @@ -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 + */ + +#include "util.h" +#include +#include + +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; +} diff --git a/tests/test-create-macvlan.c b/tests/test-create-macvlan.c new file mode 100644 index 0000000..6477923 --- /dev/null +++ b/tests/test-create-macvlan.c @@ -0,0 +1,48 @@ +#include +#include +#include + +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; +} diff --git a/tests/util.h b/tests/util.h new file mode 100644 index 0000000..c675383 --- /dev/null +++ b/tests/util.h @@ -0,0 +1,5 @@ +#include + +#define nl_fail_if(condition, error, message) \ + fail_if((condition), "nlerr=%d (%s): %s", \ + (error), nl_geterror(error), (message))