From aad041c46f6f0720bff8e8727e8ff39f2196d2a8 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Mon, 1 Apr 2013 11:14:49 +0200 Subject: [PATCH 01/30] genl: Provide internal function to resolve name to id Like genl_ops_resolve() but uses its own socket. Signed-off-by: Thomas Graf --- include/netlink-private/genl.h | 2 ++ lib/genl/mngt.c | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) 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/lib/genl/mngt.c b/lib/genl/mngt.c index a3faaf2..35bbb12 100644 --- a/lib/genl/mngt.c +++ b/lib/genl/mngt.c @@ -302,6 +302,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 */ /** From ad545f2854458f98498e0cfed136845ef029a115 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Mon, 1 Apr 2013 11:16:34 +0200 Subject: [PATCH 02/30] genl: Update mt_id of cache ops when resolving genl id The cache layer uses the message type array stored in the cache ops to lookup which cache a message belongs to. Update to the the message array with the resolved generic netlink id to make it compatible with the caching API. Allows to use nl_cache_refill() and others for generic netlink based caches with dynamic generic netlink ids. Signed-off-by: Thomas Graf --- lib/genl/mngt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/genl/mngt.c b/lib/genl/mngt.c index 35bbb12..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; From f9241d57fe1fd13aa9ae09d86fe8605451213ef2 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Mon, 1 Apr 2013 11:46:30 +0200 Subject: [PATCH 03/30] cache: Improve debugging messages of cache operations Signed-off-by: Thomas Graf --- lib/cache.c | 62 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 24 deletions(-) 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) From ff567100d6190c9014514f90de522281007c90db Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 2 Apr 2013 11:39:30 +0200 Subject: [PATCH 04/30] nl: Print file:line:func in debugging messages and provide --disable-debug to disable debugging Compiling libnl with --disable-debug will result in the ignorance of the 'NLDBG' environment variable. Signed-off-by: Thomas Graf --- configure.ac | 9 +++++++++ include/netlink-private/netlink.h | 23 ++++++++++++++--------- lib/route/neigh.c | 2 ++ lib/route/route_obj.c | 6 +++++- lib/utils.c | 2 ++ 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/configure.ac b/configure.ac index 744e34e..bd1f3ac 100644 --- a/configure.ac +++ b/configure.ac @@ -92,6 +92,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 +105,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/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/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/utils.c b/lib/utils.c index 3bfa604..3012fea 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 *)) { From 18152ca91622e11f8ac1e2b9806c134d616fd1fe Mon Sep 17 00:00:00 2001 From: Holger Eitzenberger Date: Sun, 31 Mar 2013 21:16:21 +0200 Subject: [PATCH 05/30] ct: add ICMPv6 type,code and ID Add ICMPv6 type, code and ID (if set) by using the already available conntrack atttributes. Currently the ICMPv6 conntrack objects in libnl are without type, code and ID. This e. g. is the output of nl_object_dump() without the patch: ipv6-icmp ::1 <-> ::1 id 0xdd0871f0 family inet6 timeout 30s The attached patch tries to solve that. It then looks like ipv6-icmp ::1 <-> ::1 icmp type 128 code 0 id 28253 id 0xdf3a11f0 family inet6 timeout 30s It is the 'small' approach, because it reuses the existing ICMP attributes of the conntrack object (currently only used for IPv4). This way I can avoid to add new _icmp6_get_, _icmp6_set_ and _icmp6_test_ functions. Signed-off-by: Holger Eitzenberger Signed-off-by: Thomas Graf --- lib/netfilter/ct.c | 59 +++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 16 deletions(-) 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); From df66b0f267af636848d7f0301d3f5863c58fb313 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 2 Apr 2013 11:51:53 +0200 Subject: [PATCH 06/30] genl: Fix cb reference leak in genl_ctrl_probe_by_name() nl_socket_get_cb() bumps the cb reference counter Signed-off-by: Thomas Graf --- lib/genl/ctrl.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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, From 375a6294a41e003f873821a01d947f0ecfaf76d4 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 2 Apr 2013 11:58:18 +0200 Subject: [PATCH 07/30] nl: Return -NLE_AGAIN if non-blocking socket would block Previously 0 was returned which gave the caller no chance of detecting when a non-blocking socket would block. If a caller intends to never see an error message it should utilize poll()/select() to only read when the socket has pending data or information. Reported-by: Holger Eitzenberger Signed-off-by: Thomas Graf --- lib/nl.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/nl.c b/lib/nl.c index fa43c56..0445e35 100644 --- a/lib/nl.c +++ b/lib/nl.c @@ -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; } From 56eb22fa7475843e3f5133dbef5e0e44751dc474 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 2 Apr 2013 23:13:15 +0200 Subject: [PATCH 08/30] msg: Pretty print generic netlink header in nl_msg_dump() Signed-off-by: Thomas Graf --- lib/msg.c | 138 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 97 insertions(+), 41 deletions(-) diff --git a/lib/msg.c b/lib/msg.c index b07de63..dafee67 100644 --- a/lib/msg.c +++ b/lib/msg.c @@ -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) { @@ -845,7 +892,7 @@ static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen, if (nla->nla_type & NLA_F_NESTED) 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"); } /** @} */ From ea436445ad774179d6b3196e102907272f403da8 Mon Sep 17 00:00:00 2001 From: Emmanuel Roullit Date: Wed, 3 Apr 2013 21:07:32 +0200 Subject: [PATCH 09/30] Perform no operation on nl_object_free(NULL). Passing a NULL pointer would cause a NULL pointer dereference within nl_object_free(). Returning early on NULL pointer is the behavior free(3) and other nl*_free() functions. Signed-off-by: Emmanuel Roullit --- lib/object.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/object.c b/lib/object.c index 17b98f6..b1ebe51 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"); From d3cf89ea9459ddfc1b35ef0a927ec8c969e07a5e Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 5 Apr 2013 10:37:55 +0200 Subject: [PATCH 10/30] addr: only translate more recent address family names and ARP types if defined Helps making libnl compilable with older kernel headers Signed-off-by: Thomas Graf --- lib/addr.c | 8 ++++++++ lib/utils.c | 2 ++ 2 files changed, 10 insertions(+) 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/utils.c b/lib/utils.c index 3012fea..4457b1f 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -685,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 From d505165f2b958d35581d75113f78ad4131a16468 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 5 Apr 2013 10:44:06 +0200 Subject: [PATCH 11/30] autoconf: Use PKG_CHECK_MODULES() instead of AM_PATH_CHECK() Signed-off-by: Thomas Graf --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index bd1f3ac..9a7fd65 100644 --- a/configure.ac +++ b/configure.ac @@ -75,7 +75,7 @@ AC_CHECK_PROGS(YACC, 'bison -y') AC_C_CONST AC_C_INLINE -AM_PATH_CHECK() +PKG_CHECK_MODULES([CHECK], [check >= 0.9.0]) AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH], [Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]), From 59db7fb35b260925abac7afe39ddd165deb87f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=BE=D1=80=D0=B5=D0=BD=D0=B1=D0=B5=D1=80=D0=B3=20?= =?UTF-8?q?=D0=9C=D0=B0=D1=80=D0=BA=20=28=D0=B4=D0=BE=D0=BC=D0=B0=29?= Date: Fri, 26 Apr 2013 23:50:54 +0600 Subject: [PATCH 12/30] dump_attrs: "NLA_F_NESTED" => nla_is_nested(nla) --- lib/msg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msg.c b/lib/msg.c index dafee67..6478507 100644 --- a/lib/msg.c +++ b/lib/msg.c @@ -886,10 +886,10 @@ 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_attr(ofd, nla, prefix); From ead4cdeb99029c6cf3bf85811c3ea32346b41b03 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sat, 27 Apr 2013 14:27:10 +0200 Subject: [PATCH 13/30] tests: Make unit test building optional Signed-off-by: Thomas Graf --- configure.ac | 7 ++++++- tests/Makefile.am | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9a7fd65..8c5ff1c 100644 --- a/configure.ac +++ b/configure.ac @@ -75,7 +75,12 @@ AC_CHECK_PROGS(YACC, 'bison -y') AC_C_CONST AC_C_INLINE -PKG_CHECK_MODULES([CHECK], [check >= 0.9.0]) +PKG_CHECK_MODULES([CHECK], [check >= 0.9.0], + [AC_DEFINE([ENABLE_UNIT_TESTS], [1], [Do unit tests])], + [AC_MSG_WARN([*** Disabling building of unit tests]) + AC_DEFINE([ENABLE_UNIT_TESTS], [0], [Do not do unit tests])]) + +AM_CONDITIONAL([ENABLE_UNIT_TESTS], [test "x$ENABLE_UNIT_TESTS" = "xyes"]) AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH], [Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]), diff --git a/tests/Makefile.am b/tests/Makefile.am index 6a5606a..3e9eafe 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,7 @@ # -*- Makefile -*- +if ENABLE_UNIT_TESTS + AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\" LDADD = \ @@ -45,3 +47,4 @@ test_complex_HTB_with_hash_filters_SOURCES = test-complex-HTB-with-hash-filters. check_all_SOURCES = \ check-all.c \ check-addr.c +endif From 33396faca5d48a0318cdd85b4da2c3b247063781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=BE=D1=80=D0=B5=D0=BD=D0=B1=D0=B5=D1=80=D0=B3=20?= =?UTF-8?q?=D0=9C=D0=B0=D1=80=D0=BA=20=28=D0=B4=D0=BE=D0=BC=D0=B0=29?= Date: Sun, 28 Apr 2013 00:35:37 +0600 Subject: [PATCH 14/30] Fix leak of cb if nl_socket_alloc_cb() failed to allocate socket - each *_get() should have corresponding *_put(). That rule was broken in nl_socket_alloc() - Also, check if cb is NULL in nl_socket_set_cb (calls BUG()) --- lib/socket.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) 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); } From c07a6a30c2c3219795aada01ade65c09e8fb41a3 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sun, 28 Apr 2013 10:23:28 +0200 Subject: [PATCH 15/30] attr: nla_is_nested() must access nla_type directly Can't used nla_type() as it applies NLA_TYPE_MASK first Signed-off-by: Thomas Graf --- lib/attr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/attr.c b/lib/attr.c index e6efe4e..14552c6 100644 --- a/lib/attr.c +++ b/lib/attr.c @@ -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 !!(nla->nla_type & NLA_F_NESTED); } /** @} */ From 3a6d256da598d2fd9dc20137f208b88295374b67 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sun, 28 Apr 2013 10:33:52 +0200 Subject: [PATCH 16/30] attr: Fix typo in nla_is_nested() Signed-off-by: Thomas Graf --- lib/attr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/attr.c b/lib/attr.c index 14552c6..6fc6af5 100644 --- a/lib/attr.c +++ b/lib/attr.c @@ -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->nla_type & NLA_F_NESTED); + return !!(attr->nla_type & NLA_F_NESTED); } /** @} */ From 979ea335b0141eb62682056be42ccb4b01e6e2a2 Mon Sep 17 00:00:00 2001 From: Emmanuel Thierry Date: Wed, 24 Apr 2013 18:39:19 +0200 Subject: [PATCH 17/30] Wrong calcultation in nla_reserve There seams to be an error in the calculation of needed space for the message in nla_reserve. The current size of the message is counted twice: Once in NLMSG_ALIGN, once in the condition below. This causes nla_put_* calls to be rejected if the allocation size of the message has been strictly calculated by the caller. Signed-off-by: Thomas Graf --- lib/attr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/attr.c b/lib/attr.c index 6fc6af5..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); From ffc0ee35405cc43b26575359ef09775bce4533ba Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sun, 28 Apr 2013 11:41:15 +0200 Subject: [PATCH 18/30] configure: Convert ENABLE_UNIT_TESTS to a mere AM conditional Signed-off-by: Thomas Graf --- configure.ac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 8c5ff1c..f8cfc80 100644 --- a/configure.ac +++ b/configure.ac @@ -76,11 +76,11 @@ AC_C_CONST AC_C_INLINE PKG_CHECK_MODULES([CHECK], [check >= 0.9.0], - [AC_DEFINE([ENABLE_UNIT_TESTS], [1], [Do unit tests])], + [enable_unit_tests="yes"], [AC_MSG_WARN([*** Disabling building of unit tests]) - AC_DEFINE([ENABLE_UNIT_TESTS], [0], [Do not do unit tests])]) + enable_unit_tests="no"]) -AM_CONDITIONAL([ENABLE_UNIT_TESTS], [test "x$ENABLE_UNIT_TESTS" = "xyes"]) +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]]]), From 4e9aa6a9a61c565a0caacbaf30ab73082f1cc397 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sun, 28 Apr 2013 12:31:52 +0200 Subject: [PATCH 19/30] tests: Add basic attribute unit tests Signed-off-by: Thomas Graf --- tests/Makefile.am | 3 +- tests/check-all.c | 2 ++ tests/check-attr.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++ tests/util.h | 5 +++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 tests/check-attr.c create mode 100644 tests/util.h diff --git a/tests/Makefile.am b/tests/Makefile.am index 3e9eafe..5536bc8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -46,5 +46,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/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)) From ed1f4cba2cd1f55c909bead18bcdb9dc1dd2f6a2 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sun, 28 Apr 2013 12:51:58 +0200 Subject: [PATCH 20/30] tests: Include util.h in dist Signed-off-by: Thomas Graf --- tests/Makefile.am | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Makefile.am b/tests/Makefile.am index 5536bc8..7953176 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,8 @@ # -*- 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\" From 183052d0474af6baf1d640dae94b85a23d886b9a Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sun, 28 Apr 2013 12:52:26 +0200 Subject: [PATCH 21/30] Prepare for 3.2.22-rc1 release Signed-off-by: Thomas Graf --- configure.ac | 10 +++++----- doc/configure.ac | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index f8cfc80..97eb177 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,12 +34,12 @@ 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]) + [libnl_major_version.libnl_minor_version.libnl_micro_version-rc1]) AC_INIT(libnl, [libnl_version], [], [], [http://www.infradead.org/~tgr/libnl/]) AC_CONFIG_HEADERS([lib/defs.h]) diff --git a/doc/configure.ac b/doc/configure.ac index 4c3a52f..9b4a41f 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-rc1], [http://www.infradead.org/~tgr/libnl/]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([foreign]) From 8983fa99140297aa4a57b11b0222cf26c135bd00 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Mon, 29 Apr 2013 16:29:46 -0500 Subject: [PATCH 22/30] rtnl_link_af_unregister: fix locking rtnl_link_af_unregister() attempts to write-lock info_lock twice instead of releasing it before returning. It also will return with info_lock write-locked if passed a NULL ops. Signed-off-by: Thomas Graf --- lib/route/link/api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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; } From 807fddc4cd9ecb12ba64e1b7fa26d86b6c2f19b0 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 8 May 2013 13:52:27 +0200 Subject: [PATCH 23/30] nl: Increase receive buffer size to 4 pages Assuming that the kernel does not send more than a page is no longer valid, and enabling MSG_PEEK'ing by default to figure out the exact message buffer requirements can have a negative influence on the performance of existing applications. Bumping the default receive buffer space to 4 pages seems a sane default. Signed-off-by: Thomas Graf --- lib/nl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/nl.c b/lib/nl.c index 0445e35..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); From c4d846f239036c05f516c1c71789e980b64b1e70 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 14 May 2013 14:09:07 +0200 Subject: [PATCH 24/30] 3.2.22 release --- configure.ac | 2 +- doc/configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 97eb177..34e8c9f 100644 --- a/configure.ac +++ b/configure.ac @@ -39,7 +39,7 @@ 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-rc1]) + [libnl_major_version.libnl_minor_version.libnl_micro_version]) AC_INIT(libnl, [libnl_version], [], [], [http://www.infradead.org/~tgr/libnl/]) AC_CONFIG_HEADERS([lib/defs.h]) diff --git a/doc/configure.ac b/doc/configure.ac index 9b4a41f..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.22-rc1], [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]) From c76393e2037d78eb60c32f95b26f5b1e5b9422a6 Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Thu, 16 May 2013 16:54:13 +0200 Subject: [PATCH 25/30] Add macvlan support This patch add support for kernel macvlan interfaces. Signed-off-by: Michael Braun --- doc/route.txt | 57 +++++ include/Makefile.am | 1 + include/netlink/route/link/macvlan.h | 46 ++++ lib/Makefile.am | 2 +- lib/route/link/macvlan.c | 367 +++++++++++++++++++++++++++ python/netlink/route/capi.i | 16 ++ tests/test-create-macvlan.c | 48 ++++ 7 files changed, 536 insertions(+), 1 deletion(-) create mode 100644 include/netlink/route/link/macvlan.h create mode 100644 lib/route/link/macvlan.c create mode 100644 tests/test-create-macvlan.c 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 3488c52..776323c 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -46,6 +46,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/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 677a89c..0376cbb 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -69,7 +69,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/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/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/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; +} From 87bbfb6b12322d79abc979d6a001c18eb875ae45 Mon Sep 17 00:00:00 2001 From: Andrew Collins Date: Mon, 17 Jun 2013 15:24:05 -0600 Subject: [PATCH 26/30] Default to comparing all attributes if no oo_id_attrs defined Since commit: "cache pickup: Avoid duplicates during cache pickup", nfnl_ct_alloc_cache no longer properly fills the cache, as it doesn't define oo_id_attrs so all items are considered duplicates. Instead of adding a ~0 oo_id_attrs to ct_obj, this changes nl_object_identical to default to comparing all attributes if neither oo_id_attrs_get or oo_id_attrs are provided. Signed-off-by: Thomas Graf --- lib/object.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/object.c b/lib/object.c index b1ebe51..9293df9 100644 --- a/lib/object.c +++ b/lib/object.c @@ -321,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; From 53ac502a2b2794e4339527b33158a6c3e851a83b Mon Sep 17 00:00:00 2001 From: Andrew Collins Date: Mon, 17 Jun 2013 15:58:43 -0600 Subject: [PATCH 27/30] Handle -NLE_AGAIN in nl_cache_mngr_data_ready Since commit "nl: Return -NLE_AGAIN if non-blocking socket would block", nl_cache_mngr_data_ready returns -NLE_AGAIN to callers on non-blocking sockets. Change it to consider -NLE_AGAIN as a non-error case as it is expected behavior with the nl_recv changes. Signed-off-by: Thomas Graf --- lib/cache_mngr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From 34a96ba5c2d168701b8b2621164206ddc03d8b4c Mon Sep 17 00:00:00 2001 From: Holger Eitzenberger Date: Wed, 19 Jun 2013 15:23:14 +0200 Subject: [PATCH 28/30] netfilter/queue: generalize nfnl_queue_msg_build_verdict() Generalize netfilter/queue to allow sending batched verdicts later. Signed-off-by: Holger Eitzenberger Signed-off-by: Thomas Graf --- lib/netfilter/queue_msg.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/netfilter/queue_msg.c b/lib/netfilter/queue_msg.c index 95d8ad3..dc92fce 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,12 @@ 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); +} + /** * Send a message verdict/mark * @arg nlh netlink messsage header From d612180cda43606425c3476e13a6076b6271d27a Mon Sep 17 00:00:00 2001 From: Holger Eitzenberger Date: Wed, 19 Jun 2013 15:23:15 +0200 Subject: [PATCH 29/30] netfilter/queue: introduce nfnl_queue_msg_send_verdict_batch() The batched verdict implicitely ACKs all queue packet IDs up to the one send back, which reduces the number of verdict messages send to the kernel. Signed-off-by: Holger Eitzenberger Signed-off-by: Thomas Graf --- include/netlink/netfilter/queue_msg.h | 2 ++ lib/netfilter/queue_msg.c | 29 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) 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/lib/netfilter/queue_msg.c b/lib/netfilter/queue_msg.c index dc92fce..1425577 100644 --- a/lib/netfilter/queue_msg.c +++ b/lib/netfilter/queue_msg.c @@ -199,6 +199,12 @@ 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 @@ -222,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 From 01cfa9c1dbe8215717e27aa6802290cf51ab6186 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Thu, 27 Jun 2013 18:29:17 +0200 Subject: [PATCH 30/30] msg: Avoid returning a negative value for nlmsg_attrlen() If a hdrlen was provided that was greather than the actual message length, a negative attributes length would result. This was typically happening for RTM_GETLINK requests where we can get a away with a 4 bytes header on the request side but the response would use a 16 bytes header. This resulted in strange -8 bytes leftover debug messages. Signed-off-by: Thomas Graf --- lib/msg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msg.c b/lib/msg.c index 6478507..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); } /** @} */