diff options
Diffstat (limited to 'lib')
79 files changed, 8912 insertions, 4872 deletions
diff --git a/lib/.gitignore b/lib/.gitignore index f4bf2cd..2a450e8 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -1 +1,2 @@ libnl.so* +libnl-*.so* diff --git a/lib/Makefile b/lib/Makefile deleted file mode 100644 index 0bf8af7..0000000 --- a/lib/Makefile +++ /dev/null @@ -1,76 +0,0 @@ -# -# lib/Makefile -# -# 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) 2003-2006 Thomas Graf <tgraf@suug.ch> -# - -ifeq ($(shell [ ! -r ../Makefile.opts ] && echo 1),) - include ../Makefile.opts -endif - -# Core -CIN := $(wildcard *.c) -# NETLINK_ROUTE -CIN += $(wildcard route/*.c) -# Schedulers -CIN += $(wildcard route/sch/*.c) -# Classifiers -CIN += $(wildcard route/cls/*.c) -# Link Info Modules -CIN += $(wildcard route/link/*.c) -# NETLINK_GENERIC -CIN += $(wildcard genl/*.c) -# fib lookup -CIN += $(wildcard fib_lookup/*.c) -# Netfilter -CIN += $(wildcard netfilter/*.c) - -DEPS := $(CIN:%.c=%.d) -OBJ := $(CIN:%.c=%.o) -CFLAGS += -fPIC -OUT_SLIB := $(PACKAGE_NAME).so.$(PACKAGE_VERSION) -LN_SLIB := $(PACKAGE_NAME).so -LN1_SLIB := $(LN_SLIB).1 - -export - -.PHONY: all clean install librtn.a $(OUT_SLIB) - - -all: - @echo " MAKE $(OUT_SLIB)"; \ - $(MAKE) $(OUT_SLIB) - -$(OUT_SLIB): ../Makefile.opts $(OBJ) - @echo " LD $(OUT_SLIB)"; \ - $(CC) -shared -Wl,-soname,libnl.so.1 -o $(OUT_SLIB) $(OBJ) $(LIBNL_LIB) -lc - @echo " LN $(OUT_SLIB) $(LN1_SLIB)"; \ - rm -f $(LN1_SLIB) ; $(LN) -s $(OUT_SLIB) $(LN1_SLIB) - @echo " LN $(LN1_SLIB) $(LN_SLIB)"; \ - rm -f $(LN_SLIB) ; $(LN) -s $(LN1_SLIB) $(LN_SLIB) - -clean: - @echo " CLEAN lib"; \ - $(RM) -f $(OBJ) $(OUT_SLIB) $(LN_SLIB) $(LN1_SLIB); \ - $(RM) -f $(DEPS) $(OUT_SLIB) $(LN_SLIB) $(LN1_SLIB) - -distclean: - @echo " DISTCLEAN lib"; \ - $(RM) -f $(DEPS) - -install: - mkdir -p $(DESTDIR)$(libdir)/ - install -m 0644 $(OUT_SLIB) $(DESTDIR)$(libdir) - rm -f $(DESTDIR)$(libdir)/$(LN1_SLIB) - $(LN) -s $(OUT_SLIB) $(DESTDIR)$(libdir)/$(LN1_SLIB) - rm -f $(DESTDIR)$(libdir)/$(LN_SLIB) - $(LN) -s $(LN1_SLIB) $(DESTDIR)$(libdir)/$(LN_SLIB) - -$(DEPS): ../Makefile.opts - -include ../Makefile.rules diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..92a916e --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,57 @@ +# -*- Makefile -*- + +AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\" + +lib_LTLIBRARIES = \ + libnl.la libnl-genl.la libnl-route.la libnl-nf.la + +libnl_la_LDFLAGS = -version-info 2:0:0 +libnl_la_SOURCES = \ + addr.c attr.c cache.c cache_mngr.c cache_mngt.c data.c doc.c \ + error.c handlers.c msg.c nl.c object.c socket.c utils.c + +libnl_genl_la_LDFLAGS = -version-info 2:0:0 +libnl_genl_la_LIBADD = libnl.la +libnl_genl_la_SOURCES = \ + genl/ctrl.c genl/family.c genl/genl.c genl/mngt.c + +libnl_nf_la_LDFLAGS = -version-info 2:0:0 +libnl_nf_la_LIBADD = libnl-route.la +libnl_nf_la_SOURCES = \ + netfilter/ct.c netfilter/ct_obj.c netfilter/log.c \ + netfilter/log_msg.c netfilter/log_msg_obj.c netfilter/log_obj.c \ + netfilter/netfilter.c netfilter/nfnl.c netfilter/queue.c \ + netfilter/queue_msg.c netfilter/queue_msg_obj.c netfilter/queue_obj.c + +CLEANFILES = \ + route/pktloc_grammar.c route/pktloc_grammar.h \ + route/pktloc_syntax.c route/pktloc_syntax.h + +# Hack to avoid using ylwrap. It does not function correctly in combination +# with --header-file= +route/pktloc_grammar.c: route/pktloc_grammar.l + $(LEX) --header-file=route/pktloc_grammar.h $(LFLAGS) -o $@ $^ + +route/pktloc_syntax.c: route/pktloc_syntax.y + $(YACC) -d $(YFLAGS) -o $@ $^ + +libnl_route_la_LDFLAGS = -version-info 2:0:0 +libnl_route_la_LIBADD = libnl.la +libnl_route_la_SOURCES = \ + route/addr.c route/class.c route/class_api.c route/class_obj.c \ + route/cls.c route/cls_api.c route/cls_obj.c route/link.c \ + route/neigh.c route/neightbl.c route/nexthop.c route/qdisc.c \ + route/qdisc_api.c route/qdisc_obj.c route/route.c route/route_obj.c \ + route/route_utils.c route/rtnl.c route/rule.c route/tc.c \ + \ + route/cls/fw.c route/cls/police.c route/cls/u32.c \ + \ + route/link/api.c route/link/vlan.c \ + \ + route/sch/blackhole.c route/sch/cbq.c route/sch/dsmark.c \ + route/sch/fifo.c route/sch/htb.c route/sch/netem.c route/sch/prio.c \ + route/sch/red.c route/sch/sfq.c route/sch/tbf.c \ + \ + fib_lookup/lookup.c fib_lookup/request.c \ + \ + route/pktloc_syntax.c route/pktloc_grammar.c route/pktloc.c @@ -6,11 +6,11 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** - * @ingroup utils + * @ingroup core * @defgroup addr Abstract Address * * @par 1) Transform character string to abstract address @@ -140,11 +140,11 @@ static inline int dnet_pton(const char *src, char *addrbuf) pos = dnet_num(src, &area); if ((pos == 0) || (area > 63) || ((*(src + pos) != '.') && (*(src + pos) != ','))) - return -EINVAL; + return -NLE_INVAL; pos = dnet_num(src + pos + 1, &node); if ((pos == 0) || (node > 1023)) - return -EINVAL; + return -NLE_INVAL; *(uint16_t *)addrbuf = dn_ntohs((area << 10) | node); @@ -166,10 +166,8 @@ struct nl_addr *nl_addr_alloc(size_t maxsize) struct nl_addr *addr; addr = calloc(1, sizeof(*addr) + maxsize); - if (!addr) { - nl_errno(ENOMEM); + if (!addr) return NULL; - } addr->a_refcnt = 1; addr->a_maxsize = maxsize; @@ -203,9 +201,25 @@ struct nl_addr *nl_addr_build(int family, void *buf, size_t size) } /** + * Allocate abstract address based on netlink attribute. + * @arg nla Netlink attribute of unspecific type. + * @arg family Address family. + * + * Considers the netlink attribute payload a address of the specified + * family and allocates a new abstract address based on it. + * + * @return Newly allocated address handle or NULL. + */ +struct nl_addr *nl_addr_alloc_attr(struct nlattr *nla, int family) +{ + return nl_addr_build(family, nla_data(nla), nla_len(nla)); +} + +/** * Allocate abstract address object based on a character string * @arg addrstr Address represented as character string. * @arg hint Address family hint or AF_UNSPEC. + * @arg result Pointer to store resulting address. * * Regognizes the following address formats: *@code @@ -226,9 +240,9 @@ struct nl_addr *nl_addr_build(int family, void *buf, size_t size) * The prefix length may be appened at the end prefixed with a * slash, e.g. 10.0.0.0/8. * - * @return Newly allocated abstract address object or NULL. + * @return 0 on success or a negative error code. */ -struct nl_addr *nl_addr_parse(const char *addrstr, int hint) +int nl_addr_parse(const char *addrstr, int hint, struct nl_addr **result) { int err, copy = 0, len = 0, family = AF_UNSPEC; char *str, *prefix, buf[32]; @@ -236,7 +250,7 @@ struct nl_addr *nl_addr_parse(const char *addrstr, int hint) str = strdup(addrstr); if (!str) { - err = nl_errno(ENOMEM); + err = -NLE_NOMEM; goto errout; } @@ -274,8 +288,7 @@ struct nl_addr *nl_addr_parse(const char *addrstr, int hint) goto prefix; default: - err = nl_error(EINVAL, "Unsuported address" \ - "family for default address"); + err = -NLE_AF_NOSUPPORT; goto errout; } } @@ -289,7 +302,7 @@ struct nl_addr *nl_addr_parse(const char *addrstr, int hint) goto prefix; } if (hint == AF_INET) { - err = nl_error(EINVAL, "Invalid IPv4 address"); + err = -NLE_NOADDR; goto errout; } } @@ -301,7 +314,7 @@ struct nl_addr *nl_addr_parse(const char *addrstr, int hint) goto prefix; } if (hint == AF_INET6) { - err = nl_error(EINVAL, "Invalid IPv6 address"); + err = -NLE_NOADDR; goto errout; } } @@ -323,7 +336,7 @@ struct nl_addr *nl_addr_parse(const char *addrstr, int hint) } if (hint == AF_LLC) { - err = nl_error(EINVAL, "Invalid link layer address"); + err = -NLE_NOADDR; goto errout; } } @@ -336,7 +349,7 @@ struct nl_addr *nl_addr_parse(const char *addrstr, int hint) goto prefix; } if (hint == AF_DECnet) { - err = nl_error(EINVAL, "Invalid DECnet address"); + err = -NLE_NOADDR; goto errout; } } @@ -348,7 +361,7 @@ struct nl_addr *nl_addr_parse(const char *addrstr, int hint) long l = strtol(s, &p, 16); if (s == p || l > 0xff || i >= sizeof(buf)) { - err = -EINVAL; + err = -NLE_INVAL; goto errout; } @@ -363,13 +376,13 @@ struct nl_addr *nl_addr_parse(const char *addrstr, int hint) goto prefix; } - err = nl_error(EINVAL, "Invalid address"); + err = -NLE_NOADDR; goto errout; prefix: addr = nl_addr_alloc(len); if (!addr) { - err = nl_errno(ENOMEM); + err = -NLE_NOMEM; goto errout; } @@ -383,18 +396,19 @@ prefix: long pl = strtol(++prefix, &p, 0); if (p == prefix) { nl_addr_destroy(addr); - err = -EINVAL; + err = -NLE_INVAL; goto errout; } nl_addr_set_prefixlen(addr, pl); } else nl_addr_set_prefixlen(addr, len * 8); + *result = addr; err = 0; errout: free(str); - return err ? NULL : addr; + return err; } /** @@ -619,7 +633,7 @@ int nl_addr_fill_sockaddr(struct nl_addr *addr, struct sockaddr *sa, struct sockaddr_in *sai = (struct sockaddr_in *) sa; if (*salen < sizeof(*sai)) - return -EINVAL; + return -NLE_INVAL; sai->sin_family = addr->a_family; memcpy(&sai->sin_addr, addr->a_addr, 4); @@ -631,7 +645,7 @@ int nl_addr_fill_sockaddr(struct nl_addr *addr, struct sockaddr *sa, struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa; if (*salen < sizeof(*sa6)) - return -EINVAL; + return -NLE_INVAL; sa6->sin6_family = addr->a_family; memcpy(&sa6->sin6_addr, addr->a_addr, 16); @@ -640,7 +654,7 @@ int nl_addr_fill_sockaddr(struct nl_addr *addr, struct sockaddr *sa, break; default: - return -EINVAL; + return -NLE_INVAL; } return 0; @@ -657,6 +671,7 @@ int nl_addr_fill_sockaddr(struct nl_addr *addr, struct sockaddr *sa, /** * Call getaddrinfo() for an abstract address object. * @arg addr Abstract address object. + * @arg result Pointer to store resulting address list. * * Calls getaddrinfo() for the specified abstract address in AI_NUMERICHOST * mode. @@ -664,13 +679,11 @@ int nl_addr_fill_sockaddr(struct nl_addr *addr, struct sockaddr *sa, * @note The caller is responsible for freeing the linked list using the * interface provided by getaddrinfo(3). * - * @return A linked list of addrinfo handles or NULL with an error message - * associated. + * @return 0 on success or a negative error code. */ -struct addrinfo *nl_addr_info(struct nl_addr *addr) +int nl_addr_info(struct nl_addr *addr, struct addrinfo **result) { int err; - struct addrinfo *res; char buf[INET6_ADDRSTRLEN+5]; struct addrinfo hint = { .ai_flags = AI_NUMERICHOST, @@ -679,13 +692,24 @@ struct addrinfo *nl_addr_info(struct nl_addr *addr) nl_addr2str(addr, buf, sizeof(buf)); - err = getaddrinfo(buf, NULL, &hint, &res); + err = getaddrinfo(buf, NULL, &hint, result); if (err != 0) { - nl_error(err, gai_strerror(err)); - return NULL; + switch (err) { + case EAI_ADDRFAMILY: return -NLE_AF_NOSUPPORT; + case EAI_AGAIN: return -NLE_AGAIN; + case EAI_BADFLAGS: return -NLE_INVAL; + case EAI_FAIL: return -NLE_NOADDR; + case EAI_FAMILY: return -NLE_AF_NOSUPPORT; + case EAI_MEMORY: return -NLE_NOMEM; + case EAI_NODATA: return -NLE_NOADDR; + case EAI_NONAME: return -NLE_OBJ_NOTFOUND; + case EAI_SERVICE: return -NLE_OPNOTSUPP; + case EAI_SOCKTYPE: return -NLE_BAD_SOCK; + default: return -NLE_FAILURE; + } } - return res; + return 0; } /** @@ -711,8 +735,12 @@ int nl_addr_resolve(struct nl_addr *addr, char *host, size_t hostlen) if (err < 0) return err; - return getnameinfo((struct sockaddr *) &buf, salen, - host, hostlen, NULL, 0, NI_NAMEREQD); + err = getnameinfo((struct sockaddr *) &buf, salen, host, hostlen, + NULL, 0, NI_NAMEREQD); + if (err < 0) + return nl_syserr2nlerr(err); + + return 0; } /** @} */ @@ -741,7 +769,7 @@ int nl_addr_get_family(struct nl_addr *addr) int nl_addr_set_binary_addr(struct nl_addr *addr, void *buf, size_t len) { if (len > addr->a_maxsize) - return -ERANGE; + return -NLE_RANGE; addr->a_len = len; memcpy(addr->a_addr, buf, len); @@ -804,9 +832,12 @@ char *nl_addr2str(struct nl_addr *addr, char *buf, size_t size) int i; char tmp[16]; - if (!addr->a_len) { + if (!addr || !addr->a_len) { snprintf(buf, size, "none"); - goto prefix; + if (addr) + goto prefix; + else + return buf; } switch (addr->a_family) { @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ #include <netlink-local.h> @@ -21,89 +21,376 @@ * @ingroup msg * @defgroup attr Attributes * Netlink Attributes Construction/Parsing Interface - * @par 0) Introduction - * Netlink attributes are chained together following each other: + * + * \section attr_sec Netlink Attributes + * Netlink attributes allow for data chunks of arbitary length to be + * attached to a netlink message. Each attribute is encoded with a + * type and length field, both 16 bits, stored in the attribute header + * preceding the attribute data. The main advantage of using attributes + * over packing everything into the family header is that the interface + * stays extendable as new attributes can supersede old attributes while + * remaining backwards compatible. Also attributes can be defined optional + * thus avoiding the transmission of unnecessary empty data blocks. + * Special nested attributes allow for more complex data structures to + * be transmitted, e.g. trees, lists, etc. + * + * While not required, netlink attributes typically follow the family + * header of a netlink message and must be properly aligned to NLA_ALIGNTO: + * @code + * +----------------+- - -+---------------+- - -+------------+- - -+ + * | Netlink Header | Pad | Family Header | Pad | Attributes | Pad | + * +----------------+- - -+---------------+- - -+------------+- - -+ + * @endcode + * + * The actual attributes are chained together each separately aligned to + * NLA_ALIGNTO. The position of an attribute is defined based on the + * length field of the preceding attributes: + * @code + * +-------------+- - -+-------------+- - -+------ + * | Attribute 1 | Pad | Attribute 2 | Pad | ... + * +-------------+- - -+-------------+- - -+------ + * nla_next(attr1)------^ + * @endcode + * + * The attribute itself consists of the attribute header followed by + * the actual payload also aligned to NLA_ALIGNTO. The function nla_data() + * returns a pointer to the start of the payload while nla_len() returns + * the length of the payload in bytes. + * + * \b Note: Be aware, NLA_ALIGNTO equals to 4 bytes, therefore it is not + * safe to dereference any 64 bit data types directly. + * + * @code + * <----------- nla_total_size(payload) -----------> + * <-------- nla_attr_size(payload) ---------> + * +------------------+- - -+- - - - - - - - - +- - -+ + * | Attribute Header | Pad | Payload | Pad | + * +------------------+- - -+- - - - - - - - - +- - -+ + * nla_data(nla)-------------^ + * <- nla_len(nla) -> + * @endcode + * + * @subsection attr_datatypes Attribute Data Types + * A number of basic data types are supported to simplify access and + * validation of netlink attributes. This data type information is + * not encoded in the attribute, both the kernel and userspace part + * are required to share this information on their own. + * + * One of the major advantages of these basic types is the automatic + * validation of each attribute based on an attribute policy. The + * validation covers most of the checks required to safely use + * attributes and thus keeps the individual sanity check to a minimum. + * + * Never access attribute payload without ensuring basic validation + * first, attributes may: + * - not be present even though required + * - contain less actual payload than expected + * - fake a attribute length which exceeds the end of the message + * - contain unterminated character strings + * + * Policies are defined as array of the struct nla_policy. The array is + * indexed with the attribute type, therefore the array must be sized + * accordingly. + * @code + * static struct nla_policy my_policy[ATTR_MAX+1] = { + * [ATTR_FOO] = { .type = ..., .minlen = ..., .maxlen = ... }, + * }; + * + * err = nla_validate(attrs, attrlen, ATTR_MAX, &my_policy); + * @endcode + * + * Some basic validations are performed on every attribute, regardless of type. + * - If the attribute type exceeds the maximum attribute type specified or + * the attribute type is lesser-or-equal than zero, the attribute will + * be silently ignored. + * - If the payload length falls below the \a minlen value the attribute + * will be rejected. + * - If \a maxlen is non-zero and the payload length exceeds the \a maxlen + * value the attribute will be rejected. + * + * + * @par Unspecific Attribute (NLA_UNSPEC) + * This is the standard type if no type is specified. It is used for + * binary data of arbitary length. Typically this attribute carries + * a binary structure or a stream of bytes. + * @par + * @code + * // In this example, we will assume a binary structure requires to + * // be transmitted. The definition of the structure will typically + * // go into a header file available to both the kernel and userspace + * // side. + * // + * // Note: Be careful when putting 64 bit data types into a structure. + * // The attribute payload is only aligned to 4 bytes, dereferencing + * // the member may fail. + * struct my_struct { + * int a; + * int b; + * }; + * + * // The validation function will not enforce an exact length match to + * // allow structures to grow as required. Note: While it is allowed + * // to add members to the end of the structure, changing the order or + * // inserting members in the middle of the structure will break your + * // binary interface. + * static struct nla_policy my_policy[ATTR_MAX+1] = { + * [ATTR_MY_STRICT] = { .type = NLA_UNSPEC, + * .minlen = sizeof(struct my_struct) }, + * + * // The binary structure is appened to the message using nla_put() + * struct my_struct foo = { .a = 1, .b = 2 }; + * nla_put(msg, ATTR_MY_STRUCT, sizeof(foo), &foo); + * + * // On the receiving side, a pointer to the structure pointing inside + * // the message payload is returned by nla_get(). + * if (attrs[ATTR_MY_STRUCT]) + * struct my_struct *foo = nla_get(attrs[ATTR_MY_STRUCT]); + * @endcode + * + * @par Integers (NLA_U8, NLA_U16, NLA_U32, NLA_U64) + * Integers come in different sizes from 8 bit to 64 bit. However, since the + * payload length is aligned to 4 bytes, integers smaller than 32 bit are + * only useful to enforce the maximum range of values. + * @par + * \b Note: There is no difference made between signed and unsigned integers. + * The validation only enforces the minimal payload length required to store + * an integer of specified type. + * @par + * @code + * // Even though possible, it does not make sense to specify .minlen or + * // .maxlen for integer types. The data types implies the corresponding + * // minimal payload length. + * static struct nla_policy my_policy[ATTR_MAX+1] = { + * [ATTR_FOO] = { .type = NLA_U32 }, + * + * // Numeric values can be appended directly using the respective + * // nla_put_uxxx() function + * nla_put_u32(msg, ATTR_FOO, 123); + * + * // Same for the receiving side. + * if (attrs[ATTR_FOO]) + * uint32_t foo = nla_get_u32(attrs[ATTR_FOO]); + * @endcode + * + * @par Character string (NLA_STRING) + * This data type represents a NUL terminated character string of variable + * length. For binary data streams the type NLA_UNSPEC is recommended. + * @par * @code - * <------- nla_total_size(payload) -------> - * <---- nla_attr_size(payload) -----> - * +----------+- - -+- - - - - - - - - +- - -+-------- - - - * | Header | Pad | Payload | Pad | Header - * +----------+- - -+- - - - - - - - - +- - -+-------- - - - * <- nla_len(nla) -> ^ - * nla_data(nla)----^ | - * nla_next(nla)-----------------------------' + * // Enforce a NUL terminated character string of at most 4 characters + * // including the NUL termination. + * static struct nla_policy my_policy[ATTR_MAX+1] = { + * [ATTR_BAR] = { .type = NLA_STRING, maxlen = 4 }, + * + * // nla_put_string() creates a string attribute of the necessary length + * // and appends it to the message including the NUL termination. + * nla_put_string(msg, ATTR_BAR, "some text"); + * + * // It is safe to use the returned character string directly if the + * // attribute has been validated as the validation enforces the proper + * // termination of the string. + * if (attrs[ATTR_BAR]) + * char *text = nla_get_string(attrs[ATTR_BAR]); * @endcode * + * @par Flag (NLA_FLAG) + * This attribute type may be used to indicate the presence of a flag. The + * attribute is only valid if the payload length is zero. The presence of + * the attribute header indicates the presence of the flag. * @par - * The attribute header and payload must be aligned properly: * @code - * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> - * +---------------------+- - -+- - - - - - - - - -+- - -+ - * | Header | Pad | Payload | Pad | - * | (struct nlattr) | ing | | ing | - * +---------------------+- - -+- - - - - - - - - -+- - -+ - * <-------------- nlattr->nla_len --------------> + * // This attribute type is special as .minlen and .maxlen have no effect. + * static struct nla_policy my_policy[ATTR_MAX+1] = { + * [ATTR_FLAG] = { .type = NLA_FLAG }, + * + * // nla_put_flag() appends a zero sized attribute to the message. + * nla_put_flag(msg, ATTR_FLAG); + * + * // There is no need for a receival function, the presence is the value. + * if (attrs[ATTR_FLAG]) + * // flag is present + * @endcode + * + * @par Micro Seconds (NLA_MSECS) + * + * @par Nested Attribute (NLA_NESTED) + * Attributes can be nested and put into a container to create groups, lists + * or to construct trees of attributes. Nested attributes are often used to + * pass attributes to a subsystem where the top layer has no knowledge of the + * configuration possibilities of each subsystem. + * @par + * \b Note: When validating the attributes using nlmsg_validate() or + * nlmsg_parse() it will only affect the top level attributes. Each + * level of nested attributes must be validated seperately using + * nla_parse_nested() or nla_validate(). + * @par + * @code + * // The minimal length policy may be used to enforce the presence of at + * // least one attribute. + * static struct nla_policy my_policy[ATTR_MAX+1] = { + * [ATTR_OPTS] = { .type = NLA_NESTED, minlen = NLA_HDRLEN }, + * + * // Nested attributes are constructed by enclosing the attributes + * // to be nested with calls to nla_nest_start() respetively nla_nest_end(). + * struct nlattr *opts = nla_nest_start(msg, ATTR_OPTS); + * nla_put_u32(msg, ATTR_FOO, 123); + * nla_put_string(msg, ATTR_BAR, "some text"); + * nla_nest_end(msg, opts); + * + * // Various methods exist to parse nested attributes, the easiest being + * // nla_parse_nested() which also allows validation in the same step. + * if (attrs[ATTR_OPTS]) { + * struct nlattr *nested[ATTR_MAX+1]; + * + * nla_parse_nested(nested, ATTR_MAX, attrs[ATTR_OPTS], &policy); + * + * if (nested[ATTR_FOO]) + * uint32_t foo = nla_get_u32(nested[ATTR_FOO]); + * } * @endcode * - * @par Nested TLVs: - * Nested TLVs are an array of TLVs nested into another TLV. This can be useful - * to allow subsystems to have their own formatting rules without the need to - * make the underlying layer be aware of it. It can also be useful to transfer - * arrays, lists and flattened trees. - * \code - * <-------------------- NLA_ALIGN(...) -------------------> - * +---------------+- - - - - - - - - - - - - - - - - -+- - -+ - * | |+---------+---------+- - -+-------+| | - * | TLV Header || TLV 1 | TLV 2 | | TLV n || Pad | - * | |+---------+---------+- - -+-------+| | - * +---------------+- - - - - - - - - - - - - - - - - -+- - -+ - * <--------- nla_data(nla) ---------> - * \endcode - * - * @par 1) Constructing a message with attributes + * @subsection attr_exceptions Exception Based Attribute Construction + * Often a large number of attributes are added to a message in a single + * function. In order to simplify error handling, a second set of + * construction functions exist which jump to a error label when they + * fail instead of returning an error code. This second set consists + * of macros which are named after their error code based counterpart + * except that the name is written all uppercase. + * + * All of the macros jump to the target \c nla_put_failure if they fail. * @code - * int param1 = 10; - * char *param2 = "parameter text"; - * - * struct nl_msg *msg = nlmsg_alloc(); - * nla_put_u32(msg, 1, param1); - * nla_put_string(msg, 2, param2); - * - * nl_send_auto_complete(handle, nl_msg_get(msg)); - * nlmsg_free(msg); + * void my_func(struct nl_msg *msg) + * { + * NLA_PUT_U32(msg, ATTR_FOO, 10); + * NLA_PUT_STRING(msg, ATTR_BAR, "bar"); + * + * return 0; + * + * nla_put_failure: + * return -NLE_NOMEM; + * } * @endcode * - * @par 2) Constructing nested attributes + * @subsection attr_examples Examples + * @par Example 1.1 Constructing a netlink message with attributes. * @code - * struct nl_msg * nested_config(void) + * struct nl_msg *build_msg(int ifindex, struct nl_addr *lladdr, int mtu) * { - * int a = 5, int b = 10; - * struct nl_msg *n = nlmsg_alloc(); - * nla_put_u32(n, 10, a); - * nla_put_u32(n, 20, b); - * return n; + * struct nl_msg *msg; + * struct nlattr *info, *vlan; + * struct ifinfomsg ifi = { + * .ifi_family = AF_INET, + * .ifi_index = ifindex, + * }; + * + * // Allocate a new netlink message, type=RTM_SETLINK, flags=NLM_F_ECHO + * if (!(msg = nlmsg_alloc_simple(RTM_SETLINK, NLM_F_ECHO))) + * return NULL; + * + * // Append the family specific header (struct ifinfomsg) + * if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) + * goto nla_put_failure + * + * // Append a 32 bit integer attribute to carry the MTU + * NLA_PUT_U32(msg, IFLA_MTU, mtu); + * + * // Append a unspecific attribute to carry the link layer address + * NLA_PUT_ADDR(msg, IFLA_ADDRESS, lladdr); + * + * // Append a container for nested attributes to carry link information + * if (!(info = nla_nest_start(msg, IFLA_LINKINFO))) + * goto nla_put_failure; + * + * // Put a string attribute into the container + * NLA_PUT_STRING(msg, IFLA_INFO_KIND, "vlan"); + * + * // Append another container inside the open container to carry + * // vlan specific attributes + * if (!(vlan = nla_nest_start(msg, IFLA_INFO_DATA))) + * goto nla_put_failure; + * + * // add vlan specific info attributes here... + * + * // Finish nesting the vlan attributes and close the second container. + * nla_nest_end(msg, vlan); + * + * // Finish nesting the link info attribute and close the first container. + * nla_nest_end(msg, info); + * + * return msg; + * + * // If any of the construction macros fails, we end up here. + * nla_put_failure: + * nlmsg_free(msg); + * return NULL; * } + * @endcode + * + * @par Example 2.1 Parsing a netlink message with attributes. + * @code + * int parse_message(struct nl_msg *msg) + * { + * // The policy defines two attributes: a 32 bit integer and a container + * // for nested attributes. + * struct nla_policy attr_policy[ATTR_MAX+1] = { + * [ATTR_FOO] = { .type = NLA_U32 }, + * [ATTR_BAR] = { .type = NLA_NESTED }, + * }; + * struct nlattr *attrs[ATTR_MAX+1]; + * int err; + * + * // The nlmsg_parse() function will make sure that the message contains + * // enough payload to hold the header (struct my_hdr), validates any + * // attributes attached to the messages and stores a pointer to each + * // attribute in the attrs[] array accessable by attribute type. + * if ((err = nlmsg_parse(nlmsg_hdr(msg), sizeof(struct my_hdr), attrs, + * ATTR_MAX, attr_policy)) < 0) + * goto errout; + * + * if (attrs[ATTR_FOO]) { + * // It is safe to directly access the attribute payload without + * // any further checks since nlmsg_parse() enforced the policy. + * uint32_t foo = nla_get_u32(attrs[ATTR_FOO]); + * } + * + * if (attrs[ATTR_BAR]) { + * struct nlattr *nested[NESTED_MAX+1]; + * + * // Attributes nested in a container can be parsed the same way + * // as top level attributes. + * if ((err = nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_BAR], + * nested_policy)) < 0) + * goto errout; * - * ... - * struct nl_msg *m = nlmsg_alloc(); - * struct nl_msg *nest = nested_config(); - * nla_put_nested(m, 1, nest); + * // Process nested attributes here. + * } * - * nl_send_auto_complete(handle, nl_msg_get(m)); - * nlmsg_free(nest); - * nlmsg_free(m); + * err = 0; + * errout: + * return err; + * } * @endcode + * * @{ */ /** - * @name Size Calculations + * @name Attribute Size Calculation * @{ */ /** - * length of attribute not including padding - * @arg payload length of payload + * Return size of attribute whithout padding. + * @arg payload Payload length of attribute. + * + * @code + * <-------- nla_attr_size(payload) ---------> + * +------------------+- - -+- - - - - - - - - +- - -+ + * | Attribute Header | Pad | Payload | Pad | + * +------------------+- - -+- - - - - - - - - +- - -+ + * @endcode + * + * @return Size of attribute in bytes without padding. */ int nla_attr_size(int payload) { @@ -111,8 +398,17 @@ int nla_attr_size(int payload) } /** - * total length of attribute including padding - * @arg payload length of payload + * Return size of attribute including padding. + * @arg payload Payload length of attribute. + * + * @code + * <----------- nla_total_size(payload) -----------> + * +------------------+- - -+- - - - - - - - - +- - -+ + * | Attribute Header | Pad | Payload | Pad | + * +------------------+- - -+- - - - - - - - - +- - -+ + * @endcode + * + * @return Size of attribute in bytes. */ int nla_total_size(int payload) { @@ -120,8 +416,17 @@ int nla_total_size(int payload) } /** - * length of padding at the tail of the attribute - * @arg payload length of payload + * Return length of padding at the tail of the attribute. + * @arg payload Payload length of attribute. + * + * @code + * +------------------+- - -+- - - - - - - - - +- - -+ + * | Attribute Header | Pad | Payload | Pad | + * +------------------+- - -+- - - - - - - - - +- - -+ + * <---> + * @endcode + * + * @return Length of padding in bytes. */ int nla_padlen(int payload) { @@ -131,13 +436,15 @@ int nla_padlen(int payload) /** @} */ /** - * @name Payload Access + * @name Parsing Attributes * @{ */ /** - * attribute type - * @arg nla netlink attribute + * Return type of the attribute. + * @arg nla Attribute. + * + * @return Type of attribute. */ int nla_type(const struct nlattr *nla) { @@ -145,8 +452,10 @@ int nla_type(const struct nlattr *nla) } /** - * head of payload - * @arg nla netlink attribute + * Return pointer to the payload section. + * @arg nla Attribute. + * + * @return Pointer to start of payload section. */ void *nla_data(const struct nlattr *nla) { @@ -154,25 +463,27 @@ void *nla_data(const struct nlattr *nla) } /** - * length of payload - * @arg nla netlink attribute + * Return length of the payload . + * @arg nla Attribute + * + * @return Length of payload in bytes. */ int nla_len(const struct nlattr *nla) { return nla->nla_len - NLA_HDRLEN; } -/** @} */ - -/** - * @name Attribute Parsing - * @{ - */ - /** - * check if the netlink attribute fits into the remaining bytes - * @arg nla netlink attribute - * @arg remaining number of bytes remaining in attribute stream + * Check if the attribute header and payload can be accessed safely. + * @arg nla Attribute of any kind. + * @arg remaining Number of bytes remaining in attribute stream. + * + * Verifies that the header and payload do not exceed the number of + * bytes left in the attribute stream. This function must be called + * before access the attribute header or payload when iterating over + * the attribute stream using nla_next(). + * + * @return True if the attribute can be accessed safely, false otherwise. */ int nla_ok(const struct nlattr *nla, int remaining) { @@ -182,12 +493,20 @@ int nla_ok(const struct nlattr *nla, int remaining) } /** - * next netlink attribte in attribute stream - * @arg nla netlink attribute - * @arg remaining number of bytes remaining in attribute stream + * Return next attribute in a stream of attributes. + * @arg nla Attribute of any kind. + * @arg remaining Variable to count remaining bytes in stream. * - * @return the next netlink attribute in the attribute stream and - * decrements remaining by the size of the current attribute. + * Calculates the offset to the next attribute based on the attribute + * given. The attribute provided is assumed to be accessible, the + * caller is responsible to use nla_ok() beforehand. The offset (length + * of specified attribute including padding) is then subtracted from + * the remaining bytes variable and a pointer to the next attribute is + * returned. + * + * nla_next() can be called as long as remainig is >0. + * + * @return Pointer to next attribute. */ struct nlattr *nla_next(const struct nlattr *nla, int *remaining) { @@ -203,7 +522,6 @@ static uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = { [NLA_U32] = sizeof(uint32_t), [NLA_U64] = sizeof(uint64_t), [NLA_STRING] = 1, - [NLA_NESTED] = NLA_HDRLEN, }; static int validate_nla(struct nlattr *nla, int maxtype, @@ -226,18 +544,18 @@ static int validate_nla(struct nlattr *nla, int maxtype, minlen = nla_attr_minlen[pt->type]; if (pt->type == NLA_FLAG && nla_len(nla) > 0) - return nl_errno(ERANGE); + return -NLE_RANGE; if (nla_len(nla) < minlen) - return nl_errno(ERANGE); + return -NLE_RANGE; if (pt->maxlen && nla_len(nla) > pt->maxlen) - return nl_errno(ERANGE); + return -NLE_RANGE; if (pt->type == NLA_STRING) { char *data = nla_data(nla); if (data[nla_len(nla) - 1] != '\0') - return nl_errno(EINVAL); + return -NLE_INVAL; } return 0; @@ -245,18 +563,21 @@ static int validate_nla(struct nlattr *nla, int maxtype, /** - * Parse a stream of attributes into a tb buffer - * @arg tb destination array with maxtype+1 elements - * @arg maxtype maximum attribute type to be expected - * @arg head head of attribute stream - * @arg len length of attribute stream - * @arg policy validation policy + * Create attribute index based on a stream of attributes. + * @arg tb Index array to be filled (maxtype+1 elements). + * @arg maxtype Maximum attribute type expected and accepted. + * @arg head Head of attribute stream. + * @arg len Length of attribute stream. + * @arg policy Attribute validation policy. * - * Parses a stream of attributes and stores a pointer to each attribute in - * the tb array accessable via the attribute type. Attributes with a type - * exceeding maxtype will be silently ignored for backwards compatibility - * reasons. policy may be set to NULL if no validation is required. + * Iterates over the stream of attributes and stores a pointer to each + * attribute in the index array using the attribute type as index to + * the array. Attribute with a type greater than the maximum type + * specified will be silently ignored in order to maintain backwards + * compatibility. If \a policy is not NULL, the attribute will be + * validated using the specified policy. * + * @see nla_validate * @return 0 on success or a negative error code. */ int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, @@ -295,33 +616,20 @@ errout: return err; } - /** - * parse nested attributes - * @arg tb destination array with maxtype+1 elements - * @arg maxtype maximum attribute type to be expected - * @arg nla attribute containing the nested attributes - * @arg policy validation policy + * Validate a stream of attributes. + * @arg head Head of attributes stream. + * @arg len Length of attributes stream. + * @arg maxtype Maximum attribute type expected and accepted. + * @arg policy Validation policy. * - * @see nla_parse() - */ -int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, - struct nla_policy *policy) -{ - return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); -} - -/** - * Validate a stream of attributes - * @arg head head of attribute stream - * @arg len length of attribute stream - * @arg maxtype maximum attribute type to be expected - * @arg policy validation policy + * Iterates over the stream of attributes and validates each attribute + * one by one using the specified policy. Attributes with a type greater + * than the maximum type specified will be silently ignored in order to + * maintain backwards compatibility. * - * Validates all attributes in the specified attribute stream - * against the specified policy. Attributes with a type exceeding - * maxtype will be ignored. See documenation of struct nla_policy - * for more details. + * See \ref attr_datatypes for more details on what kind of validation + * checks are performed on each attribute data type. * * @return 0 on success or a negative error code. */ @@ -343,12 +651,16 @@ errout: } /** - * Find a specific attribute in a stream of attributes - * @arg head head of attribute stream - * @arg len length of attribute stream - * @arg attrtype type of attribute to look for + * Find a single attribute in a stream of attributes. + * @arg head Head of attributes stream. + * @arg len Length of attributes stream. + * @arg attrtype Attribute type to look for. * - * @return the first attribute in the stream matching the specified type. + * Iterates over the stream of attributes and compares each type with + * the type specified. Returns the first attribute which matches the + * type. + * + * @return Pointer to attribute found or NULL. */ struct nlattr *nla_find(struct nlattr *head, int len, int attrtype) { @@ -365,20 +677,20 @@ struct nlattr *nla_find(struct nlattr *head, int len, int attrtype) /** @} */ /** - * @name Utilities + * @name Helper Functions * @{ */ /** - * Copy a netlink attribute into another memory area - * @arg dest where to copy to memcpy - * @arg src netlink attribute to copy from - * @arg count size of the destination area + * Copy attribute payload to another memory area. + * @arg dest Pointer to destination memory area. + * @arg src Attribute + * @arg count Number of bytes to copy at most. * * Note: The number of bytes copied is limited by the length of - * attribute's payload. memcpy + * the attribute payload. * - * @return the number of bytes copied. + * @return The number of bytes copied to dest. */ int nla_memcpy(void *dest, struct nlattr *src, int count) { @@ -394,16 +706,16 @@ int nla_memcpy(void *dest, struct nlattr *src, int count) } /** - * Copy string attribute payload into a sized buffer - * @arg dst where to copy the string to - * @arg nla attribute to copy the string from - * @arg dstsize size of destination buffer + * Copy string attribute payload to a buffer. + * @arg dst Pointer to destination buffer. + * @arg nla Attribute of type NLA_STRING. + * @arg dstsize Size of destination buffer in bytes. * - * Copies at most dstsize - 1 bytes into the destination buffer. - * The result is always a valid NUL-terminated string. Unlike + * Copies at most dstsize - 1 bytes to the destination buffer. + * The result is always a valid NUL terminated string. Unlike * strlcpy the destination buffer is always padded out. * - * @return the length of the source buffer. + * @return The length of string attribute without the terminating NUL. */ size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize) { @@ -424,13 +736,15 @@ size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize) } /** - * Compare an attribute with sized memory area - * @arg nla netlink attribute - * @arg data memory area - * @arg size size of memory area + * Compare attribute payload with memory area. + * @arg nla Attribute. + * @arg data Memory area to compare to. + * @arg size Number of bytes to compare. + * + * @see memcmp(3) + * @return An integer less than, equal to, or greater than zero. */ -int nla_memcmp(const struct nlattr *nla, const void *data, - size_t size) +int nla_memcmp(const struct nlattr *nla, const void *data, size_t size) { int d = nla_len(nla) - size; @@ -441,9 +755,12 @@ int nla_memcmp(const struct nlattr *nla, const void *data, } /** - * Compare a string attribute against a string - * @arg nla netlink string attribute - * @arg str another string + * Compare string attribute payload with string + * @arg nla Attribute of type NLA_STRING. + * @arg str NUL terminated string. + * + * @see strcmp(3) + * @return An integer less than, equal to, or greater than zero. */ int nla_strcmp(const struct nlattr *nla, const char *str) { @@ -459,324 +776,392 @@ int nla_strcmp(const struct nlattr *nla, const char *str) /** @} */ /** - * @name Attribute Construction + * @name Unspecific Attribute * @{ */ /** - * reserve room for attribute on the skb - * @arg n netlink message - * @arg attrtype attribute type - * @arg attrlen length of attribute payload + * Reserve space for a attribute. + * @arg msg Netlink Message. + * @arg attrtype Attribute Type. + * @arg attrlen Length of payload. + * + * Reserves room for a attribute in the specified netlink message and + * fills in the attribute header (type, length). Returns NULL if there + * is unsuficient space for the attribute. * - * Adds a netlink attribute header to a netlink message and reserves - * room for the payload but does not copy it. + * Any padding between payload and the start of the next attribute is + * zeroed out. + * + * @return Pointer to start of attribute or NULL on failure. */ -struct nlattr *nla_reserve(struct nl_msg *n, int attrtype, int attrlen) +struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int attrlen) { struct nlattr *nla; int tlen; - tlen = NLMSG_ALIGN(n->nm_nlh->nlmsg_len) + nla_total_size(attrlen); + tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen); - if ((tlen + n->nm_nlh->nlmsg_len) > n->nm_size) { - nl_errno(ENOBUFS); + if ((tlen + msg->nm_nlh->nlmsg_len) > msg->nm_size) return NULL; - } - nla = (struct nlattr *) nlmsg_tail(n->nm_nlh); + nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh); nla->nla_type = attrtype; nla->nla_len = nla_attr_size(attrlen); memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); - n->nm_nlh->nlmsg_len = tlen; + msg->nm_nlh->nlmsg_len = tlen; NL_DBG(2, "msg %p: Reserved %d bytes at offset +%td for attr %d " - "nlmsg_len=%d\n", n, attrlen, - (void *) nla - nlmsg_data(n->nm_nlh), - attrtype, n->nm_nlh->nlmsg_len); + "nlmsg_len=%d\n", msg, attrlen, + (void *) nla - nlmsg_data(msg->nm_nlh), + attrtype, msg->nm_nlh->nlmsg_len); return nla; } /** - * Add a netlink attribute to a netlink message - * @arg n netlink message - * @arg attrtype attribute type - * @arg attrlen length of attribute payload - * @arg data head of attribute payload + * Add a unspecific attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg datalen Length of data to be used as payload. + * @arg data Pointer to data to be used as attribute payload. + * + * Reserves room for a unspecific attribute and copies the provided data + * into the message as payload of the attribute. Returns an error if there + * is insufficient space for the attribute. * - * @return -1 if the tailroom of the skb is insufficient to store - * the attribute header and payload. + * @see nla_reserve + * @return 0 on success or a negative error code. */ -int nla_put(struct nl_msg *n, int attrtype, int attrlen, const void *data) +int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data) { struct nlattr *nla; - nla = nla_reserve(n, attrtype, attrlen); + nla = nla_reserve(msg, attrtype, datalen); if (!nla) - return nl_errno(ENOMEM); + return -NLE_NOMEM; - memcpy(nla_data(nla), data, attrlen); + memcpy(nla_data(nla), data, datalen); NL_DBG(2, "msg %p: Wrote %d bytes at offset +%td for attr %d\n", - n, attrlen, (void *) nla - nlmsg_data(n->nm_nlh), attrtype); + msg, datalen, (void *) nla - nlmsg_data(msg->nm_nlh), attrtype); return 0; } /** - * Add a nested netlink attribute to a netlink message - * @arg n netlink message - * @arg attrtype attribute type - * @arg nested netlink attribute to nest + * Add abstract data as unspecific attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg data Abstract data object. + * + * Equivalent to nla_put() except that the length of the payload is + * derived from the abstract data object. * - * @return -1 if the tailroom of the skb is insufficient to store - * the attribute header and payload. + * @see nla_put + * @return 0 on success or a negative error code. */ -int nla_put_nested(struct nl_msg *n, int attrtype, struct nl_msg *nested) +int nla_put_data(struct nl_msg *msg, int attrtype, struct nl_data *data) { - return nla_put(n, attrtype, nlmsg_len(nested->nm_nlh), - nlmsg_data(nested->nm_nlh)); + return nla_put(msg, attrtype, nl_data_get_size(data), + nl_data_get(data)); } /** - * Add a u16 netlink attribute to a netlink message - * @arg n netlink message - * @arg attrtype attribute type - * @arg value numeric value + * Add abstract address as unspecific attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg addr Abstract address object. + * + * @see nla_put + * @return 0 on success or a negative error code. */ -int nla_put_u8(struct nl_msg *n, int attrtype, uint8_t value) +int nla_put_addr(struct nl_msg *msg, int attrtype, struct nl_addr *addr) { - return nla_put(n, attrtype, sizeof(uint8_t), &value); + return nla_put(msg, attrtype, nl_addr_get_len(addr), + nl_addr_get_binary_addr(addr)); } +/** @} */ + /** - * Add a u16 netlink attribute to a netlink message - * @arg n netlink message - * @arg attrtype attribute type - * @arg value numeric value + * @name Integer Attributes + */ + +/** + * Add 8 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. */ -int nla_put_u16(struct nl_msg *n, int attrtype, uint16_t value) +int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value) { - return nla_put(n, attrtype, sizeof(uint16_t), &value); + return nla_put(msg, attrtype, sizeof(uint8_t), &value); } /** - * Add a u32 netlink attribute to a netlink message - * @arg n netlink message - * @arg attrtype attribute type - * @arg value numeric value + * Return value of 8 bit integer attribute. + * @arg nla 8 bit integer attribute + * + * @return Payload as 8 bit integer. */ -int nla_put_u32(struct nl_msg *n, int attrtype, uint32_t value) +uint8_t nla_get_u8(struct nlattr *nla) { - return nla_put(n, attrtype, sizeof(uint32_t), &value); + return *(uint8_t *) nla_data(nla); } /** - * Add a u64 netlink attribute to a netlink message - * @arg n netlink message - * @arg attrtype attribute type - * @arg value numeric value + * Add 16 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. */ -int nla_put_u64(struct nl_msg *n, int attrtype, uint64_t value) +int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value) { - return nla_put(n, attrtype, sizeof(uint64_t), &value); + return nla_put(msg, attrtype, sizeof(uint16_t), &value); } /** - * Add a string netlink attribute to a netlink message - * @arg n netlink message - * @arg attrtype attribute type - * @arg str NUL terminated string + * Return payload of 16 bit integer attribute. + * @arg nla 16 bit integer attribute + * + * @return Payload as 16 bit integer. */ -int nla_put_string(struct nl_msg *n, int attrtype, const char *str) +uint16_t nla_get_u16(struct nlattr *nla) { - return nla_put(n, attrtype, strlen(str) + 1, str); + return *(uint16_t *) nla_data(nla); } /** - * Add a flag netlink attribute to a netlink message - * @arg n netlink message - * @arg attrtype attribute type + * Add 32 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. */ -int nla_put_flag(struct nl_msg *n, int attrtype) +int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value) { - return nla_put(n, attrtype, 0, NULL); + return nla_put(msg, attrtype, sizeof(uint32_t), &value); } /** - * Add a msecs netlink attribute to a netlink message - * @arg n netlink message - * @arg attrtype attribute type - * @arg msecs number of msecs + * Return payload of 32 bit integer attribute. + * @arg nla 32 bit integer attribute. + * + * @return Payload as 32 bit integer. */ -int nla_put_msecs(struct nl_msg *n, int attrtype, unsigned long msecs) +uint32_t nla_get_u32(struct nlattr *nla) { - return nla_put_u64(n, attrtype, msecs); + return *(uint32_t *) nla_data(nla); } /** - * Add an abstract data netlink attribute to a netlink message - * @arg n netlink message - * @arg attrtype attribute type - * @arg data abstract data + * Add 64 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. */ -int nla_put_data(struct nl_msg *n, int attrtype, struct nl_data *data) +int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value) { - return nla_put(n, attrtype, nl_data_get_size(data), - nl_data_get(data)); + return nla_put(msg, attrtype, sizeof(uint64_t), &value); } /** - * Add an abstract address netlink attribute to a netlink message - * @arg n netlink message - * @arg attrtype attribute type - * @arg addr abstract address + * Return payload of u64 attribute + * @arg nla u64 netlink attribute + * + * @return Payload as 64 bit integer. */ -int nla_put_addr(struct nl_msg *n, int attrtype, struct nl_addr *addr) +uint64_t nla_get_u64(struct nlattr *nla) { - return nla_put(n, attrtype, nl_addr_get_len(addr), - nl_addr_get_binary_addr(addr)); + uint64_t tmp; + + nla_memcpy(&tmp, nla, sizeof(tmp)); + + return tmp; } /** @} */ /** - * @name Attribute Nesting - * @{ + * @name String Attribute */ /** - * Start a new level of nested attributes - * @arg n netlink message - * @arg attrtype attribute type of container + * Add string attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg str NUL terminated string. * - * @return the container attribute + * @see nla_put + * @return 0 on success or a negative error code. */ -struct nlattr *nla_nest_start(struct nl_msg *n, int attrtype) +int nla_put_string(struct nl_msg *msg, int attrtype, const char *str) { - struct nlattr *start = (struct nlattr *) nlmsg_tail(n->nm_nlh); - - if (nla_put(n, attrtype, 0, NULL) < 0) - return NULL; - - return start; + return nla_put(msg, attrtype, strlen(str) + 1, str); } /** - * Finalize nesting of attributes - * @arg n netlink message - * @arg start container attribute - * - * Corrects the container attribute header to include the all - * appeneded attributes. + * Return payload of string attribute. + * @arg nla String attribute. * - * @return the total data length of the skb. + * @return Pointer to attribute payload. */ -int nla_nest_end(struct nl_msg *n, struct nlattr *start) +char *nla_get_string(struct nlattr *nla) { - start->nla_len = (unsigned char *) nlmsg_tail(n->nm_nlh) - - (unsigned char *) start; - return 0; + return (char *) nla_data(nla); +} + +char *nla_strdup(struct nlattr *nla) +{ + return strdup(nla_get_string(nla)); } /** @} */ /** - * @name Attribute Reading - * @{ + * @name Flag Attribute */ /** - * Return payload of u32 attribute - * @arg nla u32 netlink attribute + * Add flag netlink attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * + * @see nla_put + * @return 0 on success or a negative error code. */ -uint32_t nla_get_u32(struct nlattr *nla) +int nla_put_flag(struct nl_msg *msg, int attrtype) { - return *(uint32_t *) nla_data(nla); + return nla_put(msg, attrtype, 0, NULL); } /** - * Return payload of u16 attribute - * @arg nla u16 netlink attribute + * Return true if flag attribute is set. + * @arg nla Flag netlink attribute. + * + * @return True if flag is set, otherwise false. */ -uint16_t nla_get_u16(struct nlattr *nla) +int nla_get_flag(struct nlattr *nla) { - return *(uint16_t *) nla_data(nla); + return !!nla; } +/** @} */ + /** - * Return payload of u8 attribute - * @arg nla u8 netlink attribute + * @name Microseconds Attribute */ -uint8_t nla_get_u8(struct nlattr *nla) -{ - return *(uint8_t *) nla_data(nla); -} /** - * Return payload of u64 attribute - * @arg nla u64 netlink attribute + * Add a msecs netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg msecs number of msecs */ -uint64_t nla_get_u64(struct nlattr *nla) +int nla_put_msecs(struct nl_msg *n, int attrtype, unsigned long msecs) { - uint64_t tmp; - - nla_memcpy(&tmp, nla, sizeof(tmp)); - - return tmp; + return nla_put_u64(n, attrtype, msecs); } /** - * return payload of string attribute - * @arg nla string netlink attribute + * Return payload of msecs attribute + * @arg nla msecs netlink attribute + * + * @return the number of milliseconds. */ -char *nla_get_string(struct nlattr *nla) +unsigned long nla_get_msecs(struct nlattr *nla) { - return (char *) nla_data(nla); + return nla_get_u64(nla); } +/** @} */ + /** - * Return payload of flag attribute - * @arg nla flag netlink attribute + * @name Nested Attribute */ -int nla_get_flag(struct nlattr *nla) + +/** + * Add nested attributes to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg nested Message containing attributes to be nested. + * + * Takes the attributes found in the \a nested message and appends them + * to the message \a msg nested in a container of the type \a attrtype. + * The \a nested message may not have a family specific header. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_nested(struct nl_msg *msg, int attrtype, struct nl_msg *nested) { - return !!nla; + return nla_put(msg, attrtype, nlmsg_len(nested->nm_nlh), + nlmsg_data(nested->nm_nlh)); } + /** - * Return payload of msecs attribute - * @arg nla msecs netlink attribute + * Start a new level of nested attributes. + * @arg msg Netlink message. + * @arg attrtype Attribute type of container. * - * @return the number of milliseconds. + * @return Pointer to container attribute. */ -unsigned long nla_get_msecs(struct nlattr *nla) +struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype) { - return nla_get_u64(nla); + struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh); + + if (nla_put(msg, attrtype, 0, NULL) < 0) + return NULL; + + return start; } /** - * Return payload of address attribute - * @arg nla address netlink attribute - * @arg family address family + * Finalize nesting of attributes. + * @arg msg Netlink message. + * @arg start Container attribute as returned from nla_nest_start(). * - * @return Newly allocated address handle or NULL + * Corrects the container attribute header to include the appeneded attributes. + * + * @return 0 */ -struct nl_addr *nla_get_addr(struct nlattr *nla, int family) +int nla_nest_end(struct nl_msg *msg, struct nlattr *start) { - return nl_addr_build(family, nla_data(nla), nla_len(nla)); + start->nla_len = (unsigned char *) nlmsg_tail(msg->nm_nlh) - + (unsigned char *) start; + return 0; } /** - * Return payload of abstract data attribute - * @arg nla abstract data netlink attribute + * Create attribute index based on nested attribute + * @arg tb Index array to be filled (maxtype+1 elements). + * @arg maxtype Maximum attribute type expected and accepted. + * @arg nla Nested Attribute. + * @arg policy Attribute validation policy. + * + * Feeds the stream of attributes nested into the specified attribute + * to nla_parse(). * - * @return Newly allocated abstract data handle or NULL + * @see nla_parse + * @return 0 on success or a negative error code. */ -struct nl_data *nla_get_data(struct nlattr *nla) +int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, + struct nla_policy *policy) { - return nl_data_alloc(nla_data(nla), nla_len(nla)); + return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); } /** @} */ diff --git a/lib/cache.c b/lib/cache.c index 285bc31..2b24946 100644 --- a/lib/cache.c +++ b/lib/cache.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -175,10 +175,8 @@ struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops) struct nl_cache *cache; cache = calloc(1, sizeof(*cache)); - if (!cache) { - nl_errno(ENOMEM); + if (!cache) return NULL; - } nl_init_list_head(&cache->c_items); cache->c_ops = ops; @@ -188,22 +186,43 @@ struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops) return cache; } +int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock, + struct nl_cache **result) +{ + struct nl_cache *cache; + int err; + + if (!(cache = nl_cache_alloc(ops))) + return -NLE_NOMEM; + + if (sock && (err = nl_cache_refill(sock, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + /** * Allocate an empty cache based on type name * @arg kind Name of cache type * @return A newly allocated and initialized cache. */ -struct nl_cache *nl_cache_alloc_name(const char *kind) +int nl_cache_alloc_name(const char *kind, struct nl_cache **result) { struct nl_cache_ops *ops; + struct nl_cache *cache; ops = nl_cache_ops_lookup(kind); - if (!ops) { - nl_error(ENOENT, "Unable to lookup cache \"%s\"", kind); - return NULL; - } + if (!ops) + return -NLE_NOCACHE; + + if (!(cache = nl_cache_alloc(ops))) + return -NLE_NOMEM; - return nl_cache_alloc(ops); + *result = cache; + return 0; } /** @@ -264,6 +283,9 @@ void nl_cache_clear(struct nl_cache *cache) */ void nl_cache_free(struct nl_cache *cache) { + if (!cache) + return; + nl_cache_clear(cache); NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache)); free(cache); @@ -304,12 +326,12 @@ int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) struct nl_object *new; if (cache->c_ops->co_obj_ops != obj->ce_ops) - return nl_error(EINVAL, "Object mismatches cache type"); + return -NLE_OBJ_MISMATCH; if (!nl_list_empty(&obj->ce_list)) { new = nl_object_clone(obj); if (!new) - return nl_errno(ENOMEM); + return -NLE_NOMEM; } else { nl_object_get(obj); new = obj; @@ -331,7 +353,7 @@ int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) int nl_cache_move(struct nl_cache *cache, struct nl_object *obj) { if (cache->c_ops->co_obj_ops != obj->ce_ops) - return nl_error(EINVAL, "Object mismatches cache type"); + return -NLE_OBJ_MISMATCH; NL_DBG(3, "Moving object %p to cache %p\n", obj, cache); @@ -405,7 +427,7 @@ struct nl_object *nl_cache_search(struct nl_cache *cache, /** * Request a full dump from the kernel to fill a cache - * @arg handle Netlink handle + * @arg sk Netlink socket. * @arg cache Cache subjected to be filled. * * Send a dumping request to the kernel causing it to dump all objects @@ -414,15 +436,15 @@ struct nl_object *nl_cache_search(struct nl_cache *cache, * Use nl_cache_pickup() to read the objects from the socket and fill them * into a cache. */ -int nl_cache_request_full_dump(struct nl_handle *handle, struct nl_cache *cache) +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 nl_error(EOPNOTSUPP, "Operation not supported"); + return -NLE_OPNOTSUPP; - return cache->c_ops->co_request_update(cache, handle); + return cache->c_ops->co_request_update(cache, sk); } /** @cond SKIP */ @@ -439,7 +461,7 @@ static int update_msg_parser(struct nl_msg *msg, void *arg) } /** @endcond */ -int __cache_pickup(struct nl_handle *handle, struct nl_cache *cache, +int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache, struct nl_parser_param *param) { int err; @@ -452,17 +474,17 @@ int __cache_pickup(struct nl_handle *handle, struct nl_cache *cache, NL_DBG(1, "Picking up answer for cache %p <%s>...\n", cache, nl_cache_name(cache)); - cb = nl_cb_clone(handle->h_cb); + cb = nl_cb_clone(sk->s_cb); if (cb == NULL) - return nl_get_errno(); + return -NLE_NOMEM; nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x); - err = nl_recvmsgs(handle, cb); + 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_geterror(err)); nl_cb_put(cb); @@ -476,7 +498,7 @@ static int pickup_cb(struct nl_object *c, struct nl_parser_param *p) /** * Pickup a netlink dump response and put it into a cache. - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg cache Cache to put items into. * * Waits for netlink messages to arrive, parses them and puts them into @@ -484,18 +506,18 @@ static int pickup_cb(struct nl_object *c, struct nl_parser_param *p) * * @return 0 on success or a negative error code. */ -int nl_cache_pickup(struct nl_handle *handle, struct nl_cache *cache) +int nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache) { struct nl_parser_param p = { .pp_cb = pickup_cb, .pp_arg = cache, }; - return __cache_pickup(handle, cache, &p); + return __cache_pickup(sk, cache, &p); } static int cache_include(struct nl_cache *cache, struct nl_object *obj, - struct nl_msgtype *type, change_func_t cb) + struct nl_msgtype *type, change_func_t cb, void *data) { struct nl_object *old; @@ -507,7 +529,7 @@ static int cache_include(struct nl_cache *cache, struct nl_object *obj, nl_cache_remove(old); if (type->mt_act == NL_ACT_DEL) { if (cb) - cb(cache, old, NL_ACT_DEL); + cb(cache, old, NL_ACT_DEL, data); nl_object_put(old); } } @@ -515,10 +537,10 @@ static int cache_include(struct nl_cache *cache, struct nl_object *obj, if (type->mt_act == NL_ACT_NEW) { nl_cache_move(cache, obj); if (old == NULL && cb) - cb(cache, obj, NL_ACT_NEW); + cb(cache, obj, NL_ACT_NEW, data); else if (old) { if (nl_object_diff(old, obj) && cb) - cb(cache, obj, NL_ACT_CHANGE); + cb(cache, obj, NL_ACT_CHANGE, data); nl_object_put(old); } @@ -533,36 +555,37 @@ static int cache_include(struct nl_cache *cache, struct nl_object *obj, } int nl_cache_include(struct nl_cache *cache, struct nl_object *obj, - change_func_t change_cb) + change_func_t change_cb, void *data) { struct nl_cache_ops *ops = cache->c_ops; int i; if (ops->co_obj_ops != obj->ce_ops) - return nl_error(EINVAL, "Object mismatches cache type"); + return -NLE_OBJ_MISMATCH; for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype) return cache_include(cache, obj, &ops->co_msgtypes[i], - change_cb); + change_cb, data); - return nl_errno(EINVAL); + return -NLE_MSGTYPE_NOSUPPORT; } static int resync_cb(struct nl_object *c, struct nl_parser_param *p) { struct nl_cache_assoc *ca = p->pp_arg; - return nl_cache_include(ca->ca_cache, c, ca->ca_change); + return nl_cache_include(ca->ca_cache, c, ca->ca_change, ca->ca_change_data); } -int nl_cache_resync(struct nl_handle *handle, struct nl_cache *cache, - change_func_t change_cb) +int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache, + change_func_t change_cb, void *data) { struct nl_object *obj, *next; struct nl_cache_assoc ca = { .ca_cache = cache, .ca_change = change_cb, + .ca_change_data = data, }; struct nl_parser_param p = { .pp_cb = resync_cb, @@ -575,17 +598,23 @@ int nl_cache_resync(struct nl_handle *handle, struct nl_cache *cache, /* Mark all objects so we can see if some of them are obsolete */ nl_cache_mark_all(cache); - err = nl_cache_request_full_dump(handle, cache); + err = nl_cache_request_full_dump(sk, cache); if (err < 0) goto errout; - err = __cache_pickup(handle, cache, &p); + err = __cache_pickup(sk, cache, &p); if (err < 0) goto errout; - nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list) - if (nl_object_is_marked(obj)) + nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list) { + if (nl_object_is_marked(obj)) { + nl_object_get(obj); nl_cache_remove(obj); + if (change_cb) + change_cb(cache, obj, NL_ACT_DEL, data); + nl_object_put(obj); + } + } NL_DBG(1, "Finished resyncing %p <%s>\n", cache, nl_cache_name(cache)); @@ -607,23 +636,19 @@ int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who, { int i, err; - if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize)) { - err = nl_error(EINVAL, "netlink message too short to be " - "of kind %s", ops->co_name); - goto errout; - } + if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize)) + return -NLE_MSG_TOOSHORT; for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) { if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) { err = ops->co_msg_parser(ops, who, nlh, params); - if (err != -ENOENT) + if (err != -NLE_OPNOTSUPP) goto errout; } } - err = nl_error(EINVAL, "Unsupported netlink message type %d", - nlh->nlmsg_type); + err = -NLE_MSGTYPE_NOSUPPORT; errout: return err; } @@ -651,7 +676,7 @@ int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg) /** * (Re)fill a cache with the contents in the kernel. - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg cache cache to update * * Clears the specified cache and fills it with the current state in @@ -659,11 +684,11 @@ int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg) * * @return 0 or a negative error code. */ -int nl_cache_refill(struct nl_handle *handle, struct nl_cache *cache) +int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache) { int err; - err = nl_cache_request_full_dump(handle, cache); + err = nl_cache_request_full_dump(sk, cache); if (err < 0) return err; @@ -671,7 +696,7 @@ int nl_cache_refill(struct nl_handle *handle, struct nl_cache *cache) cache, nl_cache_name(cache)); nl_cache_clear(cache); - return nl_cache_pickup(handle, cache); + return nl_cache_pickup(sk, cache); } /** @} */ @@ -728,7 +753,7 @@ void nl_cache_dump_filter(struct nl_cache *cache, struct nl_dump_params *params, struct nl_object *filter) { - int type = params ? params->dp_type : NL_DUMP_FULL; + int type = params ? params->dp_type : NL_DUMP_DETAILS; struct nl_object_ops *ops; struct nl_object *obj; diff --git a/lib/cache_mngr.c b/lib/cache_mngr.c index a144b92..81052aa 100644 --- a/lib/cache_mngr.c +++ b/lib/cache_mngr.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2007 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -95,7 +95,7 @@ static int include_cb(struct nl_object *obj, struct nl_parser_param *p) if (nl_debug >= 4) nl_object_dump(obj, &nl_debug_dp); #endif - return nl_cache_include(ca->ca_cache, obj, ca->ca_change); + return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data); } static int event_input(struct nl_msg *msg, void *arg) @@ -140,56 +140,55 @@ found: /** * Allocate new cache manager - * @arg handle Netlink socket/handle to be used + * @arg sk Netlink socket. * @arg protocol Netlink Protocol this manager is used for * @arg flags Flags * * @return Newly allocated cache manager or NULL on failure. */ -struct nl_cache_mngr *nl_cache_mngr_alloc(struct nl_handle *handle, - int protocol, int flags) +int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags, + struct nl_cache_mngr **result) { struct nl_cache_mngr *mngr; + int err = -NLE_NOMEM; - if (handle == NULL) + if (sk == NULL) BUG(); mngr = calloc(1, sizeof(*mngr)); if (!mngr) - goto enomem; + goto errout; - mngr->cm_handle = handle; + mngr->cm_handle = sk; mngr->cm_nassocs = 32; mngr->cm_protocol = protocol; mngr->cm_flags = flags; mngr->cm_assocs = calloc(mngr->cm_nassocs, sizeof(struct nl_cache_assoc)); if (!mngr->cm_assocs) - goto enomem; - + goto errout; nl_socket_modify_cb(mngr->cm_handle, NL_CB_VALID, NL_CB_CUSTOM, event_input, mngr); /* Required to receive async event notifications */ - nl_disable_sequence_check(mngr->cm_handle); + nl_socket_disable_seq_check(mngr->cm_handle); - if (nl_connect(mngr->cm_handle, protocol) < 0) + if ((err = nl_connect(mngr->cm_handle, protocol) < 0)) goto errout; - if (nl_socket_set_nonblocking(mngr->cm_handle) < 0) + if ((err = nl_socket_set_nonblocking(mngr->cm_handle) < 0)) goto errout; NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n", mngr, protocol, mngr->cm_nassocs); - return mngr; + *result = mngr; + return 0; -enomem: - nl_errno(ENOMEM); errout: nl_cache_mngr_free(mngr); - return NULL; + return err; } /** @@ -197,6 +196,7 @@ errout: * @arg mngr Cache manager. * @arg name Name of cache to keep track of * @arg cb Function to be called upon changes. + * @arg result Pointer to store added cache. * * Allocates a new cache of the specified type and adds it to the manager. * The operation will trigger a full dump request from the kernel to @@ -204,10 +204,10 @@ errout: * to the notification group of the cache to keep track of any further * changes. * - * @return The newly allocated cache or NULL on failure. + * @return 0 on success or a negative error code. */ -struct nl_cache *nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, - change_func_t cb) +int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, + change_func_t cb, void *data, struct nl_cache **result) { struct nl_cache_ops *ops; struct nl_cache *cache; @@ -215,28 +215,19 @@ struct nl_cache *nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, int err, i; ops = nl_cache_ops_lookup(name); - if (!ops) { - nl_error(ENOENT, "Unknown cache type"); - return NULL; - } + if (!ops) + return -NLE_NOCACHE; - if (ops->co_protocol != mngr->cm_protocol) { - nl_error(EINVAL, "Netlink protocol mismatch"); - return NULL; - } + if (ops->co_protocol != mngr->cm_protocol) + return -NLE_PROTO_MISMATCH; - if (ops->co_groups == NULL) { - nl_error(EOPNOTSUPP, NULL); - return NULL; - } + if (ops->co_groups == NULL) + return -NLE_OPNOTSUPP; - for (i = 0; i < mngr->cm_nassocs; i++) { + for (i = 0; i < mngr->cm_nassocs; i++) if (mngr->cm_assocs[i].ca_cache && - mngr->cm_assocs[i].ca_cache->c_ops == ops) { - nl_error(EEXIST, "Cache of this type already managed"); - return NULL; - } - } + mngr->cm_assocs[i].ca_cache->c_ops == ops) + return -NLE_EXIST; retry: for (i = 0; i < mngr->cm_nassocs; i++) @@ -248,10 +239,9 @@ retry: mngr->cm_assocs = realloc(mngr->cm_assocs, mngr->cm_nassocs * sizeof(struct nl_cache_assoc)); - if (mngr->cm_assocs == NULL) { - nl_errno(ENOMEM); - return NULL; - } else { + if (mngr->cm_assocs == NULL) + return -NLE_NOMEM; + else { NL_DBG(1, "Increased capacity of cache manager %p " \ "to %d\n", mngr, mngr->cm_nassocs); goto retry; @@ -259,10 +249,8 @@ retry: } cache = nl_cache_alloc(ops); - if (!cache) { - nl_errno(ENOMEM); - return NULL; - } + if (!cache) + return -NLE_NOMEM; for (grp = ops->co_groups; grp->ag_group; grp++) { err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group); @@ -276,6 +264,7 @@ retry: mngr->cm_assocs[i].ca_cache = cache; mngr->cm_assocs[i].ca_change = cb; + mngr->cm_assocs[i].ca_change_data = data; if (mngr->cm_flags & NL_AUTO_PROVIDE) nl_cache_mngt_provide(cache); @@ -283,7 +272,8 @@ retry: NL_DBG(1, "Added cache %p <%s> to cache manager %p\n", cache, nl_cache_name(cache), mngr); - return cache; + *result = cache; + return 0; errout_drop_membership: for (grp = ops->co_groups; grp->ag_group; grp++) @@ -291,7 +281,7 @@ errout_drop_membership: errout_free_cache: nl_cache_free(cache); - return NULL; + return err; } /** @@ -334,7 +324,7 @@ int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout) ret = poll(&fds, 1, timeout); NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret); if (ret < 0) - return nl_errno(errno); + return -nl_syserr2nlerr(errno); if (ret == 0) return 0; @@ -365,20 +355,24 @@ int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr) } /** - * Free cache manager - * @arg mngr Cache manager + * Free cache manager and all caches. + * @arg mngr Cache manager. * * Release all resources after usage of a cache manager. */ void nl_cache_mngr_free(struct nl_cache_mngr *mngr) { + int i; + if (!mngr) return; - if (mngr->cm_handle) { + if (mngr->cm_handle) nl_close(mngr->cm_handle); - nl_handle_destroy(mngr->cm_handle); - } + + for (i = 0; i < mngr->cm_nassocs; i++) + if (mngr->cm_assocs[i].ca_cache) + nl_cache_free(mngr->cm_assocs[i].ca_cache); free(mngr->cm_assocs); free(mngr); diff --git a/lib/cache_mngt.c b/lib/cache_mngt.c index de2bf24..d57d836 100644 --- a/lib/cache_mngt.c +++ b/lib/cache_mngt.c @@ -6,10 +6,11 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** + * @ingroup core * @defgroup cache_mngt Caching * @{ */ @@ -60,11 +61,14 @@ struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype) int i; struct nl_cache_ops *ops; - for (ops = cache_ops; ops; ops = ops->co_next) + for (ops = cache_ops; ops; ops = ops->co_next) { + if (ops->co_protocol != protocol) + continue; + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) - if (ops->co_msgtypes[i].mt_id == msgtype && - ops->co_protocol == protocol) + if (ops->co_msgtypes[i].mt_id == msgtype) return ops; + } return NULL; } @@ -126,15 +130,12 @@ void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *arg) */ int nl_cache_mngt_register(struct nl_cache_ops *ops) { - if (!ops->co_name) - return nl_error(EINVAL, "No cache name specified"); - - if (!ops->co_obj_ops) - return nl_error(EINVAL, "No obj cache ops specified"); + if (!ops->co_name || !ops->co_obj_ops) + return -NLE_INVAL; if (nl_cache_ops_lookup(ops->co_name)) - return nl_error(EEXIST, "Cache operations already exist"); - + return -NLE_EXIST; + ops->co_next = cache_ops; cache_ops = ops; @@ -163,7 +164,7 @@ int nl_cache_mngt_unregister(struct nl_cache_ops *ops) break; if (!t) - return nl_error(ENOENT, "No such cache operations"); + return -NLE_NOCACHE; NL_DBG(1, "Unregistered cache operations %s\n", ops->co_name); @@ -6,11 +6,11 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** - * @ingroup utils + * @ingroup core * @defgroup data Abstract Data * @{ */ @@ -56,11 +56,25 @@ struct nl_data *nl_data_alloc(void *buf, size_t size) return data; errout: - nl_errno(ENOMEM); return NULL; } /** + * Allocate abstract data object based on netlink attribute. + * @arg nla Netlink attribute of unspecific type. + * + * Allocates a new abstract data and copies the payload of the + * attribute to the abstract data object. + * + * @see nla_data_alloc + * @return Newly allocated data handle or NULL + */ +struct nl_data *nl_data_alloc_attr(struct nlattr *nla) +{ + return nl_data_alloc(nla_data(nla), nla_len(nla)); +} + +/** * Clone an abstract data object. * @arg src Abstract data object * @@ -90,7 +104,7 @@ int nl_data_append(struct nl_data *data, void *buf, size_t size) if (size > 0) { data->d_data = realloc(data->d_data, data->d_size + size); if (!data->d_data) - return nl_errno(ENOMEM); + return -NLE_NOMEM; if (buf) memcpy(data->d_data + data->d_size, buf, size); diff --git a/lib/defs.h.in b/lib/defs.h.in deleted file mode 100644 index ef86caa..0000000 --- a/lib/defs.h.in +++ /dev/null @@ -1,28 +0,0 @@ -/* lib/defs.h.in. Generated from configure.in by autoheader. */ - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* verbose errors */ -#undef VERBOSE_ERRORS - -/* Define to empty if `const' does not conform to ANSI C. */ -#undef const - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -#undef inline -#endif @@ -6,12 +6,28 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** * @mainpage * + * @section intro Introduction + * + * libnl is a set of libraries to deal with the netlink protocol and some + * of the high level protocols implemented on top of it. Its goal is to + * simplify netlink protocol usage and to create an abstraction layer using + * object based interfaces for various netlink based subsystems.The library + * was developed and tested on the 2.6.x kernel releases but it may work with + * older kernel series. + * + * @section toc Table of Contents + * + * - \subpage core_doc + * - \subpage route_doc + * - \subpage genl_doc + * - \subpage nf_doc + * * @section remarks Remarks * * @subsection cache_alloc Allocation of Caches @@ -19,7 +35,7 @@ * Almost all subsystem provide a function to allocate a new cache * of some form. The function usually looks like this: * @code - * struct nl_cache *<object name>_alloc_cache(struct nl_handle *handle) + * struct nl_cache *<object name>_alloc_cache(struct nl_sock *sk); * @endcode * * These functions allocate a new cache for the own object type, @@ -103,4 +119,366 @@ * to the corresponding numeric value. * * @return Identifier as numeric value or a negative value if none was found. + * + * @page core_doc Core Library (-lnl) + * + * @section core_intro Introduction + * + * The core library contains the fundamentals required to communicate over + * netlink sockets. It deals with connecting and unconnecting of sockets, + * sending and receiving of data, provides a customizeable receiving state + * machine, and provides a abstract data type framework which eases the + * implementation of object based netlink protocols where objects are added, + * removed, or modified with the help of netlink messages. + * + * @section core_toc Table of Contents + * + * - \ref proto_fund + * - \ref sk_doc + * - \ref rxtx_doc + * - \ref cb_doc + * + * @section proto_fund Netlink Protocol Fundamentals + * + * The netlink protocol is a socket based IPC mechanism used for communication + * between userspace processes and the kernel. The netlink protocol uses the + * \c AF_NETLINK address family and defines a protocol type for each subsystem + * protocol (e.g. NETLINK_ROUTE, NETLINK_NETFILTER, etc). Its addressing + * schema is based on a 32 bit port number, formerly referred to as PID, which + * uniquely identifies each peer. + * + * The netlink protocol is based on messages each limited to the size of a + * memory page and consists of the netlink message header (struct nlmsghdr) + * plus the payload attached to it. The payload can consist of arbitary data + * but often contains a fixed sized family specifc header followed by a + * stream of \ref attr_doc. The use of attributes dramatically increases + * the flexibility of the protocol and allows for the protocol to be + * extended while maintaining backwards compatibility. + * + * The netlink message header (struct nlmsghdr): + * @code + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-------------------------------------------------------------+ + * | Length | + * +------------------------------+------------------------------+ + * | Type | Flags | + * +------------------------------+------------------------------+ + * | Sequence Number | + * +-------------------------------------------------------------+ + * | Port (Address) | + * +-------------------------------------------------------------+ + * @endcode + * + * Netlink differs between requests, notifications, and replies. Requests + * are messages which have the \c NLM_F_REQUEST flag set and are meant to + * request an action from the receiver. A request is typically sent from + * a userspace process to the kernel. Every request should be assigned a + * sequence number which should be incremented for each request sent on the + * sending side. Depending on the nature of the request, the receiver may + * reply to the request with regular netlink messages which should contain + * the same sequence number as the request it relates to. Notifications are + * of informal nature and don't expect a reply, therefore the sequence number + * is typically set to 0. It should be noted that unlike in protocols such as + * TCP there is no strict enforcment of the sequence number. The sole purpose + * of sequence numbers is to assist a sender in relating replies to the + * corresponding requests. + * + * @msc + * A,B; + * A=>B [label="GET (seq=1, NLM_F_REQUEST)"]; + * A<=B [label="PUT (seq=1)"]; + * ...; + * A<=B [label="NOTIFY (seq=0)"]; + * @endmsc + * + * If the size of a reply exceeds the size of a memory page and thus exceeds + * the maximum message size, the reply can be split into a series of multipart + * messages. A multipart message has the \c flag NLM_F_MULTI set and the + * receiver is expected to continue parsing the reply until the special + * message type \c NLMSG_DONE is received. + * + * @msc + * A,B; + * A=>B [label="GET (seq=1, NLM_F_REQUEST)"]; + * A<=B [label="PUT (seq=1, NLM_F_MULTI)"]; + * ...; + * A<=B [label="PUT (seq=1, NLM_F_MULTI)"]; + * A<=B [label="NLMSG_DONE (seq=1)"]; + * @endmsc + * + * Errors can be reported using the standard message type \c NLMSG_ERROR which + * can carry an error code and the netlink mesage header of the request. + * Error messages should set their sequence number to the sequence number + * of the message which caused the error. + * + * @msc + * A,B; + * A=>B [label="GET (seq=1, NLM_F_REQUEST)"]; + * A<=B [label="NLMSG_ERROR code=EINVAL (seq=1)"]; + * @endmsc + * + * The \c NLMSG_ERROR message type is also used to send acknowledge messages. + * An acknowledge message can be requested by setting the \c NLM_F_ACK flag + * message except that the error code is set to 0. + * + * @msc + * A,B; + * A=>B [label="GET (seq=1, NLM_F_REQUEST | NLM_F_ACK)"]; + * A<=B [label="ACK (seq=1)"]; + * @endmsc + * + * @section sk_doc Dealing with Netlink Sockets + * + * In order to use the netlink protocol, a netlink socket is required. Each + * socket defines a completely independent context for sending and receiving + * of messages. The netlink socket and all its related attributes are + * represented by struct nl_sock. + * + * @code + * nl_socket_alloc() Allocate new socket structure. + * nl_socket_free(s) Free socket structure. + * @endcode + * + * @subsection local_port Local Port + * The local port number uniquely identifies the socket and is used to + * address it. A unique local port is generated automatically when the socket + * is allocated. It will consist of the Process ID (22 bits) and a random + * number (10 bits) to allow up to 1024 sockets per process. + * + * @code + * nl_socket_get_local_port(sk) Return the peer's port number. + * nl_socket_set_local_port(sk, port) Set the peer's port number. + * @endcode + * + * @subsection peer_port Peer Port + * A peer port can be assigned to the socket which will result in all unicast + * messages sent over the socket to be addresses to the corresponding peer. If + * no peer is specified, the kernel will try to automatically bind the socket + * to a kernel side socket of the same netlink protocol family. It is common + * practice not to bind the socket to a peer port as typically only one kernel + * side socket exists per netlink protocol family. + * + * @code + * nl_socket_get_peer_port(sk) Return the local port number. + * nl_socket_set_peer_port(sk, port) Set the local port number. + * @endcode + * + * @subsection sock_fd File Descriptor + * The file descriptor of the socket(2). + * + * @code + * nl_socket_get_fd(sk) Return file descriptor. + * nl_socket_set_buffer_size(sk, rx, tx) Set buffer size of socket. + * nl_socket_set_nonblocking(sk) Set socket to non-blocking state. + * @endcode + * + * @subsection group_sub Group Subscriptions + * Each socket can subscribe to multicast groups of the netlink protocol + * family it is bound to. The socket will then receive a copy of each + * message sent to any of the groups. Multicast groups are commonly used + * to implement event notifications. Prior to kernel 2.6.14 the group + * subscription was performed using a bitmask which limited the number of + * groups per protocol family to 32. This outdated interface can still be + * accessed via the function nl_join_groups even though it is not recommended + * for new code. Starting with 2.6.14 a new method was introduced which + * supports subscribing to an almost unlimited number of multicast groups. + * + * @code + * nl_socket_add_membership(sk, group) Become a member of a multicast group. + * nl_socket_drop_membership(sk, group) Drop multicast group membership. + * nl_join_groups(sk, groupmask) Join a multicast group (obsolete). + * @endcode + * + * @subsection seq_num Sequence Numbers + * The socket keeps track of the sequence numbers used. The library will + * automatically verify the sequence number of messages received unless + * the check was disabled using the function nl_socket_disable_seq_check(). + * When a message is sent using nl_send_auto_complete(), the sequence number + * is automatically filled in, and replies will be verified correctly. + * + * @code + * nl_socket_disable_seq_check(sk) Disable checking of sequece numbers. + * nl_socket_use_seq(sk) Use sequence number and bump to next. + * @endcode + * + * @subsection sock_cb Callback Configuration + * Every socket is associated a callback configuration which enables the + * applications to hook into various internal functions and control the + * receiving and sendings semantics. For more information, see section + * \ref cb_doc. + * + * @code + * nl_socket_alloc_cb(cb) Allocate socket based on callback set. + * nl_socket_get_cb(sk) Return callback configuration. + * nl_socket_set_cb(sk, cb) Replace callback configuration. + * nl_socket_modify_cb(sk, ...) Modify a specific callback function. + * @endcode + * + * @subsection sk_other Other Functions + * @code + * nl_socket_enable_auto_ack(sock) Enable automatic request of ACK. + * nl_socket_disable_auto_ack(sock) Disable automatic request of ACK. + * nl_socket_enable_msg_peek(sock) Enable message peeking. + * nl_socket_disable_msg_peek(sock) Disable message peeking. + * nl_socket_set_passcred(sk, state) Enable/disable credential passing. + * nl_socket_recv_pktinfo(sk, state) Enable/disable packet information. + * @endcode + * + * @section rxtx_doc Sending and Receiving of Data + * + * @subsection recv_semantisc Receiving Semantics + * @code + * nl_recvmsgs_default(set) + * | cb = nl_socket_get_cb(sk) + * v + * nl_recvmsgs(sk, cb) + * | [Application provides nl_recvmsgs() replacement] + * |- - - - - - - - - - - - - - - v + * | cb->cb_recvmsgs_ow() + * | + * | [Application provides nl_recv() replacement] + * +-------------->|- - - - - - - - - - - - - - - v + * | nl_recv() cb->cb_recv_ow() + * | +----------->|<- - - - - - - - - - - - - - -+ + * | | v + * | | Parse Message + * | | |- - - - - - - - - - - - - - - v + * | | | NL_CB_MSG_IN() + * | | |<- - - - - - - - - - - - - - -+ + * | | | + * | | |- - - - - - - - - - - - - - - v + * | | Sequence Check NL_CB_SEQ_CHECK() + * | | |<- - - - - - - - - - - - - - -+ + * | | | + * | | |- - - - - - - - - - - - - - - v [ NLM_F_ACK is set ] + * | | | NL_CB_SEND_ACK() + * | | |<- - - - - - - - - - - - - - -+ + * | | | + * | | +-----+------+--------------+----------------+--------------+ + * | | v v v v v + * | | Valid Message ACK NO-OP Message End of Multipart Error + * | | | | | | | + * | | v v v v v + * | |NL_CB_VALID() NL_CB_ACK() NL_CB_SKIPPED() NL_CB_FINISH() cb->cb_err() + * | | | | | | | + * | | +------------+--------------+----------------+ v + * | | | (FAILURE) + * | | | [Callback returned NL_SKIP] + * | | [More messages to be parsed] |<----------- + * | +----------------------------------| + * | | + * | [is Multipart message] | + * +-------------------------------------| [Callback returned NL_STOP] + * |<----------- + * v + * (SUCCESS) + * + * At any time: + * Message Format Error + * |- - - - - - - - - - - - v + * v NL_CB_INVALID() + * (FAILURE) + * + * Message Overrun (Kernel Lost Data) + * |- - - - - - - - - - - - v + * v NL_CB_OVERRUN() + * (FAILURE) + * + * Callback returned negative error code + * (FAILURE) + * @endcode + * + * @subsection send_semantics Sending Semantisc + * + * @code + * nl_send_auto_complete(sk, msg) + * | [Automatically completes netlink message header] + * | [(local port, sequence number) ] + * | + * | [Application provies nl_send() replacement] + * |- - - - - - - - - - - - - - - - - - - - v + * v cb->cb_send_ow() + * nl_send(sk, msg) + * | [If available, add peer port and credentials] + * v + * nl_sendmsg(sk, msg, msghdr) + * |- - - - - - - - - - - - - - - - - - - - v + * | NL_CB_MSG_OUT() + * |<- - - - - - - - - - - - - - - - - - - -+ + * v + * sendmsg() + * @endcode + * + * @section cb_doc Callback Configurations + * Callbacks and overwriting capabilities are provided to control various + * semantics of the library. All callback functions are packed together in + * struct nl_cb which is attached to a netlink socket or passed on to + * the respective functions directly. + * + * @subsection cb_ret_doc Callback Return Values + * Callback functions can control the flow of the calling layer by returning + * appropriate error codes: + * @code + * Action ID | Description + * -----------------+------------------------------------------------------- + * NL_OK | Proceed with whatever comes next. + * NL_SKIP | Skip message currently being processed and continue + * | with next message. + * NL_STOP | Stop parsing and discard all remaining messages in + * | this set of messages. + * @endcode + * + * All callbacks are optional and a default action is performed if no + * application specific implementation is provided: + * + * @code + * Callback ID | Default Return Value + * ------------------+---------------------- + * NL_CB_VALID | NL_OK + * NL_CB_FINISH | NL_STOP + * NL_CB_OVERRUN | NL_STOP + * NL_CB_SKIPPED | NL_SKIP + * NL_CB_ACK | NL_STOP + * NL_CB_MSG_IN | NL_OK + * NL_CB_MSG_OUT | NL_OK + * NL_CB_INVALID | NL_STOP + * NL_CB_SEQ_CHECK | NL_OK + * NL_CB_SEND_ACK | NL_OK + * | + * Error Callback | NL_STOP + * @endcode + * + * In order to simplify typical usages of the library, different sets of + * default callback implementations exist: + * @code + * NL_CB_DEFAULT: No additional actions + * NL_CB_VERBOSE: Automatically print warning and error messages to a file + * descriptor as appropriate. This is useful for CLI based + * applications. + * NL_CB_DEBUG: Print informal debugging information for each message + * received. This will result in every message beint sent or + * received to be printed to the screen in a decoded, + * human-readable format. + * @endcode + * + * @par 1) Setting up a callback set + * @code + * // Allocate a callback set and initialize it to the verbose default set + * struct nl_cb *cb = nl_cb_alloc(NL_CB_VERBOSE); + * + * // Modify the set to call my_func() for all valid messages + * nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL); + * + * // Set the error message handler to the verbose default implementation + * // and direct it to print all errors to the given file descriptor. + * FILE *file = fopen(...); + * nl_cb_err(cb, NL_CB_VERBOSE, NULL, file); + * @endcode + * + * @page route_doc Routing Family + * + * @page genl_doc Generic Netlink Family + * + * @page nf_doc Netfilter Subsystem */ diff --git a/lib/error.c b/lib/error.c new file mode 100644 index 0000000..9a9fac7 --- /dev/null +++ b/lib/error.c @@ -0,0 +1,110 @@ +/* + * lib/error.c Error Handling + * + * 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) 2008 Thomas Graf <tgraf@suug.ch> + */ + +#include <netlink-local.h> +#include <netlink/netlink.h> + +static const char *errmsg[NLE_MAX+1] = { +[NLE_SUCCESS] = "Success", +[NLE_FAILURE] = "Unspecific failure", +[NLE_INTR] = "Interrupted system call", +[NLE_BAD_SOCK] = "Bad socket", +[NLE_AGAIN] = "Try again", +[NLE_NOMEM] = "Out of memory", +[NLE_EXIST] = "Object exists", +[NLE_INVAL] = "Invalid input data or parameter", +[NLE_RANGE] = "Input data out of range", +[NLE_MSGSIZE] = "Message size not sufficient", +[NLE_OPNOTSUPP] = "Operation not supported", +[NLE_AF_NOSUPPORT] = "Address family not supported", +[NLE_OBJ_NOTFOUND] = "Object not found", +[NLE_NOATTR] = "Attribute not available", +[NLE_MISSING_ATTR] = "Missing attribute", +[NLE_AF_MISMATCH] = "Address family mismatch", +[NLE_SEQ_MISMATCH] = "Message sequence number mismatch", +[NLE_MSG_OVERFLOW] = "Kernel reported message overflow", +[NLE_MSG_TRUNC] = "Kernel reported truncated message", +[NLE_NOADDR] = "Invalid address for specified address family", +[NLE_SRCRT_NOSUPPORT] = "Source based routing not supported", +[NLE_MSG_TOOSHORT] = "Netlink message is too short", +[NLE_MSGTYPE_NOSUPPORT] = "Netlink message type is not supported", +[NLE_OBJ_MISMATCH] = "Object type does not match cache", +[NLE_NOCACHE] = "Unknown or invalid cache type", +[NLE_BUSY] = "Object busy", +[NLE_PROTO_MISMATCH] = "Protocol mismatch", +[NLE_NOACCESS] = "No Access", +[NLE_PERM] = "Operation not permitted", +[NLE_PKTLOC_FILE] = "Unable to open packet location file", +}; + +/** + * Return error message for an error code + * @return error message + */ +const char *nl_geterror(int error) +{ + error = abs(error); + + if (error > NLE_MAX) + error = NLE_FAILURE; + + return errmsg[error]; +} + +/** + * Print a libnl error message + * @arg s error message prefix + * + * Prints the error message of the call that failed last. + * + * If s is not NULL and *s is not a null byte the argument + * string is printed, followed by a colon and a blank. Then + * the error message and a new-line. + */ +void nl_perror(int error, const char *s) +{ + if (s && *s) + fprintf(stderr, "%s: %s\n", s, nl_geterror(error)); + else + fprintf(stderr, "%s\n", nl_geterror(error)); +} + +int nl_syserr2nlerr(int error) +{ + error = abs(error); + + switch (error) { + case EBADF: return NLE_BAD_SOCK; + case EADDRINUSE: return NLE_EXIST; + case EEXIST: return NLE_EXIST; + case EADDRNOTAVAIL: return NLE_NOADDR; + case ENOENT: return NLE_OBJ_NOTFOUND; + case EINTR: return NLE_INTR; + case EAGAIN: return NLE_AGAIN; + case ENOTSOCK: return NLE_BAD_SOCK; + case ENOPROTOOPT: return NLE_INVAL; + case EFAULT: return NLE_INVAL; + case EACCES: return NLE_NOACCESS; + case EINVAL: return NLE_INVAL; + case ENOBUFS: return NLE_NOMEM; + case ENOMEM: return NLE_NOMEM; + case EAFNOSUPPORT: return NLE_AF_NOSUPPORT; + case EPROTONOSUPPORT: return NLE_PROTO_MISMATCH; + case EOPNOTSUPP: return NLE_OPNOTSUPP; + case EPERM: return NLE_PERM; + case EBUSY: return NLE_BUSY; + case ERANGE: return NLE_RANGE; + default: return NLE_FAILURE; + } +} + +/** @} */ + diff --git a/lib/family.c b/lib/family.c deleted file mode 100644 index ba1d65f..0000000 --- a/lib/family.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * lib/family.c Netlink Family - * - * 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) 2003-2006 Thomas Graf <tgraf@suug.ch> - */ - -/** - * @defgroup nlfam Netlink Families - * @brief - * - * @{ - */ - -#include <netlink-local.h> -#include <netlink/netlink.h> -#include <netlink/utils.h> - -/** - * @name Netlink Family Name Translation - * @{ - */ - -static struct trans_tbl nlfamilies[] = { - __ADD(NETLINK_ROUTE,route) - __ADD(NETLINK_USERSOCK,usersock) - __ADD(NETLINK_FIREWALL,firewall) - __ADD(NETLINK_INET_DIAG,inetdiag) - __ADD(NETLINK_NFLOG,nflog) - __ADD(NETLINK_XFRM,xfrm) - __ADD(NETLINK_SELINUX,selinux) - __ADD(NETLINK_ISCSI,iscsi) - __ADD(NETLINK_AUDIT,audit) - __ADD(NETLINK_FIB_LOOKUP,fib_lookup) - __ADD(NETLINK_CONNECTOR,connector) - __ADD(NETLINK_NETFILTER,netfilter) - __ADD(NETLINK_IP6_FW,ip6_fw) - __ADD(NETLINK_DNRTMSG,dnrtmsg) - __ADD(NETLINK_KOBJECT_UEVENT,kobject_uevent) - __ADD(NETLINK_GENERIC,generic) - __ADD(NETLINK_SCSITRANSPORT,scsitransport) - __ADD(NETLINK_ECRYPTFS,ecryptfs) -}; - -char * nl_nlfamily2str(int family, char *buf, size_t size) -{ - return __type2str(family, buf, size, nlfamilies, - ARRAY_SIZE(nlfamilies)); -} - -int nl_str2nlfamily(const char *name) -{ - return __str2type(name, nlfamilies, ARRAY_SIZE(nlfamilies)); -} - -/** @} */ - -/** @} */ diff --git a/lib/fib_lookup/lookup.c b/lib/fib_lookup/lookup.c index 5766035..ce9c027 100644 --- a/lib/fib_lookup/lookup.c +++ b/lib/fib_lookup/lookup.c @@ -6,11 +6,10 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** - * @ingroup nlfam * @defgroup fib_lookup FIB Lookup * @brief * @{ @@ -63,7 +62,7 @@ static int result_clone(struct nl_object *_dst, struct nl_object *_src) if (src->fr_req) if (!(dst->fr_req = (struct flnl_request *) nl_object_clone(OBJ_CAST(src->fr_req)))) - return nl_get_errno(); + return -NLE_NOMEM; return 0; } @@ -74,7 +73,7 @@ static int result_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct flnl_result *res; struct fib_result_nl *fr; struct nl_addr *addr; - int err = -EINVAL; + int err = -NLE_INVAL; res = flnl_result_alloc(); if (!res) @@ -121,27 +120,24 @@ errout: return err; } -static int result_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +static void result_dump_line(struct nl_object *obj, struct nl_dump_params *p) { struct flnl_result *res = (struct flnl_result *) obj; char buf[128]; - int line = 1; - dp_dump(p, "table %s prefixlen %u next-hop-selector %u\n", + nl_dump_line(p, "table %s prefixlen %u next-hop-selector %u\n", rtnl_route_table2str(res->fr_table_id, buf, sizeof(buf)), res->fr_prefixlen, res->fr_nh_sel); - dp_dump_line(p, line++, "type %s ", + nl_dump_line(p, "type %s ", nl_rtntype2str(res->fr_type, buf, sizeof(buf))); - dp_dump(p, "scope %s error %s (%d)\n", + nl_dump(p, "scope %s error %s (%d)\n", rtnl_scope2str(res->fr_scope, buf, sizeof(buf)), strerror(-res->fr_error), res->fr_error); - - return line; } -static int result_dump_full(struct nl_object *obj, struct nl_dump_params *p) +static void result_dump_details(struct nl_object *obj, struct nl_dump_params *p) { - return result_dump_brief(obj, p); + result_dump_line(obj, p); } static int result_compare(struct nl_object *_a, struct nl_object *_b, @@ -209,7 +205,8 @@ struct nl_cache *flnl_result_alloc_cache(void) * @note Not all attributes can be changed, see * \ref link_changeable "Changeable Attributes" for more details. */ -struct nl_msg *flnl_lookup_build_request(struct flnl_request *req, int flags) +int flnl_lookup_build_request(struct flnl_request *req, int flags, + struct nl_msg **result) { struct nl_msg *msg; struct nl_addr *addr; @@ -228,30 +225,29 @@ struct nl_msg *flnl_lookup_build_request(struct flnl_request *req, int flags) fr.tb_id_in = table >= 0 ? table : RT_TABLE_UNSPEC; addr = flnl_request_get_addr(req); - if (!addr) { - nl_error(EINVAL, "Request must specify the address"); - return NULL; - } + if (!addr) + return -NLE_MISSING_ATTR; fr.fl_addr = *(uint32_t *) nl_addr_get_binary_addr(addr); msg = nlmsg_alloc_simple(0, flags); if (!msg) - goto errout; + return -NLE_NOMEM; if (nlmsg_append(msg, &fr, sizeof(fr), NLMSG_ALIGNTO) < 0) goto errout; - return msg; + *result = msg; + return 0; errout: nlmsg_free(msg); - return NULL; + return -NLE_MSGSIZE; } /** * Perform FIB Lookup - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg req Lookup request object. * @arg cache Cache for result. * @@ -260,22 +256,21 @@ errout: * * @return 0 on success or a negative error code. */ -int flnl_lookup(struct nl_handle *handle, struct flnl_request *req, +int flnl_lookup(struct nl_sock *sk, struct flnl_request *req, struct nl_cache *cache) { struct nl_msg *msg; int err; - msg = flnl_lookup_build_request(req, 0); - if (!msg) - return nl_errno(ENOMEM); + if ((err = flnl_lookup_build_request(req, 0, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); if (err < 0) return err; - return nl_cache_pickup(handle, cache); + return nl_cache_pickup(sk, cache); } /** @} */ @@ -322,8 +317,10 @@ static struct nl_object_ops result_obj_ops = { .oo_size = sizeof(struct flnl_result), .oo_free_data = result_free_data, .oo_clone = result_clone, - .oo_dump[NL_DUMP_BRIEF] = result_dump_brief, - .oo_dump[NL_DUMP_FULL] = result_dump_full, + .oo_dump = { + [NL_DUMP_LINE] = result_dump_line, + [NL_DUMP_DETAILS] = result_dump_details, + }, .oo_compare = result_compare, }; diff --git a/lib/fib_lookup/request.c b/lib/fib_lookup/request.c index 8b00224..ffcf8f5 100644 --- a/lib/fib_lookup/request.c +++ b/lib/fib_lookup/request.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -48,11 +48,9 @@ static int request_clone(struct nl_object *_dst, struct nl_object *_src) if (src->lr_addr) if (!(dst->lr_addr = nl_addr_clone(src->lr_addr))) - goto errout; + return -NLE_NOMEM; return 0; -errout: - return nl_get_errno(); } static int request_compare(struct nl_object *_a, struct nl_object *_b, @@ -152,7 +150,7 @@ int flnl_request_get_table(struct flnl_request *req) int flnl_request_set_addr(struct flnl_request *req, struct nl_addr *addr) { if (addr->a_family != AF_INET) - return nl_error(EINVAL, "Address must be an IPv4 address"); + return -NLE_AF_NOSUPPORT; if (req->lr_addr) nl_addr_put(req->lr_addr); diff --git a/lib/genl/ctrl.c b/lib/genl/ctrl.c index 9948a57..1301642 100644 --- a/lib/genl/ctrl.c +++ b/lib/genl/ctrl.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -31,7 +31,7 @@ static struct nl_cache_ops genl_ctrl_ops; /** @endcond */ -static int ctrl_request_update(struct nl_cache *c, struct nl_handle *h) +static int ctrl_request_update(struct nl_cache *c, struct nl_sock *h) { return genl_send_simple(h, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, CTRL_VERSION, NLM_F_DUMP); @@ -61,17 +61,17 @@ static int ctrl_msg_parser(struct nl_cache_ops *ops, struct genl_cmd *cmd, family = genl_family_alloc(); if (family == NULL) { - err = nl_errno(ENOMEM); + err = -NLE_NOMEM; goto errout; } if (info->attrs[CTRL_ATTR_FAMILY_NAME] == NULL) { - err = nl_error(EINVAL, "Missing family name TLV"); + err = -NLE_MISSING_ATTR; goto errout; } if (info->attrs[CTRL_ATTR_FAMILY_ID] == NULL) { - err = nl_error(EINVAL, "Missing family id TLV"); + err = -NLE_MISSING_ATTR; goto errout; } @@ -111,7 +111,7 @@ static int ctrl_msg_parser(struct nl_cache_ops *ops, struct genl_cmd *cmd, goto errout; if (tb[CTRL_ATTR_OP_ID] == NULL) { - err = nl_errno(EINVAL); + err = -NLE_MISSING_ATTR; goto errout; } @@ -128,11 +128,6 @@ static int ctrl_msg_parser(struct nl_cache_ops *ops, struct genl_cmd *cmd, } err = pp->pp_cb((struct nl_object *) family, pp); - if (err < 0) - goto errout; - - err = P_ACCEPT; - errout: genl_family_put(family); return err; @@ -143,20 +138,9 @@ errout: * @{ */ -struct nl_cache *genl_ctrl_alloc_cache(struct nl_handle *handle) +int genl_ctrl_alloc_cache(struct nl_sock *sock, struct nl_cache **result) { - struct nl_cache * cache; - - cache = nl_cache_alloc(&genl_ctrl_ops); - if (cache == NULL) - return NULL; - - if (handle && nl_cache_refill(handle, cache) < 0) { - nl_cache_free(cache); - return NULL; - } - - return cache; + return nl_cache_alloc_and_fill(&genl_ctrl_ops, sock, result); } /** @@ -227,7 +211,7 @@ struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, /** * Resolve generic netlink family name to its identifier - * @arg handle Netlink Handle + * @arg sk Netlink socket. * @arg name Name of generic netlink family * * Resolves the generic netlink family name to its identifer and returns @@ -235,19 +219,18 @@ struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, * * @return A positive identifier or a negative error code. */ -int genl_ctrl_resolve(struct nl_handle *handle, const char *name) +int genl_ctrl_resolve(struct nl_sock *sk, const char *name) { struct nl_cache *cache; struct genl_family *family; int err; - cache = genl_ctrl_alloc_cache(handle); - if (cache == NULL) - return nl_get_errno(); + if ((err = genl_ctrl_alloc_cache(sk, &cache)) < 0) + return err; family = genl_ctrl_search_by_name(cache, name); if (family == NULL) { - err = nl_error(ENOENT, "Generic Netlink Family not found"); + err = -NLE_OBJ_NOTFOUND; goto errout; } diff --git a/lib/genl/family.c b/lib/genl/family.c index e05b52c..4c6c18d 100644 --- a/lib/genl/family.c +++ b/lib/genl/family.c @@ -71,14 +71,12 @@ static int family_clone(struct nl_object *_dst, struct nl_object *_src) return 0; } -static int family_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +static void family_dump_line(struct nl_object *obj, struct nl_dump_params *p) { struct genl_family *family = (struct genl_family *) obj; - dp_dump(p, "0x%04x %s version %u\n", + nl_dump(p, "0x%04x %s version %u\n", family->gf_id, family->gf_name, family->gf_version); - - return 1; } static struct trans_tbl ops_flags[] = { @@ -93,13 +91,12 @@ static char *ops_flags2str(int flags, char *buf, size_t len) return __flags2str(flags, buf, len, ops_flags, ARRAY_SIZE(ops_flags)); } -static int family_dump_full(struct nl_object *obj, struct nl_dump_params *p) +static void family_dump_details(struct nl_object *obj, struct nl_dump_params *p) { struct genl_family *family = (struct genl_family *) obj; - int line; - line = family_dump_brief(obj, p); - dp_dump_line(p, line++, " hdrsize %u maxattr %u\n", + family_dump_line(obj, p); + nl_dump_line(p, " hdrsize %u maxattr %u\n", family->gf_hdrsize, family->gf_maxattr); if (family->ce_mask & FAMILY_ATTR_OPS) { @@ -111,24 +108,21 @@ static int family_dump_full(struct nl_object *obj, struct nl_dump_params *p) genl_op2name(family->gf_id, op->o_id, buf, sizeof(buf)); - dp_dump_line(p, line++, " op %s (0x%02x)", - buf, op->o_id); + nl_dump_line(p, " op %s (0x%02x)", buf, op->o_id); if (op->o_flags) - dp_dump(p, " <%s>", + nl_dump(p, " <%s>", ops_flags2str(op->o_flags, buf, sizeof(buf))); - dp_dump(p, "\n"); + nl_dump(p, "\n"); } } - - return line; } -static int family_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +static void family_dump_stats(struct nl_object *obj, struct nl_dump_params *p) { - return family_dump_full(obj, p); + family_dump_details(obj, p); } static int family_compare(struct nl_object *_a, struct nl_object *_b, @@ -250,7 +244,7 @@ int genl_family_add_op(struct genl_family *family, int id, int flags) op = calloc(1, sizeof(*op)); if (op == NULL) - return nl_errno(ENOMEM); + return -NLE_NOMEM; op->o_id = id; op->o_flags = flags; @@ -270,13 +264,11 @@ struct nl_object_ops genl_family_ops = { .oo_constructor = family_constructor, .oo_free_data = family_free_data, .oo_clone = family_clone, - .oo_dump[NL_DUMP_BRIEF] = family_dump_brief, - .oo_dump[NL_DUMP_FULL] = family_dump_full, - .oo_dump[NL_DUMP_STATS] = family_dump_stats, -#if 0 - .oo_dump[NL_DUMP_XML] = addr_dump_xml, - .oo_dump[NL_DUMP_ENV] = addr_dump_env, -#endif + .oo_dump = { + [NL_DUMP_LINE] = family_dump_line, + [NL_DUMP_DETAILS] = family_dump_details, + [NL_DUMP_STATS] = family_dump_stats, + }, .oo_compare = family_compare, .oo_id_attrs = FAMILY_ATTR_ID, }; diff --git a/lib/genl/genl.c b/lib/genl/genl.c index 04cfebf..055be91 100644 --- a/lib/genl/genl.c +++ b/lib/genl/genl.c @@ -6,11 +6,10 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** - * @ingroup nlfam * @defgroup genl Generic Netlink * * @par Message Format @@ -38,12 +37,12 @@ * #include <netlink/genl/genl.h> * #include <netlink/genl/ctrl.h> * - * struct nl_handle *sock; + * struct nl_sock *sock; * struct nl_msg *msg; * int family; * * // Allocate a new netlink socket - * sock = nl_handle_alloc(); + * sock = nl_socket_alloc(); * * // Connect to generic netlink socket on kernel side * genl_connect(sock); @@ -100,9 +99,9 @@ * @{ */ -int genl_connect(struct nl_handle *handle) +int genl_connect(struct nl_sock *sk) { - return nl_connect(handle, NETLINK_GENERIC); + return nl_connect(sk, NETLINK_GENERIC); } /** @} */ @@ -114,7 +113,7 @@ int genl_connect(struct nl_handle *handle) /** * Send trivial generic netlink message - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg family Generic netlink family * @arg cmd Command * @arg version Version @@ -125,7 +124,7 @@ int genl_connect(struct nl_handle *handle) * * @return 0 on success or a negative error code. */ -int genl_send_simple(struct nl_handle *handle, int family, int cmd, +int genl_send_simple(struct nl_sock *sk, int family, int cmd, int version, int flags) { struct genlmsghdr hdr = { @@ -133,7 +132,7 @@ int genl_send_simple(struct nl_handle *handle, int family, int cmd, .version = version, }; - return nl_send_simple(handle, family, flags, &hdr, sizeof(hdr)); + return nl_send_simple(sk, family, flags, &hdr, sizeof(hdr)); } /** @} */ @@ -164,7 +163,7 @@ int genlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype, struct genlmsghdr *ghdr; if (!genlmsg_valid_hdr(nlh, hdrlen)) - return nl_errno(EINVAL); + return -NLE_MSG_TOOSHORT; ghdr = nlmsg_data(nlh); return nla_validate(genlmsg_attrdata(ghdr, hdrlen), @@ -177,7 +176,7 @@ int genlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], struct genlmsghdr *ghdr; if (!genlmsg_valid_hdr(nlh, hdrlen)) - return nl_errno(EINVAL); + return -NLE_MSG_TOOSHORT; ghdr = nlmsg_data(nlh); return nla_parse(tb, maxtype, genlmsg_attrdata(ghdr, hdrlen), diff --git a/lib/genl/mngt.c b/lib/genl/mngt.c index d737697..0ebe74d 100644 --- a/lib/genl/mngt.c +++ b/lib/genl/mngt.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -108,12 +108,12 @@ static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, goto found; } - err = nl_errno(ENOENT); + err = -NLE_MSGTYPE_NOSUPPORT; goto errout; found: if (cmd->c_msg_parser == NULL) - err = nl_error(EOPNOTSUPP, "No message parser found."); + err = -NLE_OPNOTSUPP; else { struct nlattr *tb[cmd->c_maxattr + 1]; struct genl_info info = { @@ -174,22 +174,17 @@ int genl_register(struct nl_cache_ops *ops) int err; if (ops->co_protocol != NETLINK_GENERIC) { - err = nl_error(EINVAL, "cache operations not for protocol " \ - "NETLINK_GENERIC (protocol=%s)", - ops->co_protocol); + err = -NLE_PROTO_MISMATCH; goto errout; } if (ops->co_hdrsize < GENL_HDRSIZE(0)) { - err = nl_error(EINVAL, "co_hdrsize too short, probably " \ - "not including genlmsghdr, minsize=%d", - GENL_HDRSIZE(0)); + err = -NLE_INVAL; goto errout; } if (ops->co_genl == NULL) { - err = nl_error(EINVAL, "co_genl is NULL, must provide " \ - "valid genl operations"); + err = -NLE_INVAL; goto errout; } @@ -236,20 +231,16 @@ static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops) return 0; } - return nl_error(ENOENT, "Unable to find generic netlink family \"%s\"", - ops->o_name); + return -NLE_OBJ_NOTFOUND; } -int genl_ops_resolve(struct nl_handle *handle, struct genl_ops *ops) +int genl_ops_resolve(struct nl_sock *sk, struct genl_ops *ops) { struct nl_cache *ctrl; int err; - ctrl = genl_ctrl_alloc_cache(handle); - if (ctrl == NULL) { - err = nl_get_errno(); + if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0) goto errout; - } err = __genl_ops_resolve(ctrl, ops); @@ -258,17 +249,14 @@ errout: return err; } -int genl_mngt_resolve(struct nl_handle *handle) +int genl_mngt_resolve(struct nl_sock *sk) { struct nl_cache *ctrl; struct genl_ops *ops; int err = 0; - ctrl = genl_ctrl_alloc_cache(handle); - if (ctrl == NULL) { - err = nl_get_errno(); + if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0) goto errout; - } nl_list_for_each_entry(ops, &genl_ops_list, o_list) { err = __genl_ops_resolve(ctrl, ops); diff --git a/lib/handlers.c b/lib/handlers.c index 1797e4f..f13b89e 100644 --- a/lib/handlers.c +++ b/lib/handlers.c @@ -6,64 +6,14 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** - * @ingroup nl + * @ingroup core * @defgroup cb Callbacks/Customization - * @brief - * - * Callbacks and overwriting capabilities are provided to take influence - * in various control flows inside the library. All callbacks are packed - * together in struct nl_cb which is then attached to a netlink socket or - * passed on to the respective functions directly. - * - * Callbacks can control the flow of the underlying layer by returning - * the appropriate error codes: - * @code - * Action ID | Description - * -----------------+------------------------------------------------------- - * NL_OK | Proceed with whatever comes next. - * NL_SKIP | Skip message currently being processed and continue - * | with next message. - * NL_STOP | Stop parsing and discard all remaining messages in - * | this set of messages. - * @endcode - * - * All callbacks are optional and a default action is performed if no - * application specific implementation is provided: - * - * @code - * Callback ID | Default Return Value - * ------------------+---------------------- - * NL_CB_VALID | NL_OK - * NL_CB_FINISH | NL_STOP - * NL_CB_OVERRUN | NL_STOP - * NL_CB_SKIPPED | NL_SKIP - * NL_CB_ACK | NL_STOP - * NL_CB_MSG_IN | NL_OK - * NL_CB_MSG_OUT | NL_OK - * NL_CB_INVALID | NL_STOP - * NL_CB_SEQ_CHECK | NL_OK - * NL_CB_SEND_ACK | NL_OK - * | - * Error Callback | NL_STOP - * @endcode - * - * In order to simplify typical usages of the library, different sets of - * default callback implementations exist: - * @code - * NL_CB_DEFAULT: No additional actions - * NL_CB_VERBOSE: Automatically print warning and error messages to a file - * descriptor as appropriate. This is useful for CLI based - * applications. - * NL_CB_DEBUG: Print informal debugging information for each message - * received. This will result in every message beint sent or - * received to be printed to the screen in a decoded, - * human-readable format. - * @endcode * + * @details * @par 1) Setting up a callback set * @code * // Allocate a callback set and initialize it to the verbose default set @@ -140,7 +90,7 @@ static int nl_error_handler_verbose(struct sockaddr_nl *who, print_header_content(ofd, &e->msg); fprintf(ofd, "\n"); - return e->error; + return -nl_syserr2nlerr(e->error); } static int nl_valid_handler_debug(struct nl_msg *msg, void *arg) @@ -261,10 +211,8 @@ struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind) return NULL; cb = calloc(1, sizeof(*cb)); - if (!cb) { - nl_errno(ENOMEM); + if (!cb) return NULL; - } cb->cb_refcnt = 1; @@ -338,10 +286,10 @@ int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind, nl_recvmsg_msg_cb_t func, void *arg) { if (type < 0 || type > NL_CB_TYPE_MAX) - return nl_error(ERANGE, "Callback type out of range"); + return -NLE_RANGE; if (kind < 0 || kind > NL_CB_KIND_MAX) - return nl_error(ERANGE, "Callback kind out of range"); + return -NLE_RANGE; if (kind == NL_CB_CUSTOM) { cb->cb_set[type] = func; @@ -388,7 +336,7 @@ int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, nl_recvmsg_err_cb_t func, void *arg) { if (kind < 0 || kind > NL_CB_KIND_MAX) - return nl_error(ERANGE, "Callback kind out of range"); + return -NLE_RANGE; if (kind == NL_CB_CUSTOM) { cb->cb_err = func; @@ -414,7 +362,7 @@ int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, * @arg func replacement callback for nl_recvmsgs() */ void nl_cb_overwrite_recvmsgs(struct nl_cb *cb, - int (*func)(struct nl_handle *, struct nl_cb *)) + int (*func)(struct nl_sock *, struct nl_cb *)) { cb->cb_recvmsgs_ow = func; } @@ -425,7 +373,7 @@ void nl_cb_overwrite_recvmsgs(struct nl_cb *cb, * @arg func replacement callback for nl_recv() */ void nl_cb_overwrite_recv(struct nl_cb *cb, - int (*func)(struct nl_handle *, struct sockaddr_nl *, + int (*func)(struct nl_sock *, struct sockaddr_nl *, unsigned char **, struct ucred **)) { cb->cb_recv_ow = func; @@ -437,7 +385,7 @@ void nl_cb_overwrite_recv(struct nl_cb *cb, * @arg func replacement callback for nl_send() */ void nl_cb_overwrite_send(struct nl_cb *cb, - int (*func)(struct nl_handle *, struct nl_msg *)) + int (*func)(struct nl_sock *, struct nl_msg *)) { cb->cb_send_ow = func; } @@ -6,11 +6,11 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** - * @ingroup nl + * @ingroup core * @defgroup msg Messages * Netlink Message Construction/Parsing Interface * @@ -284,7 +284,7 @@ int nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen) */ int nlmsg_ok(const struct nlmsghdr *nlh, int remaining) { - return (remaining >= sizeof(struct nlmsghdr) && + return (remaining >= (int)sizeof(struct nlmsghdr) && nlh->nlmsg_len >= sizeof(struct nlmsghdr) && nlh->nlmsg_len <= remaining); } @@ -320,7 +320,7 @@ int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, struct nla_policy *policy) { if (!nlmsg_valid_hdr(nlh, hdrlen)) - return nl_errno(EINVAL); + return -NLE_MSG_TOOSHORT; return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), policy); @@ -351,7 +351,7 @@ int nlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype, struct nla_policy *policy) { if (!nlmsg_valid_hdr(nlh, hdrlen)) - return nl_errno(EINVAL); + return -NLE_MSG_TOOSHORT; return nla_validate(nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), maxtype, policy); @@ -372,10 +372,14 @@ static struct nl_msg *__nlmsg_alloc(size_t len) if (!nm) goto errout; - nm->nm_nlh = calloc(1, len); + nm->nm_refcnt = 1; + + nm->nm_nlh = malloc(len); if (!nm->nm_nlh) goto errout; + memset(nm->nm_nlh, 0, sizeof(struct nlmsghdr)); + nm->nm_protocol = -1; nm->nm_size = len; nm->nm_nlh->nlmsg_len = nlmsg_total_size(0); @@ -385,7 +389,6 @@ static struct nl_msg *__nlmsg_alloc(size_t len) return nm; errout: free(nm); - nl_errno(ENOMEM); return NULL; } @@ -517,10 +520,8 @@ void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad) tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len; - if ((tlen + nlmsg_len) > n->nm_size) { - nl_errno(ENOBUFS); + if ((tlen + nlmsg_len) > n->nm_size) return NULL; - } buf += nlmsg_len; n->nm_nlh->nlmsg_len += tlen; @@ -552,7 +553,7 @@ int nlmsg_append(struct nl_msg *n, void *data, size_t len, int pad) tmp = nlmsg_reserve(n, len, pad); if (tmp == NULL) - return nl_errno(ENOMEM); + return -NLE_NOMEM; memcpy(tmp, data, len); NL_DBG(2, "msg %p: Appended %zu bytes with padding %d\n", n, len, pad); @@ -579,11 +580,11 @@ int nlmsg_expand(struct nl_msg *n, size_t newlen) void *tmp; if (newlen <= n->nm_size) - return nl_errno(EINVAL); + return -NLE_INVAL; tmp = realloc(n->nm_nlh, newlen); if (tmp == NULL) - return nl_errno(ENOMEM); + return -NLE_NOMEM; n->nm_nlh = tmp; n->nm_size = newlen; @@ -646,21 +647,39 @@ struct nlmsghdr *nlmsg_hdr(struct nl_msg *n) } /** - * Free a netlink message - * @arg n netlink message - * - * Destroys a netlink message and frees up all used memory. + * Acquire a reference on a netlink message + * @arg msg message to acquire reference from + */ +void nlmsg_get(struct nl_msg *msg) +{ + msg->nm_refcnt++; + NL_DBG(4, "New reference to message %p, total %d\n", + msg, msg->nm_refcnt); +} + +/** + * Release a reference from an netlink message + * @arg msg message to release reference from * - * @pre The message must be unused. + * Frees memory after the last reference has been released. */ -void nlmsg_free(struct nl_msg *n) +void nlmsg_free(struct nl_msg *msg) { - if (!n) + if (!msg) return; - free(n->nm_nlh); - free(n); - NL_DBG(2, "msg %p: Freed\n", n); + msg->nm_refcnt--; + NL_DBG(4, "Returned message reference %p, %d remaining\n", + msg, msg->nm_refcnt); + + if (msg->nm_refcnt < 0) + BUG(); + + if (msg->nm_refcnt <= 0) { + free(msg->nm_nlh); + free(msg); + NL_DBG(2, "msg %p: Freed\n", msg); + } } /** @} */ @@ -821,8 +840,7 @@ int nl_msg_parse(struct nl_msg *msg, void (*cb)(struct nl_object *, void *), ops = nl_cache_ops_associate(nlmsg_get_proto(msg), nlmsg_hdr(msg)->nlmsg_type); if (ops == NULL) - return nl_error(ENOENT, "Unknown message type %d", - nlmsg_hdr(msg)->nlmsg_type); + return -NLE_MSGTYPE_NOSUPPORT; p.pp_arg = &x; return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); diff --git a/lib/netfilter/ct.c b/lib/netfilter/ct.c index e9df924..9d61b6c 100644 --- a/lib/netfilter/ct.c +++ b/lib/netfilter/ct.c @@ -6,9 +6,10 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> * Copyright (c) 2007 Philip Craig <philipc@snapgear.com> * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c= 2008 Patrick McHardy <kaber@trash.net> */ /** @@ -112,36 +113,36 @@ static int ct_parse_ip(struct nfnl_ct *ct, int repl, struct nlattr *attr) goto errout; if (tb[CTA_IP_V4_SRC]) { - addr = nla_get_addr(tb[CTA_IP_V4_SRC], AF_INET); + addr = nl_addr_alloc_attr(tb[CTA_IP_V4_SRC], AF_INET); if (addr == NULL) - goto errout_errno; + goto errout_enomem; err = nfnl_ct_set_src(ct, repl, addr); nl_addr_put(addr); if (err < 0) goto errout; } if (tb[CTA_IP_V4_DST]) { - addr = nla_get_addr(tb[CTA_IP_V4_DST], AF_INET); + addr = nl_addr_alloc_attr(tb[CTA_IP_V4_DST], AF_INET); if (addr == NULL) - goto errout_errno; + goto errout_enomem; err = nfnl_ct_set_dst(ct, repl, addr); nl_addr_put(addr); if (err < 0) goto errout; } if (tb[CTA_IP_V6_SRC]) { - addr = nla_get_addr(tb[CTA_IP_V6_SRC], AF_INET6); + addr = nl_addr_alloc_attr(tb[CTA_IP_V6_SRC], AF_INET6); if (addr == NULL) - goto errout_errno; + goto errout_enomem; err = nfnl_ct_set_src(ct, repl, addr); nl_addr_put(addr); if (err < 0) goto errout; } if (tb[CTA_IP_V6_DST]) { - addr = nla_get_addr(tb[CTA_IP_V6_DST], AF_INET6); + addr = nl_addr_alloc_attr(tb[CTA_IP_V6_DST], AF_INET6); if (addr == NULL) - goto errout_errno; + goto errout_enomem; err = nfnl_ct_set_dst(ct, repl, addr); nl_addr_put(addr); if (err < 0) @@ -150,8 +151,8 @@ static int ct_parse_ip(struct nfnl_ct *ct, int repl, struct nlattr *attr) return 0; -errout_errno: - return nl_get_errno(); +errout_enomem: + err = -NLE_NOMEM; errout: return err; } @@ -169,13 +170,13 @@ static int ct_parse_proto(struct nfnl_ct *ct, int repl, struct nlattr *attr) nfnl_ct_set_proto(ct, nla_get_u8(tb[CTA_PROTO_NUM])); if (tb[CTA_PROTO_SRC_PORT]) nfnl_ct_set_src_port(ct, repl, - nla_get_u16(tb[CTA_PROTO_SRC_PORT])); + ntohs(nla_get_u16(tb[CTA_PROTO_SRC_PORT]))); if (tb[CTA_PROTO_DST_PORT]) nfnl_ct_set_dst_port(ct, repl, - nla_get_u16(tb[CTA_PROTO_DST_PORT])); + ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT]))); if (tb[CTA_PROTO_ICMP_ID]) nfnl_ct_set_icmp_id(ct, repl, - nla_get_u16(tb[CTA_PROTO_ICMP_ID])); + 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])); @@ -286,7 +287,7 @@ int nfnlmsg_ct_group(struct nlmsghdr *nlh) } } -struct nfnl_ct *nfnlmsg_ct_parse(struct nlmsghdr *nlh) +int nfnlmsg_ct_parse(struct nlmsghdr *nlh, struct nfnl_ct **result) { struct nfnl_ct *ct; struct nlattr *tb[CTA_MAX+1]; @@ -294,7 +295,7 @@ struct nfnl_ct *nfnlmsg_ct_parse(struct nlmsghdr *nlh) ct = nfnl_ct_alloc(); if (!ct) - return NULL; + return -NLE_NOMEM; ct->ce_msgtype = nlh->nlmsg_type; @@ -345,11 +346,12 @@ struct nfnl_ct *nfnlmsg_ct_parse(struct nlmsghdr *nlh) goto errout; } - return ct; + *result = ct; + return 0; errout: nfnl_ct_put(ct); - return NULL; + return err; } static int ct_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, @@ -358,34 +360,179 @@ static int ct_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct nfnl_ct *ct; int err; - ct = nfnlmsg_ct_parse(nlh); - if (ct == NULL) - goto errout_errno; - - err = pp->pp_cb((struct nl_object *) ct, pp); - if (err < 0) + if ((err = nfnlmsg_ct_parse(nlh, &ct)) < 0) goto errout; - err = P_ACCEPT; - + err = pp->pp_cb((struct nl_object *) ct, pp); errout: nfnl_ct_put(ct); return err; - -errout_errno: - err = nl_get_errno(); - goto errout; } -int nfnl_ct_dump_request(struct nl_handle *h) +int nfnl_ct_dump_request(struct nl_sock *sk) { - return nfnl_send_simple(h, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET, + return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET, NLM_F_DUMP, AF_UNSPEC, 0); } -static int ct_request_update(struct nl_cache *c, struct nl_handle *h) +static int ct_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + return nfnl_ct_dump_request(sk); +} + +static int nfnl_ct_build_tuple(struct nl_msg *msg, const struct nfnl_ct *ct, + int repl) +{ + struct nlattr *tuple, *ip, *proto; + struct nl_addr *addr; + int family; + + family = nfnl_ct_get_family(ct); + + tuple = nla_nest_start(msg, repl ? CTA_TUPLE_REPLY : CTA_TUPLE_ORIG); + if (!tuple) + goto nla_put_failure; + + ip = nla_nest_start(msg, CTA_TUPLE_IP); + if (!ip) + goto nla_put_failure; + + addr = nfnl_ct_get_src(ct, repl); + if (addr) + NLA_PUT_ADDR(msg, + family == AF_INET ? CTA_IP_V4_SRC : CTA_IP_V6_SRC, + addr); + + addr = nfnl_ct_get_dst(ct, repl); + if (addr) + NLA_PUT_ADDR(msg, + family == AF_INET ? CTA_IP_V4_DST : CTA_IP_V6_DST, + addr); + + nla_nest_end(msg, ip); + + proto = nla_nest_start(msg, CTA_TUPLE_PROTO); + if (!proto) + goto nla_put_failure; + + if (nfnl_ct_test_proto(ct)) + NLA_PUT_U8(msg, CTA_PROTO_NUM, nfnl_ct_get_proto(ct)); + + if (nfnl_ct_test_src_port(ct, repl)) + NLA_PUT_U16(msg, CTA_PROTO_SRC_PORT, + htons(nfnl_ct_get_src_port(ct, repl))); + + if (nfnl_ct_test_dst_port(ct, repl)) + 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 (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)); + + nla_nest_end(msg, proto); + + nla_nest_end(msg, tuple); + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int nfnl_ct_build_message(const struct nfnl_ct *ct, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + int err; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_CTNETLINK, cmd, flags, + nfnl_ct_get_family(ct), 0); + if (msg == NULL) + return -NLE_NOMEM; + + if ((err = nfnl_ct_build_tuple(msg, ct, 0)) < 0) + goto err_out; + + *result = msg; + return 0; + +err_out: + nlmsg_free(msg); + return err; +} + +int nfnl_ct_build_add_request(const struct nfnl_ct *ct, int flags, + struct nl_msg **result) +{ + return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_NEW, flags, result); +} + +int nfnl_ct_add(struct nl_sock *sk, const struct nfnl_ct *ct, int flags) { - return nfnl_ct_dump_request(h); + struct nl_msg *msg; + int err; + + if ((err = nfnl_ct_build_add_request(ct, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_ct_build_delete_request(const struct nfnl_ct *ct, int flags, + struct nl_msg **result) +{ + return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_DELETE, flags, result); +} + +int nfnl_ct_del(struct nl_sock *sk, const struct nfnl_ct *ct, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_ct_build_delete_request(ct, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_ct_build_query_request(const struct nfnl_ct *ct, int flags, + struct nl_msg **result) +{ + return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_GET, flags, result); +} + +int nfnl_ct_query(struct nl_sock *sk, const struct nfnl_ct *ct, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_ct_build_query_request(ct, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); } /** @@ -395,29 +542,17 @@ static int ct_request_update(struct nl_cache *c, struct nl_handle *h) /** * Build a conntrack cache holding all conntrack currently in the kernel - * @arg handle netlink handle + * @arg sk Netlink socket. + * @arg result Pointer to store resulting cache. * * Allocates a new cache, initializes it properly and updates it to * contain all conntracks currently in the kernel. * - * @note The caller is responsible for destroying and freeing the - * cache after using it. - * @return The cache or NULL if an error has occured. + * @return 0 on success or a negative error code. */ -struct nl_cache *nfnl_ct_alloc_cache(struct nl_handle *handle) +int nfnl_ct_alloc_cache(struct nl_sock *sk, struct nl_cache **result) { - struct nl_cache *cache; - - cache = nl_cache_alloc(&nfnl_ct_ops); - if (!cache) - return NULL; - - if (handle && nl_cache_refill(handle, cache) < 0) { - free(cache); - return NULL; - } - - return cache; + return nl_cache_alloc_and_fill(&nfnl_ct_ops, sk, result); } /** @} */ diff --git a/lib/netfilter/ct_obj.c b/lib/netfilter/ct_obj.c index 0f5a79e..ae14c0d 100644 --- a/lib/netfilter/ct_obj.c +++ b/lib/netfilter/ct_obj.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> * Copyright (c) 2007 Philip Craig <philipc@snapgear.com> * Copyright (c) 2007 Secure Computing Corporation */ @@ -75,116 +75,199 @@ static int ct_clone(struct nl_object *_dst, struct nl_object *_src) if (src->ct_orig.src) { addr = nl_addr_clone(src->ct_orig.src); if (!addr) - goto errout; + return -NLE_NOMEM; dst->ct_orig.src = addr; } if (src->ct_orig.dst) { addr = nl_addr_clone(src->ct_orig.dst); if (!addr) - goto errout; + return -NLE_NOMEM; dst->ct_orig.dst = addr; } if (src->ct_repl.src) { addr = nl_addr_clone(src->ct_repl.src); if (!addr) - goto errout; + return -NLE_NOMEM; dst->ct_repl.src = addr; } if (src->ct_repl.dst) { addr = nl_addr_clone(src->ct_repl.dst); if (!addr) - goto errout; + return -NLE_NOMEM; dst->ct_repl.dst = addr; } return 0; -errout: - return nl_get_errno(); } -static void ct_dump_dir(struct nfnl_ct *ct, int repl, - struct nl_dump_params *p) +static void dump_addr(struct nl_dump_params *p, struct nl_addr *addr, int port) { - struct nl_addr *addr; - char addrbuf[64]; + char buf[64]; - addr = nfnl_ct_get_src(ct, repl); if (addr) - dp_dump(p, "src=%s ", - nl_addr2str(addr, addrbuf, sizeof(addrbuf))); + nl_dump(p, "%s", nl_addr2str(addr, buf, sizeof(buf))); - addr = nfnl_ct_get_dst(ct, repl); - if (addr) - dp_dump(p, "dst=%s ", - nl_addr2str(addr, addrbuf, sizeof(addrbuf))); + if (port) + nl_dump(p, ":%u ", port); + else if (addr) + nl_dump(p, " "); +} + +static void dump_icmp(struct nl_dump_params *p, struct nfnl_ct *ct, int reply) +{ + if (nfnl_ct_test_icmp_type(ct, reply)) + nl_dump(p, "icmp type %d ", nfnl_ct_get_icmp_type(ct, reply)); + + if (nfnl_ct_test_icmp_type(ct, reply)) + nl_dump(p, "code %d ", nfnl_ct_get_icmp_code(ct, reply)); + + if (nfnl_ct_test_icmp_type(ct, reply)) + nl_dump(p, "id %d ", nfnl_ct_get_icmp_id(ct, reply)); +} + +static void ct_dump_tuples(struct nfnl_ct *ct, struct nl_dump_params *p) +{ + struct nl_addr *orig_src, *orig_dst, *reply_src, *reply_dst; + int orig_sport = 0, orig_dport = 0, reply_sport = 0, reply_dport = 0; + int sync = 0; + + orig_src = nfnl_ct_get_src(ct, 0); + orig_dst = nfnl_ct_get_dst(ct, 0); + reply_src = nfnl_ct_get_src(ct, 1); + reply_dst = nfnl_ct_get_dst(ct, 1); + + if (nfnl_ct_test_src_port(ct, 0)) + orig_sport = nfnl_ct_get_src_port(ct, 0); + + if (nfnl_ct_test_dst_port(ct, 0)) + orig_dport = nfnl_ct_get_dst_port(ct, 0); + + if (nfnl_ct_test_src_port(ct, 1)) + reply_sport = nfnl_ct_get_src_port(ct, 1); - if (nfnl_ct_test_src_port(ct, repl)) - dp_dump(p, "sport=%u ", ntohs(nfnl_ct_get_src_port(ct, repl))); - if (nfnl_ct_test_dst_port(ct, repl)) - dp_dump(p, "dport=%u ", ntohs(nfnl_ct_get_dst_port(ct, repl))); + if (nfnl_ct_test_dst_port(ct, 1)) + reply_dport = nfnl_ct_get_dst_port(ct, 1); - if (nfnl_ct_test_icmp_type(ct, repl)) - dp_dump(p, "type=%d ", nfnl_ct_get_icmp_type(ct, repl)); - if (nfnl_ct_test_icmp_type(ct, repl)) - dp_dump(p, "code=%d ", nfnl_ct_get_icmp_code(ct, repl)); - if (nfnl_ct_test_icmp_type(ct, repl)) - dp_dump(p, "id=%d ", ntohs(nfnl_ct_get_icmp_id(ct, repl))); + if (orig_src && orig_dst && reply_src && reply_dst && + orig_sport == reply_dport && orig_dport == reply_sport && + !nl_addr_cmp(orig_src, reply_dst) && + !nl_addr_cmp(orig_dst, reply_src)) + sync = 1; - if (nfnl_ct_test_packets(ct, repl)) - dp_dump(p, "packets=%llu ", nfnl_ct_get_packets(ct, repl)); - if (nfnl_ct_test_bytes(ct, repl)) - dp_dump(p, "bytes=%llu ", nfnl_ct_get_bytes(ct, repl)); + dump_addr(p, orig_src, orig_sport); + nl_dump(p, sync ? "<-> " : "-> "); + dump_addr(p, orig_dst, orig_dport); + dump_icmp(p, ct, 0); + + if (!sync) { + dump_addr(p, reply_src, reply_sport); + nl_dump(p, "<- "); + dump_addr(p, reply_dst, reply_dport); + dump_icmp(p, ct, 1); + } } /* Compatible with /proc/net/nf_conntrack */ -static int ct_dump(struct nl_object *a, struct nl_dump_params *p) +static void ct_dump_line(struct nl_object *a, struct nl_dump_params *p) { struct nfnl_ct *ct = (struct nfnl_ct *) a; char buf[64]; - uint32_t status; - uint8_t family; - uint8_t proto; - family = nfnl_ct_get_family(ct); - dp_dump(p, "%-8s %u ", nl_af2str(family, buf, sizeof(buf)), family); + nl_new_line(p); - if (nfnl_ct_test_proto(ct)) { - proto = nfnl_ct_get_proto(ct); - dp_dump(p, "%-8s %u ", - nl_ip_proto2str(proto, buf, sizeof(buf)), proto); - } - - if (nfnl_ct_test_timeout(ct)) - dp_dump(p, "%ld ", nfnl_ct_get_timeout(ct)); + if (nfnl_ct_test_proto(ct)) + nl_dump(p, "%s ", + nl_ip_proto2str(nfnl_ct_get_proto(ct), buf, sizeof(buf))); if (nfnl_ct_test_tcp_state(ct)) - dp_dump(p, "%s ", + nl_dump(p, "%s ", nfnl_ct_tcp_state2str(nfnl_ct_get_tcp_state(ct), buf, sizeof(buf))); - ct_dump_dir(ct, 0, p); + ct_dump_tuples(ct, p); - status = nfnl_ct_get_status(ct); - if (!(status & IPS_SEEN_REPLY)) - dp_dump(p, "[UNREPLIED] "); + if (nfnl_ct_test_mark(ct) && nfnl_ct_get_mark(ct)) + nl_dump(p, "mark %u ", nfnl_ct_get_mark(ct)); - ct_dump_dir(ct, 1, p); + nl_dump(p, "\n"); +} - if (status & IPS_ASSURED) - dp_dump(p, "[ASSURED] "); +static void ct_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) a; + char buf[64]; + int fp = 0; - if (nfnl_ct_test_mark(ct)) - dp_dump(p, "mark=%u ", nfnl_ct_get_mark(ct)); + ct_dump_line(a, p); + + nl_dump(p, " id 0x%x ", ct->ct_id); + nl_dump_line(p, "family %s ", + nl_af2str(ct->ct_family, buf, sizeof(buf))); if (nfnl_ct_test_use(ct)) - dp_dump(p, "use=%u ", nfnl_ct_get_use(ct)); + nl_dump(p, "refcnt %u ", nfnl_ct_get_use(ct)); + + if (nfnl_ct_test_timeout(ct)) { + uint64_t timeout_ms = nfnl_ct_get_timeout(ct) * 1000UL; + nl_dump(p, "timeout %s ", + nl_msec2str(timeout_ms, buf, sizeof(buf))); + } + + if (ct->ct_status) + nl_dump(p, "<"); + +#define PRINT_FLAG(str) \ + { nl_dump(p, "%s%s", fp++ ? "," : "", (str)); } + + if (ct->ct_status & IPS_EXPECTED) + PRINT_FLAG("EXPECTED"); + if (!(ct->ct_status & IPS_SEEN_REPLY)) + PRINT_FLAG("NOREPLY"); + if (ct->ct_status & IPS_ASSURED) + PRINT_FLAG("ASSURED"); + if (!(ct->ct_status & IPS_CONFIRMED)) + PRINT_FLAG("NOTSENT"); + if (ct->ct_status & IPS_SRC_NAT) + PRINT_FLAG("SNAT"); + if (ct->ct_status & IPS_DST_NAT) + PRINT_FLAG("DNAT"); + if (ct->ct_status & IPS_SEQ_ADJUST) + PRINT_FLAG("SEQADJUST"); + if (!(ct->ct_status & IPS_SRC_NAT_DONE)) + PRINT_FLAG("SNAT_INIT"); + if (!(ct->ct_status & IPS_DST_NAT_DONE)) + PRINT_FLAG("DNAT_INIT"); + if (ct->ct_status & IPS_DYING) + PRINT_FLAG("DYING"); + if (ct->ct_status & IPS_FIXED_TIMEOUT) + PRINT_FLAG("FIXED_TIMEOUT"); +#undef PRINT_FLAG + + if (ct->ct_status) + nl_dump(p, ">"); + nl_dump(p, "\n"); +} + +static void ct_dump_stats(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) a; + double res; + char *unit; + + ct_dump_details(a, p); - dp_dump(p, "\n"); + nl_dump_line(p, " # packets volume\n"); - return 1; + res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, 1), &unit); + nl_dump_line(p, " rx %10llu %7.2f %s\n", + nfnl_ct_get_packets(ct, 1), res, unit); + + res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, 0), &unit); + nl_dump_line(p, " tx %10llu %7.2f %s\n", + nfnl_ct_get_packets(ct, 0), res, unit); } static int ct_compare(struct nl_object *_a, struct nl_object *_b, @@ -197,7 +280,7 @@ static int ct_compare(struct nl_object *_a, struct nl_object *_b, #define CT_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, CT_ATTR_##ATTR, a, b, EXPR) #define CT_DIFF_VAL(ATTR, FIELD) CT_DIFF(ATTR, a->FIELD != b->FIELD) #define CT_DIFF_ADDR(ATTR, FIELD) \ - ((flags & LOOSE_FLAG_COMPARISON) \ + ((flags & LOOSE_COMPARISON) \ ? CT_DIFF(ATTR, nl_addr_cmp_prefix(a->FIELD, b->FIELD)) \ : CT_DIFF(ATTR, nl_addr_cmp(a->FIELD, b->FIELD))) @@ -227,7 +310,7 @@ static int ct_compare(struct nl_object *_a, struct nl_object *_b, diff |= CT_DIFF_VAL(REPL_PACKETS, ct_repl.packets); diff |= CT_DIFF_VAL(REPL_BYTES, ct_repl.bytes); - if (flags & LOOSE_FLAG_COMPARISON) + if (flags & LOOSE_COMPARISON) diff |= CT_DIFF(STATUS, (a->ct_status ^ b->ct_status) & b->ct_status_mask); else @@ -389,6 +472,31 @@ uint32_t nfnl_ct_get_status(const struct nfnl_ct *ct) return ct->ct_status; } +static struct trans_tbl status_flags[] = { + __ADD(IPS_EXPECTED, expected) + __ADD(IPS_SEEN_REPLY, seen_reply) + __ADD(IPS_ASSURED, assured) + __ADD(IPS_CONFIRMED, confirmed) + __ADD(IPS_SRC_NAT, snat) + __ADD(IPS_DST_NAT, dnat) + __ADD(IPS_SEQ_ADJUST, seqadjust) + __ADD(IPS_SRC_NAT_DONE, snat_done) + __ADD(IPS_DST_NAT_DONE, dnat_done) + __ADD(IPS_DYING, dying) + __ADD(IPS_FIXED_TIMEOUT, fixed_timeout) +}; + +char * nfnl_ct_status2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, status_flags, + ARRAY_SIZE(status_flags)); +} + +int nfnl_ct_str2status(const char *name) +{ + return __str2flags(name, status_flags, ARRAY_SIZE(status_flags)); +} + void nfnl_ct_set_timeout(struct nfnl_ct *ct, uint32_t timeout) { ct->ct_timeout = timeout; @@ -458,7 +566,7 @@ static int ct_set_addr(struct nfnl_ct *ct, struct nl_addr *addr, { if (ct->ce_mask & CT_ATTR_FAMILY) { if (addr->a_family != ct->ct_family) - return nl_error(EINVAL, "Address family mismatch"); + return -NLE_AF_MISMATCH; } else nfnl_ct_set_family(ct, addr->a_family); @@ -665,9 +773,11 @@ struct nl_object_ops ct_obj_ops = { .oo_size = sizeof(struct nfnl_ct), .oo_free_data = ct_free_data, .oo_clone = ct_clone, - .oo_dump[NL_DUMP_BRIEF] = ct_dump, - .oo_dump[NL_DUMP_FULL] = ct_dump, - .oo_dump[NL_DUMP_STATS] = ct_dump, + .oo_dump = { + [NL_DUMP_LINE] = ct_dump_line, + [NL_DUMP_DETAILS] = ct_dump_details, + [NL_DUMP_STATS] = ct_dump_stats, + }, .oo_compare = ct_compare, .oo_attrs2str = ct_attrs2str, }; diff --git a/lib/netfilter/log.c b/lib/netfilter/log.c index a6bf3d5..96ae6c5 100644 --- a/lib/netfilter/log.c +++ b/lib/netfilter/log.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> * Copyright (c) 2007 Philip Craig <philipc@snapgear.com> * Copyright (c) 2007 Secure Computing Corporation */ @@ -26,172 +26,13 @@ #include <netlink/netfilter/nfnl.h> #include <netlink/netfilter/log.h> -static struct nl_cache_ops nfnl_log_ops; - -#if __BYTE_ORDER == __BIG_ENDIAN -static uint64_t ntohll(uint64_t x) -{ - return x; -} -#elif __BYTE_ORDER == __LITTLE_ENDIAN -static uint64_t ntohll(uint64_t x) -{ - return __bswap_64(x); -} -#endif - -static struct nla_policy log_policy[NFULA_MAX+1] = { - [NFULA_PACKET_HDR] = { - .minlen = sizeof(struct nfulnl_msg_packet_hdr) - }, - [NFULA_MARK] = { .type = NLA_U32 }, - [NFULA_TIMESTAMP] = { - .minlen = sizeof(struct nfulnl_msg_packet_timestamp) - }, - [NFULA_IFINDEX_INDEV] = { .type = NLA_U32 }, - [NFULA_IFINDEX_OUTDEV] = { .type = NLA_U32 }, - [NFULA_IFINDEX_PHYSINDEV] = { .type = NLA_U32 }, - [NFULA_IFINDEX_PHYSOUTDEV] = { .type = NLA_U32 }, - [NFULA_HWADDR] = { - .minlen = sizeof(struct nfulnl_msg_packet_hw) - }, - //[NFULA_PAYLOAD] - [NFULA_PREFIX] = { .type = NLA_STRING, }, - [NFULA_UID] = { .type = NLA_U32 }, - [NFULA_SEQ] = { .type = NLA_U32 }, - [NFULA_SEQ_GLOBAL] = { .type = NLA_U32 }, -}; - -struct nfnl_log *nfnlmsg_log_parse(struct nlmsghdr *nlh) -{ - struct nfnl_log *log; - struct nlattr *tb[NFULA_MAX+1]; - struct nlattr *attr; - int err; - - log = nfnl_log_alloc(); - if (!log) - return NULL; - - log->ce_msgtype = nlh->nlmsg_type; - - err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, NFULA_MAX, - log_policy); - if (err < 0) - goto errout; - - nfnl_log_set_family(log, nfnlmsg_family(nlh)); - - attr = tb[NFULA_PACKET_HDR]; - if (attr) { - struct nfulnl_msg_packet_hdr *hdr = nla_data(attr); - - nfnl_log_set_hwproto(log, hdr->hw_protocol); - nfnl_log_set_hook(log, hdr->hook); - } - - attr = tb[NFULA_MARK]; - if (attr) - nfnl_log_set_mark(log, ntohl(nla_get_u32(attr))); - - attr = tb[NFULA_TIMESTAMP]; - if (attr) { - struct nfulnl_msg_packet_timestamp *timestamp = nla_data(attr); - struct timeval tv; - - tv.tv_sec = ntohll(timestamp->sec); - tv.tv_usec = ntohll(timestamp->usec); - nfnl_log_set_timestamp(log, &tv); - } - - attr = tb[NFULA_IFINDEX_INDEV]; - if (attr) - nfnl_log_set_indev(log, ntohl(nla_get_u32(attr))); - - attr = tb[NFULA_IFINDEX_OUTDEV]; - if (attr) - nfnl_log_set_outdev(log, ntohl(nla_get_u32(attr))); - - attr = tb[NFULA_IFINDEX_PHYSINDEV]; - if (attr) - nfnl_log_set_physindev(log, ntohl(nla_get_u32(attr))); - - attr = tb[NFULA_IFINDEX_PHYSOUTDEV]; - if (attr) - nfnl_log_set_physoutdev(log, ntohl(nla_get_u32(attr))); - - attr = tb[NFULA_HWADDR]; - if (attr) { - struct nfulnl_msg_packet_hw *hw = nla_data(attr); - - nfnl_log_set_hwaddr(log, hw->hw_addr, ntohs(hw->hw_addrlen)); - } - - attr = tb[NFULA_PAYLOAD]; - if (attr) { - err = nfnl_log_set_payload(log, nla_data(attr), nla_len(attr)); - if (err < 0) - goto errout; - } - - attr = tb[NFULA_PREFIX]; - if (attr) { - err = nfnl_log_set_prefix(log, nla_data(attr)); - if (err < 0) - goto errout; - } - - attr = tb[NFULA_UID]; - if (attr) - nfnl_log_set_uid(log, ntohl(nla_get_u32(attr))); - - attr = tb[NFULA_SEQ]; - if (attr) - nfnl_log_set_seq(log, ntohl(nla_get_u32(attr))); - - attr = tb[NFULA_SEQ_GLOBAL]; - if (attr) - nfnl_log_set_seq_global(log, ntohl(nla_get_u32(attr))); - - return log; - -errout: - nfnl_log_put(log); - return NULL; -} - -static int log_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, - struct nlmsghdr *nlh, struct nl_parser_param *pp) -{ - struct nfnl_log *log; - int err; - - log = nfnlmsg_log_parse(nlh); - if (log == NULL) - goto errout_errno; - - err = pp->pp_cb((struct nl_object *) log, pp); - if (err < 0) - goto errout; - - err = P_ACCEPT; - -errout: - nfnl_log_put(log); - return err; - -errout_errno: - err = nl_get_errno(); - goto errout; -} - /** * @name Log Commands * @{ */ -static struct nl_msg *build_log_cmd_msg(uint8_t family, uint16_t queuenum, - uint8_t command) +static int build_log_cmd_request(uint8_t family, uint16_t queuenum, + uint8_t command, struct nl_msg **result) { struct nl_msg *msg; struct nfulnl_msg_config_cmd cmd; @@ -199,142 +40,202 @@ static struct nl_msg *build_log_cmd_msg(uint8_t family, uint16_t queuenum, msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_ULOG, NFULNL_MSG_CONFIG, 0, family, queuenum); if (msg == NULL) - return NULL; + return -NLE_NOMEM; cmd.command = command; if (nla_put(msg, NFULA_CFG_CMD, sizeof(cmd), &cmd) < 0) goto nla_put_failure; - return msg; + *result = msg; + return 0; nla_put_failure: nlmsg_free(msg); - return NULL; + return -NLE_MSGSIZE; } -static int send_log_msg(struct nl_handle *handle, struct nl_msg *msg) +static int send_log_request(struct nl_sock *sk, struct nl_msg *msg) { int err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); if (err < 0) return err; - return nl_wait_for_ack(handle); + return wait_for_ack(sk); } -struct nl_msg *nfnl_log_build_bind(uint16_t queuenum) +int nfnl_log_build_pf_bind(uint8_t pf, struct nl_msg **result) { - return build_log_cmd_msg(0, queuenum, NFULNL_CFG_CMD_BIND); + return build_log_cmd_request(pf, 0, NFULNL_CFG_CMD_PF_BIND, result); } -int nfnl_log_bind(struct nl_handle *nlh, uint16_t queuenum) +int nfnl_log_pf_bind(struct nl_sock *nlh, uint8_t pf) { struct nl_msg *msg; + int err; - msg = nfnl_log_build_bind(queuenum); - if (!msg) - return nl_get_errno(); + if ((err = nfnl_log_build_pf_bind(pf, &msg)) < 0) + return err; - return send_log_msg(nlh, msg); + return send_log_request(nlh, msg); } -struct nl_msg *nfnl_log_build_unbind(uint16_t queuenum) +int nfnl_log_build_pf_unbind(uint8_t pf, struct nl_msg **result) { - return build_log_cmd_msg(0, queuenum, NFULNL_CFG_CMD_UNBIND); + return build_log_cmd_request(pf, 0, NFULNL_CFG_CMD_PF_UNBIND, result); } -int nfnl_log_unbind(struct nl_handle *nlh, uint16_t queuenum) +int nfnl_log_pf_unbind(struct nl_sock *nlh, uint8_t pf) { struct nl_msg *msg; + int err; - msg = nfnl_log_build_bind(queuenum); - if (!msg) - return nl_get_errno(); - - return send_log_msg(nlh, msg); -} + if ((err = nfnl_log_build_pf_unbind(pf, &msg)) < 0) + return err; -struct nl_msg *nfnl_log_build_pf_bind(uint8_t pf) -{ - return build_log_cmd_msg(pf, 0, NFULNL_CFG_CMD_PF_BIND); + return send_log_request(nlh, msg); } -int nfnl_log_pf_bind(struct nl_handle *nlh, uint8_t pf) +static int nfnl_log_build_request(const struct nfnl_log *log, + struct nl_msg **result) { struct nl_msg *msg; - msg = nfnl_log_build_pf_bind(pf); - if (!msg) - return nl_get_errno(); + if (!nfnl_log_test_group(log)) + return -NLE_MISSING_ATTR; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_ULOG, NFULNL_MSG_CONFIG, 0, + 0, nfnl_log_get_group(log)); + if (msg == NULL) + return -NLE_NOMEM; + + /* This sucks. The nfnetlink_log interface always expects both + * parameters to be present. Needs to be done properly. + */ + if (nfnl_log_test_copy_mode(log)) { + struct nfulnl_msg_config_mode mode; + + switch (nfnl_log_get_copy_mode(log)) { + case NFNL_LOG_COPY_NONE: + mode.copy_mode = NFULNL_COPY_NONE; + break; + case NFNL_LOG_COPY_META: + mode.copy_mode = NFULNL_COPY_META; + break; + case NFNL_LOG_COPY_PACKET: + mode.copy_mode = NFULNL_COPY_PACKET; + break; + } + mode.copy_range = htonl(nfnl_log_get_copy_range(log)); + mode._pad = 0; + + if (nla_put(msg, NFULA_CFG_MODE, sizeof(mode), &mode) < 0) + goto nla_put_failure; + } + + if (nfnl_log_test_flush_timeout(log) && + nla_put_u32(msg, NFULA_CFG_TIMEOUT, + htonl(nfnl_log_get_flush_timeout(log))) < 0) + goto nla_put_failure; + + if (nfnl_log_test_alloc_size(log) && + nla_put_u32(msg, NFULA_CFG_NLBUFSIZ, + htonl(nfnl_log_get_alloc_size(log))) < 0) + goto nla_put_failure; + + if (nfnl_log_test_queue_threshold(log) && + nla_put_u32(msg, NFULA_CFG_QTHRESH, + htonl(nfnl_log_get_queue_threshold(log))) < 0) + goto nla_put_failure; + + *result = msg; + return 0; - return send_log_msg(nlh, msg); +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; } -struct nl_msg *nfnl_log_build_pf_unbind(uint8_t pf) +int nfnl_log_build_create_request(const struct nfnl_log *log, + struct nl_msg **result) { - return build_log_cmd_msg(pf, 0, NFULNL_CFG_CMD_PF_UNBIND); + struct nfulnl_msg_config_cmd cmd; + int err; + + if ((err = nfnl_log_build_request(log, result)) < 0) + return err; + + cmd.command = NFULNL_CFG_CMD_BIND; + + if (nla_put(*result, NFULA_CFG_CMD, sizeof(cmd), &cmd) < 0) + goto nla_put_failure; + + return 0; + +nla_put_failure: + nlmsg_free(*result); + return -NLE_MSGSIZE; } -int nfnl_log_pf_unbind(struct nl_handle *nlh, uint8_t pf) +int nfnl_log_create(struct nl_sock *nlh, const struct nfnl_log *log) { struct nl_msg *msg; + int err; - msg = nfnl_log_build_pf_unbind(pf); - if (!msg) - return nl_get_errno(); + if ((err = nfnl_log_build_create_request(log, &msg)) < 0) + return err; + + return send_log_request(nlh, msg); +} - return send_log_msg(nlh, msg); +int nfnl_log_build_change_request(const struct nfnl_log *log, + struct nl_msg **result) +{ + return nfnl_log_build_request(log, result); } -struct nl_msg *nfnl_log_build_mode(uint16_t queuenum, uint8_t copy_mode, - uint32_t copy_range) +int nfnl_log_change(struct nl_sock *nlh, const struct nfnl_log *log) { struct nl_msg *msg; - struct nfulnl_msg_config_mode mode; + int err; - msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_ULOG, NFULNL_MSG_CONFIG, 0, - 0, queuenum); - if (msg == NULL) - return NULL; + if ((err = nfnl_log_build_change_request(log, &msg)) < 0) + return err; - mode.copy_mode = copy_mode; - mode.copy_range = htonl(copy_range); - if (nla_put(msg, NFULA_CFG_MODE, sizeof(mode), &mode) < 0) - goto nla_put_failure; + return send_log_request(nlh, msg); +} - return msg; +int nfnl_log_build_delete_request(const struct nfnl_log *log, + struct nl_msg **result) +{ + if (!nfnl_log_test_group(log)) + return -NLE_MISSING_ATTR; -nla_put_failure: - nlmsg_free(msg); - return NULL; + return build_log_cmd_request(0, nfnl_log_get_group(log), + NFULNL_CFG_CMD_UNBIND, result); } -int nfnl_log_set_mode(struct nl_handle *nlh, uint16_t queuenum, - uint8_t copy_mode, uint32_t copy_range) +int nfnl_log_delete(struct nl_sock *nlh, const struct nfnl_log *log) { struct nl_msg *msg; + int err; + + if ((err = nfnl_log_build_delete_request(log, &msg)) < 0) + return err; - msg = nfnl_log_build_mode(queuenum, copy_mode, copy_range); - if (!msg) - return nl_get_errno(); - return send_log_msg(nlh, msg); + return send_log_request(nlh, msg); } /** @} */ -#define NFNLMSG_LOG_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_ULOG, (type)) static struct nl_cache_ops nfnl_log_ops = { .co_name = "netfilter/log", - .co_hdrsize = NFNL_HDRLEN, + .co_obj_ops = &log_obj_ops, .co_msgtypes = { - { NFNLMSG_LOG_TYPE(NFULNL_MSG_PACKET), NL_ACT_NEW, "new" }, END_OF_MSGTYPES_LIST, }, - .co_protocol = NETLINK_NETFILTER, - .co_msg_parser = log_msg_parser, - .co_obj_ops = &log_obj_ops, }; static void __init log_init(void) diff --git a/lib/netfilter/log_msg.c b/lib/netfilter/log_msg.c new file mode 100644 index 0000000..cad6ddd --- /dev/null +++ b/lib/netfilter/log_msg.c @@ -0,0 +1,209 @@ +/* + * lib/netfilter/log_msg.c Netfilter Log Message + * + * 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) 2003-2008 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2007 Philip Craig <philipc@snapgear.com> + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + */ + +/** + * @ingroup nfnl + * @defgroup log Log + * @brief + * @{ + */ + +#include <sys/types.h> +#include <linux/netfilter/nfnetlink_log.h> + +#include <netlink-local.h> +#include <netlink/attr.h> +#include <netlink/netfilter/nfnl.h> +#include <netlink/netfilter/log_msg.h> + +#if __BYTE_ORDER == __BIG_ENDIAN +static uint64_t ntohll(uint64_t x) +{ + return x; +} +#elif __BYTE_ORDER == __LITTLE_ENDIAN +static uint64_t ntohll(uint64_t x) +{ + return __bswap_64(x); +} +#endif + +static struct nla_policy log_msg_policy[NFULA_MAX+1] = { + [NFULA_PACKET_HDR] = { + .minlen = sizeof(struct nfulnl_msg_packet_hdr) + }, + [NFULA_MARK] = { .type = NLA_U32 }, + [NFULA_TIMESTAMP] = { + .minlen = sizeof(struct nfulnl_msg_packet_timestamp) + }, + [NFULA_IFINDEX_INDEV] = { .type = NLA_U32 }, + [NFULA_IFINDEX_OUTDEV] = { .type = NLA_U32 }, + [NFULA_IFINDEX_PHYSINDEV] = { .type = NLA_U32 }, + [NFULA_IFINDEX_PHYSOUTDEV] = { .type = NLA_U32 }, + [NFULA_HWADDR] = { + .minlen = sizeof(struct nfulnl_msg_packet_hw) + }, + //[NFULA_PAYLOAD] + [NFULA_PREFIX] = { .type = NLA_STRING, }, + [NFULA_UID] = { .type = NLA_U32 }, + [NFULA_GID] = { .type = NLA_U32 }, + [NFULA_SEQ] = { .type = NLA_U32 }, + [NFULA_SEQ_GLOBAL] = { .type = NLA_U32 }, +}; + +int nfnlmsg_log_msg_parse(struct nlmsghdr *nlh, struct nfnl_log_msg **result) +{ + struct nfnl_log_msg *msg; + struct nlattr *tb[NFULA_MAX+1]; + struct nlattr *attr; + int err; + + msg = nfnl_log_msg_alloc(); + if (!msg) + return -NLE_NOMEM; + + msg->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, NFULA_MAX, + log_msg_policy); + if (err < 0) + goto errout; + + nfnl_log_msg_set_family(msg, nfnlmsg_family(nlh)); + + attr = tb[NFULA_PACKET_HDR]; + if (attr) { + struct nfulnl_msg_packet_hdr *hdr = nla_data(attr); + + if (hdr->hw_protocol) + nfnl_log_msg_set_hwproto(msg, hdr->hw_protocol); + nfnl_log_msg_set_hook(msg, hdr->hook); + } + + attr = tb[NFULA_MARK]; + if (attr) + nfnl_log_msg_set_mark(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_TIMESTAMP]; + if (attr) { + struct nfulnl_msg_packet_timestamp *timestamp = nla_data(attr); + struct timeval tv; + + tv.tv_sec = ntohll(timestamp->sec); + tv.tv_usec = ntohll(timestamp->usec); + nfnl_log_msg_set_timestamp(msg, &tv); + } + + attr = tb[NFULA_IFINDEX_INDEV]; + if (attr) + nfnl_log_msg_set_indev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_IFINDEX_OUTDEV]; + if (attr) + nfnl_log_msg_set_outdev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_IFINDEX_PHYSINDEV]; + if (attr) + nfnl_log_msg_set_physindev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_IFINDEX_PHYSOUTDEV]; + if (attr) + nfnl_log_msg_set_physoutdev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_HWADDR]; + if (attr) { + struct nfulnl_msg_packet_hw *hw = nla_data(attr); + + nfnl_log_msg_set_hwaddr(msg, hw->hw_addr, ntohs(hw->hw_addrlen)); + } + + attr = tb[NFULA_PAYLOAD]; + if (attr) { + err = nfnl_log_msg_set_payload(msg, nla_data(attr), nla_len(attr)); + if (err < 0) + goto errout; + } + + attr = tb[NFULA_PREFIX]; + if (attr) { + err = nfnl_log_msg_set_prefix(msg, nla_data(attr)); + if (err < 0) + goto errout; + } + + attr = tb[NFULA_UID]; + if (attr) + nfnl_log_msg_set_uid(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_GID]; + if (attr) + nfnl_log_msg_set_gid(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_SEQ]; + if (attr) + nfnl_log_msg_set_seq(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_SEQ_GLOBAL]; + if (attr) + nfnl_log_msg_set_seq_global(msg, ntohl(nla_get_u32(attr))); + + *result = msg; + return 0; + +errout: + nfnl_log_msg_put(msg); + return err; +} + +static int log_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nfnl_log_msg *msg; + int err; + + if ((err = nfnlmsg_log_msg_parse(nlh, &msg)) < 0) + goto errout; + + err = pp->pp_cb((struct nl_object *) msg, pp); +errout: + nfnl_log_msg_put(msg); + return err; +} + +/** @} */ + +#define NFNLMSG_LOG_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_ULOG, (type)) +static struct nl_cache_ops nfnl_log_msg_ops = { + .co_name = "netfilter/log_msg", + .co_hdrsize = NFNL_HDRLEN, + .co_msgtypes = { + { NFNLMSG_LOG_TYPE(NFULNL_MSG_PACKET), NL_ACT_NEW, "new" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_NETFILTER, + .co_msg_parser = log_msg_parser, + .co_obj_ops = &log_msg_obj_ops, +}; + +static void __init log_msg_init(void) +{ + nl_cache_mngt_register(&nfnl_log_msg_ops); +} + +static void __exit log_msg_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_log_msg_ops); +} + +/** @} */ diff --git a/lib/netfilter/log_msg_obj.c b/lib/netfilter/log_msg_obj.c new file mode 100644 index 0000000..d2cde4e --- /dev/null +++ b/lib/netfilter/log_msg_obj.c @@ -0,0 +1,458 @@ +/* + * lib/netfilter/log_msg_obj.c Netfilter Log Object + * + * 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) 2003-2008 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2007 Philip Craig <philipc@snapgear.com> + * Copyright (c) 2007 Secure Computing Corporation + */ + +#include <netlink-local.h> +#include <netlink/netfilter/nfnl.h> +#include <netlink/netfilter/netfilter.h> +#include <netlink/netfilter/log_msg.h> + +/** @cond SKIP */ +#define LOG_MSG_ATTR_FAMILY (1UL << 0) +#define LOG_MSG_ATTR_HWPROTO (1UL << 1) +#define LOG_MSG_ATTR_HOOK (1UL << 2) +#define LOG_MSG_ATTR_MARK (1UL << 3) +#define LOG_MSG_ATTR_TIMESTAMP (1UL << 4) +#define LOG_MSG_ATTR_INDEV (1UL << 5) +#define LOG_MSG_ATTR_OUTDEV (1UL << 6) +#define LOG_MSG_ATTR_PHYSINDEV (1UL << 7) +#define LOG_MSG_ATTR_PHYSOUTDEV (1UL << 8) +#define LOG_MSG_ATTR_HWADDR (1UL << 9) +#define LOG_MSG_ATTR_PAYLOAD (1UL << 10) +#define LOG_MSG_ATTR_PREFIX (1UL << 11) +#define LOG_MSG_ATTR_UID (1UL << 12) +#define LOG_MSG_ATTR_GID (1UL << 13) +#define LOG_MSG_ATTR_SEQ (1UL << 14) +#define LOG_MSG_ATTR_SEQ_GLOBAL (1UL << 15) +/** @endcond */ + +static void log_msg_free_data(struct nl_object *c) +{ + struct nfnl_log_msg *msg = (struct nfnl_log_msg *) c; + + if (msg == NULL) + return; + + free(msg->log_msg_payload); + free(msg->log_msg_prefix); +} + +static int log_msg_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct nfnl_log_msg *dst = (struct nfnl_log_msg *) _dst; + struct nfnl_log_msg *src = (struct nfnl_log_msg *) _src; + int err; + + if (src->log_msg_payload) { + err = nfnl_log_msg_set_payload(dst, src->log_msg_payload, + src->log_msg_payload_len); + if (err < 0) + goto errout; + } + + if (src->log_msg_prefix) { + err = nfnl_log_msg_set_prefix(dst, src->log_msg_prefix); + if (err < 0) + goto errout; + } + + return 0; +errout: + return err; +} + +static void log_msg_dump(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_log_msg *msg = (struct nfnl_log_msg *) a; + struct nl_cache *link_cache; + char buf[64]; + + link_cache = nl_cache_mngt_require("route/link"); + + nl_new_line(p); + + if (msg->ce_mask & LOG_MSG_ATTR_PREFIX) + nl_dump(p, "%s", msg->log_msg_prefix); + + if (msg->ce_mask & LOG_MSG_ATTR_INDEV) { + if (link_cache) + nl_dump(p, "IN=%s ", + rtnl_link_i2name(link_cache, + msg->log_msg_indev, + buf, sizeof(buf))); + else + nl_dump(p, "IN=%d ", msg->log_msg_indev); + } + + if (msg->ce_mask & LOG_MSG_ATTR_PHYSINDEV) { + if (link_cache) + nl_dump(p, "PHYSIN=%s ", + rtnl_link_i2name(link_cache, + msg->log_msg_physindev, + buf, sizeof(buf))); + else + nl_dump(p, "IN=%d ", msg->log_msg_physindev); + } + + if (msg->ce_mask & LOG_MSG_ATTR_OUTDEV) { + if (link_cache) + nl_dump(p, "OUT=%s ", + rtnl_link_i2name(link_cache, + msg->log_msg_outdev, + buf, sizeof(buf))); + else + nl_dump(p, "OUT=%d ", msg->log_msg_outdev); + } + + if (msg->ce_mask & LOG_MSG_ATTR_PHYSOUTDEV) { + if (link_cache) + nl_dump(p, "PHYSOUT=%s ", + rtnl_link_i2name(link_cache, + msg->log_msg_physoutdev, + buf, sizeof(buf))); + else + nl_dump(p, "PHYSOUT=%d ", msg->log_msg_physoutdev); + } + + if (msg->ce_mask & LOG_MSG_ATTR_HWADDR) { + int i; + + nl_dump(p, "MAC"); + for (i = 0; i < msg->log_msg_hwaddr_len; i++) + nl_dump(p, "%c%02x", i?':':'=', msg->log_msg_hwaddr[i]); + nl_dump(p, " "); + } + + /* FIXME: parse the payload to get iptables LOG compatible format */ + + if (msg->ce_mask & LOG_MSG_ATTR_FAMILY) + nl_dump(p, "FAMILY=%s ", + nl_af2str(msg->log_msg_family, buf, sizeof(buf))); + + if (msg->ce_mask & LOG_MSG_ATTR_HWPROTO) + nl_dump(p, "HWPROTO=%s ", + nl_ether_proto2str(ntohs(msg->log_msg_hwproto), + buf, sizeof(buf))); + + if (msg->ce_mask & LOG_MSG_ATTR_HOOK) + nl_dump(p, "HOOK=%s ", + nfnl_inet_hook2str(msg->log_msg_hook, + buf, sizeof(buf))); + + if (msg->ce_mask & LOG_MSG_ATTR_MARK) + nl_dump(p, "MARK=%u ", msg->log_msg_mark); + + if (msg->ce_mask & LOG_MSG_ATTR_PAYLOAD) + nl_dump(p, "PAYLOADLEN=%d ", msg->log_msg_payload_len); + + if (msg->ce_mask & LOG_MSG_ATTR_UID) + nl_dump(p, "UID=%u ", msg->log_msg_uid); + + if (msg->ce_mask & LOG_MSG_ATTR_GID) + nl_dump(p, "GID=%u ", msg->log_msg_gid); + + if (msg->ce_mask & LOG_MSG_ATTR_SEQ) + nl_dump(p, "SEQ=%d ", msg->log_msg_seq); + + if (msg->ce_mask & LOG_MSG_ATTR_SEQ_GLOBAL) + nl_dump(p, "SEQGLOBAL=%d ", msg->log_msg_seq_global); + + nl_dump(p, "\n"); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_log_msg *nfnl_log_msg_alloc(void) +{ + return (struct nfnl_log_msg *) nl_object_alloc(&log_msg_obj_ops); +} + +void nfnl_log_msg_get(struct nfnl_log_msg *msg) +{ + nl_object_get((struct nl_object *) msg); +} + +void nfnl_log_msg_put(struct nfnl_log_msg *msg) +{ + nl_object_put((struct nl_object *) msg); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_log_msg_set_family(struct nfnl_log_msg *msg, uint8_t family) +{ + msg->log_msg_family = family; + msg->ce_mask |= LOG_MSG_ATTR_FAMILY; +} + +uint8_t nfnl_log_msg_get_family(const struct nfnl_log_msg *msg) +{ + if (msg->ce_mask & LOG_MSG_ATTR_FAMILY) + return msg->log_msg_family; + else + return AF_UNSPEC; +} + +void nfnl_log_msg_set_hwproto(struct nfnl_log_msg *msg, uint16_t hwproto) +{ + msg->log_msg_hwproto = hwproto; + msg->ce_mask |= LOG_MSG_ATTR_HWPROTO; +} + +int nfnl_log_msg_test_hwproto(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_HWPROTO); +} + +uint16_t nfnl_log_msg_get_hwproto(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_hwproto; +} + +void nfnl_log_msg_set_hook(struct nfnl_log_msg *msg, uint8_t hook) +{ + msg->log_msg_hook = hook; + msg->ce_mask |= LOG_MSG_ATTR_HOOK; +} + +int nfnl_log_msg_test_hook(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_HOOK); +} + +uint8_t nfnl_log_msg_get_hook(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_hook; +} + +void nfnl_log_msg_set_mark(struct nfnl_log_msg *msg, uint32_t mark) +{ + msg->log_msg_mark = mark; + msg->ce_mask |= LOG_MSG_ATTR_MARK; +} + +int nfnl_log_msg_test_mark(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_MARK); +} + +uint32_t nfnl_log_msg_get_mark(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_mark; +} + +void nfnl_log_msg_set_timestamp(struct nfnl_log_msg *msg, struct timeval *tv) +{ + msg->log_msg_timestamp.tv_sec = tv->tv_sec; + msg->log_msg_timestamp.tv_usec = tv->tv_usec; + msg->ce_mask |= LOG_MSG_ATTR_TIMESTAMP; +} + +const struct timeval *nfnl_log_msg_get_timestamp(const struct nfnl_log_msg *msg) +{ + if (!(msg->ce_mask & LOG_MSG_ATTR_TIMESTAMP)) + return NULL; + return &msg->log_msg_timestamp; +} + +void nfnl_log_msg_set_indev(struct nfnl_log_msg *msg, uint32_t indev) +{ + msg->log_msg_indev = indev; + msg->ce_mask |= LOG_MSG_ATTR_INDEV; +} + +uint32_t nfnl_log_msg_get_indev(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_indev; +} + +void nfnl_log_msg_set_outdev(struct nfnl_log_msg *msg, uint32_t outdev) +{ + msg->log_msg_outdev = outdev; + msg->ce_mask |= LOG_MSG_ATTR_OUTDEV; +} + +uint32_t nfnl_log_msg_get_outdev(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_outdev; +} + +void nfnl_log_msg_set_physindev(struct nfnl_log_msg *msg, uint32_t physindev) +{ + msg->log_msg_physindev = physindev; + msg->ce_mask |= LOG_MSG_ATTR_PHYSINDEV; +} + +uint32_t nfnl_log_msg_get_physindev(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_physindev; +} + +void nfnl_log_msg_set_physoutdev(struct nfnl_log_msg *msg, uint32_t physoutdev) +{ + msg->log_msg_physoutdev = physoutdev; + msg->ce_mask |= LOG_MSG_ATTR_PHYSOUTDEV; +} + +uint32_t nfnl_log_msg_get_physoutdev(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_physoutdev; +} + +void nfnl_log_msg_set_hwaddr(struct nfnl_log_msg *msg, uint8_t *hwaddr, int len) +{ + if (len > sizeof(msg->log_msg_hwaddr)) + len = sizeof(msg->log_msg_hwaddr); + msg->log_msg_hwaddr_len = len; + memcpy(msg->log_msg_hwaddr, hwaddr, len); + msg->ce_mask |= LOG_MSG_ATTR_HWADDR; +} + +const uint8_t *nfnl_log_msg_get_hwaddr(const struct nfnl_log_msg *msg, int *len) +{ + if (!(msg->ce_mask & LOG_MSG_ATTR_HWADDR)) { + *len = 0; + return NULL; + } + + *len = msg->log_msg_hwaddr_len; + return msg->log_msg_hwaddr; +} + +int nfnl_log_msg_set_payload(struct nfnl_log_msg *msg, uint8_t *payload, int len) +{ + free(msg->log_msg_payload); + msg->log_msg_payload = malloc(len); + if (!msg->log_msg_payload) + return -NLE_NOMEM; + + memcpy(msg->log_msg_payload, payload, len); + msg->log_msg_payload_len = len; + msg->ce_mask |= LOG_MSG_ATTR_PAYLOAD; + return 0; +} + +const void *nfnl_log_msg_get_payload(const struct nfnl_log_msg *msg, int *len) +{ + if (!(msg->ce_mask & LOG_MSG_ATTR_PAYLOAD)) { + *len = 0; + return NULL; + } + + *len = msg->log_msg_payload_len; + return msg->log_msg_payload; +} + +int nfnl_log_msg_set_prefix(struct nfnl_log_msg *msg, void *prefix) +{ + free(msg->log_msg_prefix); + msg->log_msg_prefix = strdup(prefix); + if (!msg->log_msg_prefix) + return -NLE_NOMEM; + + msg->ce_mask |= LOG_MSG_ATTR_PREFIX; + return 0; +} + +const char *nfnl_log_msg_get_prefix(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_prefix; +} + +void nfnl_log_msg_set_uid(struct nfnl_log_msg *msg, uint32_t uid) +{ + msg->log_msg_uid = uid; + msg->ce_mask |= LOG_MSG_ATTR_UID; +} + +int nfnl_log_msg_test_uid(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_UID); +} + +uint32_t nfnl_log_msg_get_uid(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_uid; +} + +void nfnl_log_msg_set_gid(struct nfnl_log_msg *msg, uint32_t gid) +{ + msg->log_msg_gid = gid; + msg->ce_mask |= LOG_MSG_ATTR_GID; +} + +int nfnl_log_msg_test_gid(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_GID); +} + +uint32_t nfnl_log_msg_get_gid(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_gid; +} + + +void nfnl_log_msg_set_seq(struct nfnl_log_msg *msg, uint32_t seq) +{ + msg->log_msg_seq = seq; + msg->ce_mask |= LOG_MSG_ATTR_SEQ; +} + +int nfnl_log_msg_test_seq(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_SEQ); +} + +uint32_t nfnl_log_msg_get_seq(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_seq; +} + +void nfnl_log_msg_set_seq_global(struct nfnl_log_msg *msg, uint32_t seq_global) +{ + msg->log_msg_seq_global = seq_global; + msg->ce_mask |= LOG_MSG_ATTR_SEQ_GLOBAL; +} + +int nfnl_log_msg_test_seq_global(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_SEQ_GLOBAL); +} + +uint32_t nfnl_log_msg_get_seq_global(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_seq_global; +} + +/** @} */ + +struct nl_object_ops log_msg_obj_ops = { + .oo_name = "netfilter/log_msg", + .oo_size = sizeof(struct nfnl_log_msg), + .oo_free_data = log_msg_free_data, + .oo_clone = log_msg_clone, + .oo_dump = { + [NL_DUMP_LINE] = log_msg_dump, + [NL_DUMP_DETAILS] = log_msg_dump, + [NL_DUMP_STATS] = log_msg_dump, + }, +}; + +/** @} */ diff --git a/lib/netfilter/log_obj.c b/lib/netfilter/log_obj.c index c3adc51..ff2b63a 100644 --- a/lib/netfilter/log_obj.c +++ b/lib/netfilter/log_obj.c @@ -6,9 +6,10 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> * Copyright (c) 2007 Philip Craig <philipc@snapgear.com> * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> */ #include <netlink-local.h> @@ -16,143 +17,61 @@ #include <netlink/netfilter/log.h> /** @cond SKIP */ -#define LOG_ATTR_FAMILY (1UL << 0) -#define LOG_ATTR_HWPROTO (1UL << 1) -#define LOG_ATTR_HOOK (1UL << 2) -#define LOG_ATTR_MARK (1UL << 3) -#define LOG_ATTR_TIMESTAMP (1UL << 4) -#define LOG_ATTR_INDEV (1UL << 5) -#define LOG_ATTR_OUTDEV (1UL << 6) -#define LOG_ATTR_PHYSINDEV (1UL << 7) -#define LOG_ATTR_PHYSOUTDEV (1UL << 8) -#define LOG_ATTR_HWADDR (1UL << 9) -#define LOG_ATTR_PAYLOAD (1UL << 10) -#define LOG_ATTR_PREFIX (1UL << 11) -#define LOG_ATTR_UID (1UL << 12) -#define LOG_ATTR_SEQ (1UL << 13) -#define LOG_ATTR_SEQ_GLOBAL (1UL << 14) +#define LOG_ATTR_GROUP (1UL << 0) +#define LOG_ATTR_COPY_MODE (1UL << 1) +#define LOG_ATTR_COPY_RANGE (1UL << 3) +#define LOG_ATTR_FLUSH_TIMEOUT (1UL << 4) +#define LOG_ATTR_ALLOC_SIZE (1UL << 5) +#define LOG_ATTR_QUEUE_THRESHOLD (1UL << 6) + /** @endcond */ -static void log_free_data(struct nl_object *c) +static void nfnl_log_dump(struct nl_object *a, struct nl_dump_params *p) { - struct nfnl_log *log = (struct nfnl_log *) c; + struct nfnl_log *log = (struct nfnl_log *) a; + char buf[64]; + + nl_new_line(p); + + if (log->ce_mask & LOG_ATTR_GROUP) + nl_dump(p, "group=%u ", log->log_group); + + if (log->ce_mask & LOG_ATTR_COPY_MODE) + nl_dump(p, "copy_mode=%s ", + nfnl_log_copy_mode2str(log->log_copy_mode, + buf, sizeof(buf))); + + if (log->ce_mask & LOG_ATTR_COPY_RANGE) + nl_dump(p, "copy_range=%u ", log->log_copy_range); - if (log == NULL) - return; + if (log->ce_mask & LOG_ATTR_FLUSH_TIMEOUT) + nl_dump(p, "flush_timeout=%u ", log->log_flush_timeout); - free(log->log_payload); - free(log->log_prefix); + if (log->ce_mask & LOG_ATTR_ALLOC_SIZE) + nl_dump(p, "alloc_size=%u ", log->log_alloc_size); + + if (log->ce_mask & LOG_ATTR_QUEUE_THRESHOLD) + nl_dump(p, "queue_threshold=%u ", log->log_queue_threshold); + + nl_dump(p, "\n"); } -static int log_clone(struct nl_object *_dst, struct nl_object *_src) +static struct trans_tbl copy_modes[] = { + __ADD(NFNL_LOG_COPY_NONE, none) + __ADD(NFNL_LOG_COPY_META, meta) + __ADD(NFNL_LOG_COPY_PACKET, packet) +}; + +char *nfnl_log_copy_mode2str(enum nfnl_log_copy_mode copy_mode, char *buf, + size_t len) { - struct nfnl_log *dst = (struct nfnl_log *) _dst; - struct nfnl_log *src = (struct nfnl_log *) _src; - int err; - - if (src->log_payload) { - err = nfnl_log_set_payload(dst, src->log_payload, - src->log_payload_len); - if (err < 0) - goto errout; - } - - if (src->log_prefix) { - err = nfnl_log_set_prefix(dst, src->log_prefix); - if (err < 0) - goto errout; - } - - return 0; -errout: - return err; + return __type2str(copy_mode, buf, len, copy_modes, + ARRAY_SIZE(copy_modes)); } -static int log_dump(struct nl_object *a, struct nl_dump_params *p) +enum nfnl_log_copy_mode nfnl_log_str2copy_mode(const char *name) { - struct nfnl_log *log = (struct nfnl_log *) a; - struct nl_cache *link_cache; - char buf[64]; - - link_cache = nl_cache_mngt_require("route/link"); - - if (log->ce_mask & LOG_ATTR_PREFIX) - dp_dump(p, "%s", log->log_prefix); - - if (log->ce_mask & LOG_ATTR_INDEV) { - if (link_cache) - dp_dump(p, "IN=%s ", - rtnl_link_i2name(link_cache, log->log_indev, - buf, sizeof(buf))); - else - dp_dump(p, "IN=%d ", log->log_indev); - } - - if (log->ce_mask & LOG_ATTR_PHYSINDEV) { - if (link_cache) - dp_dump(p, "PHYSIN=%s ", - rtnl_link_i2name(link_cache, log->log_physindev, - buf, sizeof(buf))); - else - dp_dump(p, "IN=%d ", log->log_physindev); - } - - if (log->ce_mask & LOG_ATTR_OUTDEV) { - if (link_cache) - dp_dump(p, "OUT=%s ", - rtnl_link_i2name(link_cache, log->log_outdev, - buf, sizeof(buf))); - else - dp_dump(p, "OUT=%d ", log->log_outdev); - } - - if (log->ce_mask & LOG_ATTR_PHYSOUTDEV) { - if (link_cache) - dp_dump(p, "PHYSOUT=%s ", - rtnl_link_i2name(link_cache,log->log_physoutdev, - buf, sizeof(buf))); - else - dp_dump(p, "PHYSOUT=%d ", log->log_physoutdev); - } - - if (log->ce_mask & LOG_ATTR_HWADDR) { - int i; - - dp_dump(p, "MAC"); - for (i = 0; i < log->log_hwaddr_len; i++) - dp_dump(p, "%c%02x", i?':':'=', log->log_hwaddr[i]); - dp_dump(p, " "); - } - - /* FIXME: parse the payload to get iptables LOG compatible format */ - - if (log->ce_mask & LOG_ATTR_FAMILY) - dp_dump(p, "FAMILY=%s ", - nl_af2str(log->log_family, buf, sizeof(buf))); - - if (log->ce_mask & LOG_ATTR_HWPROTO) - dp_dump(p, "HWPROTO=%s ", - nl_ether_proto2str(ntohs(log->log_hwproto), - buf, sizeof(buf))); - - if (log->ce_mask & LOG_ATTR_HOOK) - dp_dump(p, "HOOK=%d ", log->log_hook); - - if (log->ce_mask & LOG_ATTR_MARK) - dp_dump(p, "MARK=%d ", log->log_mark); - - if (log->ce_mask & LOG_ATTR_PAYLOAD) - dp_dump(p, "PAYLOADLEN=%d ", log->log_payload_len); - - if (log->ce_mask & LOG_ATTR_SEQ) - dp_dump(p, "SEQ=%d ", log->log_seq); - - if (log->ce_mask & LOG_ATTR_SEQ_GLOBAL) - dp_dump(p, "SEQGLOBAL=%d ", log->log_seq_global); - - dp_dump(p, "\n"); - - return 1; + return __str2type(name, copy_modes, ARRAY_SIZE(copy_modes)); } /** @@ -182,232 +101,172 @@ void nfnl_log_put(struct nfnl_log *log) * @{ */ -void nfnl_log_set_family(struct nfnl_log *log, uint8_t family) -{ - log->log_family = family; - log->ce_mask |= LOG_ATTR_FAMILY; -} - -uint8_t nfnl_log_get_family(const struct nfnl_log *log) -{ - if (log->ce_mask & LOG_ATTR_FAMILY) - return log->log_family; - else - return AF_UNSPEC; -} - -void nfnl_log_set_hwproto(struct nfnl_log *log, uint16_t hwproto) -{ - log->log_hwproto = hwproto; - log->ce_mask |= LOG_ATTR_HWPROTO; -} - -int nfnl_log_test_hwproto(const struct nfnl_log *log) -{ - return !!(log->ce_mask & LOG_ATTR_HWPROTO); -} - -uint16_t nfnl_log_get_hwproto(const struct nfnl_log *log) +void nfnl_log_set_group(struct nfnl_log *log, uint16_t group) { - return log->log_hwproto; + log->log_group = group; + log->ce_mask |= LOG_ATTR_GROUP; } -void nfnl_log_set_hook(struct nfnl_log *log, uint8_t hook) +int nfnl_log_test_group(const struct nfnl_log *log) { - log->log_hook = hook; - log->ce_mask |= LOG_ATTR_HOOK; + return !!(log->ce_mask & LOG_ATTR_GROUP); } -int nfnl_log_test_hook(const struct nfnl_log *log) +uint16_t nfnl_log_get_group(const struct nfnl_log *log) { - return !!(log->ce_mask & LOG_ATTR_HOOK); + return log->log_group; } -uint8_t nfnl_log_get_hook(const struct nfnl_log *log) +void nfnl_log_set_copy_mode(struct nfnl_log *log, enum nfnl_log_copy_mode mode) { - return log->log_hook; + log->log_copy_mode = mode; + log->ce_mask |= LOG_ATTR_COPY_MODE; } -void nfnl_log_set_mark(struct nfnl_log *log, uint32_t mark) +int nfnl_log_test_copy_mode(const struct nfnl_log *log) { - log->log_mark = mark; - log->ce_mask |= LOG_ATTR_MARK; + return !!(log->ce_mask & LOG_ATTR_COPY_MODE); } -int nfnl_log_test_mark(const struct nfnl_log *log) +enum nfnl_log_copy_mode nfnl_log_get_copy_mode(const struct nfnl_log *log) { - return !!(log->ce_mask & LOG_ATTR_MARK); + return log->log_copy_mode; } -uint32_t nfnl_log_get_mark(const struct nfnl_log *log) +void nfnl_log_set_copy_range(struct nfnl_log *log, uint32_t copy_range) { - return log->log_mark; + log->log_copy_range = copy_range; + log->ce_mask |= LOG_ATTR_COPY_RANGE; } -void nfnl_log_set_timestamp(struct nfnl_log *log, struct timeval *tv) +int nfnl_log_test_copy_range(const struct nfnl_log *log) { - log->log_timestamp.tv_sec = tv->tv_sec; - log->log_timestamp.tv_usec = tv->tv_usec; - log->ce_mask |= LOG_ATTR_TIMESTAMP; + return !!(log->ce_mask & LOG_ATTR_COPY_RANGE); } -const struct timeval *nfnl_log_get_timestamp(const struct nfnl_log *log) +uint32_t nfnl_log_get_copy_range(const struct nfnl_log *log) { - if (!(log->ce_mask & LOG_ATTR_TIMESTAMP)) - return NULL; - return &log->log_timestamp; + return log->log_copy_range; } -void nfnl_log_set_indev(struct nfnl_log *log, uint32_t indev) +void nfnl_log_set_flush_timeout(struct nfnl_log *log, uint32_t timeout) { - log->log_indev = indev; - log->ce_mask |= LOG_ATTR_INDEV; + log->log_flush_timeout = timeout; + log->ce_mask |= LOG_ATTR_FLUSH_TIMEOUT; } -uint32_t nfnl_log_get_indev(const struct nfnl_log *log) +int nfnl_log_test_flush_timeout(const struct nfnl_log *log) { - return log->log_indev; + return !!(log->ce_mask & LOG_ATTR_FLUSH_TIMEOUT); } -void nfnl_log_set_outdev(struct nfnl_log *log, uint32_t outdev) +uint32_t nfnl_log_get_flush_timeout(const struct nfnl_log *log) { - log->log_outdev = outdev; - log->ce_mask |= LOG_ATTR_OUTDEV; + return log->log_flush_timeout; } -uint32_t nfnl_log_get_outdev(const struct nfnl_log *log) +void nfnl_log_set_alloc_size(struct nfnl_log *log, uint32_t alloc_size) { - return log->log_outdev; + log->log_alloc_size = alloc_size; + log->ce_mask |= LOG_ATTR_ALLOC_SIZE; } -void nfnl_log_set_physindev(struct nfnl_log *log, uint32_t physindev) +int nfnl_log_test_alloc_size(const struct nfnl_log *log) { - log->log_physindev = physindev; - log->ce_mask |= LOG_ATTR_PHYSINDEV; + return !!(log->ce_mask & LOG_ATTR_ALLOC_SIZE); } -uint32_t nfnl_log_get_physindev(const struct nfnl_log *log) +uint32_t nfnl_log_get_alloc_size(const struct nfnl_log *log) { - return log->log_physindev; + return log->log_alloc_size; } -void nfnl_log_set_physoutdev(struct nfnl_log *log, uint32_t physoutdev) +void nfnl_log_set_queue_threshold(struct nfnl_log *log, uint32_t threshold) { - log->log_physoutdev = physoutdev; - log->ce_mask |= LOG_ATTR_PHYSOUTDEV; + log->log_queue_threshold = threshold; + log->ce_mask |= LOG_ATTR_QUEUE_THRESHOLD; } -uint32_t nfnl_log_get_physoutdev(const struct nfnl_log *log) +int nfnl_log_test_queue_threshold(const struct nfnl_log *log) { - return log->log_physoutdev; + return !!(log->ce_mask & LOG_ATTR_QUEUE_THRESHOLD); } -void nfnl_log_set_hwaddr(struct nfnl_log *log, uint8_t *hwaddr, int len) +uint32_t nfnl_log_get_queue_threshold(const struct nfnl_log *log) { - if (len > sizeof(log->log_hwaddr)) - len = sizeof(log->log_hwaddr); - log->log_hwaddr_len = len; - memcpy(log->log_hwaddr, hwaddr, len); - log->ce_mask |= LOG_ATTR_HWADDR; + return log->log_queue_threshold; } -const uint8_t *nfnl_log_get_hwaddr(const struct nfnl_log *log, int *len) -{ - if (!(log->ce_mask & LOG_ATTR_HWADDR)) { - *len = 0; - return NULL; - } - - *len = log->log_hwaddr_len; - return log->log_hwaddr; -} - -int nfnl_log_set_payload(struct nfnl_log *log, uint8_t *payload, int len) -{ - free(log->log_payload); - log->log_payload = malloc(len); - if (!log->log_payload) - return nl_errno(ENOMEM); - - memcpy(log->log_payload, payload, len); - log->log_payload_len = len; - log->ce_mask |= LOG_ATTR_PAYLOAD; - return 0; -} - -const void *nfnl_log_get_payload(const struct nfnl_log *log, int *len) +/* We don't actually use the flags for anything yet since the + * nfnetlog_log interface truly sucks - it only contains the + * flag value, but not mask, so we would have to make assumptions + * about the supported flags. + */ +void nfnl_log_set_flags(struct nfnl_log *log, unsigned int flags) { - if (!(log->ce_mask & LOG_ATTR_PAYLOAD)) { - *len = 0; - return NULL; - } - - *len = log->log_payload_len; - return log->log_payload; + log->log_flags |= flags; + log->log_flag_mask |= flags; } -int nfnl_log_set_prefix(struct nfnl_log *log, void *prefix) +void nfnl_log_unset_flags(struct nfnl_log *log, unsigned int flags) { - free(log->log_prefix); - log->log_prefix = strdup(prefix); - if (!log->log_prefix) - return nl_errno(ENOMEM); - - log->ce_mask |= LOG_ATTR_PREFIX; - return 0; + log->log_flags &= ~flags; + log->log_flag_mask |= flags; } -const char *nfnl_log_get_prefix(const struct nfnl_log *log) -{ - return log->log_prefix; -} +static struct trans_tbl log_flags[] = { + __ADD(NFNL_LOG_FLAG_SEQ, seq) + __ADD(NFNL_LOG_FLAG_SEQ_GLOBAL, seq_global) +}; -void nfnl_log_set_uid(struct nfnl_log *log, uint32_t uid) +char *nfnl_log_flags2str(unsigned int flags, char *buf, size_t len) { - log->log_uid = uid; - log->ce_mask |= LOG_ATTR_UID; + return __flags2str(flags, buf, len, log_flags, ARRAY_SIZE(log_flags)); } -int nfnl_log_test_uid(const struct nfnl_log *log) +unsigned int nfnl_log_str2flags(const char *name) { - return !!(log->ce_mask & LOG_ATTR_UID); + return __str2flags(name, log_flags, ARRAY_SIZE(log_flags)); } -uint32_t nfnl_log_get_uid(const struct nfnl_log *log) +static int nfnl_log_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) { - return log->log_uid; -} + struct nfnl_log *a = (struct nfnl_log *) _a; + struct nfnl_log *b = (struct nfnl_log *) _b; + int diff = 0; -void nfnl_log_set_seq(struct nfnl_log *log, uint32_t seq) -{ - log->log_seq = seq; - log->ce_mask |= LOG_ATTR_SEQ; -} +#define NFNL_LOG_DIFF(ATTR, EXPR) \ + ATTR_DIFF(attrs, LOG_ATTR_##ATTR, a, b, EXPR) +#define NFNL_LOG_DIFF_VAL(ATTR, FIELD) \ + NFNL_LOG_DIFF(ATTR, a->FIELD != b->FIELD) -int nfnl_log_test_seq(const struct nfnl_log *log) -{ - return !!(log->ce_mask & LOG_ATTR_SEQ); -} + diff |= NFNL_LOG_DIFF_VAL(GROUP, log_group); + diff |= NFNL_LOG_DIFF_VAL(COPY_MODE, log_copy_mode); + diff |= NFNL_LOG_DIFF_VAL(COPY_RANGE, log_copy_range); + diff |= NFNL_LOG_DIFF_VAL(FLUSH_TIMEOUT, log_flush_timeout); + diff |= NFNL_LOG_DIFF_VAL(ALLOC_SIZE, log_alloc_size); + diff |= NFNL_LOG_DIFF_VAL(QUEUE_THRESHOLD, log_queue_threshold); -uint32_t nfnl_log_get_seq(const struct nfnl_log *log) -{ - return log->log_seq; -} +#undef NFNL_LOG_DIFF +#undef NFNL_LOG_DIFF_VAL -void nfnl_log_set_seq_global(struct nfnl_log *log, uint32_t seq_global) -{ - log->log_seq_global = seq_global; - log->ce_mask |= LOG_ATTR_SEQ_GLOBAL; + return diff; } -int nfnl_log_test_seq_global(const struct nfnl_log *log) -{ - return !!(log->ce_mask & LOG_ATTR_SEQ_GLOBAL); -} +static struct trans_tbl nfnl_log_attrs[] = { + __ADD(LOG_ATTR_GROUP, group) + __ADD(LOG_ATTR_COPY_MODE, copy_mode) + __ADD(LOG_ATTR_COPY_RANGE, copy_range) + __ADD(LOG_ATTR_FLUSH_TIMEOUT, flush_timeout) + __ADD(LOG_ATTR_ALLOC_SIZE, alloc_size) + __ADD(LOG_ATTR_QUEUE_THRESHOLD, queue_threshold) +}; -uint32_t nfnl_log_get_seq_global(const struct nfnl_log *log) +static char *nfnl_log_attrs2str(int attrs, char *buf, size_t len) { - return log->log_seq_global; + return __flags2str(attrs, buf, len, nfnl_log_attrs, + ARRAY_SIZE(nfnl_log_attrs)); } /** @} */ @@ -415,11 +274,14 @@ uint32_t nfnl_log_get_seq_global(const struct nfnl_log *log) struct nl_object_ops log_obj_ops = { .oo_name = "netfilter/log", .oo_size = sizeof(struct nfnl_log), - .oo_free_data = log_free_data, - .oo_clone = log_clone, - .oo_dump[NL_DUMP_BRIEF] = log_dump, - .oo_dump[NL_DUMP_FULL] = log_dump, - .oo_dump[NL_DUMP_STATS] = log_dump, + .oo_dump = { + [NL_DUMP_LINE] = nfnl_log_dump, + [NL_DUMP_DETAILS] = nfnl_log_dump, + [NL_DUMP_STATS] = nfnl_log_dump, + }, + .oo_compare = nfnl_log_compare, + .oo_attrs2str = nfnl_log_attrs2str, + .oo_id_attrs = LOG_ATTR_GROUP, }; /** @} */ diff --git a/lib/netfilter/netfilter.c b/lib/netfilter/netfilter.c new file mode 100644 index 0000000..f88b355 --- /dev/null +++ b/lib/netfilter/netfilter.c @@ -0,0 +1,53 @@ +/* + * lib/netfilter/netfilter.c Netfilter Generic Functions + * + * 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) 2008 Patrick McHardy <kaber@trash.net> + */ + +#include <netlink-local.h> +#include <netlink/netfilter/netfilter.h> +#include <linux/netfilter.h> + +static struct trans_tbl nfnl_verdicts[] = { + __ADD(NF_DROP, NF_DROP) + __ADD(NF_ACCEPT, NF_ACCEPT) + __ADD(NF_STOLEN, NF_STOLEN) + __ADD(NF_QUEUE, NF_QUEUE) + __ADD(NF_REPEAT, NF_REPEAT) + __ADD(NF_STOP, NF_STOP) +}; + +char *nfnl_verdict2str(unsigned int verdict, char *buf, size_t len) +{ + return __type2str(verdict, buf, len, nfnl_verdicts, + ARRAY_SIZE(nfnl_verdicts)); +} + +unsigned int nfnl_str2verdict(const char *name) +{ + return __str2type(name, nfnl_verdicts, ARRAY_SIZE(nfnl_verdicts)); +} + +static struct trans_tbl nfnl_inet_hooks[] = { + __ADD(NF_INET_PRE_ROUTING, NF_INET_PREROUTING) + __ADD(NF_INET_LOCAL_IN, NF_INET_LOCAL_IN) + __ADD(NF_INET_FORWARD, NF_INET_FORWARD) + __ADD(NF_INET_LOCAL_OUT, NF_INET_LOCAL_OUT) + __ADD(NF_INET_POST_ROUTING, NF_INET_POST_ROUTING) +}; + +char *nfnl_inet_hook2str(unsigned int hook, char *buf, size_t len) +{ + return __type2str(hook, buf, len, nfnl_inet_hooks, + ARRAY_SIZE(nfnl_inet_hooks)); +} + +unsigned int nfnl_str2inet_hook(const char *name) +{ + return __str2type(name, nfnl_inet_hooks, ARRAY_SIZE(nfnl_inet_hooks)); +} diff --git a/lib/netfilter/nfnl.c b/lib/netfilter/nfnl.c index 554e234..ddce4b9 100644 --- a/lib/netfilter/nfnl.c +++ b/lib/netfilter/nfnl.c @@ -6,13 +6,12 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> * Copyright (c) 2007 Philip Craig <philipc@snapgear.com> * Copyright (c) 2007 Secure Computing Corporation */ /** - * @ingroup nlfam * @defgroup nfnl Netfilter Netlink * * @par Message Format @@ -47,7 +46,7 @@ * nla_put_u32(msg, 1, 0x10); * * // Message is ready to be sent. - * nl_send_auto_complete(nl_handle, msg); + * nl_send_auto_complete(sk, msg); * * // All done? Free the message. * nlmsg_free(msg); @@ -57,7 +56,7 @@ * @code * // For trivial messages not requiring any subsys specific header or * // attributes, nfnl_send_simple() may be used to send messages directly. - * nfnl_send_simple(nl_handle, SUBSYS, TYPE, 0, FAMILY, RES_ID); + * nfnl_send_simple(sk, SUBSYS, TYPE, 0, FAMILY, RES_ID); * @endcode * @{ */ @@ -73,7 +72,7 @@ /** * Create and connect netfilter netlink socket. - * @arg handle Netlink handle. + * @arg sk Netlink socket. * * Creates a NETLINK_NETFILTER netlink socket, binds the socket and * issues a connection attempt. @@ -82,9 +81,9 @@ * * @return 0 on success or a negative error code. */ -int nfnl_connect(struct nl_handle *handle) +int nfnl_connect(struct nl_sock *sk) { - return nl_connect(handle, NETLINK_NETFILTER); + return nl_connect(sk, NETLINK_NETFILTER); } /** @} */ @@ -96,7 +95,7 @@ int nfnl_connect(struct nl_handle *handle) /** * Send trivial netfilter netlink message - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg subsys_id nfnetlink subsystem * @arg type nfnetlink message type * @arg flags message flags @@ -105,7 +104,7 @@ int nfnl_connect(struct nl_handle *handle) * * @return Newly allocated netlink message or NULL. */ -int nfnl_send_simple(struct nl_handle *handle, uint8_t subsys_id, uint8_t type, +int nfnl_send_simple(struct nl_sock *sk, uint8_t subsys_id, uint8_t type, int flags, uint8_t family, uint16_t res_id) { struct nfgenmsg hdr = { @@ -114,7 +113,7 @@ int nfnl_send_simple(struct nl_handle *handle, uint8_t subsys_id, uint8_t type, .res_id = htons(res_id), }; - return nl_send_simple(handle, NFNLMSG_TYPE(subsys_id, type), flags, + return nl_send_simple(sk, NFNLMSG_TYPE(subsys_id, type), flags, &hdr, sizeof(hdr)); } @@ -178,7 +177,7 @@ static int nfnlmsg_append(struct nl_msg *msg, uint8_t family, uint16_t res_id) nfg = nlmsg_reserve(msg, sizeof(*nfg), NLMSG_ALIGNTO); if (nfg == NULL) - return nl_errno(ENOMEM); + return -NLE_NOMEM; nfg->nfgen_family = family; nfg->version = NFNETLINK_V0; @@ -236,7 +235,7 @@ int nfnlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, nlh = nlmsg_put(msg, pid, seq, NFNLMSG_TYPE(subsys_id, type), 0, flags); if (nlh == NULL) - return nl_get_errno(); + return -NLE_MSGSIZE; return nfnlmsg_append(msg, family, res_id); } diff --git a/lib/netfilter/queue.c b/lib/netfilter/queue.c new file mode 100644 index 0000000..ff1de0e --- /dev/null +++ b/lib/netfilter/queue.c @@ -0,0 +1,251 @@ +/* + * lib/netfilter/queue.c Netfilter Queue + * + * 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) 2007, 2008 Patrick McHardy <kaber@trash.net> + */ + +/** + * @ingroup nfnl + * @defgroup queue Queue + * @brief + * @{ + */ + +#include <sys/types.h> +#include <linux/netfilter/nfnetlink_queue.h> + +#include <netlink-local.h> +#include <netlink/attr.h> +#include <netlink/netfilter/nfnl.h> +#include <netlink/netfilter/queue.h> + +struct nl_sock *nfnl_queue_socket_alloc(void) +{ + struct nl_sock *nlsk; + + nlsk = nl_socket_alloc(); + if (nlsk) + nl_socket_disable_auto_ack(nlsk); + return nlsk; +} + +static int send_queue_request(struct nl_sock *sk, struct nl_msg *msg) +{ + int err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** + * @name Queue Commands + * @{ + */ + +static int build_queue_cmd_request(uint8_t family, uint16_t queuenum, + uint8_t command, struct nl_msg **result) +{ + struct nl_msg *msg; + struct nfqnl_msg_config_cmd cmd; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_CONFIG, 0, + family, queuenum); + if (msg == NULL) + return -NLE_NOMEM; + + cmd.pf = htons(family); + cmd._pad = 0; + cmd.command = command; + if (nla_put(msg, NFQA_CFG_CMD, sizeof(cmd), &cmd) < 0) + goto nla_put_failure; + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +int nfnl_queue_build_pf_bind(uint8_t pf, struct nl_msg **result) +{ + return build_queue_cmd_request(pf, 0, NFQNL_CFG_CMD_PF_BIND, result); +} + +int nfnl_queue_pf_bind(struct nl_sock *nlh, uint8_t pf) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_pf_bind(pf, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +int nfnl_queue_build_pf_unbind(uint8_t pf, struct nl_msg **result) +{ + return build_queue_cmd_request(pf, 0, NFQNL_CFG_CMD_PF_UNBIND, result); +} + +int nfnl_queue_pf_unbind(struct nl_sock *nlh, uint8_t pf) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_pf_unbind(pf, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +static int nfnl_queue_build_request(const struct nfnl_queue *queue, + struct nl_msg **result) +{ + struct nl_msg *msg; + + if (!nfnl_queue_test_group(queue)) + return -NLE_MISSING_ATTR; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_CONFIG, 0, + 0, nfnl_queue_get_group(queue)); + if (msg == NULL) + return -NLE_NOMEM; + + if (nfnl_queue_test_maxlen(queue) && + nla_put_u32(msg, NFQA_CFG_QUEUE_MAXLEN, + htonl(nfnl_queue_get_maxlen(queue))) < 0) + goto nla_put_failure; + + /* This sucks, the nfnetlink_queue interface always expects both + * parameters to be present. Needs to be done properly. + */ + if (nfnl_queue_test_copy_mode(queue)) { + struct nfqnl_msg_config_params params; + + switch (nfnl_queue_get_copy_mode(queue)) { + case NFNL_QUEUE_COPY_NONE: + params.copy_mode = NFQNL_COPY_NONE; + break; + case NFNL_QUEUE_COPY_META: + params.copy_mode = NFQNL_COPY_META; + break; + case NFNL_QUEUE_COPY_PACKET: + params.copy_mode = NFQNL_COPY_PACKET; + break; + } + params.copy_range = htonl(nfnl_queue_get_copy_range(queue)); + + if (nla_put(msg, NFQA_CFG_PARAMS, sizeof(params), ¶ms) < 0) + goto nla_put_failure; + } + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +int nfnl_queue_build_create_request(const struct nfnl_queue *queue, + struct nl_msg **result) +{ + struct nfqnl_msg_config_cmd cmd; + int err; + + if ((err = nfnl_queue_build_request(queue, result)) < 0) + return err; + + cmd.pf = 0; + cmd._pad = 0; + cmd.command = NFQNL_CFG_CMD_BIND; + + NLA_PUT(*result, NFQA_CFG_CMD, sizeof(cmd), &cmd); + + return 0; + +nla_put_failure: + nlmsg_free(*result); + return -NLE_MSGSIZE; +} + +int nfnl_queue_create(struct nl_sock *nlh, const struct nfnl_queue *queue) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_create_request(queue, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +int nfnl_queue_build_change_request(const struct nfnl_queue *queue, + struct nl_msg **result) +{ + return nfnl_queue_build_request(queue, result); +} + +int nfnl_queue_change(struct nl_sock *nlh, const struct nfnl_queue *queue) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_change_request(queue, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +int nfnl_queue_build_delete_request(const struct nfnl_queue *queue, + struct nl_msg **result) +{ + if (!nfnl_queue_test_group(queue)) + return -NLE_MISSING_ATTR; + + return build_queue_cmd_request(0, nfnl_queue_get_group(queue), + NFQNL_CFG_CMD_UNBIND, result); +} + +int nfnl_queue_delete(struct nl_sock *nlh, const struct nfnl_queue *queue) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_delete_request(queue, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +/** @} */ + +static struct nl_cache_ops nfnl_queue_ops = { + .co_name = "netfilter/queue", + .co_obj_ops = &queue_obj_ops, + .co_msgtypes = { + END_OF_MSGTYPES_LIST, + }, +}; + +static void __init nfnl_queue_init(void) +{ + nl_cache_mngt_register(&nfnl_queue_ops); +} + +static void __exit nfnl_queue_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_queue_ops); +} + +/** @} */ diff --git a/lib/netfilter/queue_msg.c b/lib/netfilter/queue_msg.c new file mode 100644 index 0000000..ab0a58b --- /dev/null +++ b/lib/netfilter/queue_msg.c @@ -0,0 +1,284 @@ +/* + * lib/netfilter/queue_msg.c Netfilter Queue Messages + * + * 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) 2007, 2008 Patrick McHardy <kaber@trash.net> + * Copyright (c) 2010 Karl Hiramoto <karl@hiramoto.org> + */ + +/** + * @ingroup nfnl + * @defgroup queue Queue + * @brief + * @{ + */ + +#include <sys/types.h> +#include <linux/netfilter/nfnetlink_queue.h> + +#include <netlink-local.h> +#include <netlink/attr.h> +#include <netlink/netfilter/nfnl.h> +#include <netlink/netfilter/queue_msg.h> + +static struct nl_cache_ops nfnl_queue_msg_ops; + +#if __BYTE_ORDER == __BIG_ENDIAN +static uint64_t ntohll(uint64_t x) +{ + return x; +} +#elif __BYTE_ORDER == __LITTLE_ENDIAN +static uint64_t ntohll(uint64_t x) +{ + return __bswap_64(x); +} +#endif + +static struct nla_policy queue_policy[NFQA_MAX+1] = { + [NFQA_PACKET_HDR] = { + .minlen = sizeof(struct nfqnl_msg_packet_hdr), + }, + [NFQA_VERDICT_HDR] = { + .minlen = sizeof(struct nfqnl_msg_verdict_hdr), + }, + [NFQA_MARK] = { .type = NLA_U32 }, + [NFQA_TIMESTAMP] = { + .minlen = sizeof(struct nfqnl_msg_packet_timestamp), + }, + [NFQA_IFINDEX_INDEV] = { .type = NLA_U32 }, + [NFQA_IFINDEX_OUTDEV] = { .type = NLA_U32 }, + [NFQA_IFINDEX_PHYSINDEV] = { .type = NLA_U32 }, + [NFQA_IFINDEX_PHYSOUTDEV] = { .type = NLA_U32 }, + [NFQA_HWADDR] = { + .minlen = sizeof(struct nfqnl_msg_packet_hw), + }, +}; + +int nfnlmsg_queue_msg_parse(struct nlmsghdr *nlh, + struct nfnl_queue_msg **result) +{ + struct nfnl_queue_msg *msg; + struct nlattr *tb[NFQA_MAX+1]; + struct nlattr *attr; + int err; + + msg = nfnl_queue_msg_alloc(); + if (!msg) + return -NLE_NOMEM; + + msg->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, NFQA_MAX, + queue_policy); + if (err < 0) + goto errout; + + nfnl_queue_msg_set_group(msg, nfnlmsg_res_id(nlh)); + nfnl_queue_msg_set_family(msg, nfnlmsg_family(nlh)); + + attr = tb[NFQA_PACKET_HDR]; + if (attr) { + struct nfqnl_msg_packet_hdr *hdr = nla_data(attr); + + nfnl_queue_msg_set_packetid(msg, ntohl(hdr->packet_id)); + if (hdr->hw_protocol) + nfnl_queue_msg_set_hwproto(msg, hdr->hw_protocol); + nfnl_queue_msg_set_hook(msg, hdr->hook); + } + + attr = tb[NFQA_MARK]; + if (attr) + nfnl_queue_msg_set_mark(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_TIMESTAMP]; + if (attr) { + struct nfqnl_msg_packet_timestamp *timestamp = nla_data(attr); + struct timeval tv; + + tv.tv_sec = ntohll(timestamp->sec); + tv.tv_usec = ntohll(timestamp->usec); + nfnl_queue_msg_set_timestamp(msg, &tv); + } + + attr = tb[NFQA_IFINDEX_INDEV]; + if (attr) + nfnl_queue_msg_set_indev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_IFINDEX_OUTDEV]; + if (attr) + nfnl_queue_msg_set_outdev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_IFINDEX_PHYSINDEV]; + if (attr) + nfnl_queue_msg_set_physindev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_IFINDEX_PHYSOUTDEV]; + if (attr) + nfnl_queue_msg_set_physoutdev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_HWADDR]; + if (attr) { + struct nfqnl_msg_packet_hw *hw = nla_data(attr); + + nfnl_queue_msg_set_hwaddr(msg, hw->hw_addr, + ntohs(hw->hw_addrlen)); + } + + attr = tb[NFQA_PAYLOAD]; + if (attr) { + err = nfnl_queue_msg_set_payload(msg, nla_data(attr), + nla_len(attr)); + if (err < 0) + goto errout; + } + + *result = msg; + return 0; + +errout: + nfnl_queue_msg_put(msg); + return err; +} + +static int queue_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nfnl_queue_msg *msg; + int err; + + if ((err = nfnlmsg_queue_msg_parse(nlh, &msg)) < 0) + goto errout; + + err = pp->pp_cb((struct nl_object *) msg, pp); +errout: + nfnl_queue_msg_put(msg); + return err; +} + +/** @} */ + +struct nl_msg *nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *msg) +{ + struct nl_msg *nlmsg; + struct nfqnl_msg_verdict_hdr verdict; + + nlmsg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_VERDICT, 0, + nfnl_queue_msg_get_family(msg), + nfnl_queue_msg_get_group(msg)); + if (nlmsg == NULL) + return NULL; + + verdict.id = htonl(nfnl_queue_msg_get_packetid(msg)); + verdict.verdict = htonl(nfnl_queue_msg_get_verdict(msg)); + if (nla_put(nlmsg, NFQA_VERDICT_HDR, sizeof(verdict), &verdict) < 0) + goto nla_put_failure; + + if (nfnl_queue_msg_test_mark(msg) && + nla_put_u32(nlmsg, NFQA_MARK, + ntohl(nfnl_queue_msg_get_mark(msg))) < 0) + goto nla_put_failure; + + return nlmsg; + +nla_put_failure: + nlmsg_free(nlmsg); + return NULL; +} + +/** +* Send a message verdict/mark +* @arg nlh netlink messsage header +* @arg msg queue msg +* @return 0 on OK or error code +*/ +int nfnl_queue_msg_send_verdict(struct nl_sock *nlh, + const struct nfnl_queue_msg *msg) +{ + struct nl_msg *nlmsg; + int err; + + nlmsg = nfnl_queue_msg_build_verdict(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 +* @arg msg queue msg +* @arg payload_data packet payload data +* @arg payload_len payload length +* @return 0 on OK or error code +*/ +int nfnl_queue_msg_send_verdict_payload(struct nl_sock *nlh, + const struct nfnl_queue_msg *msg, + const void *payload_data, unsigned payload_len) +{ + struct nl_msg *nlmsg; + int err; + struct iovec iov[3]; + struct nlattr nla; + + nlmsg = nfnl_queue_msg_build_verdict(msg); + if (nlmsg == NULL) + return -NLE_NOMEM; + + memset(iov, 0, sizeof(iov)); + + iov[0].iov_base = (void *) nlmsg_hdr(nlmsg); + iov[0].iov_len = nlmsg_hdr(nlmsg)->nlmsg_len; + + nla.nla_type = NFQA_PAYLOAD; + nla.nla_len = payload_len + sizeof(nla); + nlmsg_hdr(nlmsg)->nlmsg_len += nla.nla_len; + + iov[1].iov_base = (void *) &nla; + iov[1].iov_len = sizeof(nla); + + iov[2].iov_base = (void *) payload_data; + iov[2].iov_len = NLA_ALIGN(payload_len); + + nl_auto_complete(nlh, nlmsg); + err = nl_send_iovec(nlh, nlmsg, iov, 3); + + nlmsg_free(nlmsg); + if (err < 0) + return err; + return wait_for_ack(nlh); +} + +#define NFNLMSG_QUEUE_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_QUEUE, (type)) +static struct nl_cache_ops nfnl_queue_msg_ops = { + .co_name = "netfilter/queue_msg", + .co_hdrsize = NFNL_HDRLEN, + .co_msgtypes = { + { NFNLMSG_QUEUE_TYPE(NFQNL_MSG_PACKET), NL_ACT_NEW, "new" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_NETFILTER, + .co_msg_parser = queue_msg_parser, + .co_obj_ops = &queue_msg_obj_ops, +}; + +static void __init nfnl_msg_queue_init(void) +{ + nl_cache_mngt_register(&nfnl_queue_msg_ops); +} + +static void __exit nfnl_queue_msg_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_queue_msg_ops); +} + +/** @} */ diff --git a/lib/netfilter/queue_msg_obj.c b/lib/netfilter/queue_msg_obj.c new file mode 100644 index 0000000..97813e8 --- /dev/null +++ b/lib/netfilter/queue_msg_obj.c @@ -0,0 +1,492 @@ +/* + * lib/netfilter/queue_msg_obj.c Netfilter Queue Message Object + * + * 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) 2007, 2008 Patrick McHardy <kaber@trash.net> + */ + +#include <netlink-local.h> +#include <netlink/netfilter/nfnl.h> +#include <netlink/netfilter/netfilter.h> +#include <netlink/netfilter/queue_msg.h> +#include <linux/netfilter.h> + +/** @cond SKIP */ +#define QUEUE_MSG_ATTR_GROUP (1UL << 0) +#define QUEUE_MSG_ATTR_FAMILY (1UL << 1) +#define QUEUE_MSG_ATTR_PACKETID (1UL << 2) +#define QUEUE_MSG_ATTR_HWPROTO (1UL << 3) +#define QUEUE_MSG_ATTR_HOOK (1UL << 4) +#define QUEUE_MSG_ATTR_MARK (1UL << 5) +#define QUEUE_MSG_ATTR_TIMESTAMP (1UL << 6) +#define QUEUE_MSG_ATTR_INDEV (1UL << 7) +#define QUEUE_MSG_ATTR_OUTDEV (1UL << 8) +#define QUEUE_MSG_ATTR_PHYSINDEV (1UL << 9) +#define QUEUE_MSG_ATTR_PHYSOUTDEV (1UL << 10) +#define QUEUE_MSG_ATTR_HWADDR (1UL << 11) +#define QUEUE_MSG_ATTR_PAYLOAD (1UL << 12) +#define QUEUE_MSG_ATTR_VERDICT (1UL << 13) +/** @endcond */ + +static void nfnl_queue_msg_free_data(struct nl_object *c) +{ + struct nfnl_queue_msg *msg = (struct nfnl_queue_msg *) c; + + if (msg == NULL) + return; + + free(msg->queue_msg_payload); +} + +static int nfnl_queue_msg_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct nfnl_queue_msg *dst = (struct nfnl_queue_msg *) _dst; + struct nfnl_queue_msg *src = (struct nfnl_queue_msg *) _src; + int err; + + if (src->queue_msg_payload) { + err = nfnl_queue_msg_set_payload(dst, src->queue_msg_payload, + src->queue_msg_payload_len); + if (err < 0) + goto errout; + } + + return 0; +errout: + return err; +} + +static void nfnl_queue_msg_dump(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_queue_msg *msg = (struct nfnl_queue_msg *) a; + struct nl_cache *link_cache; + char buf[64]; + + link_cache = nl_cache_mngt_require("route/link"); + + nl_new_line(p); + + if (msg->ce_mask & QUEUE_MSG_ATTR_GROUP) + nl_dump(p, "GROUP=%u ", msg->queue_msg_group); + + if (msg->ce_mask & QUEUE_MSG_ATTR_INDEV) { + if (link_cache) + nl_dump(p, "IN=%s ", + rtnl_link_i2name(link_cache, + msg->queue_msg_indev, + buf, sizeof(buf))); + else + nl_dump(p, "IN=%d ", msg->queue_msg_indev); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_PHYSINDEV) { + if (link_cache) + nl_dump(p, "PHYSIN=%s ", + rtnl_link_i2name(link_cache, + msg->queue_msg_physindev, + buf, sizeof(buf))); + else + nl_dump(p, "IN=%d ", msg->queue_msg_physindev); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_OUTDEV) { + if (link_cache) + nl_dump(p, "OUT=%s ", + rtnl_link_i2name(link_cache, + msg->queue_msg_outdev, + buf, sizeof(buf))); + else + nl_dump(p, "OUT=%d ", msg->queue_msg_outdev); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_PHYSOUTDEV) { + if (link_cache) + nl_dump(p, "PHYSOUT=%s ", + rtnl_link_i2name(link_cache, + msg->queue_msg_physoutdev, + buf, sizeof(buf))); + else + nl_dump(p, "PHYSOUT=%d ", msg->queue_msg_physoutdev); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_HWADDR) { + int i; + + nl_dump(p, "MAC"); + for (i = 0; i < msg->queue_msg_hwaddr_len; i++) + nl_dump(p, "%c%02x", i?':':'=', + msg->queue_msg_hwaddr[i]); + nl_dump(p, " "); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_FAMILY) + nl_dump(p, "FAMILY=%s ", + nl_af2str(msg->queue_msg_family, buf, sizeof(buf))); + + if (msg->ce_mask & QUEUE_MSG_ATTR_HWPROTO) + nl_dump(p, "HWPROTO=%s ", + nl_ether_proto2str(ntohs(msg->queue_msg_hwproto), + buf, sizeof(buf))); + + if (msg->ce_mask & QUEUE_MSG_ATTR_HOOK) + nl_dump(p, "HOOK=%s ", + nfnl_inet_hook2str(msg->queue_msg_hook, + buf, sizeof(buf))); + + if (msg->ce_mask & QUEUE_MSG_ATTR_MARK) + nl_dump(p, "MARK=%d ", msg->queue_msg_mark); + + if (msg->ce_mask & QUEUE_MSG_ATTR_PAYLOAD) + nl_dump(p, "PAYLOADLEN=%d ", msg->queue_msg_payload_len); + + if (msg->ce_mask & QUEUE_MSG_ATTR_PACKETID) + nl_dump(p, "PACKETID=%u ", msg->queue_msg_packetid); + + if (msg->ce_mask & QUEUE_MSG_ATTR_VERDICT) + nl_dump(p, "VERDICT=%s ", + nfnl_verdict2str(msg->queue_msg_verdict, + buf, sizeof(buf))); + + nl_dump(p, "\n"); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_queue_msg *nfnl_queue_msg_alloc(void) +{ + return (struct nfnl_queue_msg *) nl_object_alloc(&queue_msg_obj_ops); +} + +void nfnl_queue_msg_get(struct nfnl_queue_msg *msg) +{ + nl_object_get((struct nl_object *) msg); +} + +void nfnl_queue_msg_put(struct nfnl_queue_msg *msg) +{ + nl_object_put((struct nl_object *) msg); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_queue_msg_set_group(struct nfnl_queue_msg *msg, uint16_t group) +{ + msg->queue_msg_group = group; + msg->ce_mask |= QUEUE_MSG_ATTR_GROUP; +} + +int nfnl_queue_msg_test_group(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_GROUP); +} + +uint16_t nfnl_queue_msg_get_group(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_group; +} + +/** +* Set the protocol family +* @arg msg NF queue message +* @arg family AF_XXX address family example: AF_INET, AF_UNIX, etc +*/ +void nfnl_queue_msg_set_family(struct nfnl_queue_msg *msg, uint8_t family) +{ + msg->queue_msg_family = family; + msg->ce_mask |= QUEUE_MSG_ATTR_FAMILY; +} + +int nfnl_queue_msg_test_family(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_FAMILY); +} + +uint8_t nfnl_queue_msg_get_family(const struct nfnl_queue_msg *msg) +{ + if (msg->ce_mask & QUEUE_MSG_ATTR_FAMILY) + return msg->queue_msg_family; + else + return AF_UNSPEC; +} + +void nfnl_queue_msg_set_packetid(struct nfnl_queue_msg *msg, uint32_t packetid) +{ + msg->queue_msg_packetid = packetid; + msg->ce_mask |= QUEUE_MSG_ATTR_PACKETID; +} + +int nfnl_queue_msg_test_packetid(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_PACKETID); +} + +uint32_t nfnl_queue_msg_get_packetid(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_packetid; +} + +void nfnl_queue_msg_set_hwproto(struct nfnl_queue_msg *msg, uint16_t hwproto) +{ + msg->queue_msg_hwproto = hwproto; + msg->ce_mask |= QUEUE_MSG_ATTR_HWPROTO; +} + +int nfnl_queue_msg_test_hwproto(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_HWPROTO); +} + +uint16_t nfnl_queue_msg_get_hwproto(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_hwproto; +} + +void nfnl_queue_msg_set_hook(struct nfnl_queue_msg *msg, uint8_t hook) +{ + msg->queue_msg_hook = hook; + msg->ce_mask |= QUEUE_MSG_ATTR_HOOK; +} + +int nfnl_queue_msg_test_hook(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_HOOK); +} + +uint8_t nfnl_queue_msg_get_hook(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_hook; +} + +void nfnl_queue_msg_set_mark(struct nfnl_queue_msg *msg, uint32_t mark) +{ + msg->queue_msg_mark = mark; + msg->ce_mask |= QUEUE_MSG_ATTR_MARK; +} + +int nfnl_queue_msg_test_mark(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_MARK); +} + +uint32_t nfnl_queue_msg_get_mark(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_mark; +} + +void nfnl_queue_msg_set_timestamp(struct nfnl_queue_msg *msg, + struct timeval *tv) +{ + msg->queue_msg_timestamp.tv_sec = tv->tv_sec; + msg->queue_msg_timestamp.tv_usec = tv->tv_usec; + msg->ce_mask |= QUEUE_MSG_ATTR_TIMESTAMP; +} + +int nfnl_queue_msg_test_timestamp(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_TIMESTAMP); +} + +const struct timeval *nfnl_queue_msg_get_timestamp(const struct nfnl_queue_msg *msg) +{ + if (!(msg->ce_mask & QUEUE_MSG_ATTR_TIMESTAMP)) + return NULL; + return &msg->queue_msg_timestamp; +} + +void nfnl_queue_msg_set_indev(struct nfnl_queue_msg *msg, uint32_t indev) +{ + msg->queue_msg_indev = indev; + msg->ce_mask |= QUEUE_MSG_ATTR_INDEV; +} + +int nfnl_queue_msg_test_indev(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_INDEV); +} + +uint32_t nfnl_queue_msg_get_indev(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_indev; +} + +void nfnl_queue_msg_set_outdev(struct nfnl_queue_msg *msg, uint32_t outdev) +{ + msg->queue_msg_outdev = outdev; + msg->ce_mask |= QUEUE_MSG_ATTR_OUTDEV; +} + +int nfnl_queue_msg_test_outdev(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_OUTDEV); +} + +uint32_t nfnl_queue_msg_get_outdev(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_outdev; +} + +void nfnl_queue_msg_set_physindev(struct nfnl_queue_msg *msg, + uint32_t physindev) +{ + msg->queue_msg_physindev = physindev; + msg->ce_mask |= QUEUE_MSG_ATTR_PHYSINDEV; +} + +int nfnl_queue_msg_test_physindev(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_PHYSINDEV); +} + +uint32_t nfnl_queue_msg_get_physindev(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_physindev; +} + +void nfnl_queue_msg_set_physoutdev(struct nfnl_queue_msg *msg, + uint32_t physoutdev) +{ + msg->queue_msg_physoutdev = physoutdev; + msg->ce_mask |= QUEUE_MSG_ATTR_PHYSOUTDEV; +} + +int nfnl_queue_msg_test_physoutdev(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_PHYSOUTDEV); +} + +uint32_t nfnl_queue_msg_get_physoutdev(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_physoutdev; +} + +void nfnl_queue_msg_set_hwaddr(struct nfnl_queue_msg *msg, uint8_t *hwaddr, + int len) +{ + if (len > sizeof(msg->queue_msg_hwaddr)) + len = sizeof(msg->queue_msg_hwaddr); + + msg->queue_msg_hwaddr_len = len; + memcpy(msg->queue_msg_hwaddr, hwaddr, len); + msg->ce_mask |= QUEUE_MSG_ATTR_HWADDR; +} + +int nfnl_queue_msg_test_hwaddr(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_HWADDR); +} + +const uint8_t *nfnl_queue_msg_get_hwaddr(const struct nfnl_queue_msg *msg, + int *len) +{ + if (!(msg->ce_mask & QUEUE_MSG_ATTR_HWADDR)) { + *len = 0; + return NULL; + } + + *len = msg->queue_msg_hwaddr_len; + return msg->queue_msg_hwaddr; +} + +int nfnl_queue_msg_set_payload(struct nfnl_queue_msg *msg, uint8_t *payload, + int len) +{ + free(msg->queue_msg_payload); + msg->queue_msg_payload = malloc(len); + if (!msg->queue_msg_payload) + return -NLE_NOMEM; + + memcpy(msg->queue_msg_payload, payload, len); + msg->queue_msg_payload_len = len; + msg->ce_mask |= QUEUE_MSG_ATTR_PAYLOAD; + return 0; +} + +int nfnl_queue_msg_test_payload(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_PAYLOAD); +} + +const void *nfnl_queue_msg_get_payload(const struct nfnl_queue_msg *msg, int *len) +{ + if (!(msg->ce_mask & QUEUE_MSG_ATTR_PAYLOAD)) { + *len = 0; + return NULL; + } + + *len = msg->queue_msg_payload_len; + return msg->queue_msg_payload; +} + +/** +* Return the number of items matching a filter in the cache +* @arg msg queue msg +* @arg verdict NF_DROP, NF_ACCEPT, NF_REPEAT, etc +*/ +void nfnl_queue_msg_set_verdict(struct nfnl_queue_msg *msg, + unsigned int verdict) +{ + msg->queue_msg_verdict = verdict; + msg->ce_mask |= QUEUE_MSG_ATTR_VERDICT; +} + +int nfnl_queue_msg_test_verdict(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_VERDICT); +} + +unsigned int nfnl_queue_msg_get_verdict(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_verdict; +} + +static struct trans_tbl nfnl_queue_msg_attrs[] = { + __ADD(QUEUE_MSG_ATTR_GROUP, group) + __ADD(QUEUE_MSG_ATTR_FAMILY, family) + __ADD(QUEUE_MSG_ATTR_PACKETID, packetid) + __ADD(QUEUE_MSG_ATTR_HWPROTO, hwproto) + __ADD(QUEUE_MSG_ATTR_HOOK, hook) + __ADD(QUEUE_MSG_ATTR_MARK, mark) + __ADD(QUEUE_MSG_ATTR_TIMESTAMP, timestamp) + __ADD(QUEUE_MSG_ATTR_INDEV, indev) + __ADD(QUEUE_MSG_ATTR_OUTDEV, outdev) + __ADD(QUEUE_MSG_ATTR_PHYSINDEV, physindev) + __ADD(QUEUE_MSG_ATTR_PHYSOUTDEV, physoutdev) + __ADD(QUEUE_MSG_ATTR_HWADDR, hwaddr) + __ADD(QUEUE_MSG_ATTR_PAYLOAD, payload) + __ADD(QUEUE_MSG_ATTR_VERDICT, verdict) +}; + +static char *nfnl_queue_msg_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, nfnl_queue_msg_attrs, + ARRAY_SIZE(nfnl_queue_msg_attrs)); +} + +/** @} */ + +struct nl_object_ops queue_msg_obj_ops = { + .oo_name = "netfilter/queuemsg", + .oo_size = sizeof(struct nfnl_queue_msg), + .oo_free_data = nfnl_queue_msg_free_data, + .oo_clone = nfnl_queue_msg_clone, + .oo_dump = { + [NL_DUMP_LINE] = nfnl_queue_msg_dump, + [NL_DUMP_DETAILS] = nfnl_queue_msg_dump, + [NL_DUMP_STATS] = nfnl_queue_msg_dump, + }, + .oo_attrs2str = nfnl_queue_msg_attrs2str, +}; + +/** @} */ diff --git a/lib/netfilter/queue_obj.c b/lib/netfilter/queue_obj.c new file mode 100644 index 0000000..ee03836 --- /dev/null +++ b/lib/netfilter/queue_obj.c @@ -0,0 +1,215 @@ +/* + * lib/netfilter/queue_obj.c Netfilter Queue + * + * 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) 2007, 2008 Patrick McHardy <kaber@trash.net> + */ + +/** + * @ingroup nfnl + * @defgroup queue Queue + * @brief + * @{ + */ + +#include <netlink-local.h> +#include <netlink/netfilter/nfnl.h> +#include <netlink/netfilter/queue.h> + +/** @cond SKIP */ +#define QUEUE_ATTR_GROUP (1UL << 0) +#define QUEUE_ATTR_MAXLEN (1UL << 1) +#define QUEUE_ATTR_COPY_MODE (1UL << 2) +#define QUEUE_ATTR_COPY_RANGE (1UL << 3) +/** @endcond */ + + +static void nfnl_queue_dump(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_queue *queue = (struct nfnl_queue *) a; + char buf[64]; + + nl_new_line(p); + + if (queue->ce_mask & QUEUE_ATTR_GROUP) + nl_dump(p, "group=%u ", queue->queue_group); + + if (queue->ce_mask & QUEUE_ATTR_MAXLEN) + nl_dump(p, "maxlen=%u ", queue->queue_maxlen); + + if (queue->ce_mask & QUEUE_ATTR_COPY_MODE) + nl_dump(p, "copy_mode=%s ", + nfnl_queue_copy_mode2str(queue->queue_copy_mode, + buf, sizeof(buf))); + + if (queue->ce_mask & QUEUE_ATTR_COPY_RANGE) + nl_dump(p, "copy_range=%u ", queue->queue_copy_range); + + nl_dump(p, "\n"); +} + +static struct trans_tbl copy_modes[] = { + __ADD(NFNL_QUEUE_COPY_NONE, none) + __ADD(NFNL_QUEUE_COPY_META, meta) + __ADD(NFNL_QUEUE_COPY_PACKET, packet) +}; + +char *nfnl_queue_copy_mode2str(enum nfnl_queue_copy_mode copy_mode, char *buf, + size_t len) +{ + return __type2str(copy_mode, buf, len, copy_modes, + ARRAY_SIZE(copy_modes)); +} + +enum nfnl_queue_copy_mode nfnl_queue_str2copy_mode(const char *name) +{ + return __str2type(name, copy_modes, ARRAY_SIZE(copy_modes)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_queue *nfnl_queue_alloc(void) +{ + return (struct nfnl_queue *) nl_object_alloc(&queue_obj_ops); +} + +void nfnl_queue_get(struct nfnl_queue *queue) +{ + nl_object_get((struct nl_object *) queue); +} + +void nfnl_queue_put(struct nfnl_queue *queue) +{ + nl_object_put((struct nl_object *) queue); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_queue_set_group(struct nfnl_queue *queue, uint16_t group) +{ + queue->queue_group = group; + queue->ce_mask |= QUEUE_ATTR_GROUP; +} + +int nfnl_queue_test_group(const struct nfnl_queue *queue) +{ + return !!(queue->ce_mask & QUEUE_ATTR_GROUP); +} + +uint16_t nfnl_queue_get_group(const struct nfnl_queue *queue) +{ + return queue->queue_group; +} + +void nfnl_queue_set_maxlen(struct nfnl_queue *queue, uint32_t maxlen) +{ + queue->queue_maxlen = maxlen; + queue->ce_mask |= QUEUE_ATTR_MAXLEN; +} + +int nfnl_queue_test_maxlen(const struct nfnl_queue *queue) +{ + return !!(queue->ce_mask & QUEUE_ATTR_MAXLEN); +} + +uint32_t nfnl_queue_get_maxlen(const struct nfnl_queue *queue) +{ + return queue->queue_maxlen; +} + +void nfnl_queue_set_copy_mode(struct nfnl_queue *queue, enum nfnl_queue_copy_mode mode) +{ + queue->queue_copy_mode = mode; + queue->ce_mask |= QUEUE_ATTR_COPY_MODE; +} + +int nfnl_queue_test_copy_mode(const struct nfnl_queue *queue) +{ + return !!(queue->ce_mask & QUEUE_ATTR_COPY_MODE); +} + +enum nfnl_queue_copy_mode nfnl_queue_get_copy_mode(const struct nfnl_queue *queue) +{ + return queue->queue_copy_mode; +} + +void nfnl_queue_set_copy_range(struct nfnl_queue *queue, uint32_t copy_range) +{ + queue->queue_copy_range = copy_range; + queue->ce_mask |= QUEUE_ATTR_COPY_RANGE; +} + +int nfnl_queue_test_copy_range(const struct nfnl_queue *queue) +{ + return !!(queue->ce_mask & QUEUE_ATTR_COPY_RANGE); +} + +uint32_t nfnl_queue_get_copy_range(const struct nfnl_queue *queue) +{ + return queue->queue_copy_range; +} + +static int nfnl_queue_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) +{ + struct nfnl_queue *a = (struct nfnl_queue *) _a; + struct nfnl_queue *b = (struct nfnl_queue *) _b; + int diff = 0; + +#define NFNL_QUEUE_DIFF(ATTR, EXPR) \ + ATTR_DIFF(attrs, QUEUE_ATTR_##ATTR, a, b, EXPR) +#define NFNL_QUEUE_DIFF_VAL(ATTR, FIELD) \ + NFNL_QUEUE_DIFF(ATTR, a->FIELD != b->FIELD) + + diff |= NFNL_QUEUE_DIFF_VAL(GROUP, queue_group); + diff |= NFNL_QUEUE_DIFF_VAL(MAXLEN, queue_maxlen); + diff |= NFNL_QUEUE_DIFF_VAL(COPY_MODE, queue_copy_mode); + diff |= NFNL_QUEUE_DIFF_VAL(COPY_RANGE, queue_copy_range); + +#undef NFNL_QUEUE_DIFF +#undef NFNL_QUEUE_DIFF_VAL + + return diff; +} + +static struct trans_tbl nfnl_queue_attrs[] = { + __ADD(QUEUE_ATTR_GROUP, group) + __ADD(QUEUE_ATTR_MAXLEN, maxlen) + __ADD(QUEUE_ATTR_COPY_MODE, copy_mode) + __ADD(QUEUE_ATTR_COPY_RANGE, copy_range) +}; + +static char *nfnl_queue_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, nfnl_queue_attrs, + ARRAY_SIZE(nfnl_queue_attrs)); +} + +/** @} */ + +struct nl_object_ops queue_obj_ops = { + .oo_name = "netfilter/queue", + .oo_size = sizeof(struct nfnl_queue), + .oo_dump = { + [NL_DUMP_LINE] = nfnl_queue_dump, + [NL_DUMP_DETAILS] = nfnl_queue_dump, + [NL_DUMP_STATS] = nfnl_queue_dump, + }, + .oo_compare = nfnl_queue_compare, + .oo_attrs2str = nfnl_queue_attrs2str, + .oo_id_attrs = QUEUE_ATTR_GROUP, +}; + +/** @} */ @@ -6,102 +6,17 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** - * @defgroup nl Core Netlink API - * @brief - * - * @par Receiving Semantics - * @code - * nl_recvmsgs_default(socket) - * | - * | cb = nl_socket_get_cb(socket) - * v - * nl_recvmsgs(socket, cb) - * | [Application provides nl_recvmsgs() replacement] - * |- - - - - - - - - - - - - - - v - * | cb->cb_recvmsgs_ow() - * | - * | [Application provides nl_recv() replacement] - * +-------------->|- - - - - - - - - - - - - - - v - * | nl_recv() cb->cb_recv_ow() - * | +----------->|<- - - - - - - - - - - - - - -+ - * | | v - * | | Parse Message - * | | |- - - - - - - - - - - - - - - v - * | | | NL_CB_MSG_IN() - * | | |<- - - - - - - - - - - - - - -+ - * | | | - * | | |- - - - - - - - - - - - - - - v - * | | Sequence Check NL_CB_SEQ_CHECK() - * | | |<- - - - - - - - - - - - - - -+ - * | | | - * | | |- - - - - - - - - - - - - - - v [ NLM_F_ACK is set ] - * | | | NL_CB_SEND_ACK() - * | | |<- - - - - - - - - - - - - - -+ - * | | | - * | | +-----+------+--------------+----------------+--------------+ - * | | v v v v v - * | | Valid Message ACK NOOP Message End of Multipart Error Message - * | | | | | | | - * | | v v v v v - * | |NL_CB_VALID() NL_CB_ACK() NL_CB_SKIPPED() NL_CB_FINISH() cb->cb_err() - * | | | | | | | - * | | +------------+--------------+----------------+ v - * | | | (FAILURE) - * | | | [Callback returned NL_SKIP] - * | | [More messages to be parsed] |<----------- - * | +----------------------------------| - * | | - * | [Multipart message] | - * +-------------------------------------| [Callback returned NL_STOP] - * |<----------- - * v - * (SUCCESS) - * - * At any time: - * Message Format Error - * |- - - - - - - - - - - - v - * v NL_CB_INVALID() - * (FAILURE) - * - * Message Overrun (Kernel Lost Data) - * |- - - - - - - - - - - - v - * v NL_CB_OVERRUN() - * (FAILURE) - * - * Callback returned negative error code - * (FAILURE) - * @endcode - * - * @par Sending Semantics - * @code - * nl_send_auto_complete() - * | - * | Automatically fill in PID and/or sequence number - * | - * | [Application provides nl_send() replacement] - * |- - - - - - - - - - - - - - - - - - - - v - * v cb->cb_send_ow() - * nl_send() - * | Add destination address and credentials - * v - * nl_sendmsg() - * | Set source address - * | - * |- - - - - - - - - - - - - - - - - - - - v - * | NL_CB_MSG_OUT() - * |<- - - - - - - - - - - - - - - - - - - -+ - * v - * sendmsg() - * @endcode + * @defgroup core Core * + * @details * @par 1) Connecting the socket * @code * // Bind and connect the socket to a protocol, NETLINK_ROUTE in this example. - * nl_connect(handle, NETLINK_ROUTE); + * nl_connect(sk, NETLINK_ROUTE); * @endcode * * @par 2) Sending data @@ -110,29 +25,29 @@ * // a piece of data to the other netlink peer. This method is not * // recommended. * const char buf[] = { 0x01, 0x02, 0x03, 0x04 }; - * nl_sendto(handle, buf, sizeof(buf)); + * nl_sendto(sk, buf, sizeof(buf)); * * // A more comfortable interface is nl_send() taking a pointer to * // a netlink message. * struct nl_msg *msg = my_msg_builder(); - * nl_send(handle, nlmsg_hdr(msg)); + * nl_send(sk, nlmsg_hdr(msg)); * * // nl_sendmsg() provides additional control over the sendmsg() message * // header in order to allow more specific addressing of multiple peers etc. * struct msghdr hdr = { ... }; - * nl_sendmsg(handle, nlmsg_hdr(msg), &hdr); + * nl_sendmsg(sk, nlmsg_hdr(msg), &hdr); * * // You're probably too lazy to fill out the netlink pid, sequence number * // and message flags all the time. nl_send_auto_complete() automatically * // extends your message header as needed with an appropriate sequence - * // number, the netlink pid stored in the netlink handle and the message - * // flags NLM_F_REQUEST and NLM_F_ACK - * nl_send_auto_complete(handle, nlmsg_hdr(msg)); + * // number, the netlink pid stored in the netlink socket and the message + * // flags NLM_F_REQUEST and NLM_F_ACK (if not disabled in the socket) + * nl_send_auto_complete(sk, nlmsg_hdr(msg)); * * // Simple protocols don't require the complex message construction interface * // and may favour nl_send_simple() to easly send a bunch of payload * // encapsulated in a netlink message header. - * nl_send_simple(handle, MY_MSG_TYPE, 0, buf, sizeof(buf)); + * nl_send_simple(sk, MY_MSG_TYPE, 0, buf, sizeof(buf)); * @endcode * * @par 3) Receiving data @@ -141,26 +56,26 @@ * // content and gives back the pointer to you. * struct sockaddr_nl peer; * unsigned char *msg; - * nl_recv(handle, &peer, &msg); + * nl_recv(sk, &peer, &msg); * * // nl_recvmsgs() receives a bunch of messages until the callback system * // orders it to state, usually after receving a compolete multi part * // message series. - * nl_recvmsgs(handle, my_callback_configuration); + * nl_recvmsgs(sk, my_callback_configuration); * * // nl_recvmsgs_default() acts just like nl_recvmsg() but uses the callback - * // configuration stored in the handle. - * nl_recvmsgs_default(handle); + * // configuration stored in the socket. + * nl_recvmsgs_default(sk); * * // In case you want to wait for the ACK to be recieved that you requested * // with your latest message, you can call nl_wait_for_ack() - * nl_wait_for_ack(handle); + * nl_wait_for_ack(sk); * @endcode * * @par 4) Closing * @code * // Close the socket first to release kernel memory - * nl_close(handle); + * nl_close(sk); * @endcode * * @{ @@ -180,7 +95,7 @@ /** * Create and connect netlink socket. - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg protocol Netlink protocol to use. * * Creates a netlink socket using the specified protocol, binds the socket @@ -188,70 +103,70 @@ * * @return 0 on success or a negative error code. */ -int nl_connect(struct nl_handle *handle, int protocol) +int nl_connect(struct nl_sock *sk, int protocol) { int err; socklen_t addrlen; - handle->h_fd = socket(AF_NETLINK, SOCK_RAW, protocol); - if (handle->h_fd < 0) { - err = nl_error(1, "socket(AF_NETLINK, ...) failed"); + sk->s_fd = socket(AF_NETLINK, SOCK_RAW, protocol); + if (sk->s_fd < 0) { + err = -nl_syserr2nlerr(errno); goto errout; } - if (!(handle->h_flags & NL_SOCK_BUFSIZE_SET)) { - err = nl_set_buffer_size(handle, 0, 0); + if (!(sk->s_flags & NL_SOCK_BUFSIZE_SET)) { + err = nl_socket_set_buffer_size(sk, 0, 0); if (err < 0) goto errout; } - err = bind(handle->h_fd, (struct sockaddr*) &handle->h_local, - sizeof(handle->h_local)); + err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local, + sizeof(sk->s_local)); if (err < 0) { - err = nl_error(1, "bind() failed"); + err = -nl_syserr2nlerr(errno); goto errout; } - addrlen = sizeof(handle->h_local); - err = getsockname(handle->h_fd, (struct sockaddr *) &handle->h_local, + addrlen = sizeof(sk->s_local); + err = getsockname(sk->s_fd, (struct sockaddr *) &sk->s_local, &addrlen); if (err < 0) { - err = nl_error(1, "getsockname failed"); + err = -nl_syserr2nlerr(errno); goto errout; } - if (addrlen != sizeof(handle->h_local)) { - err = nl_error(EADDRNOTAVAIL, "Invalid address length"); + if (addrlen != sizeof(sk->s_local)) { + err = -NLE_NOADDR; goto errout; } - if (handle->h_local.nl_family != AF_NETLINK) { - err = nl_error(EPFNOSUPPORT, "Address format not supported"); + if (sk->s_local.nl_family != AF_NETLINK) { + err = -NLE_AF_NOSUPPORT; goto errout; } - handle->h_proto = protocol; + sk->s_proto = protocol; return 0; errout: - close(handle->h_fd); - handle->h_fd = -1; + close(sk->s_fd); + sk->s_fd = -1; return err; } /** * Close/Disconnect netlink socket. - * @arg handle Netlink handle + * @arg sk Netlink socket. */ -void nl_close(struct nl_handle *handle) +void nl_close(struct nl_sock *sk) { - if (handle->h_fd >= 0) { - close(handle->h_fd); - handle->h_fd = -1; + if (sk->s_fd >= 0) { + close(sk->s_fd); + sk->s_fd = -1; } - handle->h_proto = 0; + sk->s_proto = 0; } /** @} */ @@ -263,77 +178,73 @@ void nl_close(struct nl_handle *handle) /** * Send raw data over netlink socket. - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg buf Data buffer. * @arg size Size of data buffer. * @return Number of characters written on success or a negative error code. */ -int nl_sendto(struct nl_handle *handle, void *buf, size_t size) +int nl_sendto(struct nl_sock *sk, void *buf, size_t size) { int ret; - ret = sendto(handle->h_fd, buf, size, 0, (struct sockaddr *) - &handle->h_peer, sizeof(handle->h_peer)); + ret = sendto(sk->s_fd, buf, size, 0, (struct sockaddr *) + &sk->s_peer, sizeof(sk->s_peer)); if (ret < 0) - return nl_errno(errno); + return -nl_syserr2nlerr(errno); return ret; } /** * Send netlink message with control over sendmsg() message header. - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg msg Netlink message to be sent. * @arg hdr Sendmsg() message header. * @return Number of characters sent on sucess or a negative error code. */ -int nl_sendmsg(struct nl_handle *handle, struct nl_msg *msg, struct msghdr *hdr) +int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) { struct nl_cb *cb; int ret; - struct iovec iov = { - .iov_base = (void *) nlmsg_hdr(msg), - .iov_len = nlmsg_hdr(msg)->nlmsg_len, - }; - - hdr->msg_iov = &iov; - hdr->msg_iovlen = 1; - - nlmsg_set_src(msg, &handle->h_local); + nlmsg_set_src(msg, &sk->s_local); - cb = handle->h_cb; + cb = sk->s_cb; if (cb->cb_set[NL_CB_MSG_OUT]) if (nl_cb_call(cb, NL_CB_MSG_OUT, msg) != NL_OK) return 0; - ret = sendmsg(handle->h_fd, hdr, 0); + ret = sendmsg(sk->s_fd, hdr, 0); if (ret < 0) - return nl_errno(errno); + return -nl_syserr2nlerr(errno); + NL_DBG(4, "sent %d bytes\n", ret); return ret; } /** * Send netlink message. - * @arg handle Netlink handle + * @arg sk Netlink socket. * @arg msg Netlink message to be sent. + * @arg iov iovec to be sent. + * @arg iovlen number of struct iovec to be sent. * @see nl_sendmsg() * @return Number of characters sent on success or a negative error code. */ -int nl_send(struct nl_handle *handle, struct nl_msg *msg) +int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen) { struct sockaddr_nl *dst; struct ucred *creds; - struct msghdr hdr = { - .msg_name = (void *) &handle->h_peer, + .msg_name = (void *) &sk->s_peer, .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = iov, + .msg_iovlen = iovlen, }; /* Overwrite destination if specified in the message itself, defaults - * to the peer address of the handle. + * to the peer address of the socket. */ dst = nlmsg_get_dst(msg); if (dst->nl_family == AF_NETLINK) @@ -355,12 +266,51 @@ int nl_send(struct nl_handle *handle, struct nl_msg *msg) memcpy(CMSG_DATA(cmsg), creds, sizeof(struct ucred)); } - return nl_sendmsg(handle, msg, &hdr); + return nl_sendmsg(sk, msg, &hdr); +} + + + +/** +* Send netlink message. +* @arg sk Netlink socket. +* @arg msg Netlink message to be sent. +* @see nl_sendmsg() +* @return Number of characters sent on success or a negative error code. +*/ +int nl_send(struct nl_sock *sk, struct nl_msg *msg) +{ + struct iovec iov = { + .iov_base = (void *) nlmsg_hdr(msg), + .iov_len = nlmsg_hdr(msg)->nlmsg_len, + }; + + return nl_send_iovec(sk, msg, &iov, 1); +} + +void nl_auto_complete(struct nl_sock *sk, struct nl_msg *msg) +{ + struct nlmsghdr *nlh; + + nlh = nlmsg_hdr(msg); + if (nlh->nlmsg_pid == 0) + nlh->nlmsg_pid = sk->s_local.nl_pid; + + if (nlh->nlmsg_seq == 0) + nlh->nlmsg_seq = sk->s_seq_next++; + + if (msg->nm_protocol == -1) + msg->nm_protocol = sk->s_proto; + + nlh->nlmsg_flags |= NLM_F_REQUEST; + + if (!(sk->s_flags & NL_NO_AUTO_ACK)) + nlh->nlmsg_flags |= NLM_F_ACK; } /** * Send netlink message and check & extend header values as needed. - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg msg Netlink message to be sent. * * Checks the netlink message \c nlh for completness and extends it @@ -370,32 +320,21 @@ int nl_send(struct nl_handle *handle, struct nl_msg *msg) * @see nl_send() * @return Number of characters sent or a negative error code. */ -int nl_send_auto_complete(struct nl_handle *handle, struct nl_msg *msg) +int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg) { - struct nlmsghdr *nlh; - struct nl_cb *cb = handle->h_cb; - - nlh = nlmsg_hdr(msg); - if (nlh->nlmsg_pid == 0) - nlh->nlmsg_pid = handle->h_local.nl_pid; - - if (nlh->nlmsg_seq == 0) - nlh->nlmsg_seq = handle->h_seq_next++; + struct nl_cb *cb = sk->s_cb; - if (msg->nm_protocol == -1) - msg->nm_protocol = handle->h_proto; - - nlh->nlmsg_flags |= (NLM_F_REQUEST | NLM_F_ACK); + nl_auto_complete(sk, msg); if (cb->cb_send_ow) - return cb->cb_send_ow(handle, msg); + return cb->cb_send_ow(sk, msg); else - return nl_send(handle, msg); + return nl_send(sk, msg); } /** * Send simple netlink message using nl_send_auto_complete() - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg type Netlink message type. * @arg flags Netlink message flags. * @arg buf Data buffer. @@ -407,7 +346,7 @@ int nl_send_auto_complete(struct nl_handle *handle, struct nl_msg *msg) * @see nl_send_auto_complete() * @return Number of characters sent on success or a negative error code. */ -int nl_send_simple(struct nl_handle *handle, int type, int flags, void *buf, +int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf, size_t size) { int err; @@ -415,7 +354,7 @@ int nl_send_simple(struct nl_handle *handle, int type, int flags, void *buf, msg = nlmsg_alloc_simple(type, flags); if (!msg) - return nl_errno(ENOMEM); + return -NLE_NOMEM; if (buf && size) { err = nlmsg_append(msg, buf, size, NLMSG_ALIGNTO); @@ -424,7 +363,7 @@ int nl_send_simple(struct nl_handle *handle, int type, int flags, void *buf, } - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); errout: nlmsg_free(msg); @@ -440,7 +379,7 @@ errout: /** * Receive data from netlink socket - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg nla Destination pointer for peer's netlink address. * @arg buf Destination pointer for message content. * @arg creds Destination pointer for credentials. @@ -457,7 +396,7 @@ errout: * * @return Number of octets read, 0 on EOF or a negative error code. */ -int nl_recv(struct nl_handle *handle, struct sockaddr_nl *nla, +int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, unsigned char **buf, struct ucred **creds) { int n; @@ -475,22 +414,22 @@ int nl_recv(struct nl_handle *handle, struct sockaddr_nl *nla, }; struct cmsghdr *cmsg; - if (handle->h_flags & NL_MSG_PEEK) + if (sk->s_flags & NL_MSG_PEEK) flags |= MSG_PEEK; if (page_size == 0) page_size = getpagesize(); iov.iov_len = page_size; - iov.iov_base = *buf = calloc(1, iov.iov_len); + iov.iov_base = *buf = malloc(iov.iov_len); - if (handle->h_flags & NL_SOCK_PASSCRED) { + if (sk->s_flags & NL_SOCK_PASSCRED) { msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); msg.msg_control = calloc(1, msg.msg_controllen); } retry: - n = recvmsg(handle->h_fd, &msg, flags); + n = recvmsg(sk->s_fd, &msg, flags); if (!n) goto abort; else if (n < 0) { @@ -503,7 +442,7 @@ retry: } else { free(msg.msg_control); free(*buf); - return nl_error(errno, "recvmsg failed"); + return -nl_syserr2nlerr(errno); } } @@ -527,7 +466,7 @@ retry: if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { free(msg.msg_control); free(*buf); - return nl_error(EADDRNOTAVAIL, "socket address size mismatch"); + return -NLE_NOADDR; } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { @@ -564,7 +503,7 @@ do { \ } \ } while (0) -static int recvmsgs(struct nl_handle *handle, struct nl_cb *cb) +static int recvmsgs(struct nl_sock *sk, struct nl_cb *cb) { int n, err = 0, multipart = 0; unsigned char *buf = NULL; @@ -574,30 +513,29 @@ static int recvmsgs(struct nl_handle *handle, struct nl_cb *cb) struct ucred *creds = NULL; continue_reading: - NL_DBG(3, "Attempting to read from %p\n", handle); + NL_DBG(3, "Attempting to read from %p\n", sk); if (cb->cb_recv_ow) - n = cb->cb_recv_ow(handle, &nla, &buf, &creds); + n = cb->cb_recv_ow(sk, &nla, &buf, &creds); else - n = nl_recv(handle, &nla, &buf, &creds); + n = nl_recv(sk, &nla, &buf, &creds); if (n <= 0) return n; - NL_DBG(3, "recvmsgs(%p): Read %d bytes\n", handle, n); + NL_DBG(3, "recvmsgs(%p): Read %d bytes\n", sk, n); hdr = (struct nlmsghdr *) buf; while (nlmsg_ok(hdr, n)) { - NL_DBG(3, "recgmsgs(%p): Processing valid message...\n", - handle); + NL_DBG(3, "recgmsgs(%p): Processing valid message...\n", sk); nlmsg_free(msg); msg = nlmsg_convert(hdr); if (!msg) { - err = nl_errno(ENOMEM); + err = -NLE_NOMEM; goto out; } - nlmsg_set_proto(msg, handle->h_proto); + nlmsg_set_proto(msg, sk->s_proto); nlmsg_set_src(msg, &nla); if (creds) nlmsg_set_creds(msg, creds); @@ -612,12 +550,11 @@ continue_reading: * enforcing strict ordering */ if (cb->cb_set[NL_CB_SEQ_CHECK]) NL_CB_CALL(cb, NL_CB_SEQ_CHECK, msg); - else if (hdr->nlmsg_seq != handle->h_seq_expect) { + else if (hdr->nlmsg_seq != sk->s_seq_expect) { if (cb->cb_set[NL_CB_INVALID]) NL_CB_CALL(cb, NL_CB_INVALID, msg); else { - err = nl_error(EINVAL, - "Sequence number mismatch"); + err = -NLE_SEQ_MISMATCH; goto out; } } @@ -628,10 +565,10 @@ continue_reading: hdr->nlmsg_type == NLMSG_OVERRUN) { /* We can't check for !NLM_F_MULTI since some netlink * users in the kernel are broken. */ - handle->h_seq_expect++; + sk->s_seq_expect++; NL_DBG(3, "recvmsgs(%p): Increased expected " \ "sequence number to %d\n", - handle, handle->h_seq_expect); + sk, sk->s_seq_expect); } if (hdr->nlmsg_flags & NLM_F_MULTI) @@ -674,7 +611,7 @@ continue_reading: if (cb->cb_set[NL_CB_OVERRUN]) NL_CB_CALL(cb, NL_CB_OVERRUN, msg); else { - err = nl_error(EOVERFLOW, "Overrun"); + err = -NLE_MSG_OVERFLOW; goto out; } } @@ -691,8 +628,7 @@ continue_reading: if (cb->cb_set[NL_CB_INVALID]) NL_CB_CALL(cb, NL_CB_INVALID, msg); else { - err = nl_error(EINVAL, - "Truncated error message"); + err = -NLE_MSG_TRUNC; goto out; } } else if (e->error) { @@ -705,13 +641,11 @@ continue_reading: else if (err == NL_SKIP) goto skip; else if (err == NL_STOP) { - err = nl_error(-e->error, - "Netlink Error"); + err = -nl_syserr2nlerr(e->error); goto out; } } else { - err = nl_error(-e->error, - "Netlink Error"); + err = -nl_syserr2nlerr(e->error); goto out; } } else if (cb->cb_set[NL_CB_ACK]) @@ -751,7 +685,7 @@ out: /** * Receive a set of messages from a netlink socket. - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg cb set of callbacks to control behaviour. * * Repeatedly calls nl_recv() or the respective replacement if provided @@ -764,23 +698,23 @@ out: * * @return 0 on success or a negative error code from nl_recv(). */ -int nl_recvmsgs(struct nl_handle *handle, struct nl_cb *cb) +int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb) { if (cb->cb_recvmsgs_ow) - return cb->cb_recvmsgs_ow(handle, cb); + return cb->cb_recvmsgs_ow(sk, cb); else - return recvmsgs(handle, cb); + return recvmsgs(sk, cb); } /** - * Receive a set of message from a netlink socket using handlers in nl_handle. - * @arg handle netlink handle + * Receive a set of message from a netlink socket using handlers in nl_sock. + * @arg sk Netlink socket. * - * Calls nl_recvmsgs() with the handlers configured in the netlink handle. + * Calls nl_recvmsgs() with the handlers configured in the netlink socket. */ -int nl_recvmsgs_default(struct nl_handle *handle) +int nl_recvmsgs_default(struct nl_sock *sk) { - return nl_recvmsgs(handle, handle->h_cb); + return nl_recvmsgs(sk, sk->s_cb); } @@ -791,23 +725,23 @@ static int ack_wait_handler(struct nl_msg *msg, void *arg) /** * Wait for ACK. - * @arg handle netlink handle + * @arg sk Netlink socket. * @pre The netlink socket must be in blocking state. * * Waits until an ACK is received for the latest not yet acknowledged * netlink message. */ -int nl_wait_for_ack(struct nl_handle *handle) +int nl_wait_for_ack(struct nl_sock *sk) { int err; struct nl_cb *cb; - cb = nl_cb_clone(handle->h_cb); + cb = nl_cb_clone(sk->s_cb); if (cb == NULL) - return nl_get_errno(); + return -NLE_NOMEM; nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, NULL); - err = nl_recvmsgs(handle, cb); + err = nl_recvmsgs(sk, cb); nl_cb_put(cb); return err; diff --git a/lib/object.c b/lib/object.c index 74f6e2d..d881ac9 100644 --- a/lib/object.c +++ b/lib/object.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -47,10 +47,8 @@ struct nl_object *nl_object_alloc(struct nl_object_ops *ops) BUG(); new = calloc(1, ops->oo_size); - if (!new) { - nl_errno(ENOMEM); + if (!new) return NULL; - } new->ce_refcnt = 1; nl_init_list_head(&new->ce_list); @@ -69,17 +67,18 @@ struct nl_object *nl_object_alloc(struct nl_object_ops *ops) * @arg kind name of object type * @return The new object or nULL */ -struct nl_object *nl_object_alloc_name(const char *kind) +int nl_object_alloc_name(const char *kind, struct nl_object **result) { struct nl_cache_ops *ops; ops = nl_cache_ops_lookup(kind); - if (!ops) { - nl_error(ENOENT, "Unable to lookup cache kind \"%s\"", kind); - return NULL; - } + if (!ops) + return -NLE_OPNOTSUPP; - return nl_object_alloc(ops->co_obj_ops); + if (!(*result = nl_object_alloc(ops->co_obj_ops))) + return -NLE_NOMEM; + + return 0; } struct nl_derived_object { @@ -109,6 +108,7 @@ struct nl_object *nl_object_clone(struct nl_object *obj) new->ce_ops = obj->ce_ops; new->ce_msgtype = obj->ce_msgtype; + new->ce_mask = obj->ce_mask; if (size) memcpy((void *)new + doff, (void *)obj + doff, size); @@ -265,6 +265,8 @@ int nl_object_identical(struct nl_object *a, struct nl_object *b) return 0; req_attrs = ops->oo_id_attrs; + if (req_attrs == ~0) + req_attrs = a->ce_mask & b->ce_mask; /* Both objects must provide all required attributes to uniquely * identify an object */ @@ -318,7 +320,7 @@ int nl_object_match_filter(struct nl_object *obj, struct nl_object *filter) return 0; return !(ops->oo_compare(obj, filter, filter->ce_mask, - LOOSE_FLAG_COMPARISON)); + LOOSE_COMPARISON)); } /** @@ -376,11 +378,6 @@ struct nl_cache *nl_object_get_cache(struct nl_object *obj) return obj->ce_cache; } -inline void *nl_object_priv(struct nl_object *obj) -{ - return obj; -} - /** @} */ /** @} */ diff --git a/lib/route/.gitignore b/lib/route/.gitignore new file mode 100644 index 0000000..debf3b7 --- /dev/null +++ b/lib/route/.gitignore @@ -0,0 +1,4 @@ +pktloc_grammar.h +pktloc_grammar.c +pktloc_syntax.h +pktloc_syntax.c diff --git a/lib/route/addr.c b/lib/route/addr.c index b8ec56c..2e72f6e 100644 --- a/lib/route/addr.c +++ b/lib/route/addr.c @@ -6,8 +6,8 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> - * Baruch Even <baruch@ev-en.org>, + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2006 Baruch Even <baruch@ev-en.org>, * Mediatrix Telecom, inc. <ericb@mediatrix.com> */ @@ -48,17 +48,16 @@ * // cannot be set for IPv6 addresses. * rtnl_addr_set_scope(addr, rtnl_str2scope("site")); * - * // Broadcast and anycast address may be specified using the relevant + * // Broadcast address may be specified using the relevant * // functions, the address family will be verified if one of the other * // addresses has been set already. Currently only works for IPv4. * rtnl_addr_set_broadcast(addr, broadcast_addr); - * rtnl_addr_set_anycast(addr, anycast_addr); * * // Build the netlink message and send it to the kernel, the operation will * // block until the operation has been completed. Alternatively the required * // netlink message can be built using rtnl_addr_build_add_request() to be * // sent out using nl_send_auto_complete(). - * rtnl_addr_add(handle, addr, 0); + * rtnl_addr_add(sk, addr, 0); * * // Free the memory * rtnl_addr_put(addr); @@ -99,7 +98,7 @@ * // block until the operation has been completed. Alternatively the required * // netlink message can be built using rtnl_addr_build_delete_request() * // to be sent out using nl_send_auto_complete(). - * rtnl_addr_delete(handle, addr, 0); + * rtnl_addr_delete(sk, addr, 0); * * // Free the memory * rtnl_addr_put(addr); @@ -126,13 +125,20 @@ #define ADDR_ATTR_PEER 0x0080 #define ADDR_ATTR_LOCAL 0x0100 #define ADDR_ATTR_BROADCAST 0x0200 -#define ADDR_ATTR_ANYCAST 0x0400 -#define ADDR_ATTR_MULTICAST 0x0800 +#define ADDR_ATTR_MULTICAST 0x0400 +#define ADDR_ATTR_ANYCAST 0x0800 static struct nl_cache_ops rtnl_addr_ops; static struct nl_object_ops addr_obj_ops; /** @endcond */ +static void addr_constructor(struct nl_object *obj) +{ + struct rtnl_addr *addr = nl_object_priv(obj); + + addr->a_scope = RT_SCOPE_NOWHERE; +} + static void addr_free_data(struct nl_object *obj) { struct rtnl_addr *addr = nl_object_priv(obj); @@ -143,8 +149,8 @@ static void addr_free_data(struct nl_object *obj) nl_addr_put(addr->a_peer); nl_addr_put(addr->a_local); nl_addr_put(addr->a_bcast); - nl_addr_put(addr->a_anycast); nl_addr_put(addr->a_multicast); + nl_addr_put(addr->a_anycast); } static int addr_clone(struct nl_object *_dst, struct nl_object *_src) @@ -154,27 +160,25 @@ static int addr_clone(struct nl_object *_dst, struct nl_object *_src) if (src->a_peer) if (!(dst->a_peer = nl_addr_clone(src->a_peer))) - goto errout; + return -NLE_NOMEM; if (src->a_local) if (!(dst->a_local = nl_addr_clone(src->a_local))) - goto errout; + return -NLE_NOMEM; if (src->a_bcast) if (!(dst->a_bcast = nl_addr_clone(src->a_bcast))) - goto errout; - - if (src->a_anycast) - if (!(dst->a_anycast = nl_addr_clone(src->a_anycast))) - goto errout; + return -NLE_NOMEM; if (src->a_multicast) if (!(dst->a_multicast = nl_addr_clone(src->a_multicast))) - goto errout; + return -NLE_NOMEM; + + if (src->a_anycast) + if (!(dst->a_anycast = nl_addr_clone(src->a_anycast))) + return -NLE_NOMEM; return 0; -errout: - return nl_get_errno(); } static struct nla_policy addr_policy[IFA_MAX+1] = { @@ -189,21 +193,20 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct rtnl_addr *addr; struct ifaddrmsg *ifa; struct nlattr *tb[IFA_MAX+1]; - int err = -ENOMEM, peer_prefix = 0; + int err, peer_prefix = 0, family; addr = rtnl_addr_alloc(); - if (!addr) { - err = nl_errno(ENOMEM); - goto errout; - } + if (!addr) + return -NLE_NOMEM; + addr->ce_msgtype = nlh->nlmsg_type; err = nlmsg_parse(nlh, sizeof(*ifa), tb, IFA_MAX, addr_policy); if (err < 0) - goto errout_free; + goto errout; ifa = nlmsg_data(nlh); - addr->a_family = ifa->ifa_family; + addr->a_family = family = ifa->ifa_family; addr->a_prefixlen = ifa->ifa_prefixlen; addr->a_flags = ifa->ifa_flags; addr->a_scope = ifa->ifa_scope; @@ -229,18 +232,18 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, } if (tb[IFA_LOCAL]) { - addr->a_local = nla_get_addr(tb[IFA_LOCAL], addr->a_family); + addr->a_local = nl_addr_alloc_attr(tb[IFA_LOCAL], family); if (!addr->a_local) - goto errout_free; + goto errout_nomem; addr->ce_mask |= ADDR_ATTR_LOCAL; } if (tb[IFA_ADDRESS]) { struct nl_addr *a; - a = nla_get_addr(tb[IFA_ADDRESS], addr->a_family); + a = nl_addr_alloc_attr(tb[IFA_ADDRESS], family); if (!a) - goto errout_free; + goto errout_nomem; /* IPv6 sends the local address as IFA_ADDRESS with * no IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS @@ -260,48 +263,48 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, addr->a_prefixlen); if (tb[IFA_BROADCAST]) { - addr->a_bcast = nla_get_addr(tb[IFA_BROADCAST], addr->a_family); + addr->a_bcast = nl_addr_alloc_attr(tb[IFA_BROADCAST], family); if (!addr->a_bcast) - goto errout_free; + goto errout_nomem; addr->ce_mask |= ADDR_ATTR_BROADCAST; } - if (tb[IFA_ANYCAST]) { - addr->a_anycast = nla_get_addr(tb[IFA_ANYCAST], addr->a_family); - if (!addr->a_anycast) - goto errout_free; - - addr->ce_mask |= ADDR_ATTR_ANYCAST; - } - if (tb[IFA_MULTICAST]) { - addr->a_multicast = nla_get_addr(tb[IFA_MULTICAST], - addr->a_family); + addr->a_multicast = nl_addr_alloc_attr(tb[IFA_MULTICAST], + family); if (!addr->a_multicast) - goto errout_free; + goto errout_nomem; addr->ce_mask |= ADDR_ATTR_MULTICAST; } - err = pp->pp_cb((struct nl_object *) addr, pp); - if (err < 0) - goto errout_free; + if (tb[IFA_ANYCAST]) { + addr->a_anycast = nl_addr_alloc_attr(tb[IFA_ANYCAST], + family); + if (!addr->a_anycast) + goto errout_nomem; - err = P_ACCEPT; + addr->ce_mask |= ADDR_ATTR_ANYCAST; + } -errout_free: - rtnl_addr_put(addr); + err = pp->pp_cb((struct nl_object *) addr, pp); errout: + rtnl_addr_put(addr); + return err; + +errout_nomem: + err = -NLE_NOMEM; + goto errout; } -static int addr_request_update(struct nl_cache *cache, struct nl_handle *handle) +static int addr_request_update(struct nl_cache *cache, struct nl_sock *sk) { - return nl_rtgen_request(handle, RTM_GETADDR, AF_UNSPEC, NLM_F_DUMP); + return nl_rtgen_request(sk, RTM_GETADDR, AF_UNSPEC, NLM_F_DUMP); } -static int addr_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +static void addr_dump_line(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_addr *addr = (struct rtnl_addr *) obj; struct nl_cache *link_cache; @@ -310,259 +313,166 @@ static int addr_dump_brief(struct nl_object *obj, struct nl_dump_params *p) link_cache = nl_cache_mngt_require("route/link"); if (addr->ce_mask & ADDR_ATTR_LOCAL) - dp_dump(p, "%s", + nl_dump_line(p, "%s", nl_addr2str(addr->a_local, buf, sizeof(buf))); else - dp_dump(p, "none"); + nl_dump_line(p, "none"); if (addr->ce_mask & ADDR_ATTR_PEER) - dp_dump(p, " peer %s", + nl_dump(p, " peer %s", nl_addr2str(addr->a_peer, buf, sizeof(buf))); - dp_dump(p, " %s ", nl_af2str(addr->a_family, buf, sizeof(buf))); + nl_dump(p, " %s ", nl_af2str(addr->a_family, buf, sizeof(buf))); if (link_cache) - dp_dump(p, "dev %s ", + nl_dump(p, "dev %s ", rtnl_link_i2name(link_cache, addr->a_ifindex, buf, sizeof(buf))); else - dp_dump(p, "dev %d ", addr->a_ifindex); + nl_dump(p, "dev %d ", addr->a_ifindex); - dp_dump(p, "scope %s", + nl_dump(p, "scope %s", rtnl_scope2str(addr->a_scope, buf, sizeof(buf))); rtnl_addr_flags2str(addr->a_flags, buf, sizeof(buf)); if (buf[0]) - dp_dump(p, " <%s>", buf); - - dp_dump(p, "\n"); + nl_dump(p, " <%s>", buf); - return 1; + nl_dump(p, "\n"); } -static int addr_dump_full(struct nl_object *obj, struct nl_dump_params *p) +static void addr_dump_details(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_addr *addr = (struct rtnl_addr *) obj; - int line = addr_dump_brief(obj, p); char buf[128]; + addr_dump_line(obj, p); + if (addr->ce_mask & (ADDR_ATTR_LABEL | ADDR_ATTR_BROADCAST | - ADDR_ATTR_ANYCAST | ADDR_ATTR_MULTICAST)) { - dp_dump_line(p, line++, " "); + ADDR_ATTR_MULTICAST)) { + nl_dump_line(p, " "); if (addr->ce_mask & ADDR_ATTR_LABEL) - dp_dump(p, " label %s", addr->a_label); + nl_dump(p, " label %s", addr->a_label); if (addr->ce_mask & ADDR_ATTR_BROADCAST) - dp_dump(p, " broadcast %s", + nl_dump(p, " broadcast %s", nl_addr2str(addr->a_bcast, buf, sizeof(buf))); - if (addr->ce_mask & ADDR_ATTR_ANYCAST) - dp_dump(p, " anycast %s", - nl_addr2str(addr->a_anycast, buf, - sizeof(buf))); - if (addr->ce_mask & ADDR_ATTR_MULTICAST) - dp_dump(p, " multicast %s", + nl_dump(p, " multicast %s", nl_addr2str(addr->a_multicast, buf, sizeof(buf))); - dp_dump(p, "\n"); + if (addr->ce_mask & ADDR_ATTR_ANYCAST) + nl_dump(p, " anycast %s", + nl_addr2str(addr->a_anycast, buf, + sizeof(buf))); + + nl_dump(p, "\n"); } if (addr->ce_mask & ADDR_ATTR_CACHEINFO) { struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo; - dp_dump_line(p, line++, " valid-lifetime %s", + nl_dump_line(p, " valid-lifetime %s", ci->aci_valid == 0xFFFFFFFFU ? "forever" : nl_msec2str(ci->aci_valid * 1000, buf, sizeof(buf))); - dp_dump(p, " preferred-lifetime %s\n", + nl_dump(p, " preferred-lifetime %s\n", ci->aci_prefered == 0xFFFFFFFFU ? "forever" : nl_msec2str(ci->aci_prefered * 1000, buf, sizeof(buf))); - dp_dump_line(p, line++, " created boot-time+%s ", + nl_dump_line(p, " created boot-time+%s ", nl_msec2str(addr->a_cacheinfo.aci_cstamp * 10, buf, sizeof(buf))); - dp_dump(p, "last-updated boot-time+%s\n", + nl_dump(p, "last-updated boot-time+%s\n", nl_msec2str(addr->a_cacheinfo.aci_tstamp * 10, buf, sizeof(buf))); } - - return line; -} - -static int addr_dump_stats(struct nl_object *obj, struct nl_dump_params *p) -{ - return addr_dump_full(obj, p); } -static int addr_dump_xml(struct nl_object *obj, struct nl_dump_params *p) +static void addr_dump_stats(struct nl_object *obj, struct nl_dump_params *p) { - struct rtnl_addr *addr = (struct rtnl_addr *) obj; - struct nl_cache *link_cache; - char buf[128]; - int line = 0; - - dp_dump_line(p, line++, "<address>\n"); - dp_dump_line(p, line++, " <family>%s</family>\n", - nl_af2str(addr->a_family, buf, sizeof(buf))); - - if (addr->ce_mask & ADDR_ATTR_LOCAL) - dp_dump_line(p, line++, " <local>%s</local>\n", - nl_addr2str(addr->a_local, buf, sizeof(buf))); - - if (addr->ce_mask & ADDR_ATTR_PEER) - dp_dump_line(p, line++, " <peer>%s</peer>\n", - nl_addr2str(addr->a_peer, buf, sizeof(buf))); - - if (addr->ce_mask & ADDR_ATTR_BROADCAST) - dp_dump_line(p, line++, " <broadcast>%s</broadcast>\n", - nl_addr2str(addr->a_bcast, buf, sizeof(buf))); - - if (addr->ce_mask & ADDR_ATTR_ANYCAST) - dp_dump_line(p, line++, " <anycast>%s</anycast>\n", - nl_addr2str(addr->a_anycast, buf, sizeof(buf))); - - if (addr->ce_mask & ADDR_ATTR_MULTICAST) - dp_dump_line(p, line++, " <multicast>%s</multicast>\n", - nl_addr2str(addr->a_multicast, buf, - sizeof(buf))); - - if (addr->ce_mask & ADDR_ATTR_PREFIXLEN) - dp_dump_line(p, line++, " <prefixlen>%u</prefixlen>\n", - addr->a_prefixlen); - link_cache = nl_cache_mngt_require("route/link"); - - if (link_cache) - dp_dump_line(p, line++, " <device>%s</device>\n", - rtnl_link_i2name(link_cache, addr->a_ifindex, - buf, sizeof(buf))); - else - dp_dump_line(p, line++, " <device>%u</device>\n", - addr->a_ifindex); - - if (addr->ce_mask & ADDR_ATTR_SCOPE) - dp_dump_line(p, line++, " <scope>%s</scope>\n", - rtnl_scope2str(addr->a_scope, buf, sizeof(buf))); - - if (addr->ce_mask & ADDR_ATTR_LABEL) - dp_dump_line(p, line++, " <label>%s</label>\n", addr->a_label); - - rtnl_addr_flags2str(addr->a_flags, buf, sizeof(buf)); - if (buf[0]) - dp_dump_line(p, line++, " <flags>%s</flags>\n", buf); - - if (addr->ce_mask & ADDR_ATTR_CACHEINFO) { - struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo; - - dp_dump_line(p, line++, " <cacheinfo>\n"); - - dp_dump_line(p, line++, " <valid>%s</valid>\n", - ci->aci_valid == 0xFFFFFFFFU ? "forever" : - nl_msec2str(ci->aci_valid * 1000, - buf, sizeof(buf))); - - dp_dump_line(p, line++, " <prefered>%s</prefered>\n", - ci->aci_prefered == 0xFFFFFFFFU ? "forever" : - nl_msec2str(ci->aci_prefered * 1000, - buf, sizeof(buf))); - - dp_dump_line(p, line++, " <created>%s</created>\n", - nl_msec2str(addr->a_cacheinfo.aci_cstamp * 10, - buf, sizeof(buf))); - - dp_dump_line(p, line++, " <last-update>%s</last-update>\n", - nl_msec2str(addr->a_cacheinfo.aci_tstamp * 10, - buf, sizeof(buf))); - - dp_dump_line(p, line++, " </cacheinfo>\n"); - } - - dp_dump_line(p, line++, "</address>\n"); - - return line; + addr_dump_details(obj, p); } -static int addr_dump_env(struct nl_object *obj, struct nl_dump_params *p) +static void addr_dump_env(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_addr *addr = (struct rtnl_addr *) obj; struct nl_cache *link_cache; char buf[128]; - int line = 0; - dp_dump_line(p, line++, "ADDR_FAMILY=%s\n", + nl_dump_line(p, "ADDR_FAMILY=%s\n", nl_af2str(addr->a_family, buf, sizeof(buf))); if (addr->ce_mask & ADDR_ATTR_LOCAL) - dp_dump_line(p, line++, "ADDR_LOCAL=%s\n", + nl_dump_line(p, "ADDR_LOCAL=%s\n", nl_addr2str(addr->a_local, buf, sizeof(buf))); if (addr->ce_mask & ADDR_ATTR_PEER) - dp_dump_line(p, line++, "ADDR_PEER=%s\n", + nl_dump_line(p, "ADDR_PEER=%s\n", nl_addr2str(addr->a_peer, buf, sizeof(buf))); if (addr->ce_mask & ADDR_ATTR_BROADCAST) - dp_dump_line(p, line++, "ADDR_BROADCAST=%s\n", + nl_dump_line(p, "ADDR_BROADCAST=%s\n", nl_addr2str(addr->a_bcast, buf, sizeof(buf))); if (addr->ce_mask & ADDR_ATTR_ANYCAST) - dp_dump_line(p, line++, "ADDR_ANYCAST=%s\n", + nl_dump_line(p, "ADDR_ANYCAST=%s\n", nl_addr2str(addr->a_anycast, buf, sizeof(buf))); if (addr->ce_mask & ADDR_ATTR_MULTICAST) - dp_dump_line(p, line++, "ADDR_MULTICAST=%s\n", + nl_dump_line(p, "ADDR_MULTICAST=%s\n", nl_addr2str(addr->a_multicast, buf, sizeof(buf))); if (addr->ce_mask & ADDR_ATTR_PREFIXLEN) - dp_dump_line(p, line++, "ADDR_PREFIXLEN=%u\n", + nl_dump_line(p, "ADDR_PREFIXLEN=%u\n", addr->a_prefixlen); link_cache = nl_cache_mngt_require("route/link"); - dp_dump_line(p, line++, "ADDR_IFINDEX=%u\n", addr->a_ifindex); + nl_dump_line(p, "ADDR_IFINDEX=%u\n", addr->a_ifindex); if (link_cache) - dp_dump_line(p, line++, "ADDR_IFNAME=%s\n", + nl_dump_line(p, "ADDR_IFNAME=%s\n", rtnl_link_i2name(link_cache, addr->a_ifindex, - buf, sizeof(buf))); + buf, sizeof(buf))); if (addr->ce_mask & ADDR_ATTR_SCOPE) - dp_dump_line(p, line++, "ADDR_SCOPE=%s\n", + nl_dump_line(p, "ADDR_SCOPE=%s\n", rtnl_scope2str(addr->a_scope, buf, sizeof(buf))); if (addr->ce_mask & ADDR_ATTR_LABEL) - dp_dump_line(p, line++, "ADDR_LABEL=%s\n", addr->a_label); + nl_dump_line(p, "ADDR_LABEL=%s\n", addr->a_label); rtnl_addr_flags2str(addr->a_flags, buf, sizeof(buf)); if (buf[0]) - dp_dump_line(p, line++, "ADDR_FLAGS=%s\n", buf); + nl_dump_line(p, "ADDR_FLAGS=%s\n", buf); if (addr->ce_mask & ADDR_ATTR_CACHEINFO) { struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo; - dp_dump_line(p, line++, "ADDR_CACHEINFO_VALID=%s\n", + nl_dump_line(p, "ADDR_CACHEINFO_VALID=%s\n", ci->aci_valid == 0xFFFFFFFFU ? "forever" : nl_msec2str(ci->aci_valid * 1000, buf, sizeof(buf))); - dp_dump_line(p, line++, "ADDR_CACHEINFO_PREFERED=%s\n", + nl_dump_line(p, "ADDR_CACHEINFO_PREFERED=%s\n", ci->aci_prefered == 0xFFFFFFFFU ? "forever" : nl_msec2str(ci->aci_prefered * 1000, buf, sizeof(buf))); - dp_dump_line(p, line++, "ADDR_CACHEINFO_CREATED=%s\n", + nl_dump_line(p, "ADDR_CACHEINFO_CREATED=%s\n", nl_msec2str(addr->a_cacheinfo.aci_cstamp * 10, buf, sizeof(buf))); - dp_dump_line(p, line++, "ADDR_CACHEINFO_LASTUPDATE=%s\n", + nl_dump_line(p, "ADDR_CACHEINFO_LASTUPDATE=%s\n", nl_msec2str(addr->a_cacheinfo.aci_tstamp * 10, buf, sizeof(buf))); } - - return line; } static int addr_compare(struct nl_object *_a, struct nl_object *_b, @@ -580,12 +490,12 @@ static int addr_compare(struct nl_object *_a, struct nl_object *_b, diff |= ADDR_DIFF(LABEL, strcmp(a->a_label, b->a_label)); diff |= ADDR_DIFF(PEER, nl_addr_cmp(a->a_peer, b->a_peer)); diff |= ADDR_DIFF(LOCAL, nl_addr_cmp(a->a_local, b->a_local)); - diff |= ADDR_DIFF(ANYCAST, nl_addr_cmp(a->a_anycast,b->a_anycast)); diff |= ADDR_DIFF(MULTICAST, nl_addr_cmp(a->a_multicast, b->a_multicast)); diff |= ADDR_DIFF(BROADCAST, nl_addr_cmp(a->a_bcast, b->a_bcast)); + diff |= ADDR_DIFF(ANYCAST, nl_addr_cmp(a->a_anycast, b->a_anycast)); - if (flags & LOOSE_FLAG_COMPARISON) + if (flags & LOOSE_COMPARISON) diff |= ADDR_DIFF(FLAGS, (a->a_flags ^ b->a_flags) & b->a_flag_mask); else @@ -607,7 +517,6 @@ static struct trans_tbl addr_attrs[] = { __ADD(ADDR_ATTR_PEER, peer) __ADD(ADDR_ATTR_LOCAL, local) __ADD(ADDR_ATTR_BROADCAST, broadcast) - __ADD(ADDR_ATTR_ANYCAST, anycast) __ADD(ADDR_ATTR_MULTICAST, multicast) }; @@ -639,25 +548,15 @@ void rtnl_addr_put(struct rtnl_addr *addr) * @{ */ -struct nl_cache *rtnl_addr_alloc_cache(struct nl_handle *handle) +int rtnl_addr_alloc_cache(struct nl_sock *sk, struct nl_cache **result) { - struct nl_cache *cache; - - cache = nl_cache_alloc(&rtnl_addr_ops); - if (!cache) - return NULL; - - if (handle && nl_cache_refill(handle, cache) < 0) { - nl_cache_free(cache); - return NULL; - } - - return cache; + return nl_cache_alloc_and_fill(&rtnl_addr_ops, sk, result); } /** @} */ -static struct nl_msg *build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags) +static int build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags, + struct nl_msg **result) { struct nl_msg *msg; struct ifaddrmsg am = { @@ -680,7 +579,7 @@ static struct nl_msg *build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags) msg = nlmsg_alloc_simple(cmd, flags); if (!msg) - goto nla_put_failure; + return -NLE_NOMEM; if (nlmsg_append(msg, &am, sizeof(am), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -690,7 +589,7 @@ static struct nl_msg *build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags) if (tmpl->ce_mask & ADDR_ATTR_PEER) NLA_PUT_ADDR(msg, IFA_ADDRESS, tmpl->a_peer); - else + else if (tmpl->ce_mask & ADDR_ATTR_LOCAL) NLA_PUT_ADDR(msg, IFA_ADDRESS, tmpl->a_local); if (tmpl->ce_mask & ADDR_ATTR_LABEL) @@ -699,14 +598,22 @@ static struct nl_msg *build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags) if (tmpl->ce_mask & ADDR_ATTR_BROADCAST) NLA_PUT_ADDR(msg, IFA_BROADCAST, tmpl->a_bcast); - if (tmpl->ce_mask & ADDR_ATTR_ANYCAST) - NLA_PUT_ADDR(msg, IFA_ANYCAST, tmpl->a_anycast); + if (tmpl->ce_mask & ADDR_ATTR_CACHEINFO) { + struct ifa_cacheinfo ca = { + .ifa_valid = tmpl->a_cacheinfo.aci_valid, + .ifa_prefered = tmpl->a_cacheinfo.aci_prefered, + }; + + NLA_PUT(msg, IFA_CACHEINFO, sizeof(ca), &ca); + } + - return msg; + *result = msg; + return 0; nla_put_failure: nlmsg_free(msg); - return NULL; + return -NLE_MSGSIZE; } /** @@ -718,6 +625,7 @@ nla_put_failure: * Build netlink request message to request addition of new address * @arg addr Address object representing the new address. * @arg flags Additional netlink message flags. + * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting the addition of a new * address. The netlink message header isn't fully equipped with @@ -732,25 +640,24 @@ nla_put_failure: * which case a host scope is used if not specified otherwise. * * @note Free the memory after usage using nlmsg_free(). - * @return Newly allocated netlink message or NULL if an error occured. + * + * @return 0 on success or a negative error code. */ -struct nl_msg *rtnl_addr_build_add_request(struct rtnl_addr *addr, int flags) +int rtnl_addr_build_add_request(struct rtnl_addr *addr, int flags, + struct nl_msg **result) { int required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY | ADDR_ATTR_PREFIXLEN | ADDR_ATTR_LOCAL; - if ((addr->ce_mask & required) != required) { - nl_error(EINVAL, "Missing mandatory attributes, required are: " - "ifindex, family, prefixlen, local address."); - return NULL; - } + if ((addr->ce_mask & required) != required) + return -NLE_MISSING_ATTR; - return build_addr_msg(addr, RTM_NEWADDR, NLM_F_CREATE | flags); + return build_addr_msg(addr, RTM_NEWADDR, NLM_F_CREATE | flags, result); } /** * Request addition of new address - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg addr Address object representing the new address. * @arg flags Additional netlink message flags. * @@ -762,21 +669,20 @@ struct nl_msg *rtnl_addr_build_add_request(struct rtnl_addr *addr, int flags) * * @return 0 on sucess or a negative error if an error occured. */ -int rtnl_addr_add(struct nl_handle *handle, struct rtnl_addr *addr, int flags) +int rtnl_addr_add(struct nl_sock *sk, struct rtnl_addr *addr, int flags) { struct nl_msg *msg; int err; - msg = rtnl_addr_build_add_request(addr, flags); - if (!msg) - return nl_get_errno(); + if ((err = rtnl_addr_build_add_request(addr, flags, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); if (err < 0) return err; - return nl_wait_for_ack(handle); + return wait_for_ack(sk); } /** @} */ @@ -790,6 +696,7 @@ int rtnl_addr_add(struct nl_handle *handle, struct rtnl_addr *addr, int flags) * Build a netlink request message to request deletion of an address * @arg addr Address object to be deleteted. * @arg flags Additional netlink message flags. + * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting a deletion of an address. * The netlink message header isn't fully equipped with all relevant @@ -806,24 +713,23 @@ int rtnl_addr_add(struct nl_handle *handle, struct rtnl_addr *addr, int flags) * - peer address (rtnl_addr_set_peer(), IPv4 only) * * @note Free the memory after usage using nlmsg_free(). - * @return Newly allocated netlink message or NULL if an error occured. + * + * @return 0 on success or a negative error code. */ -struct nl_msg *rtnl_addr_build_delete_request(struct rtnl_addr *addr, int flags) +int rtnl_addr_build_delete_request(struct rtnl_addr *addr, int flags, + struct nl_msg **result) { int required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY; - if ((addr->ce_mask & required) != required) { - nl_error(EINVAL, "Missing mandatory attributes, required are: " - "ifindex, family"); - return NULL; - } - - return build_addr_msg(addr, RTM_DELADDR, flags); + if ((addr->ce_mask & required) != required) + return -NLE_MISSING_ATTR; + + return build_addr_msg(addr, RTM_DELADDR, flags, result); } /** * Request deletion of an address - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg addr Address object to be deleted. * @arg flags Additional netlink message flags. * @@ -835,22 +741,20 @@ struct nl_msg *rtnl_addr_build_delete_request(struct rtnl_addr *addr, int flags) * * @return 0 on sucess or a negative error if an error occured. */ -int rtnl_addr_delete(struct nl_handle *handle, struct rtnl_addr *addr, - int flags) +int rtnl_addr_delete(struct nl_sock *sk, struct rtnl_addr *addr, int flags) { struct nl_msg *msg; int err; - msg = rtnl_addr_build_delete_request(addr, flags); - if (!msg) - return nl_get_errno(); + if ((err = rtnl_addr_build_delete_request(addr, flags, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); if (err < 0) return err; - return nl_wait_for_ack(handle); + return wait_for_ack(sk); } /** @} */ @@ -860,10 +764,15 @@ int rtnl_addr_delete(struct nl_handle *handle, struct rtnl_addr *addr, * @{ */ -void rtnl_addr_set_label(struct rtnl_addr *addr, const char *label) +int rtnl_addr_set_label(struct rtnl_addr *addr, const char *label) { - strncpy(addr->a_label, label, sizeof(addr->a_label) - 1); + if (strlen(label) > sizeof(addr->a_label) - 1) + return -NLE_RANGE; + + strcpy(addr->a_label, label); addr->ce_mask |= ADDR_ATTR_LABEL; + + return 0; } char *rtnl_addr_get_label(struct rtnl_addr *addr) @@ -882,10 +791,7 @@ void rtnl_addr_set_ifindex(struct rtnl_addr *addr, int ifindex) int rtnl_addr_get_ifindex(struct rtnl_addr *addr) { - if (addr->ce_mask & ADDR_ATTR_IFINDEX) - return addr->a_ifindex; - else - return RTNL_LINK_NOT_FOUND; + return addr->a_ifindex; } void rtnl_addr_set_family(struct rtnl_addr *addr, int family) @@ -896,10 +802,7 @@ void rtnl_addr_set_family(struct rtnl_addr *addr, int family) int rtnl_addr_get_family(struct rtnl_addr *addr) { - if (addr->ce_mask & ADDR_ATTR_FAMILY) - return addr->a_family; - else - return AF_UNSPEC; + return addr->a_family; } void rtnl_addr_set_prefixlen(struct rtnl_addr *addr, int prefix) @@ -910,10 +813,7 @@ void rtnl_addr_set_prefixlen(struct rtnl_addr *addr, int prefix) int rtnl_addr_get_prefixlen(struct rtnl_addr *addr) { - if (addr->ce_mask & ADDR_ATTR_PREFIXLEN) - return addr->a_prefixlen; - else - return -1; + return addr->a_prefixlen; } void rtnl_addr_set_scope(struct rtnl_addr *addr, int scope) @@ -924,10 +824,7 @@ void rtnl_addr_set_scope(struct rtnl_addr *addr, int scope) int rtnl_addr_get_scope(struct rtnl_addr *addr) { - if (addr->ce_mask & ADDR_ATTR_SCOPE) - return addr->a_scope; - else - return -1; + return addr->a_scope; } void rtnl_addr_set_flags(struct rtnl_addr *addr, unsigned int flags) @@ -954,7 +851,7 @@ static inline int __assign_addr(struct rtnl_addr *addr, struct nl_addr **pos, { if (addr->ce_mask & ADDR_ATTR_FAMILY) { if (new->a_family != addr->a_family) - return nl_error(EINVAL, "Address family mismatch"); + return -NLE_AF_MISMATCH; } else addr->a_family = new->a_family; @@ -985,10 +882,7 @@ int rtnl_addr_set_local(struct rtnl_addr *addr, struct nl_addr *local) struct nl_addr *rtnl_addr_get_local(struct rtnl_addr *addr) { - if (addr->ce_mask & ADDR_ATTR_LOCAL) - return addr->a_local; - else - return NULL; + return addr->a_local; } int rtnl_addr_set_peer(struct rtnl_addr *addr, struct nl_addr *peer) @@ -1003,10 +897,7 @@ int rtnl_addr_set_peer(struct rtnl_addr *addr, struct nl_addr *peer) struct nl_addr *rtnl_addr_get_peer(struct rtnl_addr *addr) { - if (addr->ce_mask & ADDR_ATTR_PEER) - return addr->a_peer; - else - return NULL; + return addr->a_peer; } int rtnl_addr_set_broadcast(struct rtnl_addr *addr, struct nl_addr *bcast) @@ -1016,10 +907,18 @@ int rtnl_addr_set_broadcast(struct rtnl_addr *addr, struct nl_addr *bcast) struct nl_addr *rtnl_addr_get_broadcast(struct rtnl_addr *addr) { - if (addr->ce_mask & ADDR_ATTR_BROADCAST) - return addr->a_bcast; - else - return NULL; + return addr->a_bcast; +} + +int rtnl_addr_set_multicast(struct rtnl_addr *addr, struct nl_addr *multicast) +{ + return __assign_addr(addr, &addr->a_multicast, multicast, + ADDR_ATTR_MULTICAST); +} + +struct nl_addr *rtnl_addr_get_multicast(struct rtnl_addr *addr) +{ + return addr->a_multicast; } int rtnl_addr_set_anycast(struct rtnl_addr *addr, struct nl_addr *anycast) @@ -1030,24 +929,45 @@ int rtnl_addr_set_anycast(struct rtnl_addr *addr, struct nl_addr *anycast) struct nl_addr *rtnl_addr_get_anycast(struct rtnl_addr *addr) { - if (addr->ce_mask & ADDR_ATTR_ANYCAST) - return addr->a_anycast; + return addr->a_anycast; +} + +uint32_t rtnl_addr_get_valid_lifetime(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_CACHEINFO) + return addr->a_cacheinfo.aci_valid; else - return NULL; + return 0xFFFFFFFFU; } -int rtnl_addr_set_multicast(struct rtnl_addr *addr, struct nl_addr *multicast) +void rtnl_addr_set_valid_lifetime(struct rtnl_addr *addr, uint32_t lifetime) { - return __assign_addr(addr, &addr->a_multicast, multicast, - ADDR_ATTR_MULTICAST); + addr->a_cacheinfo.aci_valid = lifetime; + addr->ce_mask |= ADDR_ATTR_CACHEINFO; } -struct nl_addr *rtnl_addr_get_multicast(struct rtnl_addr *addr) +uint32_t rtnl_addr_get_preferred_lifetime(struct rtnl_addr *addr) { - if (addr->ce_mask & ADDR_ATTR_MULTICAST) - return addr->a_multicast; + if (addr->ce_mask & ADDR_ATTR_CACHEINFO) + return addr->a_cacheinfo.aci_prefered; else - return NULL; + return 0xFFFFFFFFU; +} + +void rtnl_addr_set_preferred_lifetime(struct rtnl_addr *addr, uint32_t lifetime) +{ + addr->a_cacheinfo.aci_prefered = lifetime; + addr->ce_mask |= ADDR_ATTR_CACHEINFO; +} + +uint32_t rtnl_addr_get_create_time(struct rtnl_addr *addr) +{ + return addr->a_cacheinfo.aci_cstamp; +} + +uint32_t rtnl_addr_get_last_update_time(struct rtnl_addr *addr) +{ + return addr->a_cacheinfo.aci_tstamp; } /** @} */ @@ -1059,6 +979,9 @@ struct nl_addr *rtnl_addr_get_multicast(struct rtnl_addr *addr) static struct trans_tbl addr_flags[] = { __ADD(IFA_F_SECONDARY, secondary) + __ADD(IFA_F_NODAD, nodad) + __ADD(IFA_F_OPTIMISTIC, optimistic) + __ADD(IFA_F_HOMEADDRESS, homeaddress) __ADD(IFA_F_DEPRECATED, deprecated) __ADD(IFA_F_TENTATIVE, tentative) __ADD(IFA_F_PERMANENT, permanent) @@ -1080,18 +1003,19 @@ int rtnl_addr_str2flags(const char *name) static struct nl_object_ops addr_obj_ops = { .oo_name = "route/addr", .oo_size = sizeof(struct rtnl_addr), + .oo_constructor = addr_constructor, .oo_free_data = addr_free_data, .oo_clone = addr_clone, - .oo_dump[NL_DUMP_BRIEF] = addr_dump_brief, - .oo_dump[NL_DUMP_FULL] = addr_dump_full, - .oo_dump[NL_DUMP_STATS] = addr_dump_stats, - .oo_dump[NL_DUMP_XML] = addr_dump_xml, - .oo_dump[NL_DUMP_ENV] = addr_dump_env, + .oo_dump = { + [NL_DUMP_LINE] = addr_dump_line, + [NL_DUMP_DETAILS] = addr_dump_details, + [NL_DUMP_STATS] = addr_dump_stats, + [NL_DUMP_ENV] = addr_dump_env, + }, .oo_compare = addr_compare, .oo_attrs2str = addr_attrs2str, .oo_id_attrs = (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX | - ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN | - ADDR_ATTR_PEER), + ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN), }; static struct nl_af_group addr_groups[] = { diff --git a/lib/route/class.c b/lib/route/class.c index 7966b09..ddf2d2e 100644 --- a/lib/route/class.c +++ b/lib/route/class.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -36,7 +36,7 @@ static int class_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, class = rtnl_class_alloc(); if (!class) { - err = nl_errno(ENOMEM); + err = -NLE_NOMEM; goto errout; } class->ce_msgtype = n->nlmsg_type; @@ -53,26 +53,20 @@ static int class_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, } err = pp->pp_cb((struct nl_object *) class, pp); - if (err < 0) - goto errout_free; - - err = P_ACCEPT; - errout_free: rtnl_class_put(class); errout: return err; } -static int class_request_update(struct nl_cache *cache, - struct nl_handle *handle) +static int class_request_update(struct nl_cache *cache, struct nl_sock *sk) { struct tcmsg tchdr = { .tcm_family = AF_UNSPEC, .tcm_ifindex = cache->c_iarg1, }; - return nl_send_simple(handle, RTM_GETTCLASS, NLM_F_DUMP, &tchdr, + return nl_send_simple(sk, RTM_GETTCLASS, NLM_F_DUMP, &tchdr, sizeof(tchdr)); } @@ -81,15 +75,15 @@ static int class_request_update(struct nl_cache *cache, * @{ */ -static struct nl_msg *class_build(struct rtnl_class *class, int type, int flags) +static int class_build(struct rtnl_class *class, int type, int flags, + struct nl_msg **result) { struct rtnl_class_ops *cops; - struct nl_msg *msg; int err; - msg = tca_build_msg((struct rtnl_tca *) class, type, flags); - if (!msg) - goto errout; + err = tca_build_msg((struct rtnl_tca *) class, type, flags, result); + if (err < 0) + return err; cops = rtnl_class_lookup_ops(class); if (cops && cops->co_get_opts) { @@ -97,23 +91,24 @@ static struct nl_msg *class_build(struct rtnl_class *class, int type, int flags) opts = cops->co_get_opts(class); if (opts) { - err = nla_put_nested(msg, TCA_OPTIONS, opts); + err = nla_put_nested(*result, TCA_OPTIONS, opts); nlmsg_free(opts); if (err < 0) goto errout; } } - return msg; + return 0; errout: - nlmsg_free(msg); - return NULL; + nlmsg_free(*result); + return err; } /** * Build a netlink message to add a new class * @arg class class to add * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting an addition of a class. * The netlink message header isn't fully equipped with all relevant @@ -123,16 +118,17 @@ errout: * Common message flags * - NLM_F_REPLACE - replace possibly existing classes * - * @return New netlink message + * @return 0 on success or a negative error code. */ -struct nl_msg *rtnl_class_build_add_request(struct rtnl_class *class, int flags) +int rtnl_class_build_add_request(struct rtnl_class *class, int flags, + struct nl_msg **result) { - return class_build(class, RTM_NEWTCLASS, NLM_F_CREATE | flags); + return class_build(class, RTM_NEWTCLASS, NLM_F_CREATE | flags, result); } /** * Add a new class - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg class class to delete * @arg flags additional netlink message flags * @@ -145,22 +141,74 @@ struct nl_msg *rtnl_class_build_add_request(struct rtnl_class *class, int flags) * * @return 0 on success or a negative error code */ -int rtnl_class_add(struct nl_handle *handle, struct rtnl_class *class, - int flags) +int rtnl_class_add(struct nl_sock *sk, struct rtnl_class *class, int flags) { struct nl_msg *msg; int err; - msg = rtnl_class_build_add_request(class, flags); - if (!msg) - return nl_errno(ENOMEM); + if ((err = rtnl_class_build_add_request(class, flags, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); if (err < 0) return err; + return wait_for_ack(sk); +} + +int rtnl_class_build_delete_request(struct rtnl_class *class, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct tcmsg tchdr; + int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT; + + if ((class->ce_mask & required) != required) + BUG(); + + msg = nlmsg_alloc_simple(RTM_DELTCLASS, 0); + if (!msg) + return -NLE_NOMEM; + + tchdr.tcm_family = AF_UNSPEC; + tchdr.tcm_handle = class->c_handle; + tchdr.tcm_parent = class->c_parent; + tchdr.tcm_ifindex = class->c_ifindex; + if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) { + nlmsg_free(msg); + return -NLE_MSGSIZE; + } + + *result = msg; + return 0; +} + +/** + * Delete a class + * @arg sk Netlink socket. + * @arg class class to delete + * + * Builds a netlink message by calling rtnl_class_build_delete_request(), + * sends the request to the kernel and waits for the ACK to be + * received and thus blocks until the request has been processed. + * + * @return 0 on success or a negative error code + */ +int rtnl_class_delete(struct nl_sock *sk, struct rtnl_class *class) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_class_build_delete_request(class, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); - return nl_wait_for_ack(handle); + if (err < 0) + return err; + + return wait_for_ack(sk); } /** @} */ @@ -172,7 +220,7 @@ int rtnl_class_add(struct nl_handle *handle, struct rtnl_class *class, /** * Build a class cache including all classes attached to the specified interface - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg ifindex interface index of the link the classes are * attached to. * @@ -181,22 +229,49 @@ int rtnl_class_add(struct nl_handle *handle, struct rtnl_class *class, * * @return The cache or NULL if an error has occured. */ -struct nl_cache * rtnl_class_alloc_cache(struct nl_handle *handle, int ifindex) +int rtnl_class_alloc_cache(struct nl_sock *sk, int ifindex, + struct nl_cache **result) { struct nl_cache * cache; + int err; cache = nl_cache_alloc(&rtnl_class_ops); if (!cache) - return NULL; + return -NLE_NOMEM; cache->c_iarg1 = ifindex; - if (handle && nl_cache_refill(handle, cache) < 0) { + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { nl_cache_free(cache); - return NULL; + return err; } - return cache; + *result = cache; + return 0; +} + +/** + * Look up class by its handle in the provided cache + * @arg cache class cache + * @arg ifindex interface the class is attached to + * @arg handle class handle + * @return pointer to class inside the cache or NULL if no match was found. + */ +struct rtnl_class *rtnl_class_get(struct nl_cache *cache, int ifindex, + uint32_t handle) +{ + struct rtnl_class *class; + + if (cache->c_ops != &rtnl_class_ops) + return NULL; + + nl_list_for_each_entry(class, &cache->c_items, ce_list) { + if (class->c_handle == handle && class->c_ifindex == ifindex) { + nl_object_get((struct nl_object *) class); + return class; + } + } + return NULL; } /** @} */ diff --git a/lib/route/class_api.c b/lib/route/class_api.c index c814486..374cf0f 100644 --- a/lib/route/class_api.c +++ b/lib/route/class_api.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -43,7 +43,7 @@ int rtnl_class_register(struct rtnl_class_ops *cops) for (op = &class_ops_list; (o = *op) != NULL; op = &o->co_next) if (!strcasecmp(cops->co_kind, o->co_kind)) - return nl_errno(EEXIST); + return -NLE_EXIST; cops->co_next = NULL; *op = cops; @@ -64,7 +64,7 @@ int rtnl_class_unregister(struct rtnl_class_ops *cops) break; if (!o) - return nl_errno(ENOENT); + return -NLE_OBJ_NOTFOUND; *op = cops->co_next; diff --git a/lib/route/class_obj.c b/lib/route/class_obj.c index 0601bdf..5c2e5be 100644 --- a/lib/route/class_obj.c +++ b/lib/route/class_obj.c @@ -55,62 +55,54 @@ errout: return err; } -static int class_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +static void class_dump_line(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_class *class = (struct rtnl_class *) obj; struct rtnl_class_ops *cops; - int line = tca_dump_brief((struct rtnl_tca *) class, "class", p, 0); + tca_dump_line((struct rtnl_tca *) class, "class", p); cops = rtnl_class_lookup_ops(class); - if (cops && cops->co_dump[NL_DUMP_BRIEF]) - line = cops->co_dump[NL_DUMP_BRIEF](class, p, line); - dp_dump(p, "\n"); - - return line; + if (cops && cops->co_dump[NL_DUMP_LINE]) + cops->co_dump[NL_DUMP_LINE](class, p); + nl_dump(p, "\n"); } -static int class_dump_full(struct nl_object *obj, struct nl_dump_params *p) +static void class_dump_details(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_class *class = (struct rtnl_class *) obj; struct rtnl_class_ops *cops; - int line; - line = class_dump_brief(obj, p); - line = tca_dump_full((struct rtnl_tca *) class, p, line); + class_dump_line(obj, p); + tca_dump_details((struct rtnl_tca *) class, p); if (class->c_info) { char buf[32]; - dp_dump(p, "child-qdisc %s ", + nl_dump(p, "child-qdisc %s ", rtnl_tc_handle2str(class->c_info, buf, sizeof(buf))); } cops = rtnl_class_lookup_ops(class); - if (cops && cops->co_dump[NL_DUMP_FULL]) - line = cops->co_dump[NL_DUMP_FULL](class, p, line); + if (cops && cops->co_dump[NL_DUMP_DETAILS]) + cops->co_dump[NL_DUMP_DETAILS](class, p); else if (!class->c_info) - dp_dump(p, "noop (no leaf qdisc)"); - - dp_dump(p, "\n"); + nl_dump(p, "noop (no leaf qdisc)"); - return line; + nl_dump(p, "\n"); } -static int class_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +static void class_dump_stats(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_class *class = (struct rtnl_class *) obj; struct rtnl_class_ops *cops; - int line; - line = class_dump_full(obj, p); - line = tca_dump_stats((struct rtnl_tca *) class, p, line); - dp_dump(p, "\n"); + class_dump_details(obj, p); + tca_dump_stats((struct rtnl_tca *) class, p); + nl_dump(p, "\n"); cops = rtnl_class_lookup_ops(class); if (cops && cops->co_dump[NL_DUMP_STATS]) - line = cops->co_dump[NL_DUMP_STATS](class, p, line); - - return line; + cops->co_dump[NL_DUMP_STATS](class, p); } /** @@ -277,9 +269,11 @@ struct nl_object_ops class_obj_ops = { .oo_size = sizeof(struct rtnl_class), .oo_free_data = class_free_data, .oo_clone = class_clone, - .oo_dump[NL_DUMP_BRIEF] = class_dump_brief, - .oo_dump[NL_DUMP_FULL] = class_dump_full, - .oo_dump[NL_DUMP_STATS] = class_dump_stats, + .oo_dump = { + [NL_DUMP_LINE] = class_dump_line, + [NL_DUMP_DETAILS] = class_dump_details, + [NL_DUMP_STATS] = class_dump_stats, + }, .oo_compare = tca_compare, .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), }; diff --git a/lib/route/classifier.c b/lib/route/cls.c index df6d3ae..cbf0345 100644 --- a/lib/route/classifier.c +++ b/lib/route/cls.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch> */ /** @@ -38,13 +38,13 @@ static struct nl_cache_ops rtnl_cls_ops; static int cls_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct nlmsghdr *nlh, struct nl_parser_param *pp) { - int err; - struct rtnl_cls *cls; struct rtnl_cls_ops *cops; + struct rtnl_cls *cls; + int err; cls = rtnl_cls_alloc(); if (!cls) { - err = nl_errno(ENOMEM); + err = -NLE_NOMEM; goto errout; } cls->ce_msgtype = nlh->nlmsg_type; @@ -57,25 +57,17 @@ static int cls_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, cls->c_protocol = ntohs(TC_H_MIN(cls->c_info)); cops = rtnl_cls_lookup_ops(cls); - if (cops && cops->co_msg_parser) { - err = cops->co_msg_parser(cls); - if (err < 0) - goto errout_free; - } - - err = pp->pp_cb((struct nl_object *) cls, pp); - if (err < 0) + if (cops && cops->co_msg_parser && (err = cops->co_msg_parser(cls)) < 0) goto errout_free; - err = P_ACCEPT; - + err = pp->pp_cb((struct nl_object *) cls, pp); errout_free: rtnl_cls_put(cls); errout: return err; } -static int cls_request_update(struct nl_cache *cache, struct nl_handle *handle) +static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk) { struct tcmsg tchdr = { .tcm_family = AF_UNSPEC, @@ -83,44 +75,48 @@ static int cls_request_update(struct nl_cache *cache, struct nl_handle *handle) .tcm_parent = cache->c_iarg2, }; - return nl_send_simple(handle, RTM_GETTFILTER, NLM_F_DUMP, &tchdr, + return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr, sizeof(tchdr)); } -static struct nl_msg *cls_build(struct rtnl_cls *cls, int type, int flags) +static int cls_build(struct rtnl_cls *cls, int type, int flags, + struct nl_msg **result) { - struct nl_msg *msg; struct rtnl_cls_ops *cops; int err, prio, proto; struct tcmsg *tchdr; - msg = tca_build_msg((struct rtnl_tca *) cls, type, flags); - if (!msg) - goto errout; + err = tca_build_msg((struct rtnl_tca *) cls, type, flags, result); + if (err < 0) + return err; - tchdr = nlmsg_data(nlmsg_hdr(msg)); + tchdr = nlmsg_data(nlmsg_hdr(*result)); prio = rtnl_cls_get_prio(cls); proto = rtnl_cls_get_protocol(cls); - tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto)), + tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto)); cops = rtnl_cls_lookup_ops(cls); if (cops && cops->co_get_opts) { struct nl_msg *opts; - - opts = cops->co_get_opts(cls); - if (opts) { - err = nla_put_nested(msg, TCA_OPTIONS, opts); - nlmsg_free(opts); - if (err < 0) - goto errout; + + if (!(opts = nlmsg_alloc())) { + err = -NLE_NOMEM; + goto errout; } + + if (!(err = cops->co_get_opts(cls, opts))) + err = nla_put_nested(*result, TCA_OPTIONS, opts); + + nlmsg_free(opts); + if (err < 0) + goto errout; } - return msg; + return 0; errout: - nlmsg_free(msg); - return NULL; + nlmsg_free(*result); + return err; } /** @@ -132,6 +128,7 @@ errout: * Build a netlink message to add a new classifier * @arg cls classifier to add * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting an addition of a classifier * The netlink message header isn't fully equipped with all relevant @@ -140,16 +137,17 @@ errout: * the new classifier set via \c rtnl_cls_set_* functions. \a opts * may point to the clsasifier specific options. * - * @return New netlink message + * @return 0 on success or a negative error code. */ -struct nl_msg * rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags) +int rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags, + struct nl_msg **result) { - return cls_build(cls, RTM_NEWTFILTER, NLM_F_CREATE | flags); + return cls_build(cls, RTM_NEWTFILTER, NLM_F_CREATE | flags, result); } /** * Add a new classifier - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg cls classifier to add * @arg flags additional netlink message flags * @@ -159,43 +157,44 @@ struct nl_msg * rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags) * * @return 0 on sucess or a negative error if an error occured. */ -int rtnl_cls_add(struct nl_handle *handle, struct rtnl_cls *cls, int flags) +int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags) { - int err; struct nl_msg *msg; + int err; - msg = rtnl_cls_build_add_request(cls, flags); - if (!msg) - return nl_errno(ENOMEM); + if ((err = rtnl_cls_build_add_request(cls, flags, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); if (err < 0) return err; - nlmsg_free(msg); - return nl_wait_for_ack(handle); + return nl_wait_for_ack(sk); } /** * Build a netlink message to change classifier attributes * @arg cls classifier to change * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting a change of a neigh * attributes. The netlink message header isn't fully equipped with * all relevant fields and must thus be sent out via nl_send_auto_complete() * or supplemented as needed. * - * @return The netlink message + * @return 0 on success or a negative error code. */ -struct nl_msg *rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags) +int rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags, + struct nl_msg **result) { - return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags); + return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags, result); } /** * Change a classifier - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg cls classifier to change * @arg flags additional netlink message flags * @@ -205,45 +204,45 @@ struct nl_msg *rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags) * * @return 0 on sucess or a negative error if an error occured. */ -int rtnl_cls_change(struct nl_handle *handle, struct rtnl_cls *cls, - int flags) +int rtnl_cls_change(struct nl_sock *sk, struct rtnl_cls *cls, int flags) { - int err; struct nl_msg *msg; + int err; - msg = rtnl_cls_build_change_request(cls, flags); - if (!msg) - return nl_errno(ENOMEM); + if ((err = rtnl_cls_build_change_request(cls, flags, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); if (err < 0) return err; - nlmsg_free(msg); - return nl_wait_for_ack(handle); + return nl_wait_for_ack(sk); } /** * Build a netlink request message to delete a classifier * @arg cls classifier to delete * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting a deletion of a classifier. * The netlink message header isn't fully equipped with all relevant * fields and must thus be sent out via nl_send_auto_complete() * or supplemented as needed. * - * @return New netlink message + * @return 0 on success or a negative error code. */ -struct nl_msg *rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags) +int rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags, + struct nl_msg **result) { - return cls_build(cls, RTM_DELTFILTER, flags); + return cls_build(cls, RTM_DELTFILTER, flags, result); } /** * Delete a classifier - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg cls classifier to delete * @arg flags additional netlink message flags * @@ -253,21 +252,20 @@ struct nl_msg *rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags) * * @return 0 on sucess or a negative error if an error occured. */ -int rtnl_cls_delete(struct nl_handle *handle, struct rtnl_cls *cls, int flags) +int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags) { - int err; struct nl_msg *msg; + int err; - msg = rtnl_cls_build_delete_request(cls, flags); - if (!msg) - return nl_errno(ENOMEM); + if ((err = rtnl_cls_build_delete_request(cls, flags, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); if (err < 0) return err; - nlmsg_free(msg); - return nl_wait_for_ack(handle); + return nl_wait_for_ack(sk); } /** @} */ @@ -280,36 +278,37 @@ int rtnl_cls_delete(struct nl_handle *handle, struct rtnl_cls *cls, int flags) /** * Build a classifier cache including all classifiers attached to the * specified class/qdisc on eht specified interface. - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg ifindex interface index of the link the classes are * attached to. * @arg parent parent qdisc/class + * @arg result Pointer to store resulting cache. * * Allocates a new cache, initializes it properly and updates it to * include all classes attached to the specified interface. * * @note The caller is responsible for destroying and freeing the * cache after using it. - * @return The cache or NULL if an error has occured. + * @return 0 on success or a negative error code. */ -struct nl_cache *rtnl_cls_alloc_cache(struct nl_handle *handle, - int ifindex, uint32_t parent) +int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent, struct nl_cache **result) { struct nl_cache * cache; + int err; - cache = nl_cache_alloc(&rtnl_cls_ops); - if (cache == NULL) - return NULL; + if (!(cache = nl_cache_alloc(&rtnl_cls_ops))) + return -NLE_NOMEM; cache->c_iarg1 = ifindex; cache->c_iarg2 = parent; - if (handle && nl_cache_refill(handle, cache) < 0) { + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { nl_cache_free(cache); - return NULL; + return err; } - return cache; + *result = cache; + return 0; } /** @} */ diff --git a/lib/route/cls/basic.c b/lib/route/cls/basic.c new file mode 100644 index 0000000..1460b72 --- /dev/null +++ b/lib/route/cls/basic.c @@ -0,0 +1,211 @@ +/* + * lib/route/cls/basic.c Basic Classifier + * + * 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) 2008-2009 Thomas Graf <tgraf@suug.ch> + */ + +/** + * @ingroup cls + * @defgroup basic Basic Classifier + * + * @par Introduction + * The basic classifier is the simplest form of a classifier. It does + * not have any special classification capabilities, instead it can be + * used to classify exclusively based on extended matches or to + * create a "catch-all" filter. + * + * @{ + */ + +#include <netlink-local.h> +#include <netlink-tc.h> +#include <netlink/netlink.h> +#include <netlink/route/classifier.h> +#include <netlink/route/classifier-modules.h> +#include <netlink/route/cls/basic.h> +#include <netlink/route/cls/ematch.h> + +struct rtnl_basic +{ + uint32_t b_classid; + struct rtnl_ematch_tree * b_ematch; + int b_mask; +}; + +/** @cond SKIP */ +#define BASIC_ATTR_CLASSID 0x001 +#define BASIC_ATTR_EMATCH 0x002 +/** @endcond */ + +static struct nla_policy basic_policy[TCA_FW_MAX+1] = { + [TCA_BASIC_CLASSID] = { .type = NLA_U32 }, + [TCA_BASIC_EMATCHES] = { .type = NLA_NESTED }, + [TCA_BASIC_ACT] = { .type = NLA_NESTED }, + [TCA_BASIC_POLICE] = { .type = NLA_NESTED }, +}; + +static int basic_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src) +{ + return -NLE_OPNOTSUPP; +} + +static void basic_free_data(struct rtnl_cls *cls) +{ + struct rtnl_basic *basic = rtnl_cls_data(cls); + + rtnl_ematch_tree_free(basic->b_ematch); +} + +static int basic_msg_parser(struct rtnl_cls *cls) +{ + struct nlattr *tb[TCA_BASIC_MAX + 1]; + struct rtnl_basic *basic = rtnl_cls_data(cls); + int err; + + err = tca_parse(tb, TCA_BASIC_MAX, (struct rtnl_tca *) cls, basic_policy); + if (err < 0) + return err; + + if (tb[TCA_BASIC_CLASSID]) { + basic->b_classid = nla_get_u32(tb[TCA_BASIC_CLASSID]); + basic->b_mask |= BASIC_ATTR_CLASSID; + } + + if (tb[TCA_BASIC_EMATCHES]) { + if ((err = rtnl_ematch_parse(tb[TCA_BASIC_EMATCHES], + &basic->b_ematch)) < 0) + return err; + + if (basic->b_ematch) + basic->b_mask |= BASIC_ATTR_EMATCH; + } + + if (tb[TCA_BASIC_ACT]) { + /* XXX */ + } + + if (tb[TCA_BASIC_POLICE]) { + /* XXX */ + } + + return 0; +} + +static void basic_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p) +{ + struct rtnl_basic *b = rtnl_cls_data(cls); + char buf[32]; + + if (b->b_mask & BASIC_ATTR_EMATCH) + nl_dump(p, " ematch"); + else + nl_dump(p, " match-all"); + + if (b->b_mask & BASIC_ATTR_CLASSID) + nl_dump(p, " classify-to %s", + rtnl_tc_handle2str(b->b_classid, buf, sizeof(buf))); +} + +static void basic_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p) +{ + struct rtnl_basic *b = rtnl_cls_data(cls); + + if (b->b_mask & BASIC_ATTR_EMATCH) { + nl_dump(p, "\n"); + nl_dump_line(p, " ematch "); + rtnl_ematch_tree_dump(b->b_ematch, p); + } else + nl_dump(p, "no options.\n"); +} + +static int basic_get_opts(struct rtnl_cls *cls, struct nl_msg *msg) +{ + struct rtnl_basic *b = rtnl_cls_data(cls); + + if (!(b->b_mask & BASIC_ATTR_CLASSID)) + return -NLE_MISSING_ATTR; + + NLA_PUT_U32(msg, TCA_BASIC_CLASSID, b->b_classid); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_basic_set_classid(struct rtnl_cls *cls, uint32_t classid) +{ + struct rtnl_basic *b = rtnl_cls_data(cls); + + b->b_classid = classid; + b->b_mask |= BASIC_ATTR_CLASSID; + + return 0; +} + +uint32_t rtnl_basic_get_classid(struct rtnl_cls *cls) +{ + struct rtnl_basic *b = rtnl_cls_data(cls); + + return b->b_classid; +} + +int rtnl_basic_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree) +{ + struct rtnl_basic *b = rtnl_cls_data(cls); + + if (b->b_ematch) { + rtnl_ematch_tree_free(b->b_ematch); + b->b_mask &= ~BASIC_ATTR_EMATCH; + } + + b->b_ematch = tree; + + if (tree) + b->b_mask |= BASIC_ATTR_EMATCH; + + return 0; +} + +struct rtnl_ematch_tree *rtnl_basic_get_ematch(struct rtnl_cls *cls) +{ + struct rtnl_basic *b = rtnl_cls_data(cls); + return b->b_ematch; +} + +/** @} */ + +static struct rtnl_cls_ops basic_ops = { + .co_kind = "basic", + .co_size = sizeof(struct rtnl_basic), + .co_msg_parser = basic_msg_parser, + .co_clone = basic_clone, + .co_free_data = basic_free_data, + .co_get_opts = basic_get_opts, + .co_dump = { + [NL_DUMP_LINE] = basic_dump_line, + [NL_DUMP_DETAILS] = basic_dump_details, + }, +}; + +static void __init basic_init(void) +{ + rtnl_cls_register(&basic_ops); +} + +static void __exit basic_exit(void) +{ + rtnl_cls_unregister(&basic_ops); +} + +/** @} */ diff --git a/lib/route/cls/cgroup.c b/lib/route/cls/cgroup.c new file mode 100644 index 0000000..e5f38b8 --- /dev/null +++ b/lib/route/cls/cgroup.c @@ -0,0 +1,141 @@ +/* + * lib/route/cls/cgroup.c Control Groups Classifier + * + * 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) 2009 Thomas Graf <tgraf@suug.ch> + */ + +/** + * @ingroup cls_api + * @defgroup cgroup Control Groups Classifier + * + * @{ + */ + +#include <netlink-local.h> +#include <netlink-tc.h> +#include <netlink/netlink.h> +#include <netlink/attr.h> +#include <netlink/utils.h> +#include <netlink/route/classifier.h> +#include <netlink/route/classifier-modules.h> +#include <netlink/route/cls/cgroup.h> +#include <netlink/route/cls/ematch.h> + +/** @cond SKIP */ +#define CGROUP_ATTR_EMATCH 0x001 +/** @endcond */ + +static struct nla_policy cgroup_policy[TCA_CGROUP_MAX+1] = { + [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, +}; + +static void cgroup_free_data(struct rtnl_cls *cls) +{ + struct rtnl_cgroup *cg = rtnl_cls_data(cls); + + rtnl_ematch_tree_free(cg->cg_ematch); +} + +static int cgroup_msg_parser(struct rtnl_cls *cls) +{ + struct rtnl_cgroup *cg = rtnl_cls_data(cls); + struct nlattr *tb[TCA_CGROUP_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_CGROUP_MAX, (struct rtnl_tca *) cls, + cgroup_policy); + if (err < 0) + return err; + + if (tb[TCA_CGROUP_EMATCHES]) { + if ((err = rtnl_ematch_parse(tb[TCA_CGROUP_EMATCHES], + &cg->cg_ematch)) < 0) + return err; + cg->cg_mask |= CGROUP_ATTR_EMATCH; + } + +#if 0 + TODO: + TCA_CGROUP_ACT, + TCA_CGROUP_POLICE, +#endif + + return 0; +} + +static void cgroup_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p) +{ + struct rtnl_cgroup *cg = rtnl_cls_data(cls); + + if (cg->cg_mask & CGROUP_ATTR_EMATCH) + nl_dump(p, " ematch"); + else + nl_dump(p, " match-all"); +} + +static void cgroup_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p) +{ + struct rtnl_cgroup *cg = rtnl_cls_data(cls); + + if (cg->cg_mask & CGROUP_ATTR_EMATCH) { + nl_dump(p, "\n"); + nl_dump_line(p, " ematch "); + rtnl_ematch_tree_dump(cg->cg_ematch, p); + } +} + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_cgroup_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree) +{ + struct rtnl_cgroup *cg = rtnl_cls_data(cls); + + if (cg->cg_ematch) { + rtnl_ematch_tree_free(cg->cg_ematch); + cg->cg_mask &= ~CGROUP_ATTR_EMATCH; + } + + cg->cg_ematch = tree; + + if (tree) + cg->cg_mask |= CGROUP_ATTR_EMATCH; + + return 0; +} + +struct rtnl_ematch_tree *rtnl_cgroup_get_ematch(struct rtnl_cls *cls) +{ + struct rtnl_cgroup *cg = rtnl_cls_data(cls); + return cg->cg_ematch; +} + +static struct rtnl_cls_ops cgroup_ops = { + .co_kind = "cgroup", + .co_size = sizeof(struct rtnl_cgroup), + .co_msg_parser = cgroup_msg_parser, + .co_free_data = cgroup_free_data, + .co_dump = { + [NL_DUMP_LINE] = cgroup_dump_line, + [NL_DUMP_DETAILS] = cgroup_dump_details, + }, +}; + +static void __init cgroup_init(void) +{ + rtnl_cls_register(&cgroup_ops); +} + +static void __exit cgroup_exit(void) +{ + rtnl_cls_unregister(&cgroup_ops); +} + +/** @} */ diff --git a/lib/route/cls/ematch.c b/lib/route/cls/ematch.c new file mode 100644 index 0000000..cb77b16 --- /dev/null +++ b/lib/route/cls/ematch.c @@ -0,0 +1,410 @@ +/* + * lib/route/cls/ematch.c Extended Matches + * + * 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) 2008-2009 Thomas Graf <tgraf@suug.ch> + */ + +/** + * @ingroup cls + * @defgroup ematch Extended Match + * + * @{ + */ + +#include <netlink-local.h> +#include <netlink-tc.h> +#include <netlink/netlink.h> +#include <netlink/route/classifier.h> +#include <netlink/route/classifier-modules.h> +#include <netlink/route/cls/ematch.h> + +/** + * @name Module Registration + * @{ + */ + +static NL_LIST_HEAD(ematch_ops_list); + +/** + * Register ematch module + * @arg ops Module operations. + * + * @return 0 on success or a negative error code. + */ +int rtnl_ematch_register(struct rtnl_ematch_ops *ops) +{ + if (rtnl_ematch_lookup_ops(ops->eo_kind)) + return -NLE_EXIST; + + nl_list_add_tail(&ops->eo_list, &ematch_ops_list); + + return 0; +} + +/** + * Unregister ematch module + * @arg ops Module operations. + * + * @return 0 on success or a negative error code. + */ +int rtnl_ematch_unregister(struct rtnl_ematch_ops *ops) +{ + struct rtnl_ematch_ops *o; + + nl_list_for_each_entry(o, &ematch_ops_list, eo_list) { + if (ops->eo_kind == o->eo_kind) { + nl_list_del(&o->eo_list); + return 0; + } + } + + return -NLE_OBJ_NOTFOUND; +} + +/** + * Lookup ematch module by kind + * @arg kind Module kind. + * + * @return Module operations or NULL if not found. + */ +struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind) +{ + struct rtnl_ematch_ops *ops; + + nl_list_for_each_entry(ops, &ematch_ops_list, eo_list) + if (ops->eo_kind == kind) + return ops; + + return NULL; +} + +/** + * Lookup ematch module by name + * @arg name Name of ematch module. + * + * @return Module operations or NULL if not fuond. + */ +struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_name(const char *name) +{ + struct rtnl_ematch_ops *ops; + + nl_list_for_each_entry(ops, &ematch_ops_list, eo_list) + if (!strcasecmp(ops->eo_name, name)) + return ops; + + return NULL; +} + +/** @} */ + +/** + * @name Match + */ + +struct rtnl_ematch *rtnl_ematch_alloc(struct rtnl_ematch_ops *ops) +{ + struct rtnl_ematch *e; + size_t len = sizeof(*e) + (ops ? ops->eo_datalen : 0); + + if (!(e = calloc(1, len))) + return NULL; + + NL_INIT_LIST_HEAD(&e->e_list); + NL_INIT_LIST_HEAD(&e->e_childs); + + if (ops) { + e->e_ops = ops; + e->e_kind = ops->eo_kind; + } + + return e; +} + +/** + * Add ematch to the end of the parent's list of children. + * @arg parent Parent ematch. + * @arg child Ematch to be added as new child of parent. + */ +void rtnl_ematch_add_child(struct rtnl_ematch *parent, + struct rtnl_ematch *child) +{ + nl_list_add_tail(&child->e_list, &parent->e_childs); +} + +/** + * Remove ematch from the list it is linked to. + * @arg ematch Ematch to be unlinked. + */ +void rtnl_ematch_unlink(struct rtnl_ematch *ematch) +{ + nl_list_del(&ematch->e_list); +} + +void rtnl_ematch_free(struct rtnl_ematch *ematch) +{ + if (!ematch) + return; + + free(ematch); +} + +void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags) +{ + ematch->e_flags |= flags; +} + +void rtnl_ematch_unset_flags(struct rtnl_ematch *ematch, uint16_t flags) +{ + ematch->e_flags &= ~flags; +} + +uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *ematch) +{ + return ematch->e_flags; +} + +void *rtnl_ematch_data(struct rtnl_ematch *ematch) +{ + return ematch->e_data; +} + +/** @} */ + +/** + * @name Tree + */ + +struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t progid) +{ + struct rtnl_ematch_tree *tree; + + if (!(tree = calloc(1, sizeof(*tree)))) + return NULL; + + NL_INIT_LIST_HEAD(&tree->et_list); + tree->et_progid = progid; + + return tree; +} + +static void free_ematch_list(struct nl_list_head *head) +{ + struct rtnl_ematch *pos, *next; + + nl_list_for_each_entry_safe(pos, next, head, e_list) { + if (!nl_list_empty(&pos->e_childs)) + free_ematch_list(&pos->e_childs); + rtnl_ematch_free(pos); + } +} + +void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree) +{ + if (!tree) + return; + + free_ematch_list(&tree->et_list); + free(tree); +} + +void rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree *tree, + struct rtnl_ematch *ematch) +{ + nl_list_add_tail(&ematch->e_list, &tree->et_list); +} + +static inline uint32_t container_ref(struct rtnl_ematch *ematch) +{ + return *((uint32_t *) rtnl_ematch_data(ematch)); +} + +static int link_tree(struct rtnl_ematch *index[], int nmatches, int pos, + struct nl_list_head *root) +{ + struct rtnl_ematch *ematch; + int i; + + for (i = pos; i < nmatches; i++) { + ematch = index[i]; + + nl_list_add_tail(&ematch->e_list, root); + + if (ematch->e_kind == TCF_EM_CONTAINER) + link_tree(index, nmatches, container_ref(ematch), + &ematch->e_childs); + + if (!(ematch->e_flags & TCF_EM_REL_MASK)) + return 0; + } + + /* Last entry in chain can't possibly have no relation */ + return -NLE_INVAL; +} + +static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = { + [TCA_EMATCH_TREE_HDR] = { .minlen=sizeof(struct tcf_ematch_tree_hdr) }, + [TCA_EMATCH_TREE_LIST] = { .type = NLA_NESTED }, +}; + +/** + * Parse ematch netlink attributes + * + * @return 0 on success or a negative error code. + */ +int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result) +{ + struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1]; + struct tcf_ematch_tree_hdr *thdr; + struct rtnl_ematch_tree *tree; + struct rtnl_ematch **index; + int nmatches = 0, err, remaining; + + err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy); + if (err < 0) + return err; + + if (!tb[TCA_EMATCH_TREE_HDR]) + return -NLE_MISSING_ATTR; + + thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]); + + /* Ignore empty trees */ + if (thdr->nmatches == 0) + return 0; + + if (!tb[TCA_EMATCH_TREE_LIST]) + return -NLE_MISSING_ATTR; + + if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) / + nla_total_size(sizeof(struct tcf_ematch_hdr)))) + return -NLE_INVAL; + + if (!(index = calloc(thdr->nmatches, sizeof(struct rtnl_ematch *)))) + return -NLE_NOMEM; + + if (!(tree = rtnl_ematch_tree_alloc(thdr->progid))) { + err = -NLE_NOMEM; + goto errout; + } + + nla_for_each_nested(a, tb[TCA_EMATCH_TREE_LIST], remaining) { + struct rtnl_ematch_ops *ops; + struct tcf_ematch_hdr *hdr; + struct rtnl_ematch *ematch; + void *data; + size_t len; + + if (nla_len(a) < sizeof(*hdr)) { + err = -NLE_INVAL; + goto errout; + } + + if (nmatches >= thdr->nmatches) { + err = -NLE_RANGE; + goto errout; + } + + hdr = nla_data(a); + data = nla_data(a) + NLA_ALIGN(sizeof(*hdr)); + len = nla_len(a) - NLA_ALIGN(sizeof(*hdr)); + + ops = rtnl_ematch_lookup_ops(hdr->kind); + if (ops && ops->eo_datalen && len < ops->eo_datalen) { + err = -NLE_INVAL; + goto errout; + } + + if (!(ematch = rtnl_ematch_alloc(ops))) { + err = -NLE_NOMEM; + goto errout; + } + + ematch->e_id = hdr->matchid; + ematch->e_kind = hdr->kind; + ematch->e_flags = hdr->flags; + + if (ops && (err = ops->eo_parse(ematch, data, len)) < 0) + goto errout; + + if (hdr->kind == TCF_EM_CONTAINER && + container_ref(ematch) >= thdr->nmatches) { + err = -NLE_INVAL; + goto errout; + } + + index[nmatches++] = ematch; + } + + if (nmatches != thdr->nmatches) { + err = -NLE_INVAL; + goto errout; + } + + err = link_tree(index, nmatches, 0, &tree->et_list); + if (err < 0) + goto errout; + + free(index); + *result = tree; + + return 0; + +errout: + rtnl_ematch_tree_free(tree); + free(index); + return err; +} + +static void dump_ematch_sequence(struct nl_list_head *head, + struct nl_dump_params *p) +{ + struct rtnl_ematch *match; + + nl_list_for_each_entry(match, head, e_list) { + if (match->e_flags & TCF_EM_INVERT) + nl_dump(p, "NOT "); + + if (match->e_kind == TCF_EM_CONTAINER) { + nl_dump(p, "("); + dump_ematch_sequence(&match->e_childs, p); + nl_dump(p, ")"); + } else if (!match->e_ops) { + nl_dump(p, "[unknown ematch %d]", match->e_kind); + } else { + nl_dump(p, "%s(", match->e_ops->eo_name); + + if (match->e_ops->eo_dump) + match->e_ops->eo_dump(match, p); + + nl_dump(p, ")"); + } + + switch (match->e_flags & TCF_EM_REL_MASK) { + case TCF_EM_REL_AND: + nl_dump(p, " AND "); + break; + case TCF_EM_REL_OR: + nl_dump(p, " OR "); + break; + default: + /* end of first level ematch sequence */ + return; + } + } +} + +void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree, + struct nl_dump_params *p) +{ + dump_ematch_sequence(&tree->et_list, p); + nl_dump(p, "\n"); +} + +/** @} */ + +/** @} */ diff --git a/lib/route/cls/ematch/cmp.c b/lib/route/cls/ematch/cmp.c new file mode 100644 index 0000000..ec25320 --- /dev/null +++ b/lib/route/cls/ematch/cmp.c @@ -0,0 +1,116 @@ +/* + * lib/route/cls/ematch/cmp.c Simple packet data comparison ematch + * + * 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) 2008-2009 Thomas Graf <tgraf@suug.ch> + */ + +/** + * @ingroup ematch + * @defgroup em_cmp Simple packet data comparison + * + * @{ + */ + +#include <netlink-local.h> +#include <netlink-tc.h> +#include <netlink/netlink.h> +#include <netlink/route/cls/ematch.h> +#include <linux/tc_ematch/tc_em_cmp.h> + +void rtnl_ematch_cmp_set(struct rtnl_ematch *ematch, + struct tcf_em_cmp *cfg) +{ + memcpy(rtnl_ematch_data(ematch), cfg, sizeof(*cfg)); +} + +struct tcf_em_cmp *rtnl_ematch_cmp_get(struct rtnl_ematch *ematch) +{ + return rtnl_ematch_data(ematch); +} + +static const char *align_txt(struct tcf_em_cmp *cmp) +{ + switch (cmp->align) { + case TCF_EM_ALIGN_U8: + return "u8"; + case TCF_EM_ALIGN_U16: + return (cmp->flags & TCF_EM_CMP_TRANS) ? "h16" : "u16"; + case TCF_EM_ALIGN_U32: + return (cmp->flags & TCF_EM_CMP_TRANS) ? "h32" : "u32"; + default: + return (cmp->flags & TCF_EM_CMP_TRANS) ? "h?" : "u?"; + } +} + +static const char *layer_txt(struct tcf_em_cmp *cmp) +{ + switch (cmp->layer) { + case TCF_LAYER_LINK: + return "link"; + case TCF_LAYER_NETWORK: + return "network"; + case TCF_LAYER_TRANSPORT: + return "transport"; + default: + return "?"; + } +} + +static const char *relation_txt(struct tcf_em_cmp *cmp) +{ + switch (cmp->opnd) { + case TCF_EM_OPND_EQ: + return "eq"; + case TCF_EM_OPND_LT: + return "lt"; + case TCF_EM_OPND_GT: + return "gt"; + default: + return "?"; + } +} + +static int cmp_parse(struct rtnl_ematch *m, void *data, size_t len) +{ + memcpy(rtnl_ematch_data(m), data, len); + + return 0; +} + +static void cmp_dump(struct rtnl_ematch *m, struct nl_dump_params *p) +{ + struct tcf_em_cmp *cmp = rtnl_ematch_data(m); + + nl_dump(p, "%s at %s+%u ", + align_txt(cmp), layer_txt(cmp), cmp->off); + + if (cmp->mask) + nl_dump(p, "& 0x%x ", cmp->mask); + + nl_dump(p, "%s %u", relation_txt(cmp), cmp->val); +} + +static struct rtnl_ematch_ops cmp_ops = { + .eo_kind = TCF_EM_CMP, + .eo_name = "cmp", + .eo_datalen = sizeof(struct tcf_em_cmp), + .eo_parse = cmp_parse, + .eo_dump = cmp_dump, +}; + +static void __init cmp_init(void) +{ + rtnl_ematch_register(&cmp_ops); +} + +static void __exit cmp_exit(void) +{ + rtnl_ematch_unregister(&cmp_ops); +} + +/** @} */ diff --git a/lib/route/cls/ematch/container.c b/lib/route/cls/ematch/container.c new file mode 100644 index 0000000..54d836f --- /dev/null +++ b/lib/route/cls/ematch/container.c @@ -0,0 +1,39 @@ +/* + * lib/route/cls/ematch/container.c Container Ematch + * + * 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) 2008-2009 Thomas Graf <tgraf@suug.ch> + */ + +#include <netlink-local.h> +#include <netlink-tc.h> +#include <netlink/netlink.h> +#include <netlink/route/cls/ematch.h> + +static int container_parse(struct rtnl_ematch *m, void *data, size_t len) +{ + memcpy(m->e_data, data, sizeof(uint32_t)); + + return 0; +} + +static struct rtnl_ematch_ops container_ops = { + .eo_kind = TCF_EM_CONTAINER, + .eo_name = "container", + .eo_datalen = sizeof(uint32_t), + .eo_parse = container_parse, +}; + +static void __init container_init(void) +{ + rtnl_ematch_register(&container_ops); +} + +static void __exit container_exit(void) +{ + rtnl_ematch_unregister(&container_ops); +} diff --git a/lib/route/cls/fw.c b/lib/route/cls/fw.c index 7ca7619..8cf25b9 100644 --- a/lib/route/cls/fw.c +++ b/lib/route/cls/fw.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch> * Copyright (c) 2006 Petr Gotthard <petr.gotthard@siemens.com> * Copyright (c) 2006 Siemens AG Oesterreich */ @@ -32,19 +32,6 @@ #define FW_ATTR_INDEV 0x008 /** @endcond */ -static inline struct rtnl_fw *fw_cls(struct rtnl_cls *cls) -{ - return (struct rtnl_fw *) cls->c_subdata; -} - -static inline struct rtnl_fw *fw_alloc(struct rtnl_cls *cls) -{ - if (!cls->c_subdata) - cls->c_subdata = calloc(1, sizeof(struct rtnl_fw)); - - return fw_cls(cls); -} - static struct nla_policy fw_policy[TCA_FW_MAX+1] = { [TCA_FW_CLASSID] = { .type = NLA_U32 }, [TCA_FW_INDEV] = { .type = NLA_STRING, @@ -53,34 +40,30 @@ static struct nla_policy fw_policy[TCA_FW_MAX+1] = { static int fw_msg_parser(struct rtnl_cls *cls) { - int err; + struct rtnl_fw *f = rtnl_cls_data(cls); struct nlattr *tb[TCA_FW_MAX + 1]; - struct rtnl_fw *f; + int err; err = tca_parse(tb, TCA_FW_MAX, (struct rtnl_tca *) cls, fw_policy); if (err < 0) return err; - f = fw_alloc(cls); - if (!f) - goto errout_nomem; - if (tb[TCA_FW_CLASSID]) { f->cf_classid = nla_get_u32(tb[TCA_FW_CLASSID]); f->cf_mask |= FW_ATTR_CLASSID; } if (tb[TCA_FW_ACT]) { - f->cf_act = nla_get_data(tb[TCA_FW_ACT]); + f->cf_act = nl_data_alloc_attr(tb[TCA_FW_ACT]); if (!f->cf_act) - goto errout_nomem; + return -NLE_NOMEM; f->cf_mask |= FW_ATTR_ACTION; } if (tb[TCA_FW_POLICE]) { - f->cf_police = nla_get_data(tb[TCA_FW_POLICE]); + f->cf_police = nl_data_alloc_attr(tb[TCA_FW_POLICE]); if (!f->cf_police) - goto errout_nomem; + return -NLE_NOMEM; f->cf_mask |= FW_ATTR_POLICE; } @@ -90,120 +73,68 @@ static int fw_msg_parser(struct rtnl_cls *cls) } return 0; - -errout_nomem: - err = nl_errno(ENOMEM); - - return err; } static void fw_free_data(struct rtnl_cls *cls) { - struct rtnl_fw *f = fw_cls(cls); - - if (!f) - return; + struct rtnl_fw *f = rtnl_cls_data(cls); nl_data_free(f->cf_act); nl_data_free(f->cf_police); - - free(cls->c_subdata); } static int fw_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src) { - struct rtnl_fw *dst, *src = fw_cls(_src); - - if (!src) - return 0; + struct rtnl_fw *dst = rtnl_cls_data(_dst); + struct rtnl_fw *src = rtnl_cls_data(_src); - dst = fw_alloc(_dst); - if (!dst) - return nl_errno(ENOMEM); - - if (src->cf_act) - if (!(dst->cf_act = nl_data_clone(src->cf_act))) - goto errout; + if (src->cf_act && !(dst->cf_act = nl_data_clone(src->cf_act))) + return -NLE_NOMEM; - if (src->cf_police) - if (!(dst->cf_police = nl_data_clone(src->cf_police))) - goto errout; + if (src->cf_police && !(dst->cf_police = nl_data_clone(src->cf_police))) + return -NLE_NOMEM; return 0; -errout: - return nl_get_errno(); } -static int fw_dump_brief(struct rtnl_cls *cls, struct nl_dump_params *p, - int line) +static void fw_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p) { - struct rtnl_fw *f = fw_cls(cls); + struct rtnl_fw *f = rtnl_cls_data(cls); char buf[32]; - if (!f) - goto ignore; - if (f->cf_mask & FW_ATTR_CLASSID) - dp_dump(p, " target %s", + nl_dump(p, " target %s", rtnl_tc_handle2str(f->cf_classid, buf, sizeof(buf))); - -ignore: - return line; } -static int fw_dump_full(struct rtnl_cls *cls, struct nl_dump_params *p, - int line) +static void fw_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p) { - struct rtnl_fw *f = fw_cls(cls); - - if (!f) - goto ignore; + struct rtnl_fw *f = rtnl_cls_data(cls); if (f->cf_mask & FW_ATTR_INDEV) - dp_dump(p, "indev %s ", f->cf_indev); - -ignore: - return line; + nl_dump(p, "indev %s ", f->cf_indev); } -static int fw_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p, - int line) +static int fw_get_opts(struct rtnl_cls *cls, struct nl_msg *msg) { - struct rtnl_fw *f = fw_cls(cls); - - if (!f) - goto ignore; - -ignore: - return line; -} - -static struct nl_msg *fw_get_opts(struct rtnl_cls *cls) -{ - struct rtnl_fw *f; - struct nl_msg *msg; + struct rtnl_fw *f = rtnl_cls_data(cls); - f = fw_cls(cls); - if (!f) - return NULL; - - msg = nlmsg_alloc(); - if (!msg) - return NULL; - if (f->cf_mask & FW_ATTR_CLASSID) - nla_put_u32(msg, TCA_FW_CLASSID, f->cf_classid); + NLA_PUT_U32(msg, TCA_FW_CLASSID, f->cf_classid); if (f->cf_mask & FW_ATTR_ACTION) - nla_put_data(msg, TCA_FW_ACT, f->cf_act); + NLA_PUT_DATA(msg, TCA_FW_ACT, f->cf_act); if (f->cf_mask & FW_ATTR_POLICE) - nla_put_data(msg, TCA_FW_POLICE, f->cf_police); + NLA_PUT_DATA(msg, TCA_FW_POLICE, f->cf_police); if (f->cf_mask & FW_ATTR_INDEV) - nla_put_string(msg, TCA_FW_INDEV, f->cf_indev); + NLA_PUT_STRING(msg, TCA_FW_INDEV, f->cf_indev); - return msg; + return 0; + +nla_put_failure: + return -NLE_NOMEM; } /** @@ -213,12 +144,8 @@ static struct nl_msg *fw_get_opts(struct rtnl_cls *cls) int rtnl_fw_set_classid(struct rtnl_cls *cls, uint32_t classid) { - struct rtnl_fw *f; + struct rtnl_fw *f = rtnl_cls_data(cls); - f = fw_alloc(cls); - if (!f) - return nl_errno(ENOMEM); - f->cf_classid = classid; f->cf_mask |= FW_ATTR_CLASSID; @@ -229,13 +156,15 @@ int rtnl_fw_set_classid(struct rtnl_cls *cls, uint32_t classid) static struct rtnl_cls_ops fw_ops = { .co_kind = "fw", + .co_size = sizeof(struct rtnl_fw), .co_msg_parser = fw_msg_parser, .co_free_data = fw_free_data, .co_clone = fw_clone, .co_get_opts = fw_get_opts, - .co_dump[NL_DUMP_BRIEF] = fw_dump_brief, - .co_dump[NL_DUMP_FULL] = fw_dump_full, - .co_dump[NL_DUMP_STATS] = fw_dump_stats, + .co_dump = { + [NL_DUMP_LINE] = fw_dump_line, + [NL_DUMP_DETAILS] = fw_dump_details, + }, }; static void __init fw_init(void) diff --git a/lib/route/cls/u32.c b/lib/route/cls/u32.c index 596e63f..80b8851 100644 --- a/lib/route/cls/u32.c +++ b/lib/route/cls/u32.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch> * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com> * Copyright (c) 2005-2006 Siemens AG Oesterreich */ @@ -40,19 +40,6 @@ #define U32_ATTR_INDEV 0x100 /** @endcond */ -static inline struct rtnl_u32 *u32_cls(struct rtnl_cls *cls) -{ - return (struct rtnl_u32 *) cls->c_subdata; -} - -static inline struct rtnl_u32 *u32_alloc(struct rtnl_cls *cls) -{ - if (!cls->c_subdata) - cls->c_subdata = calloc(1, sizeof(struct rtnl_u32)); - - return u32_cls(cls); -} - static inline struct tc_u32_sel *u32_selector(struct rtnl_u32 *u) { return (struct tc_u32_sel *) u->cu_selector->d_data; @@ -79,25 +66,21 @@ static struct nla_policy u32_policy[TCA_U32_MAX+1] = { static int u32_msg_parser(struct rtnl_cls *cls) { - int err; + struct rtnl_u32 *u = rtnl_cls_data(cls); struct nlattr *tb[TCA_U32_MAX + 1]; - struct rtnl_u32 *u; + int err; err = tca_parse(tb, TCA_U32_MAX, (struct rtnl_tca *) cls, u32_policy); if (err < 0) return err; - u = u32_alloc(cls); - if (!u) - goto errout_nomem; - if (tb[TCA_U32_DIVISOR]) { u->cu_divisor = nla_get_u32(tb[TCA_U32_DIVISOR]); u->cu_mask |= U32_ATTR_DIVISOR; } if (tb[TCA_U32_SEL]) { - u->cu_selector = nla_get_data(tb[TCA_U32_SEL]); + u->cu_selector = nl_data_alloc_attr(tb[TCA_U32_SEL]); if (!u->cu_selector) goto errout_nomem; u->cu_mask |= U32_ATTR_SELECTOR; @@ -119,14 +102,14 @@ static int u32_msg_parser(struct rtnl_cls *cls) } if (tb[TCA_U32_ACT]) { - u->cu_act = nla_get_data(tb[TCA_U32_ACT]); + u->cu_act = nl_data_alloc_attr(tb[TCA_U32_ACT]); if (!u->cu_act) goto errout_nomem; u->cu_mask |= U32_ATTR_ACTION; } if (tb[TCA_U32_POLICE]) { - u->cu_police = nla_get_data(tb[TCA_U32_POLICE]); + u->cu_police = nl_data_alloc_attr(tb[TCA_U32_POLICE]); if (!u->cu_police) goto errout_nomem; u->cu_mask |= U32_ATTR_POLICE; @@ -137,8 +120,7 @@ static int u32_msg_parser(struct rtnl_cls *cls) int pcnt_size; if (!tb[TCA_U32_SEL]) { - err = nl_error(EINVAL, "Missing TCA_U32_SEL required " - "for TCA_U32_PCNT"); + err = -NLE_MISSING_ATTR; goto errout; } @@ -146,11 +128,11 @@ static int u32_msg_parser(struct rtnl_cls *cls) pcnt_size = sizeof(struct tc_u32_pcnt) + (sel->nkeys * sizeof(uint64_t)); if (nla_len(tb[TCA_U32_PCNT]) < pcnt_size) { - err = nl_error(EINVAL, "Invalid size for TCA_U32_PCNT"); + err = -NLE_INVAL; goto errout; } - u->cu_pcnt = nla_get_data(tb[TCA_U32_PCNT]); + u->cu_pcnt = nl_data_alloc_attr(tb[TCA_U32_PCNT]); if (!u->cu_pcnt) goto errout_nomem; u->cu_mask |= U32_ATTR_PCNT; @@ -164,79 +146,56 @@ static int u32_msg_parser(struct rtnl_cls *cls) return 0; errout_nomem: - err = nl_errno(ENOMEM); + err = -NLE_NOMEM; errout: return err; } static void u32_free_data(struct rtnl_cls *cls) { - struct rtnl_u32 *u = u32_cls(cls); - - if (!u) - return; + struct rtnl_u32 *u = rtnl_cls_data(cls); nl_data_free(u->cu_selector); nl_data_free(u->cu_act); nl_data_free(u->cu_police); nl_data_free(u->cu_pcnt); - - free(cls->c_subdata); } static int u32_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src) { - struct rtnl_u32 *dst, *src = u32_cls(_src); + struct rtnl_u32 *dst = rtnl_cls_data(_dst); + struct rtnl_u32 *src = rtnl_cls_data(_src); - if (!src) - return 0; + if (src->cu_selector && + !(dst->cu_selector = nl_data_clone(src->cu_selector))) + return -NLE_NOMEM; - dst = u32_alloc(_dst); - if (!dst) - return nl_errno(ENOMEM); + if (src->cu_act && !(dst->cu_act = nl_data_clone(src->cu_act))) + return -NLE_NOMEM; - if (src->cu_selector) - if (!(dst->cu_selector = nl_data_clone(src->cu_selector))) - goto errout; - - if (src->cu_act) - if (!(dst->cu_act = nl_data_clone(src->cu_act))) - goto errout; - - if (src->cu_police) - if (!(dst->cu_police = nl_data_clone(src->cu_police))) - goto errout; + if (src->cu_police && !(dst->cu_police = nl_data_clone(src->cu_police))) + return -NLE_NOMEM; - if (src->cu_pcnt) - if (!(dst->cu_pcnt = nl_data_clone(src->cu_pcnt))) - goto errout; + if (src->cu_pcnt && !(dst->cu_pcnt = nl_data_clone(src->cu_pcnt))) + return -NLE_NOMEM; return 0; -errout: - return nl_get_errno(); } -static int u32_dump_brief(struct rtnl_cls *cls, struct nl_dump_params *p, - int line) +static void u32_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p) { - struct rtnl_u32 *u = u32_cls(cls); + struct rtnl_u32 *u = rtnl_cls_data(cls); char buf[32]; - if (!u) - goto ignore; - if (u->cu_mask & U32_ATTR_DIVISOR) - dp_dump(p, " divisor %u", u->cu_divisor); + nl_dump(p, " divisor %u", u->cu_divisor); else if (u->cu_mask & U32_ATTR_CLASSID) - dp_dump(p, " target %s", + nl_dump(p, " target %s", rtnl_tc_handle2str(u->cu_classid, buf, sizeof(buf))); - -ignore: - return line; } -static int print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel, - struct rtnl_cls *cls, struct rtnl_u32 *u, int line) +static void print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel, + struct rtnl_cls *cls, struct rtnl_u32 *u) { int i; struct tc_u32_key *key; @@ -246,23 +205,23 @@ static int print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel, * exports the selector if no divisor is set but hash offset * and hash mask make only sense in hash filters with divisor * set */ - dp_dump(p, " hash at %u & 0x%x", sel->hoff, sel->hmask); + nl_dump(p, " hash at %u & 0x%x", sel->hoff, sel->hmask); } if (sel->flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) { - dp_dump(p, " offset at %u", sel->off); + nl_dump(p, " offset at %u", sel->off); if (sel->flags & TC_U32_VAROFFSET) - dp_dump(p, " variable (at %u & 0x%x) >> %u", + nl_dump(p, " variable (at %u & 0x%x) >> %u", sel->offoff, ntohs(sel->offmask), sel->offshift); } if (sel->flags) { int flags = sel->flags; - dp_dump(p, " <"); + nl_dump(p, " <"); #define PRINT_FLAG(f) if (flags & TC_U32_##f) { \ - flags &= ~TC_U32_##f; dp_dump(p, #f "%s", flags ? "," : ""); } + flags &= ~TC_U32_##f; nl_dump(p, #f "%s", flags ? "," : ""); } PRINT_FLAG(TERMINAL); PRINT_FLAG(OFFSET); @@ -270,66 +229,56 @@ static int print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel, PRINT_FLAG(EAT); #undef PRINT_FLAG - dp_dump(p, ">"); + nl_dump(p, ">"); } for (i = 0; i < sel->nkeys; i++) { key = (struct tc_u32_key *) ((char *) sel + sizeof(*sel)) + i; - dp_dump(p, "\n"); - dp_dump_line(p, line++, " match key at %s%u ", - key->offmask ? "nexthdr+" : "", key->off); + nl_dump(p, "\n"); + nl_dump_line(p, " match key at %s%u ", + key->offmask ? "nexthdr+" : "", key->off); if (key->offmask) - dp_dump(p, "[0x%u] ", key->offmask); + nl_dump(p, "[0x%u] ", key->offmask); - dp_dump(p, "& 0x%08x == 0x%08x", ntohl(key->mask), ntohl(key->val)); + nl_dump(p, "& 0x%08x == 0x%08x", ntohl(key->mask), ntohl(key->val)); if (p->dp_type == NL_DUMP_STATS && (u->cu_mask & U32_ATTR_PCNT)) { struct tc_u32_pcnt *pcnt = u->cu_pcnt->d_data; - dp_dump(p, " successful %" PRIu64, pcnt->kcnts[i]); + nl_dump(p, " successful %" PRIu64, pcnt->kcnts[i]); } } - - return line; } - -static int u32_dump_full(struct rtnl_cls *cls, struct nl_dump_params *p, - int line) +static void u32_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p) { - struct rtnl_u32 *u = u32_cls(cls); + struct rtnl_u32 *u = rtnl_cls_data(cls); struct tc_u32_sel *s; - if (!u) - goto ignore; - if (!(u->cu_mask & U32_ATTR_SELECTOR)) { - dp_dump(p, "no-selector\n"); - return line; + nl_dump(p, "no-selector\n"); + return; } s = u->cu_selector->d_data; - dp_dump(p, "nkeys %u ", s->nkeys); + nl_dump(p, "nkeys %u ", s->nkeys); if (u->cu_mask & U32_ATTR_HASH) - dp_dump(p, "ht key 0x%x hash 0x%u", + nl_dump(p, "ht key 0x%x hash 0x%u", TC_U32_USERHTID(u->cu_hash), TC_U32_HASH(u->cu_hash)); if (u->cu_mask & U32_ATTR_LINK) - dp_dump(p, "link %u ", u->cu_link); + nl_dump(p, "link %u ", u->cu_link); if (u->cu_mask & U32_ATTR_INDEV) - dp_dump(p, "indev %s ", u->cu_indev); - - line = print_selector(p, s, cls, u, line); - dp_dump(p, "\n"); + nl_dump(p, "indev %s ", u->cu_indev); -ignore: - return line; + print_selector(p, s, cls, u); + nl_dump(p, "\n"); #if 0 #define U32_ATTR_ACTION 0x040 @@ -340,64 +289,50 @@ ignore: #endif } -static int u32_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p, - int line) +static void u32_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p) { - struct rtnl_u32 *u = u32_cls(cls); - - if (!u) - goto ignore; + struct rtnl_u32 *u = rtnl_cls_data(cls); if (u->cu_mask & U32_ATTR_PCNT) { struct tc_u32_pcnt *pc = u->cu_pcnt->d_data; - dp_dump(p, "\n"); - dp_dump_line(p, line++, "%s successful hits\n"); - dp_dump_line(p, line++, "%s %8llu %8llu\n", + nl_dump(p, "\n"); + nl_dump_line(p, " hit %8llu count %8llu\n", pc->rhit, pc->rcnt); } - -ignore: - return line; } -static struct nl_msg *u32_get_opts(struct rtnl_cls *cls) +static int u32_get_opts(struct rtnl_cls *cls, struct nl_msg *msg) { - struct rtnl_u32 *u; - struct nl_msg *msg; + struct rtnl_u32 *u = rtnl_cls_data(cls); - u = u32_cls(cls); - if (!u) - return NULL; - - msg = nlmsg_alloc(); - if (!msg) - return NULL; - if (u->cu_mask & U32_ATTR_DIVISOR) - nla_put_u32(msg, TCA_U32_DIVISOR, u->cu_divisor); + NLA_PUT_U32(msg, TCA_U32_DIVISOR, u->cu_divisor); if (u->cu_mask & U32_ATTR_HASH) - nla_put_u32(msg, TCA_U32_HASH, u->cu_hash); + NLA_PUT_U32(msg, TCA_U32_HASH, u->cu_hash); if (u->cu_mask & U32_ATTR_CLASSID) - nla_put_u32(msg, TCA_U32_CLASSID, u->cu_classid); + NLA_PUT_U32(msg, TCA_U32_CLASSID, u->cu_classid); if (u->cu_mask & U32_ATTR_LINK) - nla_put_u32(msg, TCA_U32_LINK, u->cu_link); + NLA_PUT_U32(msg, TCA_U32_LINK, u->cu_link); if (u->cu_mask & U32_ATTR_SELECTOR) - nla_put_data(msg, TCA_U32_SEL, u->cu_selector); + NLA_PUT_DATA(msg, TCA_U32_SEL, u->cu_selector); if (u->cu_mask & U32_ATTR_ACTION) - nla_put_data(msg, TCA_U32_ACT, u->cu_act); + NLA_PUT_DATA(msg, TCA_U32_ACT, u->cu_act); if (u->cu_mask & U32_ATTR_POLICE) - nla_put_data(msg, TCA_U32_POLICE, u->cu_police); + NLA_PUT_DATA(msg, TCA_U32_POLICE, u->cu_police); if (u->cu_mask & U32_ATTR_INDEV) - nla_put_string(msg, TCA_U32_INDEV, u->cu_indev); + NLA_PUT_STRING(msg, TCA_U32_INDEV, u->cu_indev); - return msg; + return 0; + +nla_put_failure: + return -NLE_NOMEM; } /** @@ -415,12 +350,8 @@ void rtnl_u32_set_handle(struct rtnl_cls *cls, int htid, int hash, int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid) { - struct rtnl_u32 *u; + struct rtnl_u32 *u = rtnl_cls_data(cls); - u = u32_alloc(cls); - if (!u) - return nl_errno(ENOMEM); - u->cu_classid = classid; u->cu_mask |= U32_ATTR_CLASSID; @@ -437,15 +368,11 @@ int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid) int rtnl_u32_set_flags(struct rtnl_cls *cls, int flags) { struct tc_u32_sel *sel; - struct rtnl_u32 *u; - - u = u32_alloc(cls); - if (!u) - return nl_errno(ENOMEM); + struct rtnl_u32 *u = rtnl_cls_data(cls); sel = u32_selector_alloc(u); if (!sel) - return nl_errno(ENOMEM); + return -NLE_NOMEM; sel->flags |= flags; u->cu_mask |= U32_ATTR_SELECTOR; @@ -471,16 +398,12 @@ int rtnl_u32_add_key(struct rtnl_cls *cls, uint32_t val, uint32_t mask, int off, int offmask) { struct tc_u32_sel *sel; - struct rtnl_u32 *u; + struct rtnl_u32 *u = rtnl_cls_data(cls); int err; - u = u32_alloc(cls); - if (!u) - return nl_errno(ENOMEM); - sel = u32_selector_alloc(u); if (!sel) - return nl_errno(ENOMEM); + return -NLE_NOMEM; err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key)); if (err < 0) @@ -523,7 +446,7 @@ int rtnl_u32_add_key_uint16(struct rtnl_cls *cls, uint16_t val, uint16_t mask, { int shift = ((off & 3) == 0 ? 16 : 0); if (off % 2) - return nl_error(EINVAL, "Invalid offset alignment"); + return -NLE_INVAL; return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift), htonl((uint32_t)mask << shift), @@ -580,13 +503,16 @@ int rtnl_u32_add_key_in6_addr(struct rtnl_cls *cls, struct in6_addr *addr, static struct rtnl_cls_ops u32_ops = { .co_kind = "u32", + .co_size = sizeof(struct rtnl_u32), .co_msg_parser = u32_msg_parser, .co_free_data = u32_free_data, .co_clone = u32_clone, .co_get_opts = u32_get_opts, - .co_dump[NL_DUMP_BRIEF] = u32_dump_brief, - .co_dump[NL_DUMP_FULL] = u32_dump_full, - .co_dump[NL_DUMP_STATS] = u32_dump_stats, + .co_dump = { + [NL_DUMP_LINE] = u32_dump_line, + [NL_DUMP_DETAILS] = u32_dump_details, + [NL_DUMP_STATS] = u32_dump_stats, + }, }; static void __init u32_init(void) diff --git a/lib/route/cls_api.c b/lib/route/cls_api.c index f5a083a..73f05df 100644 --- a/lib/route/cls_api.c +++ b/lib/route/cls_api.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -44,7 +44,7 @@ int rtnl_cls_register(struct rtnl_cls_ops *cops) for (op = &cls_ops_list; (o = *op) != NULL; op = &o->co_next) if (!strcasecmp(cops->co_kind, o->co_kind)) - return nl_errno(EEXIST); + return -NLE_EXIST; cops->co_next = NULL; *op = cops; @@ -65,7 +65,7 @@ int rtnl_cls_unregister(struct rtnl_cls_ops *cops) break; if (!o) - return nl_errno(ENOENT); + return -NLE_OBJ_NOTFOUND; *op = cops->co_next; diff --git a/lib/route/cls_obj.c b/lib/route/cls_obj.c index e12bc95..c8218c0 100644 --- a/lib/route/cls_obj.c +++ b/lib/route/cls_obj.c @@ -39,6 +39,8 @@ static void cls_free_data(struct nl_object *obj) cops = rtnl_cls_lookup_ops(cls); if (cops && cops->co_free_data) cops->co_free_data(cls); + + nl_data_free(cls->c_subdata); } static int cls_clone(struct nl_object *_dst, struct nl_object *_src) @@ -52,6 +54,13 @@ static int cls_clone(struct nl_object *_dst, struct nl_object *_src) if (err < 0) goto errout; + if (src->c_subdata) { + if (!(dst->c_subdata = nl_data_clone(src->c_subdata))) { + err = -NLE_NOMEM; + goto errout; + } + } + cops = rtnl_cls_lookup_ops(src); if (cops && cops->co_clone) err = cops->co_clone(dst, src); @@ -59,59 +68,50 @@ errout: return err; } -static int cls_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +static void cls_dump_line(struct nl_object *obj, struct nl_dump_params *p) { char buf[32]; struct rtnl_cls *cls = (struct rtnl_cls *) obj; struct rtnl_cls_ops *cops; - int line; - line = tca_dump_brief((struct rtnl_tca *) cls, "cls", p, 0); + tca_dump_line((struct rtnl_tca *) cls, "cls", p); - dp_dump(p, " prio %u protocol %s", cls->c_prio, + nl_dump(p, " prio %u protocol %s", cls->c_prio, nl_ether_proto2str(cls->c_protocol, buf, sizeof(buf))); cops = rtnl_cls_lookup_ops(cls); - if (cops && cops->co_dump[NL_DUMP_BRIEF]) - line = cops->co_dump[NL_DUMP_BRIEF](cls, p, line); - dp_dump(p, "\n"); - - return line; + if (cops && cops->co_dump[NL_DUMP_LINE]) + cops->co_dump[NL_DUMP_LINE](cls, p); + nl_dump(p, "\n"); } -static int cls_dump_full(struct nl_object *obj, struct nl_dump_params *p) +static void cls_dump_details(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_cls *cls = (struct rtnl_cls *) obj; struct rtnl_cls_ops *cops; - int line; - line = cls_dump_brief(obj, p); - line = tca_dump_full((struct rtnl_tca *) cls, p, line); + cls_dump_line(obj, p); + tca_dump_details((struct rtnl_tca *) cls, p); cops = rtnl_cls_lookup_ops(cls); - if (cops && cops->co_dump[NL_DUMP_FULL]) - line = cops->co_dump[NL_DUMP_FULL](cls, p, line); + if (cops && cops->co_dump[NL_DUMP_DETAILS]) + cops->co_dump[NL_DUMP_DETAILS](cls, p); else - dp_dump(p, "no options\n"); - - return line; + nl_dump(p, "no options\n"); } -static int cls_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +static void cls_dump_stats(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_cls *cls = (struct rtnl_cls *) obj; struct rtnl_cls_ops *cops; - int line; - line = cls_dump_full(obj, p); - line = tca_dump_stats((struct rtnl_tca *) cls, p, line); - dp_dump(p, "\n"); + cls_dump_details(obj, p); + tca_dump_stats((struct rtnl_tca *) cls, p); + nl_dump(p, "\n"); cops = rtnl_cls_lookup_ops(cls); if (cops && cops->co_dump[NL_DUMP_STATS]) - line = cops->co_dump[NL_DUMP_STATS](cls, p, line); - - return line; + cops->co_dump[NL_DUMP_STATS](cls, p); } /** @@ -142,6 +142,11 @@ void rtnl_cls_set_ifindex(struct rtnl_cls *f, int ifindex) tca_set_ifindex((struct rtnl_tca *) f, ifindex); } +int rtnl_cls_get_ifindex(struct rtnl_cls *cls) +{ + return cls->c_ifindex; +} + void rtnl_cls_set_handle(struct rtnl_cls *f, uint32_t handle) { tca_set_handle((struct rtnl_tca *) f, handle); @@ -152,19 +157,36 @@ void rtnl_cls_set_parent(struct rtnl_cls *f, uint32_t parent) tca_set_parent((struct rtnl_tca *) f, parent); } -void rtnl_cls_set_kind(struct rtnl_cls *f, const char *kind) +uint32_t rtnl_cls_get_parent(struct rtnl_cls *cls) +{ + return cls->c_parent; +} + +int rtnl_cls_set_kind(struct rtnl_cls *cls, const char *kind) +{ + if (cls->ce_mask & TCA_ATTR_KIND) + return -NLE_EXIST; + + tca_set_kind((struct rtnl_tca *) cls, kind); + + /* Force allocation of data */ + rtnl_cls_data(cls); + + return 0; +} + +struct rtnl_cls_ops *rtnl_cls_get_ops(struct rtnl_cls *cls) { - tca_set_kind((struct rtnl_tca *) f, kind); - f->c_ops = __rtnl_cls_lookup_ops(kind); + return cls->c_ops; } -void rtnl_cls_set_prio(struct rtnl_cls *cls, int prio) +void rtnl_cls_set_prio(struct rtnl_cls *cls, uint16_t prio) { cls->c_prio = prio; cls->ce_mask |= CLS_ATTR_PRIO; } -int rtnl_cls_get_prio(struct rtnl_cls *cls) +uint16_t rtnl_cls_get_prio(struct rtnl_cls *cls) { if (cls->ce_mask & CLS_ATTR_PRIO) return cls->c_prio; @@ -172,13 +194,13 @@ int rtnl_cls_get_prio(struct rtnl_cls *cls) return 0; } -void rtnl_cls_set_protocol(struct rtnl_cls *cls, int protocol) +void rtnl_cls_set_protocol(struct rtnl_cls *cls, uint16_t protocol) { cls->c_protocol = protocol; cls->ce_mask |= CLS_ATTR_PROTOCOL; } -int rtnl_cls_get_protocol(struct rtnl_cls *cls) +uint16_t rtnl_cls_get_protocol(struct rtnl_cls *cls) { if (cls->ce_mask & CLS_ATTR_PROTOCOL) return cls->c_protocol; @@ -186,6 +208,32 @@ int rtnl_cls_get_protocol(struct rtnl_cls *cls) return ETH_P_ALL; } +void *rtnl_cls_data(struct rtnl_cls *cls) +{ + if (!cls->c_subdata) { + struct rtnl_cls_ops *ops = cls->c_ops; + + if (!ops) { + if (!cls->c_kind[0]) + BUG(); + + ops = __rtnl_cls_lookup_ops(cls->c_kind); + if (ops == NULL) + return NULL; + + cls->c_ops = ops; + } + + if (!ops->co_size) + BUG(); + + if (!(cls->c_subdata = nl_data_alloc(NULL, ops->co_size))) + return NULL; + } + + return nl_data_get(cls->c_subdata); +} + /** @} */ struct nl_object_ops cls_obj_ops = { @@ -193,9 +241,11 @@ struct nl_object_ops cls_obj_ops = { .oo_size = sizeof(struct rtnl_cls), .oo_free_data = cls_free_data, .oo_clone = cls_clone, - .oo_dump[NL_DUMP_BRIEF] = cls_dump_brief, - .oo_dump[NL_DUMP_FULL] = cls_dump_full, - .oo_dump[NL_DUMP_STATS] = cls_dump_stats, + .oo_dump = { + [NL_DUMP_LINE] = cls_dump_line, + [NL_DUMP_DETAILS] = cls_dump_details, + [NL_DUMP_STATS] = cls_dump_stats, + }, .oo_compare = tca_compare, .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), }; diff --git a/lib/route/link.c b/lib/route/link.c index ab89c24..cf488e5 100644 --- a/lib/route/link.c +++ b/lib/route/link.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -78,7 +78,7 @@ * @code * // The first step is to retrieve a list of all available interfaces within * // the kernel and put them into a cache. - * struct nl_cache *cache = rtnl_link_alloc_cache(nl_handle); + * struct nl_cache *cache = rtnl_link_alloc_cache(sk); * * // In a second step, a specific link may be looked up by either interface * // index or interface name. @@ -112,12 +112,12 @@ * // Two ways exist to commit this change request, the first one is to * // build the required netlink message and send it out in one single * // step: - * rtnl_link_change(nl_handle, old, request); + * rtnl_link_change(sk, old, request); * * // An alternative way is to build the netlink message and send it * // out yourself using nl_send_auto_complete() * struct nl_msg *msg = rtnl_link_build_change_request(old, request); - * nl_send_auto_complete(nl_handle, nlmsg_hdr(msg)); + * nl_send_auto_complete(sk, nlmsg_hdr(msg)); * nlmsg_free(msg); * * // Don't forget to give back the link object ;-> @@ -215,21 +215,19 @@ static int link_clone(struct nl_object *_dst, struct nl_object *_src) if (src->l_addr) if (!(dst->l_addr = nl_addr_clone(src->l_addr))) - goto errout; + return -NLE_NOMEM; if (src->l_bcast) if (!(dst->l_bcast = nl_addr_clone(src->l_bcast))) - goto errout; + return -NLE_NOMEM; if (src->l_info_ops && src->l_info_ops->io_clone) { err = src->l_info_ops->io_clone(dst, src); if (err < 0) - goto errout; + return err; } return 0; -errout: - return nl_get_errno(); } static struct nla_policy link_policy[IFLA_MAX+1] = { @@ -265,7 +263,7 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, link = rtnl_link_alloc(); if (link == NULL) { - err = nl_errno(ENOMEM); + err = -NLE_NOMEM; goto errout; } @@ -276,7 +274,7 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, goto errout; if (tb[IFLA_IFNAME] == NULL) { - err = nl_error(EINVAL, "Missing link name TLV"); + err = -NLE_MISSING_ATTR; goto errout; } @@ -332,18 +330,23 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, } if (tb[IFLA_ADDRESS]) { - link->l_addr = nla_get_addr(tb[IFLA_ADDRESS], AF_UNSPEC); - if (link->l_addr == NULL) + link->l_addr = nl_addr_alloc_attr(tb[IFLA_ADDRESS], AF_UNSPEC); + if (link->l_addr == NULL) { + err = -NLE_NOMEM; goto errout; + } nl_addr_set_family(link->l_addr, nl_addr_guess_family(link->l_addr)); link->ce_mask |= LINK_ATTR_ADDR; } if (tb[IFLA_BROADCAST]) { - link->l_bcast = nla_get_addr(tb[IFLA_BROADCAST], AF_UNSPEC); - if (link->l_bcast == NULL) + link->l_bcast = nl_addr_alloc_attr(tb[IFLA_BROADCAST], + AF_UNSPEC); + if (link->l_bcast == NULL) { + err = -NLE_NOMEM; goto errout; + } nl_addr_set_family(link->l_bcast, nl_addr_guess_family(link->l_bcast)); link->ce_mask |= LINK_ATTR_BRD; @@ -365,13 +368,8 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, } if (tb[IFLA_MAP]) { - struct rtnl_link_ifmap *map = nla_data(tb[IFLA_MAP]); - link->l_map.lm_mem_start = map->mem_start; - link->l_map.lm_mem_end = map->mem_end; - link->l_map.lm_base_addr = map->base_addr; - link->l_map.lm_irq = map->irq; - link->l_map.lm_dma = map->dma; - link->l_map.lm_port = map->port; + nla_memcpy(&link->l_map, tb[IFLA_MAP], + sizeof(struct rtnl_link_ifmap)); link->ce_mask |= LINK_ATTR_MAP; } @@ -419,125 +417,109 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, } err = pp->pp_cb((struct nl_object *) link, pp); - if (err < 0) - goto errout; - - err = P_ACCEPT; - errout: rtnl_link_put(link); return err; } -static int link_request_update(struct nl_cache *c, struct nl_handle *h) +static int link_request_update(struct nl_cache *cache, struct nl_sock *sk) { - return nl_rtgen_request(h, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP); + return nl_rtgen_request(sk, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP); } -static int link_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +static void link_dump_line(struct nl_object *obj, struct nl_dump_params *p) { char buf[128]; struct nl_cache *cache = dp_cache(obj); struct rtnl_link *link = (struct rtnl_link *) obj; - int line = 1; - dp_dump(p, "%s %s ", link->l_name, - nl_llproto2str(link->l_arptype, buf, sizeof(buf))); + nl_dump_line(p, "%s %s ", link->l_name, + nl_llproto2str(link->l_arptype, buf, sizeof(buf))); if (link->l_addr && !nl_addr_iszero(link->l_addr)) - dp_dump(p, "%s ", nl_addr2str(link->l_addr, buf, sizeof(buf))); + nl_dump(p, "%s ", nl_addr2str(link->l_addr, buf, sizeof(buf))); if (link->ce_mask & LINK_ATTR_MASTER) { struct rtnl_link *master = rtnl_link_get(cache, link->l_master); - dp_dump(p, "master %s ", master ? master->l_name : "inv"); + nl_dump(p, "master %s ", master ? master->l_name : "inv"); if (master) rtnl_link_put(master); } rtnl_link_flags2str(link->l_flags, buf, sizeof(buf)); if (buf[0]) - dp_dump(p, "<%s> ", buf); + nl_dump(p, "<%s> ", buf); if (link->ce_mask & LINK_ATTR_LINK) { struct rtnl_link *ll = rtnl_link_get(cache, link->l_link); - dp_dump(p, "slave-of %s ", ll ? ll->l_name : "NONE"); + nl_dump(p, "slave-of %s ", ll ? ll->l_name : "NONE"); if (ll) rtnl_link_put(ll); } - if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_BRIEF]) - line = link->l_info_ops->io_dump[NL_DUMP_BRIEF](link, p, line); - - dp_dump(p, "\n"); + if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_LINE]) + link->l_info_ops->io_dump[NL_DUMP_LINE](link, p); - return line; + nl_dump(p, "\n"); } -static int link_dump_full(struct nl_object *obj, struct nl_dump_params *p) +static void link_dump_details(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_link *link = (struct rtnl_link *) obj; char buf[64]; - int line; - line = link_dump_brief(obj, p); - dp_new_line(p, line++); + link_dump_line(obj, p); - dp_dump(p, " mtu %u ", link->l_mtu); - dp_dump(p, "txqlen %u weight %u ", link->l_txqlen, link->l_weight); + nl_dump_line(p, " mtu %u ", link->l_mtu); + nl_dump(p, "txqlen %u weight %u ", link->l_txqlen, link->l_weight); if (link->ce_mask & LINK_ATTR_QDISC) - dp_dump(p, "qdisc %s ", link->l_qdisc); + nl_dump(p, "qdisc %s ", link->l_qdisc); if (link->ce_mask & LINK_ATTR_MAP && link->l_map.lm_irq) - dp_dump(p, "irq %u ", link->l_map.lm_irq); + nl_dump(p, "irq %u ", link->l_map.lm_irq); if (link->ce_mask & LINK_ATTR_IFINDEX) - dp_dump(p, "index %u ", link->l_index); - + nl_dump(p, "index %u ", link->l_index); - dp_dump(p, "\n"); - dp_new_line(p, line++); - dp_dump(p, " "); + nl_dump(p, "\n"); + nl_dump_line(p, " "); if (link->ce_mask & LINK_ATTR_BRD) - dp_dump(p, "brd %s ", nl_addr2str(link->l_bcast, buf, + nl_dump(p, "brd %s ", nl_addr2str(link->l_bcast, buf, sizeof(buf))); if ((link->ce_mask & LINK_ATTR_OPERSTATE) && link->l_operstate != IF_OPER_UNKNOWN) { rtnl_link_operstate2str(link->l_operstate, buf, sizeof(buf)); - dp_dump(p, "state %s ", buf); + nl_dump(p, "state %s ", buf); } - dp_dump(p, "mode %s\n", + nl_dump(p, "mode %s\n", rtnl_link_mode2str(link->l_linkmode, buf, sizeof(buf))); - if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_FULL]) - line = link->l_info_ops->io_dump[NL_DUMP_FULL](link, p, line); - - return line; + if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_DETAILS]) + link->l_info_ops->io_dump[NL_DUMP_DETAILS](link, p); } -static int link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_link *link = (struct rtnl_link *) obj; char *unit, fmt[64]; float res; - int line; - line = link_dump_full(obj, p); + link_dump_details(obj, p); - dp_dump_line(p, line++, " Stats: bytes packets errors " - " dropped fifo-err compressed\n"); + nl_dump_line(p, " Stats: bytes packets errors " + " dropped fifo-err compressed\n"); res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_RX_BYTES], &unit); - strcpy(fmt, " RX %X.2f %s %10llu %10llu %10llu %10llu %10llu\n"); + strcpy(fmt, " RX %X.2f %s %10llu %10llu %10llu %10llu %10llu\n"); fmt[9] = *unit == 'B' ? '9' : '7'; - dp_dump_line(p, line++, fmt, - res, unit, + nl_dump_line(p, fmt, res, unit, link->l_stats[RTNL_LINK_RX_PACKETS], link->l_stats[RTNL_LINK_RX_ERRORS], link->l_stats[RTNL_LINK_RX_DROPPED], @@ -546,21 +528,20 @@ static int link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_TX_BYTES], &unit); - strcpy(fmt, " TX %X.2f %s %10llu %10llu %10llu %10llu %10llu\n"); + strcpy(fmt, " TX %X.2f %s %10llu %10llu %10llu %10llu %10llu\n"); fmt[9] = *unit == 'B' ? '9' : '7'; - dp_dump_line(p, line++, fmt, - res, unit, + nl_dump_line(p, fmt, res, unit, link->l_stats[RTNL_LINK_TX_PACKETS], link->l_stats[RTNL_LINK_TX_ERRORS], link->l_stats[RTNL_LINK_TX_DROPPED], link->l_stats[RTNL_LINK_TX_FIFO_ERR], link->l_stats[RTNL_LINK_TX_COMPRESSED]); - dp_dump_line(p, line++, " Errors: length over crc " - " frame missed multicast\n"); + nl_dump_line(p, " Errors: length over crc " + " frame missed multicast\n"); - dp_dump_line(p, line++, " RX %10" PRIu64 " %10" PRIu64 " %10" + nl_dump_line(p, " RX %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n", link->l_stats[RTNL_LINK_RX_LEN_ERR], @@ -570,11 +551,11 @@ static int link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) link->l_stats[RTNL_LINK_RX_MISSED_ERR], link->l_stats[RTNL_LINK_MULTICAST]); - dp_dump_line(p, line++, " Errors: aborted carrier heartbeat " - " window collision\n"); + nl_dump_line(p, " aborted carrier heartbeat " + " window collision\n"); - dp_dump_line(p, line++, " TX %10" PRIu64 " %10" PRIu64 " %10" - PRIu64 " %10" PRIu64 " %10" PRIu64 "\n", + nl_dump_line(p, " TX %10" PRIu64 " %10" PRIu64 " %10" + PRIu64 " %10" PRIu64 " %10" PRIu64 "\n", link->l_stats[RTNL_LINK_TX_ABORT_ERR], link->l_stats[RTNL_LINK_TX_CARRIER_ERR], link->l_stats[RTNL_LINK_TX_HBEAT_ERR], @@ -582,132 +563,56 @@ static int link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) link->l_stats[RTNL_LINK_TX_COLLISIONS]); if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_STATS]) - line = link->l_info_ops->io_dump[NL_DUMP_STATS](link, p, line); - - return line; + link->l_info_ops->io_dump[NL_DUMP_STATS](link, p); } -static int link_dump_xml(struct nl_object *obj, struct nl_dump_params *p) +static void link_dump_env(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_link *link = (struct rtnl_link *) obj; struct nl_cache *cache = dp_cache(obj); char buf[128]; - int i, line = 0; + int i; - dp_dump_line(p, line++, "<link name=\"%s\" index=\"%u\">\n", - link->l_name, link->l_index); - dp_dump_line(p, line++, " <family>%s</family>\n", + nl_dump_line(p, "LINK_NAME=%s\n", link->l_name); + nl_dump_line(p, "LINK_IFINDEX=%u\n", link->l_index); + nl_dump_line(p, "LINK_FAMILY=%s\n", nl_af2str(link->l_family, buf, sizeof(buf))); - dp_dump_line(p, line++, " <arptype>%s</arptype>\n", - nl_llproto2str(link->l_arptype, buf, sizeof(buf))); - dp_dump_line(p, line++, " <address>%s</address>\n", - nl_addr2str(link->l_addr, buf, sizeof(buf))); - dp_dump_line(p, line++, " <mtu>%u</mtu>\n", link->l_mtu); - dp_dump_line(p, line++, " <txqlen>%u</txqlen>\n", link->l_txqlen); - dp_dump_line(p, line++, " <weight>%u</weight>\n", link->l_weight); - - rtnl_link_flags2str(link->l_flags, buf, sizeof(buf)); - if (buf[0]) - dp_dump_line(p, line++, " <flags>%s</flags>\n", buf); - - if (link->ce_mask & LINK_ATTR_QDISC) - dp_dump_line(p, line++, " <qdisc>%s</qdisc>\n", link->l_qdisc); - - if (link->ce_mask & LINK_ATTR_LINK) { - struct rtnl_link *ll = rtnl_link_get(cache, link->l_link); - dp_dump_line(p, line++, " <link>%s</link>\n", - ll ? ll->l_name : "none"); - if (ll) - rtnl_link_put(ll); - } - - if (link->ce_mask & LINK_ATTR_MASTER) { - struct rtnl_link *master = rtnl_link_get(cache, link->l_master); - dp_dump_line(p, line++, " <master>%s</master>\n", - master ? master->l_name : "none"); - if (master) - rtnl_link_put(master); - } - - if (link->ce_mask & LINK_ATTR_BRD) - dp_dump_line(p, line++, " <broadcast>%s</broadcast>\n", - nl_addr2str(link->l_bcast, buf, sizeof(buf))); - - if (link->ce_mask & LINK_ATTR_STATS) { - dp_dump_line(p, line++, " <stats>\n"); - for (i = 0; i <= RTNL_LINK_STATS_MAX; i++) { - rtnl_link_stat2str(i, buf, sizeof(buf)); - dp_dump_line(p, line++, - " <%s>%" PRIu64 "</%s>\n", - buf, link->l_stats[i], buf); - } - dp_dump_line(p, line++, " </stats>\n"); - } - - if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_XML]) { - dp_dump_line(p, line++, " <info>\n"); - line = link->l_info_ops->io_dump[NL_DUMP_XML](link, p, line); - dp_dump_line(p, line++, " </info>\n"); - } - - dp_dump_line(p, line++, "</link>\n"); - -#if 0 - uint32_t l_change; /**< Change mask */ - struct rtnl_lifmap l_map; /**< Interface device mapping */ -#endif - - return line; -} - -static int link_dump_env(struct nl_object *obj, struct nl_dump_params *p) -{ - struct rtnl_link *link = (struct rtnl_link *) obj; - struct nl_cache *cache = dp_cache(obj); - char buf[128]; - int i, line = 0; - - dp_dump_line(p, line++, "LINK_NAME=%s\n", link->l_name); - dp_dump_line(p, line++, "LINK_IFINDEX=%u\n", link->l_index); - dp_dump_line(p, line++, "LINK_FAMILY=%s\n", - nl_af2str(link->l_family, buf, sizeof(buf))); - dp_dump_line(p, line++, "LINK_TYPE=%s\n", + nl_dump_line(p, "LINK_TYPE=%s\n", nl_llproto2str(link->l_arptype, buf, sizeof(buf))); if (link->ce_mask & LINK_ATTR_ADDR) - dp_dump_line(p, line++, "LINK_ADDRESS=%s\n", + nl_dump_line(p, "LINK_ADDRESS=%s\n", nl_addr2str(link->l_addr, buf, sizeof(buf))); - dp_dump_line(p, line++, "LINK_MTU=%u\n", link->l_mtu); - dp_dump_line(p, line++, "LINK_TXQUEUELEN=%u\n", link->l_txqlen); - dp_dump_line(p, line++, "LINK_WEIGHT=%u\n", link->l_weight); + nl_dump_line(p, "LINK_MTU=%u\n", link->l_mtu); + nl_dump_line(p, "LINK_TXQUEUELEN=%u\n", link->l_txqlen); + nl_dump_line(p, "LINK_WEIGHT=%u\n", link->l_weight); rtnl_link_flags2str(link->l_flags & ~IFF_RUNNING, buf, sizeof(buf)); if (buf[0]) - dp_dump_line(p, line++, "LINK_FLAGS=%s\n", buf); + nl_dump_line(p, "LINK_FLAGS=%s\n", buf); if (link->ce_mask & LINK_ATTR_QDISC) - dp_dump_line(p, line++, "LINK_QDISC=%s\n", link->l_qdisc); + nl_dump_line(p, "LINK_QDISC=%s\n", link->l_qdisc); if (link->ce_mask & LINK_ATTR_LINK) { struct rtnl_link *ll = rtnl_link_get(cache, link->l_link); - dp_dump_line(p, line++, "LINK_LINK_IFINDEX=%d\n", link->l_link); + nl_dump_line(p, "LINK_LINK_IFINDEX=%d\n", link->l_link); if (ll) { - dp_dump_line(p, line++, "LINK_LINK_IFNAME=%s\n", - ll->l_name); + nl_dump_line(p, "LINK_LINK_IFNAME=%s\n", ll->l_name); rtnl_link_put(ll); } } if (link->ce_mask & LINK_ATTR_MASTER) { struct rtnl_link *master = rtnl_link_get(cache, link->l_master); - dp_dump_line(p, line++, "LINK_MASTER=%s\n", + nl_dump_line(p, "LINK_MASTER=%s\n", master ? master->l_name : "none"); if (master) rtnl_link_put(master); } if (link->ce_mask & LINK_ATTR_BRD) - dp_dump_line(p, line++, "LINK_BROADCAST=%s\n", + nl_dump_line(p, "LINK_BROADCAST=%s\n", nl_addr2str(link->l_bcast, buf, sizeof(buf))); if (link->ce_mask & LINK_ATTR_STATS) { @@ -720,15 +625,12 @@ static int link_dump_env(struct nl_object *obj, struct nl_dump_params *p) *c = toupper(*c); c++; } - dp_dump_line(p, line++, - "%s=%" PRIu64 "\n", buf, link->l_stats[i]); + nl_dump_line(p, "%s=%" PRIu64 "\n", buf, link->l_stats[i]); } } if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_ENV]) - line = link->l_info_ops->io_dump[NL_DUMP_ENV](link, p, line); - - return line; + link->l_info_ops->io_dump[NL_DUMP_ENV](link, p); } #if 0 @@ -800,7 +702,7 @@ static int link_compare(struct nl_object *_a, struct nl_object *_b, diff |= LINK_DIFF(ADDR, nl_addr_cmp(a->l_addr, b->l_addr)); diff |= LINK_DIFF(BRD, nl_addr_cmp(a->l_bcast, b->l_bcast)); - if (flags & LOOSE_FLAG_COMPARISON) + if (flags & LOOSE_COMPARISON) diff |= LINK_DIFF(FLAGS, (a->l_flags ^ b->l_flags) & b->l_flag_mask); else @@ -863,28 +765,17 @@ void rtnl_link_put(struct rtnl_link *link) /** * Allocate link cache and fill in all configured links. - * @arg handle Netlink handle. + * @arg sk Netlink socket. + * @arg result Pointer to store resulting cache. * * Allocates a new link cache, initializes it properly and updates it * to include all links currently configured in the kernel. * - * @note Free the memory after usage. - * @return Newly allocated cache or NULL if an error occured. + * @return 0 on success or a negative error code. */ -struct nl_cache *rtnl_link_alloc_cache(struct nl_handle *handle) +int rtnl_link_alloc_cache(struct nl_sock *sk, struct nl_cache **result) { - struct nl_cache * cache; - - cache = nl_cache_alloc(&rtnl_link_ops); - if (cache == NULL) - return NULL; - - if (handle && nl_cache_refill(handle, cache) < 0) { - nl_cache_free(cache); - return NULL; - } - - return cache; + return nl_cache_alloc_and_fill(&rtnl_link_ops, sk, result); } /** @@ -967,9 +858,9 @@ struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache, * @note Not all attributes can be changed, see * \ref link_changeable "Changeable Attributes" for more details. */ -struct nl_msg * rtnl_link_build_change_request(struct rtnl_link *old, - struct rtnl_link *tmpl, - int flags) +int rtnl_link_build_change_request(struct rtnl_link *old, + struct rtnl_link *tmpl, int flags, + struct nl_msg **result) { struct nl_msg *msg; struct ifinfomsg ifi = { @@ -984,7 +875,7 @@ struct nl_msg * rtnl_link_build_change_request(struct rtnl_link *old, msg = nlmsg_alloc_simple(RTM_SETLINK, flags); if (!msg) - goto nla_put_failure; + return -NLE_NOMEM; if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -1028,16 +919,17 @@ struct nl_msg * rtnl_link_build_change_request(struct rtnl_link *old, nla_nest_end(msg, info); } - return msg; + *result = msg; + return 0; nla_put_failure: nlmsg_free(msg); - return NULL; + return -NLE_MSGSIZE; } /** * Change link attributes - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg old link to be changed * @arg tmpl template with requested changes * @arg flags additional netlink message flags @@ -1050,22 +942,21 @@ nla_put_failure: * @note Not all attributes can be changed, see * \ref link_changeable "Changeable Attributes" for more details. */ -int rtnl_link_change(struct nl_handle *handle, struct rtnl_link *old, +int rtnl_link_change(struct nl_sock *sk, struct rtnl_link *old, struct rtnl_link *tmpl, int flags) { - int err; struct nl_msg *msg; + int err; - msg = rtnl_link_build_change_request(old, tmpl, flags); - if (!msg) - return nl_errno(ENOMEM); + if ((err = rtnl_link_build_change_request(old, tmpl, flags, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); if (err < 0) return err; - nlmsg_free(msg); - return nl_wait_for_ack(handle); + return wait_for_ack(sk); } /** @} */ @@ -1106,11 +997,11 @@ char * rtnl_link_i2name(struct nl_cache *cache, int ifindex, char *dst, * @arg cache link cache * @arg name link name * - * @return interface index or RTNL_LINK_NOT_FOUND if no match was found. + * @return interface index or 0 if no match was found. */ int rtnl_link_name2i(struct nl_cache *cache, const char *name) { - int ifindex = RTNL_LINK_NOT_FOUND; + int ifindex = 0; struct rtnl_link *link; link = rtnl_link_get_by_name(cache, name); @@ -1380,10 +1271,7 @@ void rtnl_link_set_ifindex(struct rtnl_link *link, int ifindex) int rtnl_link_get_ifindex(struct rtnl_link *link) { - if (link->ce_mask & LINK_ATTR_IFINDEX) - return link->l_index; - else - return RTNL_LINK_NOT_FOUND; + return link->l_index; } void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu) @@ -1436,10 +1324,7 @@ void rtnl_link_set_link(struct rtnl_link *link, int ifindex) int rtnl_link_get_link(struct rtnl_link *link) { - if (link->ce_mask & LINK_ATTR_LINK) - return link->l_link; - else - return RTNL_LINK_NOT_FOUND; + return link->l_link; } void rtnl_link_set_master(struct rtnl_link *link, int ifindex) @@ -1450,10 +1335,7 @@ void rtnl_link_set_master(struct rtnl_link *link, int ifindex) int rtnl_link_get_master(struct rtnl_link *link) { - if (link->ce_mask & LINK_ATTR_MASTER) - return link->l_master; - else - return RTNL_LINK_NOT_FOUND; + return link->l_master; } void rtnl_link_set_operstate(struct rtnl_link *link, uint8_t operstate) @@ -1509,7 +1391,7 @@ int rtnl_link_set_info_type(struct rtnl_link *link, const char *type) int err; if ((io = rtnl_link_info_ops_lookup(type)) == NULL) - return nl_error(ENOENT, "No such link info type exists"); + return -NLE_OPNOTSUPP; if (link->l_info_ops) release_link_info(link); @@ -1544,11 +1426,12 @@ static struct nl_object_ops link_obj_ops = { .oo_size = sizeof(struct rtnl_link), .oo_free_data = link_free_data, .oo_clone = link_clone, - .oo_dump[NL_DUMP_BRIEF] = link_dump_brief, - .oo_dump[NL_DUMP_FULL] = link_dump_full, - .oo_dump[NL_DUMP_STATS] = link_dump_stats, - .oo_dump[NL_DUMP_XML] = link_dump_xml, - .oo_dump[NL_DUMP_ENV] = link_dump_env, + .oo_dump = { + [NL_DUMP_LINE] = link_dump_line, + [NL_DUMP_DETAILS] = link_dump_details, + [NL_DUMP_STATS] = link_dump_stats, + [NL_DUMP_ENV] = link_dump_env, + }, .oo_compare = link_compare, .oo_attrs2str = link_attrs2str, .oo_id_attrs = LINK_ATTR_IFINDEX, diff --git a/lib/route/link/api.c b/lib/route/link/api.c index afe00b1..a0e7679 100644 --- a/lib/route/link/api.c +++ b/lib/route/link/api.c @@ -61,10 +61,10 @@ struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *name) int rtnl_link_register_info(struct rtnl_link_info_ops *ops) { if (ops->io_name == NULL) - return nl_error(EINVAL, "No name specified"); + return -NLE_INVAL; if (rtnl_link_info_ops_lookup(ops->io_name)) - return nl_error(EEXIST, "Link info operations already exist"); + return -NLE_EXIST; NL_DBG(1, "Registered link info operations %s\n", ops->io_name); @@ -83,10 +83,10 @@ int rtnl_link_unregister_info(struct rtnl_link_info_ops *ops) break; if (!t) - return nl_error(ENOENT, "No such link info operations"); + return -NLE_OPNOTSUPP; if (t->io_refcnt > 0) - return nl_error(EBUSY, "Info operations in use"); + return -NLE_BUSY; NL_DBG(1, "Unregistered link info perations %s\n", ops->io_name); diff --git a/lib/route/link/vlan.c b/lib/route/link/vlan.c index c148dca..c466afe 100644 --- a/lib/route/link/vlan.c +++ b/lib/route/link/vlan.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2007 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -73,7 +73,7 @@ static int vlan_alloc(struct rtnl_link *link) struct vlan_info *vi; if ((vi = calloc(1, sizeof(*vi))) == NULL) - return nl_errno(ENOMEM); + return -NLE_NOMEM; link->l_info = vi; @@ -119,12 +119,11 @@ static int vlan_parse(struct rtnl_link *link, struct nlattr *data, nla_for_each_nested(nla, tb[IFLA_VLAN_INGRESS_QOS], remaining) { if (nla_len(nla) < sizeof(*map)) - return nl_error(EINVAL, "Malformed mapping"); + return -NLE_INVAL; map = nla_data(nla); if (map->from < 0 || map->from > VLAN_PRIO_MAX) { - return nl_error(EINVAL, "VLAN prio %d out of " - "range", map->from); + return -NLE_INVAL; } vi->vi_ingress_qos[map->from] = map->to; @@ -140,7 +139,7 @@ static int vlan_parse(struct rtnl_link *link, struct nlattr *data, nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) { if (nla_len(nla) < sizeof(*map)) - return nl_error(EINVAL, "Malformed mapping"); + return -NLE_INVAL; i++; } @@ -148,7 +147,7 @@ static int vlan_parse(struct rtnl_link *link, struct nlattr *data, vi->vi_egress_size = (i + 32) & ~31; vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*map)); if (vi->vi_egress_qos == NULL) - return nl_errno(ENOMEM); + return -NLE_NOMEM; i = 0; nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) { @@ -180,71 +179,60 @@ static void vlan_free(struct rtnl_link *link) link->l_info = NULL; } -static int vlan_dump_brief(struct rtnl_link *link, struct nl_dump_params *p, - int line) +static void vlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p) { struct vlan_info *vi = link->l_info; - dp_dump(p, "vlan-id %d", vi->vi_vlan_id); - - return line; + nl_dump(p, "vlan-id %d", vi->vi_vlan_id); } -static int vlan_dump_full(struct rtnl_link *link, struct nl_dump_params *p, - int line) +static void vlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p) { struct vlan_info *vi = link->l_info; int i, printed; char buf[64]; rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf)); - dp_dump_line(p, line++, " vlan-info id %d <%s>\n", - vi->vi_vlan_id, buf); + nl_dump_line(p, " vlan-info id %d <%s>\n", vi->vi_vlan_id, buf); if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) { - dp_dump_line(p, line++, + nl_dump_line(p, " ingress vlan prio -> qos/socket prio mapping:\n"); for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) { if (vi->vi_ingress_qos[i]) { - if (printed == 0) { - dp_new_line(p, line); - dp_dump(p, " "); - } - dp_dump(p, "%x -> %#08x, ", + if (printed == 0) + nl_dump_line(p, " "); + nl_dump(p, "%x -> %#08x, ", i, vi->vi_ingress_qos[i]); if (printed++ == 3) { - dp_dump(p, "\n"); + nl_dump(p, "\n"); printed = 0; } } } if (printed > 0 && printed != 4) - dp_dump(p, "\n"); + nl_dump(p, "\n"); } if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { - dp_dump_line(p, line++, + nl_dump_line(p, " egress qos/socket prio -> vlan prio mapping:\n"); for (i = 0, printed = 0; i < vi->vi_negress; i++) { - if (printed == 0) { - dp_new_line(p, line); - dp_dump(p, " "); - } - dp_dump(p, "%#08x -> %x, ", + if (printed == 0) + nl_dump_line(p, " "); + nl_dump(p, "%#08x -> %x, ", vi->vi_egress_qos[i].vm_from, vi->vi_egress_qos[i].vm_to); if (printed++ == 3) { - dp_dump(p, "\n"); + nl_dump(p, "\n"); printed = 0; } } if (printed > 0 && printed != 4) - dp_dump(p, "\n"); + nl_dump(p, "\n"); } - - return line; } static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src) @@ -260,7 +248,7 @@ static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src) vdst->vi_egress_qos = calloc(vsrc->vi_egress_size, sizeof(struct vlan_map)); if (!vdst->vi_egress_qos) - return nl_errno(ENOMEM); + return -NLE_NOMEM; memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos, vsrc->vi_egress_size * sizeof(struct vlan_map)); @@ -274,7 +262,7 @@ static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link) struct nlattr *data; if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - return nl_errno(ENOBUFS); + return -NLE_MSGSIZE; if (vi->vi_mask & VLAN_HAS_ID) NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id); @@ -337,8 +325,10 @@ static struct rtnl_link_info_ops vlan_info_ops = { .io_name = "vlan", .io_alloc = vlan_alloc, .io_parse = vlan_parse, - .io_dump[NL_DUMP_BRIEF] = vlan_dump_brief, - .io_dump[NL_DUMP_FULL] = vlan_dump_full, + .io_dump = { + [NL_DUMP_LINE] = vlan_dump_line, + [NL_DUMP_DETAILS] = vlan_dump_details, + }, .io_clone = vlan_clone, .io_put_attrs = vlan_put_attrs, .io_free = vlan_free, @@ -349,7 +339,7 @@ int rtnl_link_vlan_set_id(struct rtnl_link *link, int id) struct vlan_info *vi = link->l_info; if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) - return nl_error(EOPNOTSUPP, "Not a VLAN link"); + return -NLE_OPNOTSUPP; vi->vi_vlan_id = id; vi->vi_mask |= VLAN_HAS_ID; @@ -362,7 +352,7 @@ int rtnl_link_vlan_get_id(struct rtnl_link *link) struct vlan_info *vi = link->l_info; if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) - return nl_error(EOPNOTSUPP, "Not a VLAN link"); + return -NLE_OPNOTSUPP; if (vi->vi_mask & VLAN_HAS_ID) return vi->vi_vlan_id; @@ -375,7 +365,7 @@ int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags) struct vlan_info *vi = link->l_info; if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) - return nl_error(EOPNOTSUPP, "Not a VLAN link"); + return -NLE_OPNOTSUPP; vi->vi_flags_mask |= flags; vi->vi_flags |= flags; @@ -389,7 +379,7 @@ int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags) struct vlan_info *vi = link->l_info; if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) - return nl_error(EOPNOTSUPP, "Not a VLAN link"); + return -NLE_OPNOTSUPP; vi->vi_flags_mask |= flags; vi->vi_flags &= ~flags; @@ -403,7 +393,7 @@ unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *link) struct vlan_info *vi = link->l_info; if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) - return nl_error(EOPNOTSUPP, "Not a VLAN link"); + return -NLE_OPNOTSUPP; return vi->vi_flags; } @@ -414,11 +404,10 @@ int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from, struct vlan_info *vi = link->l_info; if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) - return nl_error(EOPNOTSUPP, "Not a VLAN link"); + return -NLE_OPNOTSUPP; if (from < 0 || from > VLAN_PRIO_MAX) - return nl_error(EINVAL, "Invalid vlan prio 0..%d", - VLAN_PRIO_MAX); + return -NLE_INVAL; vi->vi_ingress_qos[from] = to; vi->vi_mask |= VLAN_HAS_INGRESS_QOS; @@ -430,10 +419,8 @@ uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link) { struct vlan_info *vi = link->l_info; - if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) { - nl_error(EOPNOTSUPP, "Not a VLAN link"); + if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) return NULL; - } if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) return vi->vi_ingress_qos; @@ -446,11 +433,10 @@ int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to) struct vlan_info *vi = link->l_info; if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) - return nl_error(EOPNOTSUPP, "Not a VLAN link"); + return -NLE_OPNOTSUPP; if (to < 0 || to > VLAN_PRIO_MAX) - return nl_error(EINVAL, "Invalid vlan prio 0..%d", - VLAN_PRIO_MAX); + return -NLE_INVAL; if (vi->vi_negress >= vi->vi_egress_size) { int new_size = vi->vi_egress_size + 32; @@ -458,7 +444,7 @@ int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to) ptr = realloc(vi->vi_egress_qos, new_size); if (!ptr) - return nl_errno(ENOMEM); + return -NLE_NOMEM; vi->vi_egress_qos = ptr; vi->vi_egress_size = new_size; @@ -477,15 +463,11 @@ struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link, { struct vlan_info *vi = link->l_info; - if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) { - nl_error(EOPNOTSUPP, "Not a VLAN link"); + if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) return NULL; - } - if (negress == NULL) { - nl_error(EINVAL, "Require pointer to store negress"); + if (negress == NULL) return NULL; - } if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { *negress = vi->vi_negress; diff --git a/lib/route/neigh.c b/lib/route/neigh.c index 6f2f0d3..d4dc82c 100644 --- a/lib/route/neigh.c +++ b/lib/route/neigh.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -61,7 +61,7 @@ * @code * // The first step is to retrieve a list of all available neighbour within * // the kernel and put them into a cache. - * struct nl_cache *cache = rtnl_neigh_alloc_cache(handle); + * struct nl_cache *cache = rtnl_neigh_alloc_cache(sk); * * // Neighbours can then be looked up by the interface and destination * // address: @@ -86,7 +86,7 @@ * // block until the operation has been completed. Alternatively the required * // netlink message can be built using rtnl_neigh_build_add_request() * // to be sent out using nl_send_auto_complete(). - * rtnl_neigh_add(nl_handle, neigh, NLM_F_REPLACE); + * rtnl_neigh_add(sk, neigh, NLM_F_CREATE); * * // Free the memory * rtnl_neigh_put(neigh); @@ -109,7 +109,7 @@ * // block until the operation has been completed. Alternatively the required * // netlink message can be built using rtnl_neigh_build_delete_request() * // to be sent out using nl_send_auto_complete(). - * rtnl_neigh_delete(handle, neigh, 0); + * rtnl_neigh_delete(sk, neigh, 0); * * // Free the memory * rtnl_neigh_put(neigh); @@ -139,7 +139,7 @@ * // block until the operation has been completed. Alternatively the required * // netlink message can be built using rtnl_neigh_build_change_request() * // to be sent out using nl_send_auto_complete(). - * rtnl_neigh_change(handle, neigh, 0); + * rtnl_neigh_add(sk, neigh, NLM_F_REPLACE); * * // Free the memory * rtnl_neigh_put(neigh); @@ -187,15 +187,13 @@ static int neigh_clone(struct nl_object *_dst, struct nl_object *_src) if (src->n_lladdr) if (!(dst->n_lladdr = nl_addr_clone(src->n_lladdr))) - goto errout; + return -NLE_NOMEM; if (src->n_dst) if (!(dst->n_dst = nl_addr_clone(src->n_dst))) - goto errout; + return -NLE_NOMEM; return 0; -errout: - return nl_get_errno(); } static int neigh_compare(struct nl_object *_a, struct nl_object *_b, @@ -213,7 +211,7 @@ static int neigh_compare(struct nl_object *_a, struct nl_object *_b, diff |= NEIGH_DIFF(LLADDR, nl_addr_cmp(a->n_lladdr, b->n_lladdr)); diff |= NEIGH_DIFF(DST, nl_addr_cmp(a->n_dst, b->n_dst)); - if (flags & LOOSE_FLAG_COMPARISON) { + if (flags & LOOSE_COMPARISON) { diff |= NEIGH_DIFF(STATE, (a->n_state ^ b->n_state) & b->n_state_mask); diff |= NEIGH_DIFF(FLAGS, @@ -261,7 +259,7 @@ static int neigh_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, neigh = rtnl_neigh_alloc(); if (!neigh) { - err = nl_errno(ENOMEM); + err = -NLE_NOMEM; goto errout; } @@ -283,18 +281,22 @@ static int neigh_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, NEIGH_ATTR_TYPE); if (tb[NDA_LLADDR]) { - neigh->n_lladdr = nla_get_addr(tb[NDA_LLADDR], AF_UNSPEC); - if (!neigh->n_lladdr) + neigh->n_lladdr = nl_addr_alloc_attr(tb[NDA_LLADDR], AF_UNSPEC); + if (!neigh->n_lladdr) { + err = -NLE_NOMEM; goto errout; + } nl_addr_set_family(neigh->n_lladdr, nl_addr_guess_family(neigh->n_lladdr)); neigh->ce_mask |= NEIGH_ATTR_LLADDR; } if (tb[NDA_DST]) { - neigh->n_dst = nla_get_addr(tb[NDA_DST], neigh->n_family); - if (!neigh->n_dst) + neigh->n_dst = nl_addr_alloc_attr(tb[NDA_DST], neigh->n_family); + if (!neigh->n_dst) { + err = -NLE_NOMEM; goto errout; + } neigh->ce_mask |= NEIGH_ATTR_DST; } @@ -315,23 +317,18 @@ static int neigh_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, } err = pp->pp_cb((struct nl_object *) neigh, pp); - if (err < 0) - goto errout; - - err = P_ACCEPT; - errout: rtnl_neigh_put(neigh); return err; } -static int neigh_request_update(struct nl_cache *c, struct nl_handle *h) +static int neigh_request_update(struct nl_cache *c, struct nl_sock *h) { return nl_rtgen_request(h, RTM_GETNEIGH, AF_UNSPEC, NLM_F_DUMP); } -static int neigh_dump_brief(struct nl_object *a, struct nl_dump_params *p) +static void neigh_dump_line(struct nl_object *a, struct nl_dump_params *p) { char dst[INET6_ADDRSTRLEN+5], lladdr[INET6_ADDRSTRLEN+5]; struct rtnl_neigh *n = (struct rtnl_neigh *) a; @@ -340,162 +337,95 @@ static int neigh_dump_brief(struct nl_object *a, struct nl_dump_params *p) link_cache = nl_cache_mngt_require("route/link"); - dp_dump(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst))); + nl_dump_line(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst))); if (link_cache) - dp_dump(p, "dev %s ", + nl_dump(p, "dev %s ", rtnl_link_i2name(link_cache, n->n_ifindex, state, sizeof(state))); else - dp_dump(p, "dev %d ", n->n_ifindex); + nl_dump(p, "dev %d ", n->n_ifindex); if (n->ce_mask & NEIGH_ATTR_LLADDR) - dp_dump(p, "lladdr %s ", + nl_dump(p, "lladdr %s ", nl_addr2str(n->n_lladdr, lladdr, sizeof(lladdr))); rtnl_neigh_state2str(n->n_state, state, sizeof(state)); rtnl_neigh_flags2str(n->n_flags, flags, sizeof(flags)); if (state[0]) - dp_dump(p, "<%s", state); + nl_dump(p, "<%s", state); if (flags[0]) - dp_dump(p, "%s%s", state[0] ? "," : "<", flags); + nl_dump(p, "%s%s", state[0] ? "," : "<", flags); if (state[0] || flags[0]) - dp_dump(p, ">"); - dp_dump(p, "\n"); - - return 1; + nl_dump(p, ">"); + nl_dump(p, "\n"); } -static int neigh_dump_full(struct nl_object *a, struct nl_dump_params *p) +static void neigh_dump_details(struct nl_object *a, struct nl_dump_params *p) { char rtn_type[32]; struct rtnl_neigh *n = (struct rtnl_neigh *) a; int hz = nl_get_hz(); - int line = neigh_dump_brief(a, p); + neigh_dump_line(a, p); - dp_dump_line(p, line++, " refcnt %u type %s confirmed %u used " + nl_dump_line(p, " refcnt %u type %s confirmed %u used " "%u updated %u\n", n->n_cacheinfo.nci_refcnt, nl_rtntype2str(n->n_type, rtn_type, sizeof(rtn_type)), n->n_cacheinfo.nci_confirmed/hz, n->n_cacheinfo.nci_used/hz, n->n_cacheinfo.nci_updated/hz); - - return line; -} - -static int neigh_dump_stats(struct nl_object *a, struct nl_dump_params *p) -{ - return neigh_dump_full(a, p); } -static int neigh_dump_xml(struct nl_object *obj, struct nl_dump_params *p) +static void neigh_dump_stats(struct nl_object *a, struct nl_dump_params *p) { - struct rtnl_neigh *neigh = (struct rtnl_neigh *) obj; - char buf[128]; - int line = 0; - - dp_dump_line(p, line++, "<neighbour>\n"); - dp_dump_line(p, line++, " <family>%s</family>\n", - nl_af2str(neigh->n_family, buf, sizeof(buf))); - - if (neigh->ce_mask & NEIGH_ATTR_LLADDR) - dp_dump_line(p, line++, " <lladdr>%s</lladdr>\n", - nl_addr2str(neigh->n_lladdr, buf, sizeof(buf))); - - if (neigh->ce_mask & NEIGH_ATTR_DST) - dp_dump_line(p, line++, " <dst>%s</dst>\n", - nl_addr2str(neigh->n_dst, buf, sizeof(buf))); - - if (neigh->ce_mask & NEIGH_ATTR_IFINDEX) { - struct nl_cache *link_cache; - - link_cache = nl_cache_mngt_require("route/link"); - - if (link_cache) - dp_dump_line(p, line++, " <device>%s</device>\n", - rtnl_link_i2name(link_cache, - neigh->n_ifindex, - buf, sizeof(buf))); - else - dp_dump_line(p, line++, " <device>%u</device>\n", - neigh->n_ifindex); - } - - if (neigh->ce_mask & NEIGH_ATTR_PROBES) - dp_dump_line(p, line++, " <probes>%u</probes>\n", - neigh->n_probes); - - if (neigh->ce_mask & NEIGH_ATTR_TYPE) - dp_dump_line(p, line++, " <type>%s</type>\n", - nl_rtntype2str(neigh->n_type, buf, sizeof(buf))); - - rtnl_neigh_flags2str(neigh->n_flags, buf, sizeof(buf)); - if (buf[0]) - dp_dump_line(p, line++, " <flags>%s</flags>\n", buf); - - rtnl_neigh_state2str(neigh->n_state, buf, sizeof(buf)); - if (buf[0]) - dp_dump_line(p, line++, " <state>%s</state>\n", buf); - - dp_dump_line(p, line++, "</neighbour>\n"); - -#if 0 - struct rtnl_ncacheinfo n_cacheinfo; -#endif - - return line; + neigh_dump_details(a, p); } -static int neigh_dump_env(struct nl_object *obj, struct nl_dump_params *p) +static void neigh_dump_env(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_neigh *neigh = (struct rtnl_neigh *) obj; char buf[128]; - int line = 0; - dp_dump_line(p, line++, "NEIGH_FAMILY=%s\n", + nl_dump_line(p, "NEIGH_FAMILY=%s\n", nl_af2str(neigh->n_family, buf, sizeof(buf))); if (neigh->ce_mask & NEIGH_ATTR_LLADDR) - dp_dump_line(p, line++, "NEIGHT_LLADDR=%s\n", + nl_dump_line(p, "NEIGHT_LLADDR=%s\n", nl_addr2str(neigh->n_lladdr, buf, sizeof(buf))); if (neigh->ce_mask & NEIGH_ATTR_DST) - dp_dump_line(p, line++, "NEIGH_DST=%s\n", + nl_dump_line(p, "NEIGH_DST=%s\n", nl_addr2str(neigh->n_dst, buf, sizeof(buf))); if (neigh->ce_mask & NEIGH_ATTR_IFINDEX) { struct nl_cache *link_cache; - dp_dump_line(p, line++, "NEIGH_IFINDEX=%u\n", - neigh->n_ifindex); + nl_dump_line(p, "NEIGH_IFINDEX=%u\n", neigh->n_ifindex); link_cache = nl_cache_mngt_require("route/link"); if (link_cache) - dp_dump_line(p, line++, "NEIGH_IFNAME=%s\n", + nl_dump_line(p, "NEIGH_IFNAME=%s\n", rtnl_link_i2name(link_cache, neigh->n_ifindex, buf, sizeof(buf))); } if (neigh->ce_mask & NEIGH_ATTR_PROBES) - dp_dump_line(p, line++, "NEIGH_PROBES=%u\n", - neigh->n_probes); + nl_dump_line(p, "NEIGH_PROBES=%u\n", neigh->n_probes); if (neigh->ce_mask & NEIGH_ATTR_TYPE) - dp_dump_line(p, line++, "NEIGH_TYPE=%s\n", + nl_dump_line(p, "NEIGH_TYPE=%s\n", nl_rtntype2str(neigh->n_type, buf, sizeof(buf))); rtnl_neigh_flags2str(neigh->n_flags, buf, sizeof(buf)); if (buf[0]) - dp_dump_line(p, line++, "NEIGH_FLAGS=%s\n", buf); + nl_dump_line(p, "NEIGH_FLAGS=%s\n", buf); rtnl_neigh_state2str(neigh->n_state, buf, sizeof(buf)); if (buf[0]) - dp_dump_line(p, line++, "NEIGH_STATE=%s\n", buf); - - return line; + nl_dump_line(p, "NEIGH_STATE=%s\n", buf); } /** @@ -522,31 +452,17 @@ void rtnl_neigh_put(struct rtnl_neigh *neigh) /** * Build a neighbour cache including all neighbours currently configured in the kernel. - * @arg handle netlink handle + * @arg sk Netlink socket. + * @arg result Pointer to store resulting cache. * * Allocates a new neighbour cache, initializes it properly and updates it * to include all neighbours currently configured in the kernel. * - * @note The caller is responsible for destroying and freeing the - * cache after using it. - * @return The new cache or NULL if an error occured. + * @return 0 on success or a negative error code. */ -struct nl_cache *rtnl_neigh_alloc_cache(struct nl_handle *handle) +int rtnl_neigh_alloc_cache(struct nl_sock *sock, struct nl_cache **result) { - struct nl_cache *cache; - - cache = nl_cache_alloc(&rtnl_neigh_ops); - if (cache == NULL) - return NULL; - - if (handle && nl_cache_refill(handle, cache) < 0) { - nl_cache_free(cache); - return NULL; - } - - NL_DBG(2, "Returning new cache %p\n", cache); - - return cache; + return nl_cache_alloc_and_fill(&rtnl_neigh_ops, sock, result); } /** @@ -579,22 +495,26 @@ struct rtnl_neigh * rtnl_neigh_get(struct nl_cache *cache, int ifindex, * @{ */ -static struct nl_msg * build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, - int flags) +static int build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, int flags, + struct nl_msg **result) { struct nl_msg *msg; struct ndmsg nhdr = { .ndm_ifindex = tmpl->n_ifindex, - .ndm_family = nl_addr_get_family(tmpl->n_dst), .ndm_state = NUD_PERMANENT, }; + if (!(tmpl->ce_mask & NEIGH_ATTR_DST)) + return -NLE_MISSING_ATTR; + + nhdr.ndm_family = nl_addr_get_family(tmpl->n_dst); + if (tmpl->ce_mask & NEIGH_ATTR_STATE) nhdr.ndm_state = tmpl->n_state; msg = nlmsg_alloc_simple(cmd, flags); if (!msg) - return NULL; + return -NLE_NOMEM; if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -604,17 +524,19 @@ static struct nl_msg * build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, if (tmpl->ce_mask & NEIGH_ATTR_LLADDR) NLA_PUT_ADDR(msg, NDA_LLADDR, tmpl->n_lladdr); - return msg; + *result = msg; + return 0; nla_put_failure: nlmsg_free(msg); - return NULL; + return -NLE_MSGSIZE; } /** * Build netlink request message to add a new neighbour * @arg tmpl template with data of new neighbour * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting a addition of a new * neighbour. The netlink message header isn't fully equipped with @@ -628,16 +550,17 @@ nla_put_failure: * - Destination address (rtnl_neigh_set_dst()) * - Link layer address (rtnl_neigh_set_lladdr()) * - * @return The netlink message + * @return 0 on success or a negative error code. */ -struct nl_msg * rtnl_neigh_build_add_request(struct rtnl_neigh *tmpl, int flags) +int rtnl_neigh_build_add_request(struct rtnl_neigh *tmpl, int flags, + struct nl_msg **result) { - return build_neigh_msg(tmpl, RTM_NEWNEIGH, NLM_F_CREATE | flags); + return build_neigh_msg(tmpl, RTM_NEWNEIGH, flags, result); } /** * Add a new neighbour - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg tmpl template with requested changes * @arg flags additional netlink message flags * @@ -653,21 +576,20 @@ struct nl_msg * rtnl_neigh_build_add_request(struct rtnl_neigh *tmpl, int flags) * * @return 0 on sucess or a negative error if an error occured. */ -int rtnl_neigh_add(struct nl_handle *handle, struct rtnl_neigh *tmpl, int flags) +int rtnl_neigh_add(struct nl_sock *sk, struct rtnl_neigh *tmpl, int flags) { int err; struct nl_msg *msg; - msg = rtnl_neigh_build_add_request(tmpl, flags); - if (!msg) - return nl_errno(ENOMEM); + if ((err = rtnl_neigh_build_add_request(tmpl, flags, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); if (err < 0) return err; - nlmsg_free(msg); - return nl_wait_for_ack(handle); + return wait_for_ack(sk); } /** @} */ @@ -681,6 +603,7 @@ int rtnl_neigh_add(struct nl_handle *handle, struct rtnl_neigh *tmpl, int flags) * Build a netlink request message to delete a neighbour * @arg neigh neighbour to delete * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting a deletion of a neighbour. * The netlink message header isn't fully equipped with all relevant @@ -688,17 +611,17 @@ int rtnl_neigh_add(struct nl_handle *handle, struct rtnl_neigh *tmpl, int flags) * or supplemented as needed. \a neigh must point to an existing * neighbour. * - * @return The netlink message + * @return 0 on success or a negative error code. */ -struct nl_msg *rtnl_neigh_build_delete_request(struct rtnl_neigh *neigh, - int flags) +int rtnl_neigh_build_delete_request(struct rtnl_neigh *neigh, int flags, + struct nl_msg **result) { - return build_neigh_msg(neigh, RTM_DELNEIGH, flags); + return build_neigh_msg(neigh, RTM_DELNEIGH, flags, result); } /** * Delete a neighbour - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg neigh neighbour to delete * @arg flags additional netlink message flags * @@ -708,81 +631,21 @@ struct nl_msg *rtnl_neigh_build_delete_request(struct rtnl_neigh *neigh, * * @return 0 on sucess or a negative error if an error occured. */ -int rtnl_neigh_delete(struct nl_handle *handle, struct rtnl_neigh *neigh, +int rtnl_neigh_delete(struct nl_sock *sk, struct rtnl_neigh *neigh, int flags) { - int err; struct nl_msg *msg; + int err; - msg = rtnl_neigh_build_delete_request(neigh, flags); - if (!msg) - return nl_errno(ENOMEM); - - err = nl_send_auto_complete(handle, msg); - if (err < 0) + if ((err = rtnl_neigh_build_delete_request(neigh, flags, &msg)) < 0) return err; + err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); - return nl_wait_for_ack(handle); -} - -/** @} */ - -/** - * @name Neighbour Modification - * @{ - */ - -/** - * Build a netlink request message to change neighbour attributes - * @arg neigh the neighbour to change - * @arg flags additional netlink message flags - * - * Builds a new netlink message requesting a change of a neigh - * attributes. The netlink message header isn't fully equipped with - * all relevant fields and must thus be sent out via nl_send_auto_complete() - * or supplemented as needed. - * - * @return The netlink message - * @note Not all attributes can be changed, see - * \ref neigh_changeable "Changeable Attributes" for a list. - */ -struct nl_msg *rtnl_neigh_build_change_request(struct rtnl_neigh *neigh, - int flags) -{ - return build_neigh_msg(neigh, RTM_NEWNEIGH, NLM_F_REPLACE | flags); -} - -/** - * Change neighbour attributes - * @arg handle netlink handle - * @arg neigh neighbour to be changed - * @arg flags additional netlink message flags - * - * Builds a netlink message by calling rtnl_neigh_build_change_request(), - * sends the request to the kernel and waits for the next ACK to be - * received and thus blocks until the request has been fullfilled. - * - * @return 0 on sucess or a negative error if an error occured. - * @note Not all attributes can be changed, see - * \ref neigh_changeable "Changeable Attributes" for a list. - */ -int rtnl_neigh_change(struct nl_handle *handle, struct rtnl_neigh *neigh, - int flags) -{ - int err; - struct nl_msg *msg; - - msg = rtnl_neigh_build_change_request(neigh, flags); - if (!msg) - return nl_errno(ENOMEM); - - err = nl_send_auto_complete(handle, msg); if (err < 0) return err; - nlmsg_free(msg); - return nl_wait_for_ack(handle); + return wait_for_ack(sk); } /** @} */ @@ -893,10 +756,7 @@ void rtnl_neigh_set_ifindex(struct rtnl_neigh *neigh, int ifindex) int rtnl_neigh_get_ifindex(struct rtnl_neigh *neigh) { - if (neigh->ce_mask & NEIGH_ATTR_IFINDEX) - return neigh->n_ifindex; - else - return RTNL_LINK_NOT_FOUND; + return neigh->n_ifindex; } static inline int __assign_addr(struct rtnl_neigh *neigh, struct nl_addr **pos, @@ -905,8 +765,7 @@ static inline int __assign_addr(struct rtnl_neigh *neigh, struct nl_addr **pos, if (!nocheck) { if (neigh->ce_mask & NEIGH_ATTR_FAMILY) { if (new->a_family != neigh->n_family) - return nl_error(EINVAL, - "Address family mismatch"); + return -NLE_AF_MISMATCH; } else { neigh->n_family = new->a_family; neigh->ce_mask |= NEIGH_ATTR_FAMILY; @@ -957,6 +816,11 @@ void rtnl_neigh_set_family(struct rtnl_neigh *neigh, int family) neigh->ce_mask |= NEIGH_ATTR_FAMILY; } +int rtnl_neigh_get_family(struct rtnl_neigh *neigh) +{ + return neigh->n_family; +} + void rtnl_neigh_set_type(struct rtnl_neigh *neigh, int type) { neigh->n_type = type; @@ -978,14 +842,15 @@ static struct nl_object_ops neigh_obj_ops = { .oo_size = sizeof(struct rtnl_neigh), .oo_free_data = neigh_free_data, .oo_clone = neigh_clone, - .oo_dump[NL_DUMP_BRIEF] = neigh_dump_brief, - .oo_dump[NL_DUMP_FULL] = neigh_dump_full, - .oo_dump[NL_DUMP_STATS] = neigh_dump_stats, - .oo_dump[NL_DUMP_XML] = neigh_dump_xml, - .oo_dump[NL_DUMP_ENV] = neigh_dump_env, + .oo_dump = { + [NL_DUMP_LINE] = neigh_dump_line, + [NL_DUMP_DETAILS] = neigh_dump_details, + [NL_DUMP_STATS] = neigh_dump_stats, + [NL_DUMP_ENV] = neigh_dump_env, + }, .oo_compare = neigh_compare, .oo_attrs2str = neigh_attrs2str, - .oo_id_attrs = (NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY), + .oo_id_attrs = (NEIGH_ATTR_IFINDEX | NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY), }; static struct nl_af_group neigh_groups[] = { diff --git a/lib/route/neightbl.c b/lib/route/neightbl.c index 3191b5b..9599faa 100644 --- a/lib/route/neightbl.c +++ b/lib/route/neightbl.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -129,7 +129,7 @@ static int neightbl_msg_parser(struct nl_cache_ops *ops, ntbl = rtnl_neightbl_alloc(); if (!ntbl) { - err = nl_errno(ENOMEM); + err = -NLE_NOMEM; goto errout; } @@ -143,7 +143,7 @@ static int neightbl_msg_parser(struct nl_cache_ops *ops, ntbl->nt_family = rtmsg->rtgen_family; if (tb[NDTA_NAME] == NULL) { - err = nl_error(EINVAL, "NDTA_NAME is missing"); + return -NLE_MISSING_ATTR; goto errout; } @@ -217,27 +217,22 @@ static int neightbl_msg_parser(struct nl_cache_ops *ops, } err = pp->pp_cb((struct nl_object *) ntbl, pp); - if (err < 0) - goto errout; - - err = P_ACCEPT; errout: rtnl_neightbl_put(ntbl); return err; } -static int neightbl_request_update(struct nl_cache *c, struct nl_handle *h) +static int neightbl_request_update(struct nl_cache *c, struct nl_sock *h) { return nl_rtgen_request(h, RTM_GETNEIGHTBL, AF_UNSPEC, NLM_F_DUMP); } -static int neightbl_dump_brief(struct nl_object *arg, struct nl_dump_params *p) +static void neightbl_dump_line(struct nl_object *arg, struct nl_dump_params *p) { - int line = 1; struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *) arg; - dp_dump(p, "%s", ntbl->nt_name); + nl_dump_line(p, "%s", ntbl->nt_name); if (ntbl->nt_parms.ntp_mask & NEIGHTBLPARM_ATTR_IFINDEX) { struct nl_cache *link_cache; @@ -246,57 +241,52 @@ static int neightbl_dump_brief(struct nl_object *arg, struct nl_dump_params *p) if (link_cache) { char buf[32]; - dp_dump(p, "<%s> ", + nl_dump(p, "<%s> ", rtnl_link_i2name(link_cache, ntbl->nt_parms.ntp_ifindex, buf, sizeof(buf))); } else - dp_dump(p, "<%u> ", ntbl->nt_parms.ntp_ifindex); + nl_dump(p, "<%u> ", ntbl->nt_parms.ntp_ifindex); } else - dp_dump(p, " "); + nl_dump(p, " "); if (ntbl->ce_mask & NEIGHTBL_ATTR_CONFIG) - dp_dump(p, "entries %u ", ntbl->nt_config.ndtc_entries); + nl_dump(p, "entries %u ", ntbl->nt_config.ndtc_entries); if (ntbl->ce_mask & NEIGHTBL_ATTR_PARMS) { char rt[32], rt2[32]; struct rtnl_neightbl_parms *pa = &ntbl->nt_parms; - dp_dump(p, "reachable-time %s retransmit-time %s", + nl_dump(p, "reachable-time %s retransmit-time %s", nl_msec2str(pa->ntp_reachable_time, rt, sizeof(rt)), nl_msec2str(pa->ntp_retrans_time, rt2, sizeof(rt2))); } - dp_dump(p, "\n"); - - return line; + nl_dump(p, "\n"); } -static int neightbl_dump_full(struct nl_object *arg, struct nl_dump_params *p) +static void neightbl_dump_details(struct nl_object *arg, struct nl_dump_params *p) { char x[32], y[32], z[32]; struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *) arg; - int line = neightbl_dump_brief(arg, p); + neightbl_dump_line(arg, p); if (ntbl->ce_mask & NEIGHTBL_ATTR_CONFIG) { - dp_new_line(p, line++); - dp_dump(p, " key-len %u entry-size %u last-flush %s\n", + nl_dump_line(p, " key-len %u entry-size %u last-flush %s\n", ntbl->nt_config.ndtc_key_len, ntbl->nt_config.ndtc_entry_size, nl_msec2str(ntbl->nt_config.ndtc_last_flush, x, sizeof(x))); - dp_new_line(p, line++); - dp_dump(p, " gc threshold %u/%u/%u interval %s " \ + nl_dump_line(p, " gc threshold %u/%u/%u interval %s " \ "chain-position %u\n", ntbl->nt_gc_thresh1, ntbl->nt_gc_thresh2, ntbl->nt_gc_thresh3, nl_msec2str(ntbl->nt_gc_interval, x, sizeof(x)), ntbl->nt_config.ndtc_hash_chain_gc); - dp_new_line(p, line++); - dp_dump(p, " hash-rand 0x%08X/0x%08X last-rand %s\n", + nl_dump_line(p, " hash-rand 0x%08X/0x%08X last-rand %s\n", ntbl->nt_config.ndtc_hash_rnd, ntbl->nt_config.ndtc_hash_mask, nl_msec2str(ntbl->nt_config.ndtc_last_rand, @@ -306,49 +296,43 @@ static int neightbl_dump_full(struct nl_object *arg, struct nl_dump_params *p) if (ntbl->ce_mask & NEIGHTBL_ATTR_PARMS) { struct rtnl_neightbl_parms *pa = &ntbl->nt_parms; - dp_new_line(p, line++); - dp_dump(p, " refcnt %u pending-queue-limit %u " \ + nl_dump_line(p, " refcnt %u pending-queue-limit %u " \ "proxy-delayed-queue-limit %u\n", pa->ntp_refcnt, pa->ntp_queue_len, pa->ntp_proxy_qlen); - dp_new_line(p, line++); - dp_dump(p, " num-userspace-probes %u num-unicast-probes " \ + nl_dump_line(p, " num-userspace-probes %u num-unicast-probes " \ "%u num-multicast-probes %u\n", pa->ntp_app_probes, pa->ntp_ucast_probes, pa->ntp_mcast_probes); - dp_new_line(p, line++); - dp_dump(p, " min-age %s base-reachable-time %s " \ + nl_dump_line(p, " min-age %s base-reachable-time %s " \ "stale-check-interval %s\n", nl_msec2str(pa->ntp_locktime, x, sizeof(x)), nl_msec2str(pa->ntp_base_reachable_time, y, sizeof(y)), nl_msec2str(pa->ntp_gc_stale_time, z, sizeof(z))); - dp_new_line(p, line++); - dp_dump(p, " initial-probe-delay %s answer-delay %s " \ + nl_dump_line(p, " initial-probe-delay %s answer-delay %s " \ "proxy-answer-delay %s\n", nl_msec2str(pa->ntp_probe_delay, x, sizeof(x)), nl_msec2str(pa->ntp_anycast_delay, y, sizeof(y)), nl_msec2str(pa->ntp_proxy_delay, z, sizeof(z))); } - - return line; } -static int neightbl_dump_stats(struct nl_object *arg, struct nl_dump_params *p) +static void neightbl_dump_stats(struct nl_object *arg, struct nl_dump_params *p) { struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *) arg; - int line = neightbl_dump_full(arg, p); + + neightbl_dump_details(arg, p); if (!(ntbl->ce_mask & NEIGHTBL_ATTR_STATS)) - return line; + return; - dp_new_line(p, line++); - dp_dump(p, " lookups %lld hits %lld failed %lld " \ + nl_dump_line(p, " lookups %lld hits %lld failed %lld " \ "allocations %lld destroys %lld\n", ntbl->nt_stats.ndts_lookups, ntbl->nt_stats.ndts_hits, @@ -356,18 +340,15 @@ static int neightbl_dump_stats(struct nl_object *arg, struct nl_dump_params *p) ntbl->nt_stats.ndts_allocs, ntbl->nt_stats.ndts_destroys); - dp_new_line(p, line++); - dp_dump(p, " hash-grows %lld forced-gc-runs %lld " \ + nl_dump_line(p, " hash-grows %lld forced-gc-runs %lld " \ "periodic-gc-runs %lld\n", ntbl->nt_stats.ndts_hash_grows, ntbl->nt_stats.ndts_forced_gc_runs, ntbl->nt_stats.ndts_periodic_gc_runs); - dp_dump(p, " rcv-unicast-probes %lld rcv-multicast-probes %lld\n", + nl_dump_line(p, " rcv-unicast-probes %lld rcv-multicast-probes %lld\n", ntbl->nt_stats.ndts_rcv_probes_ucast, ntbl->nt_stats.ndts_rcv_probes_mcast); - - return line; } /** @@ -394,30 +375,18 @@ void rtnl_neightbl_put(struct rtnl_neightbl *neightbl) /** * Build a neighbour table cache including all neighbour tables currently configured in the kernel. - * @arg handle netlink handle + * @arg sk Netlink socket. + * @arg result Pointer to store resulting cache. * * Allocates a new neighbour table cache, initializes it properly and * updates it to include all neighbour tables currently configured in * the kernel. * - * @note The caller is responsible for destroying and freeing the - * cache after using it. - * @return The new cache or NULL if an error occured. + * @return 0 on success or a negative error code. */ -struct nl_cache * rtnl_neightbl_alloc_cache(struct nl_handle *handle) +int rtnl_neightbl_alloc_cache(struct nl_sock *sk, struct nl_cache **result) { - struct nl_cache * cache; - - cache = nl_cache_alloc(&rtnl_neightbl_ops); - if (cache == NULL) - return NULL; - - if (handle && nl_cache_refill(handle, cache) < 0) { - nl_cache_free(cache); - return NULL; - } - - return cache; + return nl_cache_alloc_and_fill(&rtnl_neightbl_ops, sk, result); } /** @@ -464,6 +433,7 @@ struct rtnl_neightbl *rtnl_neightbl_get(struct nl_cache *cache, * Builds a netlink change request message to change neighbour table attributes * @arg old neighbour table to change * @arg tmpl template with requested changes + * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting a change of neighbour table * attributes. The netlink message header isn't fully equipped with all @@ -473,98 +443,115 @@ struct rtnl_neightbl *rtnl_neightbl_get(struct nl_cache *cache, * kernel and \a tmpl must contain the attributes to be changed set via * \c rtnl_neightbl_set_* functions. * - * @return New netlink message + * @return 0 on success or a negative error code. */ -struct nl_msg * rtnl_neightbl_build_change_request(struct rtnl_neightbl *old, - struct rtnl_neightbl *tmpl) +int rtnl_neightbl_build_change_request(struct rtnl_neightbl *old, + struct rtnl_neightbl *tmpl, + struct nl_msg **result) { - struct nl_msg *m; + struct nl_msg *m, *parms = NULL; struct ndtmsg ndt = { .ndtm_family = old->nt_family, }; m = nlmsg_alloc_simple(RTM_SETNEIGHTBL, 0); - nlmsg_append(m, &ndt, sizeof(ndt), NLMSG_ALIGNTO); + if (!m) + return -NLE_NOMEM; - nla_put_string(m, NDTA_NAME, old->nt_name); + if (nlmsg_append(m, &ndt, sizeof(ndt), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + NLA_PUT_STRING(m, NDTA_NAME, old->nt_name); if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH1) - nla_put_u32(m, NDTA_THRESH1, tmpl->nt_gc_thresh1); + NLA_PUT_U32(m, NDTA_THRESH1, tmpl->nt_gc_thresh1); if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH2) - nla_put_u32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2); + NLA_PUT_U32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2); if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH2) - nla_put_u32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2); + NLA_PUT_U32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2); if (tmpl->ce_mask & NEIGHTBL_ATTR_GC_INTERVAL) - nla_put_u64(m, NDTA_GC_INTERVAL, + NLA_PUT_U64(m, NDTA_GC_INTERVAL, tmpl->nt_gc_interval); if (tmpl->ce_mask & NEIGHTBL_ATTR_PARMS) { struct rtnl_neightbl_parms *p = &tmpl->nt_parms; - struct nl_msg *parms = nlmsg_alloc(); + + parms = nlmsg_alloc(); + if (!parms) + goto nla_put_failure; if (old->nt_parms.ntp_mask & NEIGHTBLPARM_ATTR_IFINDEX) - nla_put_u32(parms, NDTPA_IFINDEX, + NLA_PUT_U32(parms, NDTPA_IFINDEX, old->nt_parms.ntp_ifindex); if (p->ntp_mask & NEIGHTBLPARM_ATTR_QUEUE_LEN) - nla_put_u32(parms, NDTPA_QUEUE_LEN, p->ntp_queue_len); + NLA_PUT_U32(parms, NDTPA_QUEUE_LEN, p->ntp_queue_len); if (p->ntp_mask & NEIGHTBLPARM_ATTR_APP_PROBES) - nla_put_u32(parms, NDTPA_APP_PROBES, p->ntp_app_probes); + NLA_PUT_U32(parms, NDTPA_APP_PROBES, p->ntp_app_probes); if (p->ntp_mask & NEIGHTBLPARM_ATTR_UCAST_PROBES) - nla_put_u32(parms, NDTPA_UCAST_PROBES, + NLA_PUT_U32(parms, NDTPA_UCAST_PROBES, p->ntp_ucast_probes); if (p->ntp_mask & NEIGHTBLPARM_ATTR_MCAST_PROBES) - nla_put_u32(parms, NDTPA_MCAST_PROBES, + NLA_PUT_U32(parms, NDTPA_MCAST_PROBES, p->ntp_mcast_probes); if (p->ntp_mask & NEIGHTBLPARM_ATTR_PROXY_QLEN) - nla_put_u32(parms, NDTPA_PROXY_QLEN, + NLA_PUT_U32(parms, NDTPA_PROXY_QLEN, p->ntp_proxy_qlen); if (p->ntp_mask & NEIGHTBLPARM_ATTR_BASE_REACHABLE_TIME) - nla_put_u64(parms, NDTPA_BASE_REACHABLE_TIME, + NLA_PUT_U64(parms, NDTPA_BASE_REACHABLE_TIME, p->ntp_base_reachable_time); if (p->ntp_mask & NEIGHTBLPARM_ATTR_RETRANS_TIME) - nla_put_u64(parms, NDTPA_RETRANS_TIME, + NLA_PUT_U64(parms, NDTPA_RETRANS_TIME, p->ntp_retrans_time); if (p->ntp_mask & NEIGHTBLPARM_ATTR_GC_STALETIME) - nla_put_u64(parms, NDTPA_GC_STALETIME, + NLA_PUT_U64(parms, NDTPA_GC_STALETIME, p->ntp_gc_stale_time); if (p->ntp_mask & NEIGHTBLPARM_ATTR_DELAY_PROBE_TIME) - nla_put_u64(parms, NDTPA_DELAY_PROBE_TIME, + NLA_PUT_U64(parms, NDTPA_DELAY_PROBE_TIME, p->ntp_proxy_delay); if (p->ntp_mask & NEIGHTBLPARM_ATTR_ANYCAST_DELAY) - nla_put_u64(parms, NDTPA_ANYCAST_DELAY, + NLA_PUT_U64(parms, NDTPA_ANYCAST_DELAY, p->ntp_anycast_delay); if (p->ntp_mask & NEIGHTBLPARM_ATTR_PROXY_DELAY) - nla_put_u64(parms, NDTPA_PROXY_DELAY, + NLA_PUT_U64(parms, NDTPA_PROXY_DELAY, p->ntp_proxy_delay); if (p->ntp_mask & NEIGHTBLPARM_ATTR_LOCKTIME) - nla_put_u64(parms, NDTPA_LOCKTIME, p->ntp_locktime); + NLA_PUT_U64(parms, NDTPA_LOCKTIME, p->ntp_locktime); + + if (nla_put_nested(m, NDTA_PARMS, parms) < 0) + goto nla_put_failure; - nla_put_nested(m, NDTA_PARMS, parms); nlmsg_free(parms); } - - return m; + + *result = m; + return 0; + +nla_put_failure: + if (parms) + nlmsg_free(parms); + nlmsg_free(m); + return -NLE_MSGSIZE; } /** * Change neighbour table attributes - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg old neighbour table to be changed * @arg tmpl template with requested changes * @@ -575,19 +562,21 @@ struct nl_msg * rtnl_neightbl_build_change_request(struct rtnl_neightbl *old, * * @return 0 on success or a negative error code */ -int rtnl_neightbl_change(struct nl_handle *handle, struct rtnl_neightbl *old, +int rtnl_neightbl_change(struct nl_sock *sk, struct rtnl_neightbl *old, struct rtnl_neightbl *tmpl) { - int err; struct nl_msg *msg; + int err; - msg = rtnl_neightbl_build_change_request(old, tmpl); - err = nl_send_auto_complete(handle, msg); - if (err < 0) + if ((err = rtnl_neightbl_build_change_request(old, tmpl, &msg)) < 0) return err; + err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); - return nl_wait_for_ack(handle); + if (err < 0) + return err; + + return wait_for_ack(sk); } /** @} */ @@ -790,9 +779,11 @@ void rtnl_neightbl_set_locktime(struct rtnl_neightbl *ntbl, uint64_t ms) static struct nl_object_ops neightbl_obj_ops = { .oo_name = "route/neightbl", .oo_size = sizeof(struct rtnl_neightbl), - .oo_dump[NL_DUMP_BRIEF] = neightbl_dump_brief, - .oo_dump[NL_DUMP_FULL] = neightbl_dump_full, - .oo_dump[NL_DUMP_STATS] = neightbl_dump_stats, + .oo_dump = { + [NL_DUMP_LINE] = neightbl_dump_line, + [NL_DUMP_DETAILS] = neightbl_dump_details, + [NL_DUMP_STATS] = neightbl_dump_stats, + }, .oo_compare = neightbl_compare, }; diff --git a/lib/route/nexthop.c b/lib/route/nexthop.c index 7486769..788e255 100644 --- a/lib/route/nexthop.c +++ b/lib/route/nexthop.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -21,6 +21,14 @@ #include <netlink/route/rtnl.h> #include <netlink/route/route.h> +/** @cond SKIP */ +#define NH_ATTR_FLAGS 0x000001 +#define NH_ATTR_WEIGHT 0x000002 +#define NH_ATTR_IFINDEX 0x000004 +#define NH_ATTR_GATEWAY 0x000008 +#define NH_ATTR_REALMS 0x000010 +/** @endcond */ + /** * @name Allocation/Freeing * @{ @@ -31,10 +39,8 @@ struct rtnl_nexthop *rtnl_route_nh_alloc(void) struct rtnl_nexthop *nh; nh = calloc(1, sizeof(*nh)); - if (!nh) { - nl_errno(ENOMEM); + if (!nh) return NULL; - } nl_init_list_head(&nh->rtnh_list); @@ -53,7 +59,7 @@ struct rtnl_nexthop *rtnl_route_nh_clone(struct rtnl_nexthop *src) nh->rtnh_flag_mask = src->rtnh_flag_mask; nh->rtnh_weight = src->rtnh_weight; nh->rtnh_ifindex = src->rtnh_ifindex; - nh->rtnh_mask = src->rtnh_mask; + nh->ce_mask = src->ce_mask; if (src->rtnh_gateway) { nh->rtnh_gateway = nl_addr_clone(src->rtnh_gateway); @@ -74,78 +80,251 @@ void rtnl_route_nh_free(struct rtnl_nexthop *nh) /** @} */ +int rtnl_route_nh_compare(struct rtnl_nexthop *a, struct rtnl_nexthop *b, + uint32_t attrs, int loose) +{ + int diff = 0; + +#define NH_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NH_ATTR_##ATTR, a, b, EXPR) + + diff |= NH_DIFF(IFINDEX, a->rtnh_ifindex != b->rtnh_ifindex); + diff |= NH_DIFF(WEIGHT, a->rtnh_weight != b->rtnh_weight); + diff |= NH_DIFF(REALMS, a->rtnh_realms != b->rtnh_realms); + diff |= NH_DIFF(GATEWAY, nl_addr_cmp(a->rtnh_gateway, + b->rtnh_gateway)); + + if (loose) + diff |= NH_DIFF(FLAGS, + (a->rtnh_flags ^ b->rtnh_flags) & b->rtnh_flag_mask); + else + diff |= NH_DIFF(FLAGS, a->rtnh_flags != b->rtnh_flags); + +#undef NH_DIFF + + return diff; +} + +static void nh_dump_line(struct rtnl_nexthop *nh, struct nl_dump_params *dp) +{ + struct nl_cache *link_cache; + char buf[128]; + + link_cache = nl_cache_mngt_require("route/link"); + + nl_dump(dp, "via"); + + if (nh->ce_mask & NH_ATTR_GATEWAY) + nl_dump(dp, " %s", nl_addr2str(nh->rtnh_gateway, + buf, sizeof(buf))); + + if(nh->ce_mask & NH_ATTR_IFINDEX) { + if (link_cache) { + nl_dump(dp, " dev %s", + rtnl_link_i2name(link_cache, + nh->rtnh_ifindex, + buf, sizeof(buf))); + } else + nl_dump(dp, " dev %d", nh->rtnh_ifindex); + } + + nl_dump(dp, " "); +} + +static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp) +{ + struct nl_cache *link_cache; + char buf[128]; + + link_cache = nl_cache_mngt_require("route/link"); + + nl_dump(dp, "nexthop"); + + if (nh->ce_mask & NH_ATTR_GATEWAY) + nl_dump(dp, " via %s", nl_addr2str(nh->rtnh_gateway, + buf, sizeof(buf))); + + if(nh->ce_mask & NH_ATTR_IFINDEX) { + if (link_cache) { + nl_dump(dp, " dev %s", + rtnl_link_i2name(link_cache, + nh->rtnh_ifindex, + buf, sizeof(buf))); + } else + nl_dump(dp, " dev %d", nh->rtnh_ifindex); + } + + if (nh->ce_mask & NH_ATTR_WEIGHT) + nl_dump(dp, " weight %u", nh->rtnh_weight); + + if (nh->ce_mask & NH_ATTR_REALMS) + nl_dump(dp, " realm %04x:%04x", + RTNL_REALM_FROM(nh->rtnh_realms), + RTNL_REALM_TO(nh->rtnh_realms)); + + if (nh->ce_mask & NH_ATTR_FLAGS) + nl_dump(dp, " <%s>", rtnl_route_nh_flags2str(nh->rtnh_flags, + buf, sizeof(buf))); +} + +static void nh_dump_env(struct rtnl_nexthop *nh, struct nl_dump_params *dp) +{ + struct nl_cache *link_cache; + char buf[128]; + + link_cache = nl_cache_mngt_require("route/link"); + + if (nh->ce_mask & NH_ATTR_GATEWAY) + nl_dump_line(dp, "ROUTE_NH%d_VIA=%s\n", dp->dp_ivar, + nl_addr2str(nh->rtnh_gateway, buf, sizeof(buf))); + + if(nh->ce_mask & NH_ATTR_IFINDEX) { + if (link_cache) { + nl_dump_line(dp, "ROUTE_NH%d_DEV=%s\n", dp->dp_ivar, + rtnl_link_i2name(link_cache, + nh->rtnh_ifindex, + buf, sizeof(buf))); + } else + nl_dump_line(dp, "ROUTE_NH%d_DEV=%d\n", dp->dp_ivar, + nh->rtnh_ifindex); + } + + if (nh->ce_mask & NH_ATTR_WEIGHT) + nl_dump_line(dp, "ROUTE_NH%d_WEIGHT=%u\n", dp->dp_ivar, + nh->rtnh_weight); + + if (nh->ce_mask & NH_ATTR_REALMS) + nl_dump_line(dp, "ROUTE_NH%d_REALM=%04x:%04x\n", dp->dp_ivar, + RTNL_REALM_FROM(nh->rtnh_realms), + RTNL_REALM_TO(nh->rtnh_realms)); + + if (nh->ce_mask & NH_ATTR_FLAGS) + nl_dump_line(dp, "ROUTE_NH%d_FLAGS=<%s>\n", dp->dp_ivar, + rtnl_route_nh_flags2str(nh->rtnh_flags, + buf, sizeof(buf))); +} +void rtnl_route_nh_dump(struct rtnl_nexthop *nh, struct nl_dump_params *dp) +{ + switch (dp->dp_type) { + case NL_DUMP_LINE: + nh_dump_line(nh, dp); + break; + + case NL_DUMP_DETAILS: + case NL_DUMP_STATS: + if (dp->dp_ivar == NH_DUMP_FROM_DETAILS) + nh_dump_details(nh, dp); + break; + + case NL_DUMP_ENV: + nh_dump_env(nh, dp); + break; + + default: + break; + } +} + /** * @name Attributes + * @{ */ -void rtnl_route_nh_set_weight(struct rtnl_nexthop *nh, int weight) +void rtnl_route_nh_set_weight(struct rtnl_nexthop *nh, uint8_t weight) { nh->rtnh_weight = weight; - nh->rtnh_mask |= NEXTHOP_HAS_WEIGHT; + nh->ce_mask |= NH_ATTR_WEIGHT; } -int rtnl_route_nh_get_weight(struct rtnl_nexthop *nh) +uint8_t rtnl_route_nh_get_weight(struct rtnl_nexthop *nh) { - if (nh->rtnh_mask & NEXTHOP_HAS_WEIGHT) - return nh->rtnh_weight; - else - return 0; + return nh->rtnh_weight; } void rtnl_route_nh_set_ifindex(struct rtnl_nexthop *nh, int ifindex) { nh->rtnh_ifindex = ifindex; - nh->rtnh_mask |= NEXTHOP_HAS_IFINDEX; + nh->ce_mask |= NH_ATTR_IFINDEX; } int rtnl_route_nh_get_ifindex(struct rtnl_nexthop *nh) { - if (nh->rtnh_mask & NEXTHOP_HAS_IFINDEX) - return nh->rtnh_ifindex; - else - return -1; + return nh->rtnh_ifindex; } void rtnl_route_nh_set_gateway(struct rtnl_nexthop *nh, struct nl_addr *addr) { struct nl_addr *old = nh->rtnh_gateway; - nh->rtnh_gateway = nl_addr_get(addr); + if (addr) { + nh->rtnh_gateway = nl_addr_get(addr); + nh->ce_mask |= NH_ATTR_GATEWAY; + } else { + nh->ce_mask &= ~NH_ATTR_GATEWAY; + nh->rtnh_gateway = NULL; + } + if (old) nl_addr_put(old); - - nh->rtnh_mask |= NEXTHOP_HAS_GATEWAY; } struct nl_addr *rtnl_route_nh_get_gateway(struct rtnl_nexthop *nh) { - if (nh->rtnh_mask & NEXTHOP_HAS_GATEWAY) - return nh->rtnh_gateway; - else - return NULL; + return nh->rtnh_gateway; } void rtnl_route_nh_set_flags(struct rtnl_nexthop *nh, unsigned int flags) { nh->rtnh_flag_mask |= flags; nh->rtnh_flags |= flags; - nh->rtnh_mask |= NEXTHOP_HAS_FLAGS; + nh->ce_mask |= NH_ATTR_FLAGS; } void rtnl_route_nh_unset_flags(struct rtnl_nexthop *nh, unsigned int flags) { nh->rtnh_flag_mask |= flags; nh->rtnh_flags &= ~flags; - nh->rtnh_mask |= NEXTHOP_HAS_FLAGS; + nh->ce_mask |= NH_ATTR_FLAGS; } unsigned int rtnl_route_nh_get_flags(struct rtnl_nexthop *nh) { - if (nh->rtnh_mask & NEXTHOP_HAS_FLAGS) - return nh->rtnh_flags; - else - return 0; + return nh->rtnh_flags; +} + +void rtnl_route_nh_set_realms(struct rtnl_nexthop *nh, uint32_t realms) +{ + nh->rtnh_realms = realms; + nh->ce_mask |= NH_ATTR_REALMS; +} + +uint32_t rtnl_route_nh_get_realms(struct rtnl_nexthop *nh) +{ + return nh->rtnh_realms; +} + +/** @} */ + +/** + * @name Nexthop Flags Translations + * @{ + */ + +static struct trans_tbl nh_flags[] = { + __ADD(RTNH_F_DEAD, dead) + __ADD(RTNH_F_PERVASIVE, pervasive) + __ADD(RTNH_F_ONLINK, onlink) +}; + +char *rtnl_route_nh_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, nh_flags, ARRAY_SIZE(nh_flags)); +} + +int rtnl_route_nh_str2flags(const char *name) +{ + return __str2flags(name, nh_flags, ARRAY_SIZE(nh_flags)); } /** @} */ + /** @} */ diff --git a/lib/route/pktloc.c b/lib/route/pktloc.c new file mode 100644 index 0000000..f0d0155 --- /dev/null +++ b/lib/route/pktloc.c @@ -0,0 +1,168 @@ +/* + * lib/route/pktloc.c Packet Location Aliasing + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2 of the License. + * + * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch> + */ + +/** + * @ingroup tc + * @defgroup pktloc Packet Location Aliasing + * Packet Location Aliasing + * + * The packet location aliasing interface eases the use of offset definitions + * inside packets by allowing them to be referenced by name. Known positions + * of protocol fields are stored in a configuration file and associated with + * a name for later reference. The configuration file is distributed with the + * library and provides a well defined set of definitions for most common + * protocol fields. + * + * @subsection pktloc_examples Examples + * @par Example 1.1 Looking up a packet location + * @code + * struct rtnl_pktloc *loc; + * + * rtnl_pktloc_lookup("ip.src", &loc); + * @endcode + * @{ + */ + +#include <netlink-local.h> +#include <netlink-tc.h> +#include <netlink/netlink.h> +#include <netlink/utils.h> +#include <netlink/route/pktloc.h> + +#include "pktloc_syntax.h" +#include "pktloc_grammar.h" + +/** @cond */ +#define PKTLOC_NAME_HT_SIZ 256 + +static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ]; + +/* djb2 */ +unsigned int pktloc_hash(const char *str) +{ + unsigned long hash = 5381; + int c; + + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash % PKTLOC_NAME_HT_SIZ; +} + + +void rtnl_pktloc_add(struct rtnl_pktloc *loc) +{ + nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]); +} + +extern int pktloc_parse(void *scanner); + +/** @endcond */ + +static void rtnl_pktloc_free(struct rtnl_pktloc *loc) +{ + if (!loc) + return; + + free(loc->name); + free(loc); +} + +static int read_pktlocs(void) +{ + YY_BUFFER_STATE buf; + yyscan_t scanner = NULL; + static time_t last_read; + struct stat st = {0}; + char *path; + int i, err; + FILE *fd; + + asprintf(&path, "%s/pktloc", SYSCONFDIR); + + /* if stat fails, just try to read the file */ + if (stat(path, &st) == 0) { + /* Don't re-read file if file is unchanged */ + if (last_read == st.st_mtime) + return 0; + } + + if (!(fd = fopen(path, "r"))) + return -NLE_PKTLOC_FILE; + + for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) { + struct rtnl_pktloc *loc, *n; + + nl_list_for_each_entry_safe(loc, n, &pktloc_name_ht[i], list) + rtnl_pktloc_free(loc); + + nl_init_list_head(&pktloc_name_ht[i]); + } + + if ((err = pktloc_lex_init(&scanner)) < 0) + return -NLE_FAILURE; + + buf = pktloc__create_buffer(fd, YY_BUF_SIZE, scanner); + pktloc__switch_to_buffer(buf, scanner); + + if ((err = pktloc_parse(scanner)) < 0) + return -NLE_FAILURE; + + if (scanner) + pktloc_lex_destroy(scanner); + + free(path); + last_read = st.st_mtime; + + return 0; +} + +/** + * Lookup packet location alias + * @arg name Name of packet location. + * + * Tries to find a matching packet location alias for the supplied + * packet location name. + * + * The file containing the packet location definitions is automatically + * re-read if its modification time has changed since the last call. + * + * @return 0 on success or a negative error code. + * @retval NLE_PKTLOC_FILE Unable to open packet location file. + * @retval NLE_OBJ_NOTFOUND No matching packet location alias found. + */ +int rtnl_pktloc_lookup(const char *name, struct rtnl_pktloc **result) +{ + struct rtnl_pktloc *loc; + int hash, err; + + if ((err = read_pktlocs()) < 0) + return err; + + hash = pktloc_hash(name); + nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) { + if (!strcasecmp(loc->name, name)) { + *result = loc; + return 0; + } + } + + return -NLE_OBJ_NOTFOUND; +} + +static int __init pktloc_init(void) +{ + int i; + + for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) + nl_init_list_head(&pktloc_name_ht[i]); + + return 0; +} diff --git a/lib/route/pktloc_grammar.l b/lib/route/pktloc_grammar.l new file mode 100644 index 0000000..f710430 --- /dev/null +++ b/lib/route/pktloc_grammar.l @@ -0,0 +1,42 @@ +%{ + #include <netlink-local.h> + #include <netlink-tc.h> + #include <netlink/netlink.h> + #include <netlink/utils.h> + #include <netlink/route/pktloc.h> + #include "pktloc_syntax.h" +%} + +%option 8bit +%option reentrant +%option warn +%option noyywrap +%option nounput +%option bison-bridge +%option bison-locations +%option prefix="pktloc_" + +%% + +[ \t\r\n]+ + +"#".* + +[[:digit:]]+ | +0[xX][[:xdigit:]]+ { + yylval->i = strtoul(yytext, NULL, 0); + return NUMBER; + } + +"+" { return yylval->i = yytext[0]; } + +[lL][iI][nN][kK] { yylval->i = TCF_LAYER_LINK; return LAYER; } +[nN][eE][tT] { yylval->i = TCF_LAYER_NETWORK; return LAYER; } +[tT][cC][pP] { yylval->i = TCF_LAYER_TRANSPORT; return LAYER; } + +[^ \t\r\n+]+ { + yylval->s = strdup(yytext); + if (yylval->s == NULL) + return ERROR; + return NAME; + } diff --git a/lib/route/pktloc_syntax.y b/lib/route/pktloc_syntax.y new file mode 100644 index 0000000..05d609a --- /dev/null +++ b/lib/route/pktloc_syntax.y @@ -0,0 +1,108 @@ +%{ +#include <netlink-local.h> +#include <netlink-tc.h> +#include <netlink/netlink.h> +#include <netlink/utils.h> +#include <netlink/route/pktloc.h> +%} + +%locations +%error-verbose +%define api.pure +%name-prefix "pktloc_" + +%parse-param {void *scanner} +%lex-param {void *scanner} + +%union { + struct rtnl_pktloc *l; + uint32_t i; + char *s; +} + +%{ +extern int pktloc_lex(YYSTYPE *, YYLTYPE *, void *); +extern void rtnl_pktloc_add(struct rtnl_pktloc *); + +static void yyerror(YYLTYPE *locp, void *scanner, const char *msg) +{ + /* FIXME */ +} +%} + +%token <i> ERROR NUMBER LAYER +%token <s> NAME + +%type <i> mask layer +%type <l> location + +%destructor { free($$); } NAME + +%start input + +%% + +input: + def + { } + ; + +def: + /* empty */ + { } + | location def + { } + ; + +location: + NAME NAME layer NUMBER mask + { + struct rtnl_pktloc *loc; + + if (!(loc = calloc(1, sizeof(*loc)))) { + /* FIXME */ + } + + if (!strcasecmp($2, "u8")) + loc->align = TCF_EM_ALIGN_U8; + else if (!strcasecmp($2, "h8")) { + loc->align = TCF_EM_ALIGN_U8; + loc->flags = TCF_EM_CMP_TRANS; + } else if (!strcasecmp($2, "u16")) + loc->align = TCF_EM_ALIGN_U16; + else if (!strcasecmp($2, "h16")) { + loc->align = TCF_EM_ALIGN_U16; + loc->flags = TCF_EM_CMP_TRANS; + } else if (!strcasecmp($2, "u32")) + loc->align = TCF_EM_ALIGN_U32; + else if (!strcasecmp($2, "h32")) { + loc->align = TCF_EM_ALIGN_U32; + loc->flags = TCF_EM_CMP_TRANS; + } + + free($2); + + loc->name = $1; + loc->layer = $3; + loc->offset = $4; + loc->mask = $5; + + rtnl_pktloc_add(loc); + + $$ = loc; + } + ; + +layer: + /* empty */ + { $$ = TCF_LAYER_NETWORK; } + | LAYER '+' + { $$ = $1; } + ; + +mask: + /* empty */ + { $$ = 0; } + | NUMBER + { $$ = $1; } + ; diff --git a/lib/route/qdisc.c b/lib/route/qdisc.c index 8eda51b..cfeaf05 100644 --- a/lib/route/qdisc.c +++ b/lib/route/qdisc.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -98,13 +98,13 @@ static struct nl_cache_ops rtnl_qdisc_ops; static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct nlmsghdr *n, struct nl_parser_param *pp) { - int err = -ENOMEM; + int err; struct rtnl_qdisc *qdisc; struct rtnl_qdisc_ops *qops; qdisc = rtnl_qdisc_alloc(); if (!qdisc) { - err = nl_errno(ENOMEM); + err = -NLE_NOMEM; goto errout; } @@ -122,25 +122,20 @@ static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, } err = pp->pp_cb((struct nl_object *) qdisc, pp); - if (err < 0) - goto errout_free; - - err = P_ACCEPT; - errout_free: rtnl_qdisc_put(qdisc); errout: return err; } -static int qdisc_request_update(struct nl_cache *c, struct nl_handle *h) +static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk) { struct tcmsg tchdr = { .tcm_family = AF_UNSPEC, .tcm_ifindex = c->c_iarg1, }; - return nl_send_simple(h, RTM_GETQDISC, NLM_F_DUMP, &tchdr, + return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr, sizeof(tchdr)); } @@ -149,15 +144,15 @@ static int qdisc_request_update(struct nl_cache *c, struct nl_handle *h) * @{ */ -static struct nl_msg *qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags) +static int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags, + struct nl_msg **result) { struct rtnl_qdisc_ops *qops; - struct nl_msg *msg; int err; - msg = tca_build_msg((struct rtnl_tca *) qdisc, type, flags); - if (!msg) - goto errout; + err = tca_build_msg((struct rtnl_tca *) qdisc, type, flags, result); + if (err < 0) + return err; qops = rtnl_qdisc_lookup_ops(qdisc); if (qops && qops->qo_get_opts) { @@ -165,24 +160,33 @@ static struct nl_msg *qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags) opts = qops->qo_get_opts(qdisc); if (opts) { - err = nla_put_nested(msg, TCA_OPTIONS, opts); + err = nla_put_nested(*result, TCA_OPTIONS, opts); nlmsg_free(opts); if (err < 0) goto errout; } } + /* Some qdiscs don't accept properly nested messages (e.g. netem). To + * accomodate for this, they can complete the message themselves. + */ + else if (qops && qops->qo_build_msg) { + err = qops->qo_build_msg(qdisc, *result); + if (err < 0) + goto errout; + } - return msg; + return 0; errout: - nlmsg_free(msg); + nlmsg_free(*result); - return NULL; + return err; } /** * Build a netlink message to add a new qdisc * @arg qdisc qdisc to add * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting an addition of a qdisc. * The netlink message header isn't fully equipped with all relevant @@ -192,23 +196,17 @@ errout: * Common message flags used: * - NLM_F_REPLACE - replace a potential existing qdisc * - * @return New netlink message + * @return 0 on success or a negative error code. */ -struct nl_msg *rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, - int flags) +int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags, + struct nl_msg **result) { - struct nl_msg *msg; - - msg = qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags); - if (!msg) - nl_errno(ENOMEM); - - return msg; + return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags, result); } /** * Add a new qdisc - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg qdisc qdisc to delete * @arg flags additional netlink message flags * @@ -221,22 +219,21 @@ struct nl_msg *rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, * * @return 0 on success or a negative error code */ -int rtnl_qdisc_add(struct nl_handle *handle, struct rtnl_qdisc *qdisc, +int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc, int flags) { struct nl_msg *msg; int err; - msg = rtnl_qdisc_build_add_request(qdisc, flags); - if (!msg) - return nl_errno(ENOMEM); + if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); if (err < 0) return err; - nlmsg_free(msg); - return nl_wait_for_ack(handle); + return wait_for_ack(sk); } /** @} */ @@ -250,23 +247,25 @@ int rtnl_qdisc_add(struct nl_handle *handle, struct rtnl_qdisc *qdisc, * Build a netlink message to change attributes of a existing qdisc * @arg qdisc qdisc to change * @arg new new qdisc attributes + * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting an change of qdisc * attributes. The netlink message header isn't fully equipped * with all relevant fields and must be sent out via * nl_send_auto_complete() or supplemented as needed. * - * @return New netlink message + * @return 0 on success or a negative error code. */ -struct nl_msg *rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, - struct rtnl_qdisc *new) +int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new, + struct nl_msg **result) { - return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE); + return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE, result); } /** * Change attributes of a qdisc - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg qdisc qdisc to change * @arg new new qdisc attributes * @@ -276,22 +275,21 @@ struct nl_msg *rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, * * @return 0 on success or a negative error code */ -int rtnl_qdisc_change(struct nl_handle *handle, struct rtnl_qdisc *qdisc, +int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc, struct rtnl_qdisc *new) { struct nl_msg *msg; int err; - msg = rtnl_qdisc_build_change_request(qdisc, new); - if (!msg) - return nl_errno(ENOMEM); + if ((err = rtnl_qdisc_build_change_request(qdisc, new, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); if (err < 0) return err; - nlmsg_free(msg); - return nl_wait_for_ack(handle); + return wait_for_ack(sk); } /** @} */ @@ -304,15 +302,17 @@ int rtnl_qdisc_change(struct nl_handle *handle, struct rtnl_qdisc *qdisc, /** * Build a netlink request message to delete a qdisc * @arg qdisc qdisc to delete + * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting a deletion of a qdisc. * The netlink message header isn't fully equipped with all relevant * fields and must thus be sent out via nl_send_auto_complete() * or supplemented as needed. * - * @return New netlink message + * @return 0 on success or a negative error code. */ -struct nl_msg *rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc) +int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc, + struct nl_msg **result) { struct nl_msg *msg; struct tcmsg tchdr; @@ -323,20 +323,24 @@ struct nl_msg *rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc) msg = nlmsg_alloc_simple(RTM_DELQDISC, 0); if (!msg) - return NULL; - - tchdr.tcm_family = AF_UNSPEC, - tchdr.tcm_handle = qdisc->q_handle, - tchdr.tcm_parent = qdisc->q_parent, - tchdr.tcm_ifindex = qdisc->q_ifindex, - nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO); + return -NLE_NOMEM; + + tchdr.tcm_family = AF_UNSPEC; + tchdr.tcm_handle = qdisc->q_handle; + tchdr.tcm_parent = qdisc->q_parent; + tchdr.tcm_ifindex = qdisc->q_ifindex; + if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) { + nlmsg_free(msg); + return -NLE_MSGSIZE; + } - return msg; + *result = msg; + return 0; } /** * Delete a qdisc - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg qdisc qdisc to delete * * Builds a netlink message by calling rtnl_qdisc_build_delete_request(), @@ -345,21 +349,20 @@ struct nl_msg *rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc) * * @return 0 on success or a negative error code */ -int rtnl_qdisc_delete(struct nl_handle *handle, struct rtnl_qdisc *qdisc) +int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc) { struct nl_msg *msg; int err; - msg = rtnl_qdisc_build_delete_request(qdisc); - if (!msg) - return nl_errno(ENOMEM); + if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); if (err < 0) return err; - nlmsg_free(msg); - return nl_wait_for_ack(handle); + return wait_for_ack(sk); } /** @} */ @@ -372,29 +375,17 @@ int rtnl_qdisc_delete(struct nl_handle *handle, struct rtnl_qdisc *qdisc) /** * Build a qdisc cache including all qdiscs currently configured in * the kernel - * @arg handle netlink handle + * @arg sk Netlink socket. + * @arg result Pointer to store resulting message. * * Allocates a new cache, initializes it properly and updates it to * include all qdiscs currently configured in the kernel. * - * @note The caller is responsible for destroying and freeing the - * cache after using it. - * @return The cache or NULL if an error has occured. + * @return 0 on success or a negative error code. */ -struct nl_cache * rtnl_qdisc_alloc_cache(struct nl_handle *handle) +int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result) { - struct nl_cache * cache; - - cache = nl_cache_alloc(&rtnl_qdisc_ops); - if (cache == NULL) - return NULL; - - if (handle && nl_cache_refill(handle, cache) < 0) { - nl_cache_free(cache); - return NULL; - } - - return cache; + return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result); } /** diff --git a/lib/route/qdisc_api.c b/lib/route/qdisc_api.c index ef4d07a..089f212 100644 --- a/lib/route/qdisc_api.c +++ b/lib/route/qdisc_api.c @@ -46,7 +46,7 @@ int rtnl_qdisc_register(struct rtnl_qdisc_ops *qops) for (op = &qdisc_ops_list; (o = *op) != NULL; op = &o->qo_next) if (!strcasecmp(qops->qo_kind, o->qo_kind)) - return nl_errno(EEXIST); + return -NLE_EXIST; qops->qo_next = NULL; *op = qops; @@ -67,7 +67,7 @@ int rtnl_qdisc_unregister(struct rtnl_qdisc_ops *qops) break; if (!o) - return nl_errno(ENOENT); + return -NLE_OBJ_NOTFOUND; *op = qops->qo_next; diff --git a/lib/route/qdisc_obj.c b/lib/route/qdisc_obj.c index bbbb954..dc52ae8 100644 --- a/lib/route/qdisc_obj.c +++ b/lib/route/qdisc_obj.c @@ -56,54 +56,49 @@ errout: return err; } -static int qdisc_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +static void qdisc_dump_line(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj; struct rtnl_qdisc_ops *qops; - - int line = tca_dump_brief((struct rtnl_tca *) qdisc, "qdisc", p, 0); + + tca_dump_line((struct rtnl_tca *) qdisc, "qdisc", p); qops = rtnl_qdisc_lookup_ops(qdisc); - if (qops && qops->qo_dump[NL_DUMP_BRIEF]) - line = qops->qo_dump[NL_DUMP_BRIEF](qdisc, p, line); + if (qops && qops->qo_dump[NL_DUMP_LINE]) + qops->qo_dump[NL_DUMP_LINE](qdisc, p); - dp_dump(p, "\n"); - - return line; + nl_dump(p, "\n"); } -static int qdisc_dump_full(struct nl_object *arg, struct nl_dump_params *p) +static void qdisc_dump_details(struct nl_object *arg, struct nl_dump_params *p) { struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) arg; struct rtnl_qdisc_ops *qops; - int line = qdisc_dump_brief(arg, p); + qdisc_dump_line(arg, p); - line = tca_dump_full((struct rtnl_tca *) qdisc, p, line); - dp_dump(p, "refcnt %u ", qdisc->q_info); + tca_dump_details((struct rtnl_tca *) qdisc, p); + nl_dump(p, "refcnt %u ", qdisc->q_info); qops = rtnl_qdisc_lookup_ops(qdisc); - if (qops && qops->qo_dump[NL_DUMP_FULL]) - line = qops->qo_dump[NL_DUMP_FULL](qdisc, p, line); + if (qops && qops->qo_dump[NL_DUMP_DETAILS]) + qops->qo_dump[NL_DUMP_DETAILS](qdisc, p); - dp_dump(p, "\n"); - return line; + nl_dump(p, "\n"); } -static int qdisc_dump_stats(struct nl_object *arg, struct nl_dump_params *p) +static void qdisc_dump_stats(struct nl_object *arg, struct nl_dump_params *p) { struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) arg; struct rtnl_qdisc_ops *qops; - int line = qdisc_dump_full(arg, p); - line = tca_dump_stats((struct rtnl_tca *) qdisc, p, line ); - dp_dump(p, "\n"); + qdisc_dump_details(arg, p); + tca_dump_stats((struct rtnl_tca *) qdisc, p); + nl_dump(p, "\n"); qops = rtnl_qdisc_lookup_ops(qdisc); if (qops && qops->qo_dump[NL_DUMP_STATS]) - line = qops->qo_dump[NL_DUMP_STATS](qdisc, p, line); - - return line; + qops->qo_dump[NL_DUMP_STATS](qdisc, p); } /** @@ -263,9 +258,11 @@ struct nl_object_ops qdisc_obj_ops = { .oo_size = sizeof(struct rtnl_qdisc), .oo_free_data = qdisc_free_data, .oo_clone = qdisc_clone, - .oo_dump[NL_DUMP_BRIEF] = qdisc_dump_brief, - .oo_dump[NL_DUMP_FULL] = qdisc_dump_full, - .oo_dump[NL_DUMP_STATS] = qdisc_dump_stats, + .oo_dump = { + [NL_DUMP_LINE] = qdisc_dump_line, + [NL_DUMP_DETAILS] = qdisc_dump_details, + [NL_DUMP_STATS] = qdisc_dump_stats, + }, .oo_compare = tca_compare, .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), }; diff --git a/lib/route/route.c b/lib/route/route.c index 0644bd7..c85c225 100644 --- a/lib/route/route.c +++ b/lib/route/route.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -27,199 +27,31 @@ static struct nl_cache_ops rtnl_route_ops; -static struct nla_policy route_policy[RTA_MAX+1] = { - [RTA_IIF] = { .type = NLA_STRING, - .maxlen = IFNAMSIZ, }, - [RTA_OIF] = { .type = NLA_U32 }, - [RTA_PRIORITY] = { .type = NLA_U32 }, - [RTA_FLOW] = { .type = NLA_U32 }, - [RTA_MP_ALGO] = { .type = NLA_U32 }, - [RTA_CACHEINFO] = { .minlen = sizeof(struct rta_cacheinfo) }, - [RTA_METRICS] = { .type = NLA_NESTED }, - [RTA_MULTIPATH] = { .type = NLA_NESTED }, -}; - -static void copy_cacheinfo_into_route(struct rta_cacheinfo *ci, - struct rtnl_route *route) -{ - struct rtnl_rtcacheinfo nci = { - .rtci_clntref = ci->rta_clntref, - .rtci_last_use = ci->rta_lastuse, - .rtci_expires = ci->rta_expires, - .rtci_error = ci->rta_error, - .rtci_used = ci->rta_used, - .rtci_id = ci->rta_id, - .rtci_ts = ci->rta_ts, - .rtci_tsage = ci->rta_tsage, - }; - - rtnl_route_set_cacheinfo(route, &nci); -} - static int route_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct nlmsghdr *nlh, struct nl_parser_param *pp) { - struct rtmsg *rtm; struct rtnl_route *route; - struct nlattr *tb[RTA_MAX + 1]; - struct nl_addr *src = NULL, *dst = NULL, *addr; int err; - route = rtnl_route_alloc(); - if (!route) { - err = nl_errno(ENOMEM); - goto errout; - } - - route->ce_msgtype = nlh->nlmsg_type; - - err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, - route_policy); - if (err < 0) - goto errout; - - rtm = nlmsg_data(nlh); - rtnl_route_set_family(route, rtm->rtm_family); - rtnl_route_set_tos(route, rtm->rtm_tos); - rtnl_route_set_table(route, rtm->rtm_table); - rtnl_route_set_type(route, rtm->rtm_type); - rtnl_route_set_scope(route, rtm->rtm_scope); - rtnl_route_set_protocol(route, rtm->rtm_protocol); - rtnl_route_set_flags(route, rtm->rtm_flags); - - if (tb[RTA_DST]) { - dst = nla_get_addr(tb[RTA_DST], rtm->rtm_family); - if (dst == NULL) - goto errout_errno; - } else { - dst = nl_addr_alloc(0); - nl_addr_set_family(dst, rtm->rtm_family); - } - - nl_addr_set_prefixlen(dst, rtm->rtm_dst_len); - err = rtnl_route_set_dst(route, dst); - if (err < 0) - goto errout; - - nl_addr_put(dst); - - if (tb[RTA_SRC]) { - src = nla_get_addr(tb[RTA_SRC], rtm->rtm_family); - if (src == NULL) - goto errout_errno; - } else if (rtm->rtm_src_len) - src = nl_addr_alloc(0); - - if (src) { - nl_addr_set_prefixlen(src, rtm->rtm_src_len); - rtnl_route_set_src(route, src); - nl_addr_put(src); - } - - if (tb[RTA_IIF]) - rtnl_route_set_iif(route, nla_get_string(tb[RTA_IIF])); - - if (tb[RTA_OIF]) - rtnl_route_set_oif(route, nla_get_u32(tb[RTA_OIF])); - - if (tb[RTA_GATEWAY]) { - addr = nla_get_addr(tb[RTA_GATEWAY], route->rt_family); - if (addr == NULL) - goto errout_errno; - rtnl_route_set_gateway(route, addr); - nl_addr_put(addr); - } - - if (tb[RTA_PRIORITY]) - rtnl_route_set_prio(route, nla_get_u32(tb[RTA_PRIORITY])); - - if (tb[RTA_PREFSRC]) { - addr = nla_get_addr(tb[RTA_PREFSRC], route->rt_family); - if (addr == NULL) - goto errout_errno; - rtnl_route_set_pref_src(route, addr); - nl_addr_put(addr); - } - - if (tb[RTA_METRICS]) { - struct nlattr *mtb[RTAX_MAX + 1]; - int i; - - err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL); - if (err < 0) - goto errout; - - for (i = 1; i <= RTAX_MAX; i++) { - if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) { - uint32_t m = nla_get_u32(mtb[i]); - if (rtnl_route_set_metric(route, i, m) < 0) - goto errout_errno; - } - } - } - - if (tb[RTA_MULTIPATH]) { - struct rtnl_nexthop *nh; - struct rtnexthop *rtnh = nla_data(tb[RTA_MULTIPATH]); - size_t tlen = nla_len(tb[RTA_MULTIPATH]); - - while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) { - nh = rtnl_route_nh_alloc(); - if (!nh) - goto errout; - - rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops); - rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex); - rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags); - - if (rtnh->rtnh_len > sizeof(*rtnh)) { - struct nlattr *ntb[RTA_MAX + 1]; - nla_parse(ntb, RTA_MAX, (struct nlattr *) - RTNH_DATA(rtnh), - rtnh->rtnh_len - sizeof(*rtnh), - route_policy); - - if (ntb[RTA_GATEWAY]) { - nh->rtnh_gateway = nla_get_addr( - ntb[RTA_GATEWAY], - route->rt_family); - nh->rtnh_mask = NEXTHOP_HAS_GATEWAY; - } - } - - rtnl_route_add_nexthop(route, nh); - tlen -= RTNH_ALIGN(rtnh->rtnh_len); - rtnh = RTNH_NEXT(rtnh); - } - } - - if (tb[RTA_FLOW]) - rtnl_route_set_realms(route, nla_get_u32(tb[RTA_FLOW])); - - if (tb[RTA_CACHEINFO]) - copy_cacheinfo_into_route(nla_data(tb[RTA_CACHEINFO]), route); - - if (tb[RTA_MP_ALGO]) - rtnl_route_set_mp_algo(route, nla_get_u32(tb[RTA_MP_ALGO])); + if ((err = rtnl_route_parse(nlh, &route)) < 0) + return err; err = pp->pp_cb((struct nl_object *) route, pp); - if (err < 0) - goto errout; - - err = P_ACCEPT; -errout: rtnl_route_put(route); return err; - -errout_errno: - err = nl_get_errno(); - goto errout; } -static int route_request_update(struct nl_cache *c, struct nl_handle *h) +static int route_request_update(struct nl_cache *c, struct nl_sock *h) { - return nl_rtgen_request(h, RTM_GETROUTE, AF_UNSPEC, NLM_F_DUMP); + struct rtmsg rhdr = { + .rtm_family = c->c_iarg1, + }; + + if (c->c_iarg2 & ROUTE_CACHE_CONTENT) + rhdr.rtm_flags |= RTM_F_CLONED; + + return nl_send_simple(h, RTM_GETROUTE, NLM_F_DUMP, &rhdr, sizeof(rhdr)); } /** @@ -229,7 +61,9 @@ static int route_request_update(struct nl_cache *c, struct nl_handle *h) /** * Build a route cache holding all routes currently configured in the kernel - * @arg handle netlink handle + * @arg sk Netlink socket. + * @arg family Address family of routes to cover or AF_UNSPEC + * @arg flags Flags * * Allocates a new cache, initializes it properly and updates it to * contain all routes currently configured in the kernel. @@ -238,20 +72,25 @@ static int route_request_update(struct nl_cache *c, struct nl_handle *h) * cache after using it. * @return The cache or NULL if an error has occured. */ -struct nl_cache *rtnl_route_alloc_cache(struct nl_handle *handle) +int rtnl_route_alloc_cache(struct nl_sock *sk, int family, int flags, + struct nl_cache **result) { struct nl_cache *cache; + int err; - cache = nl_cache_alloc(&rtnl_route_ops); - if (!cache) - return NULL; + if (!(cache = nl_cache_alloc(&rtnl_route_ops))) + return -NLE_NOMEM; - if (handle && nl_cache_refill(handle, cache) < 0) { + cache->c_iarg1 = family; + cache->c_iarg2 = flags; + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { free(cache); - return NULL; + return err; } - return cache; + *result = cache; + return 0; } /** @} */ @@ -261,152 +100,67 @@ struct nl_cache *rtnl_route_alloc_cache(struct nl_handle *handle) * @{ */ -static struct nl_msg *build_route_msg(struct rtnl_route *tmpl, int cmd, - int flags) +static int build_route_msg(struct rtnl_route *tmpl, int cmd, int flags, + struct nl_msg **result) { struct nl_msg *msg; - struct nl_addr *addr; - int scope, i, oif, nmetrics = 0; - struct nlattr *metrics; - struct rtmsg rtmsg = { - .rtm_family = rtnl_route_get_family(tmpl), - .rtm_dst_len = rtnl_route_get_dst_len(tmpl), - .rtm_src_len = rtnl_route_get_src_len(tmpl), - .rtm_tos = rtnl_route_get_tos(tmpl), - .rtm_table = rtnl_route_get_table(tmpl), - .rtm_type = rtnl_route_get_type(tmpl), - .rtm_protocol = rtnl_route_get_protocol(tmpl), - .rtm_flags = rtnl_route_get_flags(tmpl), - }; - - if (rtmsg.rtm_family == AF_UNSPEC) { - nl_error(EINVAL, "Cannot build route message, address " \ - "family is unknown."); - return NULL; - } - - scope = rtnl_route_get_scope(tmpl); - if (scope == RT_SCOPE_NOWHERE) { - if (rtmsg.rtm_type == RTN_LOCAL) - scope = RT_SCOPE_HOST; - else { - /* XXX Change to UNIVERSE if gw || nexthops */ - scope = RT_SCOPE_LINK; - } - } - - rtmsg.rtm_scope = scope; - - msg = nlmsg_alloc_simple(cmd, flags); - if (msg == NULL) - return NULL; - - if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0) - goto nla_put_failure; - - addr = rtnl_route_get_dst(tmpl); - if (addr) - NLA_PUT_ADDR(msg, RTA_DST, addr); - - addr = rtnl_route_get_src(tmpl); - if (addr) - NLA_PUT_ADDR(msg, RTA_SRC, addr); - - addr = rtnl_route_get_gateway(tmpl); - if (addr) - NLA_PUT_ADDR(msg, RTA_GATEWAY, addr); - - addr = rtnl_route_get_pref_src(tmpl); - if (addr) - NLA_PUT_ADDR(msg, RTA_PREFSRC, addr); - - NLA_PUT_U32(msg, RTA_PRIORITY, rtnl_route_get_prio(tmpl)); - - oif = rtnl_route_get_oif(tmpl); - if (oif != RTNL_LINK_NOT_FOUND) - NLA_PUT_U32(msg, RTA_OIF, oif); - - for (i = 1; i <= RTAX_MAX; i++) - if (rtnl_route_get_metric(tmpl, i) != UINT_MAX) - nmetrics++; - - if (nmetrics > 0) { - unsigned int val; - - metrics = nla_nest_start(msg, RTA_METRICS); - if (metrics == NULL) - goto nla_put_failure; + int err; - for (i = 1; i <= RTAX_MAX; i++) { - val = rtnl_route_get_metric(tmpl, i); - if (val != UINT_MAX) - NLA_PUT_U32(msg, i, val); - } + if (!(msg = nlmsg_alloc_simple(cmd, flags))) + return -NLE_NOMEM; - nla_nest_end(msg, metrics); + if ((err = rtnl_route_build_msg(msg, tmpl)) < 0) { + nlmsg_free(msg); + return err; } -#if 0 - RTA_IIF, - RTA_MULTIPATH, - RTA_PROTOINFO, - RTA_FLOW, - RTA_CACHEINFO, - RTA_SESSION, - RTA_MP_ALGO, -#endif - - return msg; - -nla_put_failure: - nlmsg_free(msg); - return NULL; + *result = msg; + return 0; } -struct nl_msg *rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags) +int rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags, + struct nl_msg **result) { - return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags); + return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags, + result); } -int rtnl_route_add(struct nl_handle *handle, struct rtnl_route *route, - int flags) +int rtnl_route_add(struct nl_sock *sk, struct rtnl_route *route, int flags) { struct nl_msg *msg; int err; - msg = rtnl_route_build_add_request(route, flags); - if (!msg) - return nl_get_errno(); + if ((err = rtnl_route_build_add_request(route, flags, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); if (err < 0) return err; - return nl_wait_for_ack(handle); + return wait_for_ack(sk); } -struct nl_msg *rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags) +int rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags, + struct nl_msg **result) { - return build_route_msg(tmpl, RTM_DELROUTE, flags); + return build_route_msg(tmpl, RTM_DELROUTE, flags, result); } -int rtnl_route_del(struct nl_handle *handle, struct rtnl_route *route, - int flags) +int rtnl_route_delete(struct nl_sock *sk, struct rtnl_route *route, int flags) { struct nl_msg *msg; int err; - msg = rtnl_route_build_del_request(route, flags); - if (!msg) - return nl_get_errno(); + if ((err = rtnl_route_build_del_request(route, flags, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); if (err < 0) return err; - return nl_wait_for_ack(handle); + return wait_for_ack(sk); } /** @} */ diff --git a/lib/route/route_obj.c b/lib/route/route_obj.c index 78e7712..7f26bfd 100644 --- a/lib/route/route_obj.c +++ b/lib/route/route_obj.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -20,14 +20,11 @@ * routing table RT_TABLE_MAIN * scope RT_SCOPE_NOWHERE * tos 0 - * realms 0 * protocol RTPROT_STATIC * prio 0 * family AF_UNSPEC * type RTN_UNICAST - * oif RTNL_LINK_NOT_FOUND * iif NULL - * mpalgo IP_MP_ALG_NONE * @endcode * * @{ @@ -41,6 +38,7 @@ #include <netlink/route/rtnl.h> #include <netlink/route/route.h> #include <netlink/route/link.h> +#include <netlink/route/nexthop.h> /** @cond SKIP */ #define ROUTE_ATTR_FAMILY 0x000001 @@ -61,15 +59,18 @@ #define ROUTE_ATTR_MULTIPATH 0x008000 #define ROUTE_ATTR_REALMS 0x010000 #define ROUTE_ATTR_CACHEINFO 0x020000 -#define ROUTE_ATTR_MP_ALGO 0x040000 /** @endcond */ -static int route_dump_brief(struct nl_object *a, struct nl_dump_params *p); - static void route_constructor(struct nl_object *c) { struct rtnl_route *r = (struct rtnl_route *) c; + r->rt_family = AF_UNSPEC; + r->rt_scope = RT_SCOPE_NOWHERE; + r->rt_table = RT_TABLE_MAIN; + r->rt_protocol = RTPROT_STATIC; + r->rt_type = RTN_UNICAST; + nl_init_list_head(&r->rt_nexthops); } @@ -83,11 +84,10 @@ static void route_free_data(struct nl_object *c) nl_addr_put(r->rt_dst); nl_addr_put(r->rt_src); - nl_addr_put(r->rt_gateway); nl_addr_put(r->rt_pref_src); nl_list_for_each_entry_safe(nh, tmp, &r->rt_nexthops, rtnh_list) { - rtnl_route_remove_nexthop(nh); + rtnl_route_remove_nexthop(r, nh); rtnl_route_nh_free(nh); } } @@ -100,366 +100,258 @@ static int route_clone(struct nl_object *_dst, struct nl_object *_src) if (src->rt_dst) if (!(dst->rt_dst = nl_addr_clone(src->rt_dst))) - goto errout; + return -NLE_NOMEM; if (src->rt_src) if (!(dst->rt_src = nl_addr_clone(src->rt_src))) - goto errout; + return -NLE_NOMEM; - if (src->rt_gateway) - if (!(dst->rt_gateway = nl_addr_clone(src->rt_gateway))) - goto errout; - if (src->rt_pref_src) if (!(dst->rt_pref_src = nl_addr_clone(src->rt_pref_src))) - goto errout; + return -NLE_NOMEM; nl_init_list_head(&dst->rt_nexthops); nl_list_for_each_entry(nh, &src->rt_nexthops, rtnh_list) { new = rtnl_route_nh_clone(nh); if (!new) - goto errout; + return -NLE_NOMEM; rtnl_route_add_nexthop(dst, new); } return 0; -errout: - return nl_get_errno(); } -static int route_dump_brief(struct nl_object *a, struct nl_dump_params *p) +static void route_dump_line(struct nl_object *a, struct nl_dump_params *p) { struct rtnl_route *r = (struct rtnl_route *) a; struct nl_cache *link_cache; + int cache = 0, flags; char buf[64]; link_cache = nl_cache_mngt_require("route/link"); + if (r->rt_flags & RTM_F_CLONED) + cache = 1; + + nl_dump_line(p, "%s ", nl_af2str(r->rt_family, buf, sizeof(buf))); + + if (cache) + nl_dump(p, "cache "); + if (!(r->ce_mask & ROUTE_ATTR_DST) || nl_addr_get_len(r->rt_dst) == 0) - dp_dump(p, "default "); + nl_dump(p, "default "); else - dp_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf))); + nl_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf))); - if (r->ce_mask & ROUTE_ATTR_OIF) { - if (link_cache) - dp_dump(p, "dev %s ", - rtnl_link_i2name(link_cache, r->rt_oif, - buf, sizeof(buf))); - else - dp_dump(p, "dev %d ", r->rt_oif); - } + if (r->ce_mask & ROUTE_ATTR_TABLE && !cache) + nl_dump(p, "table %s ", + rtnl_route_table2str(r->rt_table, buf, sizeof(buf))); - if (r->ce_mask & ROUTE_ATTR_GATEWAY) - dp_dump(p, "via %s ", nl_addr2str(r->rt_gateway, buf, - sizeof(buf))); - else if (r->ce_mask & ROUTE_ATTR_MULTIPATH) - dp_dump(p, "via nexthops "); + if (r->ce_mask & ROUTE_ATTR_TYPE) + nl_dump(p, "type %s ", + nl_rtntype2str(r->rt_type, buf, sizeof(buf))); - if (r->ce_mask & ROUTE_ATTR_SCOPE) - dp_dump(p, "scope %s ", - rtnl_scope2str(r->rt_scope, buf, sizeof(buf))); + if (r->ce_mask & ROUTE_ATTR_TOS && r->rt_tos != 0) + nl_dump(p, "tos %#x ", r->rt_tos); - if (r->ce_mask & ROUTE_ATTR_FLAGS && r->rt_flags) { - int flags = r->rt_flags; + if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { + struct rtnl_nexthop *nh; + + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + p->dp_ivar = NH_DUMP_FROM_ONELINE; + rtnl_route_nh_dump(nh, p); + } + } + + flags = r->rt_flags & ~(RTM_F_CLONED); + if (r->ce_mask & ROUTE_ATTR_FLAGS && flags) { + + nl_dump(p, "<"); - dp_dump(p, "<"); - #define PRINT_FLAG(f) if (flags & RTNH_F_##f) { \ - flags &= ~RTNH_F_##f; dp_dump(p, #f "%s", flags ? "," : ""); } + flags &= ~RTNH_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); } PRINT_FLAG(DEAD); PRINT_FLAG(ONLINK); PRINT_FLAG(PERVASIVE); #undef PRINT_FLAG #define PRINT_FLAG(f) if (flags & RTM_F_##f) { \ - flags &= ~RTM_F_##f; dp_dump(p, #f "%s", flags ? "," : ""); } + flags &= ~RTM_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); } PRINT_FLAG(NOTIFY); - PRINT_FLAG(CLONED); PRINT_FLAG(EQUALIZE); PRINT_FLAG(PREFIX); #undef PRINT_FLAG - dp_dump(p, ">"); - } +#define PRINT_FLAG(f) if (flags & RTCF_##f) { \ + flags &= ~RTCF_##f; nl_dump(p, #f "%s", flags ? "," : ""); } + PRINT_FLAG(NOTIFY); + PRINT_FLAG(REDIRECTED); + PRINT_FLAG(DOREDIRECT); + PRINT_FLAG(DIRECTSRC); + PRINT_FLAG(DNAT); + PRINT_FLAG(BROADCAST); + PRINT_FLAG(MULTICAST); + PRINT_FLAG(LOCAL); +#undef PRINT_FLAG - dp_dump(p, "\n"); + nl_dump(p, ">"); + } - return 1; + nl_dump(p, "\n"); } -static int route_dump_full(struct nl_object *a, struct nl_dump_params *p) +static void route_dump_details(struct nl_object *a, struct nl_dump_params *p) { struct rtnl_route *r = (struct rtnl_route *) a; struct nl_cache *link_cache; char buf[128]; - int i, line; + int i; link_cache = nl_cache_mngt_require("route/link"); - line = route_dump_brief(a, p); - if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { - struct rtnl_nexthop *nh; - - nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { - dp_dump_line(p, line++, " via "); - - if (nh->rtnh_mask & NEXTHOP_HAS_GATEWAY) - dp_dump(p, "%s ", - nl_addr2str(nh->rtnh_gateway, - buf, sizeof(buf))); - if (link_cache) { - dp_dump(p, "dev %s ", - rtnl_link_i2name(link_cache, - nh->rtnh_ifindex, - buf, sizeof(buf))); - } else - dp_dump(p, "dev %d ", nh->rtnh_ifindex); - - dp_dump(p, "weight %u <%s>\n", nh->rtnh_weight, - rtnl_route_nh_flags2str(nh->rtnh_flags, - buf, sizeof(buf))); - } - } - - dp_dump_line(p, line++, " "); + route_dump_line(a, p); + nl_dump_line(p, " "); if (r->ce_mask & ROUTE_ATTR_PREF_SRC) - dp_dump(p, "preferred-src %s ", + nl_dump(p, "preferred-src %s ", nl_addr2str(r->rt_pref_src, buf, sizeof(buf))); - if (r->ce_mask & ROUTE_ATTR_TABLE) - dp_dump(p, "table %s ", - rtnl_route_table2str(r->rt_table, buf, sizeof(buf))); - - if (r->ce_mask & ROUTE_ATTR_TYPE) - dp_dump(p, "type %s ", - nl_rtntype2str(r->rt_type, buf, sizeof(buf))); + if (r->ce_mask & ROUTE_ATTR_SCOPE && r->rt_scope != RT_SCOPE_NOWHERE) + nl_dump(p, "scope %s ", + rtnl_scope2str(r->rt_scope, buf, sizeof(buf))); if (r->ce_mask & ROUTE_ATTR_PRIO) - dp_dump(p, "metric %#x ", r->rt_prio); - - if (r->ce_mask & ROUTE_ATTR_FAMILY) - dp_dump(p, "family %s ", - nl_af2str(r->rt_family, buf, sizeof(buf))); + nl_dump(p, "priority %#x ", r->rt_prio); if (r->ce_mask & ROUTE_ATTR_PROTOCOL) - dp_dump(p, "protocol %s ", + nl_dump(p, "protocol %s ", rtnl_route_proto2str(r->rt_protocol, buf, sizeof(buf))); - dp_dump(p, "\n"); - - if ((r->ce_mask & (ROUTE_ATTR_IIF | ROUTE_ATTR_SRC | ROUTE_ATTR_TOS | - ROUTE_ATTR_REALMS)) || - ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && - r->rt_cacheinfo.rtci_error)) { - dp_dump_line(p, line++, " "); - - if (r->ce_mask & ROUTE_ATTR_IIF) - dp_dump(p, "iif %s ", r->rt_iif); + if (r->ce_mask & ROUTE_ATTR_IIF) { + if (link_cache) { + nl_dump(p, "iif %s ", + rtnl_link_i2name(link_cache, r->rt_iif, + buf, sizeof(buf))); + } else + nl_dump(p, "iif %d ", r->rt_iif); + } - if (r->ce_mask & ROUTE_ATTR_SRC) - dp_dump(p, "src %s ", - nl_addr2str(r->rt_src, buf, sizeof(buf))); + if (r->ce_mask & ROUTE_ATTR_SRC) + nl_dump(p, "src %s ", nl_addr2str(r->rt_src, buf, sizeof(buf))); - if (r->ce_mask & ROUTE_ATTR_TOS) - dp_dump(p, "tos %#x ", r->rt_tos); + nl_dump(p, "\n"); - if (r->ce_mask & ROUTE_ATTR_REALMS) - dp_dump(p, "realm %04x:%04x ", - RTNL_REALM_FROM(r->rt_realms), - RTNL_REALM_TO(r->rt_realms)); + if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { + struct rtnl_nexthop *nh; - if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && - r->rt_cacheinfo.rtci_error) - dp_dump(p, "error %d (%s) ", r->rt_cacheinfo.rtci_error, - strerror(-r->rt_cacheinfo.rtci_error)); + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + nl_dump_line(p, " "); + p->dp_ivar = NH_DUMP_FROM_DETAILS; + rtnl_route_nh_dump(nh, p); + nl_dump(p, "\n"); + } + } - dp_dump(p, "\n"); + if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && r->rt_cacheinfo.rtci_error) { + nl_dump_line(p, " cacheinfo error %d (%s)\n", + r->rt_cacheinfo.rtci_error, + strerror(-r->rt_cacheinfo.rtci_error)); } if (r->ce_mask & ROUTE_ATTR_METRICS) { - dp_dump_line(p, line++, " "); + nl_dump_line(p, " metrics ["); for (i = 0; i < RTAX_MAX; i++) if (r->rt_metrics_mask & (1 << i)) - dp_dump(p, "%s %u ", + nl_dump(p, "%s %u ", rtnl_route_metric2str(i+1, buf, sizeof(buf)), r->rt_metrics[i]); - dp_dump(p, "\n"); + nl_dump(p, "]\n"); } - - return line; } -static int route_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +static void route_dump_stats(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_route *route = (struct rtnl_route *) obj; - int line; - line = route_dump_full(obj, p); + route_dump_details(obj, p); if (route->ce_mask & ROUTE_ATTR_CACHEINFO) { struct rtnl_rtcacheinfo *ci = &route->rt_cacheinfo; - dp_dump_line(p, line++, " used %u refcnt %u ", - ci->rtci_used, ci->rtci_clntref); - dp_dump_line(p, line++, "last-use %us expires %us\n", + + nl_dump_line(p, " used %u refcnt %u last-use %us " + "expires %us\n", + ci->rtci_used, ci->rtci_clntref, ci->rtci_last_use / nl_get_hz(), ci->rtci_expires / nl_get_hz()); } - - return line; } -static int route_dump_xml(struct nl_object *obj, struct nl_dump_params *p) +static void route_dump_env(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_route *route = (struct rtnl_route *) obj; + struct nl_cache *link_cache; char buf[128]; - int line = 0; - - dp_dump_line(p, line++, "<route>\n"); - dp_dump_line(p, line++, " <family>%s</family>\n", + + link_cache = nl_cache_mngt_require("route/link"); + + nl_dump_line(p, "ROUTE_FAMILY=%s\n", nl_af2str(route->rt_family, buf, sizeof(buf))); if (route->ce_mask & ROUTE_ATTR_DST) - dp_dump_line(p, line++, " <dst>%s</dst>\n", + nl_dump_line(p, "ROUTE_DST=%s\n", nl_addr2str(route->rt_dst, buf, sizeof(buf))); if (route->ce_mask & ROUTE_ATTR_SRC) - dp_dump_line(p, line++, " <src>%s</src>\n", + nl_dump_line(p, "ROUTE_SRC=%s\n", nl_addr2str(route->rt_src, buf, sizeof(buf))); - if (route->ce_mask & ROUTE_ATTR_GATEWAY) - dp_dump_line(p, line++, " <gateway>%s</gateway>\n", - nl_addr2str(route->rt_gateway, buf, sizeof(buf))); - if (route->ce_mask & ROUTE_ATTR_PREF_SRC) - dp_dump_line(p, line++, " <prefsrc>%s</prefsrc>\n", + nl_dump_line(p, "ROUTE_PREFSRC=%s\n", nl_addr2str(route->rt_pref_src, buf, sizeof(buf))); - if (route->ce_mask & ROUTE_ATTR_IIF) - dp_dump_line(p, line++, " <iif>%s</iif>\n", route->rt_iif); - - if (route->ce_mask & ROUTE_ATTR_REALMS) - dp_dump_line(p, line++, " <realms>%u</realms>\n", - route->rt_realms); + if (route->ce_mask & ROUTE_ATTR_IIF) { + if (link_cache) { + nl_dump_line(p, "ROUTE_IIF=%s", + rtnl_link_i2name(link_cache, route->rt_iif, + buf, sizeof(buf))); + } else + nl_dump_line(p, "ROUTE_IIF=%d", route->rt_iif); + } if (route->ce_mask & ROUTE_ATTR_TOS) - dp_dump_line(p, line++, " <tos>%u</tos>\n", route->rt_tos); + nl_dump_line(p, "ROUTE_TOS=%u\n", route->rt_tos); if (route->ce_mask & ROUTE_ATTR_TABLE) - dp_dump_line(p, line++, " <table>%u</table>\n", + nl_dump_line(p, "ROUTE_TABLE=%u\n", route->rt_table); if (route->ce_mask & ROUTE_ATTR_SCOPE) - dp_dump_line(p, line++, " <scope>%s</scope>\n", + nl_dump_line(p, "ROUTE_SCOPE=%s\n", rtnl_scope2str(route->rt_scope, buf, sizeof(buf))); if (route->ce_mask & ROUTE_ATTR_PRIO) - dp_dump_line(p, line++, " <metric>%u</metric>\n", + nl_dump_line(p, "ROUTE_PRIORITY=%u\n", route->rt_prio); - if (route->ce_mask & ROUTE_ATTR_OIF) { - struct nl_cache *link_cache; - - link_cache = nl_cache_mngt_require("route/link"); - if (link_cache) - dp_dump_line(p, line++, " <oif>%s</oif>\n", - rtnl_link_i2name(link_cache, - route->rt_oif, - buf, sizeof(buf))); - else - dp_dump_line(p, line++, " <oif>%u</oif>\n", - route->rt_oif); - } - if (route->ce_mask & ROUTE_ATTR_TYPE) - dp_dump_line(p, line++, " <type>%s</type>\n", + nl_dump_line(p, "ROUTE_TYPE=%s\n", nl_rtntype2str(route->rt_type, buf, sizeof(buf))); - dp_dump_line(p, line++, "</route>\n"); - -#if 0 - uint8_t rt_protocol; - uint32_t rt_flags; - uint32_t rt_metrics[RTAX_MAX]; - uint32_t rt_metrics_mask; - struct rtnl_nexthop * rt_nexthops; - struct rtnl_rtcacheinfo rt_cacheinfo; - uint32_t rt_mp_algo; - -#endif - - return line; -} - -static int route_dump_env(struct nl_object *obj, struct nl_dump_params *p) -{ - struct rtnl_route *route = (struct rtnl_route *) obj; - char buf[128]; - int line = 0; - - dp_dump_line(p, line++, "ROUTE_FAMILY=%s\n", - nl_af2str(route->rt_family, buf, sizeof(buf))); - - if (route->ce_mask & ROUTE_ATTR_DST) - dp_dump_line(p, line++, "ROUTE_DST=%s\n", - nl_addr2str(route->rt_dst, buf, sizeof(buf))); - - if (route->ce_mask & ROUTE_ATTR_SRC) - dp_dump_line(p, line++, "ROUTE_SRC=%s\n", - nl_addr2str(route->rt_src, buf, sizeof(buf))); - - if (route->ce_mask & ROUTE_ATTR_GATEWAY) - dp_dump_line(p, line++, "ROUTE_GATEWAY=%s\n", - nl_addr2str(route->rt_gateway, buf, sizeof(buf))); - - if (route->ce_mask & ROUTE_ATTR_PREF_SRC) - dp_dump_line(p, line++, "ROUTE_PREFSRC=%s\n", - nl_addr2str(route->rt_pref_src, buf, sizeof(buf))); - - if (route->ce_mask & ROUTE_ATTR_IIF) - dp_dump_line(p, line++, "ROUTE_IIF=%s\n", route->rt_iif); - - if (route->ce_mask & ROUTE_ATTR_REALMS) - dp_dump_line(p, line++, "ROUTE_REALM=%u\n", - route->rt_realms); - - if (route->ce_mask & ROUTE_ATTR_TOS) - dp_dump_line(p, line++, "ROUTE_TOS=%u\n", route->rt_tos); - - if (route->ce_mask & ROUTE_ATTR_TABLE) - dp_dump_line(p, line++, "ROUTE_TABLE=%u\n", - route->rt_table); - - if (route->ce_mask & ROUTE_ATTR_SCOPE) - dp_dump_line(p, line++, "ROUTE_SCOPE=%s\n", - rtnl_scope2str(route->rt_scope, buf, sizeof(buf))); - - if (route->ce_mask & ROUTE_ATTR_PRIO) - dp_dump_line(p, line++, "ROUTE_METRIC=%u\n", - route->rt_prio); - - if (route->ce_mask & ROUTE_ATTR_OIF) { - struct nl_cache *link_cache; + if (route->ce_mask & ROUTE_ATTR_MULTIPATH) { + struct rtnl_nexthop *nh; + int index = 1; - dp_dump_line(p, line++, "ROUTE_OIF_IFINDEX=%u\n", - route->rt_oif); + if (route->rt_nr_nh > 0) + nl_dump_line(p, "ROUTE_NR_NH=%u\n", route->rt_nr_nh); - link_cache = nl_cache_mngt_require("route/link"); - if (link_cache) - dp_dump_line(p, line++, "ROUTE_OIF_IFNAME=%s\n", - rtnl_link_i2name(link_cache, - route->rt_oif, - buf, sizeof(buf))); + nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) { + p->dp_ivar = index++; + rtnl_route_nh_dump(nh, p); + } } - - if (route->ce_mask & ROUTE_ATTR_TYPE) - dp_dump_line(p, line++, "ROUTE_TYPE=%s\n", - nl_rtntype2str(route->rt_type, buf, sizeof(buf))); - - return line; } static int route_compare(struct nl_object *_a, struct nl_object *_b, @@ -467,7 +359,8 @@ static int route_compare(struct nl_object *_a, struct nl_object *_b, { struct rtnl_route *a = (struct rtnl_route *) _a; struct rtnl_route *b = (struct rtnl_route *) _b; - int diff = 0; + struct rtnl_nexthop *nh_a, *nh_b; + int i, diff = 0, found; #define ROUTE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ROUTE_ATTR_##ATTR, a, b, EXPR) @@ -477,29 +370,89 @@ static int route_compare(struct nl_object *_a, struct nl_object *_b, diff |= ROUTE_DIFF(PROTOCOL, a->rt_protocol != b->rt_protocol); diff |= ROUTE_DIFF(SCOPE, a->rt_scope != b->rt_scope); diff |= ROUTE_DIFF(TYPE, a->rt_type != b->rt_type); - diff |= ROUTE_DIFF(OIF, a->rt_oif != b->rt_oif); diff |= ROUTE_DIFF(PRIO, a->rt_prio != b->rt_prio); - diff |= ROUTE_DIFF(REALMS, a->rt_realms != b->rt_realms); - diff |= ROUTE_DIFF(MP_ALGO, a->rt_mp_algo != b->rt_mp_algo); diff |= ROUTE_DIFF(DST, nl_addr_cmp(a->rt_dst, b->rt_dst)); diff |= ROUTE_DIFF(SRC, nl_addr_cmp(a->rt_src, b->rt_src)); - diff |= ROUTE_DIFF(IIF, strcmp(a->rt_iif, b->rt_iif)); + diff |= ROUTE_DIFF(IIF, a->rt_iif != b->rt_iif); diff |= ROUTE_DIFF(PREF_SRC, nl_addr_cmp(a->rt_pref_src, b->rt_pref_src)); - diff |= ROUTE_DIFF(GATEWAY, nl_addr_cmp(a->rt_gateway, - b->rt_gateway)); - /* FIXME: Compare metrics, multipath config */ + if (flags & LOOSE_COMPARISON) { + nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) { + found = 0; + nl_list_for_each_entry(nh_a, &a->rt_nexthops, + rtnh_list) { + if (!rtnl_route_nh_compare(nh_a, nh_b, + nh_b->ce_mask, 1)) { + found = 1; + break; + } + } + + if (!found) + goto nh_mismatch; + } + + for (i = 0; i < RTAX_MAX - 1; i++) { + if (a->rt_metrics_mask & (1 << i) && + (!(b->rt_metrics_mask & (1 << i)) || + a->rt_metrics[i] != b->rt_metrics[i])) + ROUTE_DIFF(METRICS, 1); + } - if (flags & LOOSE_FLAG_COMPARISON) diff |= ROUTE_DIFF(FLAGS, (a->rt_flags ^ b->rt_flags) & b->rt_flag_mask); - else + } else { + if (a->rt_nr_nh != a->rt_nr_nh) + goto nh_mismatch; + + /* search for a dup in each nh of a */ + nl_list_for_each_entry(nh_a, &a->rt_nexthops, rtnh_list) { + found = 0; + nl_list_for_each_entry(nh_b, &b->rt_nexthops, + rtnh_list) { + if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) + found = 1; + break; + } + if (!found) + goto nh_mismatch; + } + + /* search for a dup in each nh of b, covers case where a has + * dupes itself */ + nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) { + found = 0; + nl_list_for_each_entry(nh_a, &a->rt_nexthops, + rtnh_list) { + if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) + found = 1; + break; + } + if (!found) + goto nh_mismatch; + } + + for (i = 0; i < RTAX_MAX - 1; i++) { + if ((a->rt_metrics_mask & (1 << i)) ^ + (b->rt_metrics_mask & (1 << i))) + diff |= ROUTE_DIFF(METRICS, 1); + else + diff |= ROUTE_DIFF(METRICS, + a->rt_metrics[i] != b->rt_metrics[i]); + } + diff |= ROUTE_DIFF(FLAGS, a->rt_flags != b->rt_flags); - -#undef ROUTE_DIFF + } +out: return diff; + +nh_mismatch: + diff |= ROUTE_DIFF(MULTIPATH, 1); + goto out; + +#undef ROUTE_DIFF } static struct trans_tbl route_attrs[] = { @@ -521,7 +474,6 @@ static struct trans_tbl route_attrs[] = { __ADD(ROUTE_ATTR_MULTIPATH, multipath) __ADD(ROUTE_ATTR_REALMS, realms) __ADD(ROUTE_ATTR_CACHEINFO, cacheinfo) - __ADD(ROUTE_ATTR_MP_ALGO, mp_algo) }; static char *route_attrs2str(int attrs, char *buf, size_t len) @@ -557,100 +509,82 @@ void rtnl_route_put(struct rtnl_route *route) * @{ */ -void rtnl_route_set_table(struct rtnl_route *route, int table) +void rtnl_route_set_table(struct rtnl_route *route, uint32_t table) { route->rt_table = table; route->ce_mask |= ROUTE_ATTR_TABLE; } -int rtnl_route_get_table(struct rtnl_route *route) +uint32_t rtnl_route_get_table(struct rtnl_route *route) { - if (route->ce_mask & ROUTE_ATTR_TABLE) - return route->rt_table; - else - return RT_TABLE_MAIN; + return route->rt_table; } -void rtnl_route_set_scope(struct rtnl_route *route, int scope) +void rtnl_route_set_scope(struct rtnl_route *route, uint8_t scope) { route->rt_scope = scope; route->ce_mask |= ROUTE_ATTR_SCOPE; } -int rtnl_route_get_scope(struct rtnl_route *route) +uint8_t rtnl_route_get_scope(struct rtnl_route *route) { - if (route->ce_mask & ROUTE_ATTR_SCOPE) - return route->rt_scope; - else - return RT_SCOPE_NOWHERE; + return route->rt_scope; } -void rtnl_route_set_tos(struct rtnl_route *route, int tos) +void rtnl_route_set_tos(struct rtnl_route *route, uint8_t tos) { route->rt_tos = tos; route->ce_mask |= ROUTE_ATTR_TOS; } -int rtnl_route_get_tos(struct rtnl_route *route) +uint8_t rtnl_route_get_tos(struct rtnl_route *route) { return route->rt_tos; } -void rtnl_route_set_realms(struct rtnl_route *route, realm_t realms) -{ - route->rt_realms = realms; - route->ce_mask |= ROUTE_ATTR_REALMS; -} - -realm_t rtnl_route_get_realms(struct rtnl_route *route) +void rtnl_route_set_protocol(struct rtnl_route *route, uint8_t protocol) { - return route->rt_realms; -} - -void rtnl_route_set_protocol(struct rtnl_route *route, int proto) -{ - route->rt_protocol = proto; + route->rt_protocol = protocol; route->ce_mask |= ROUTE_ATTR_PROTOCOL; } -int rtnl_route_get_protocol(struct rtnl_route *route) +uint8_t rtnl_route_get_protocol(struct rtnl_route *route) { - if (route->ce_mask & ROUTE_ATTR_PROTOCOL) - return route->rt_protocol; - else - return RTPROT_STATIC; + return route->rt_protocol; } -void rtnl_route_set_prio(struct rtnl_route *route, int prio) +void rtnl_route_set_priority(struct rtnl_route *route, uint32_t prio) { route->rt_prio = prio; route->ce_mask |= ROUTE_ATTR_PRIO; } -int rtnl_route_get_prio(struct rtnl_route *route) +uint32_t rtnl_route_get_priority(struct rtnl_route *route) { return route->rt_prio; } -void rtnl_route_set_family(struct rtnl_route *route, int family) +int rtnl_route_set_family(struct rtnl_route *route, uint8_t family) { + if (family != AF_INET && family != AF_INET6 && family != AF_DECnet) + return -NLE_AF_NOSUPPORT; + route->rt_family = family; route->ce_mask |= ROUTE_ATTR_FAMILY; + + return 0; } -int rtnl_route_get_family(struct rtnl_route *route) +uint8_t rtnl_route_get_family(struct rtnl_route *route) { - if (route->ce_mask & ROUTE_ATTR_FAMILY) - return route->rt_family; - else - return AF_UNSPEC; + return route->rt_family; } int rtnl_route_set_dst(struct rtnl_route *route, struct nl_addr *addr) { if (route->ce_mask & ROUTE_ATTR_FAMILY) { if (addr->a_family != route->rt_family) - return nl_error(EINVAL, "Address family mismatch"); + return -NLE_AF_MISMATCH; } else route->rt_family = addr->a_family; @@ -670,19 +604,14 @@ struct nl_addr *rtnl_route_get_dst(struct rtnl_route *route) return route->rt_dst; } -int rtnl_route_get_dst_len(struct rtnl_route *route) -{ - if (route->ce_mask & ROUTE_ATTR_DST) - return nl_addr_get_prefixlen(route->rt_dst); - else - return 0; -} - int rtnl_route_set_src(struct rtnl_route *route, struct nl_addr *addr) { + if (addr->a_family == AF_INET) + return -NLE_SRCRT_NOSUPPORT; + if (route->ce_mask & ROUTE_ATTR_FAMILY) { if (addr->a_family != route->rt_family) - return nl_error(EINVAL, "Address family mismatch"); + return -NLE_AF_MISMATCH; } else route->rt_family = addr->a_family; @@ -701,66 +630,37 @@ struct nl_addr *rtnl_route_get_src(struct rtnl_route *route) return route->rt_src; } -int rtnl_route_get_src_len(struct rtnl_route *route) +int rtnl_route_set_type(struct rtnl_route *route, uint8_t type) { - if (route->ce_mask & ROUTE_ATTR_SRC) - return nl_addr_get_prefixlen(route->rt_src); - else - return 0; -} - -int rtnl_route_set_gateway(struct rtnl_route *route, struct nl_addr *addr) -{ - if (route->ce_mask & ROUTE_ATTR_FAMILY) { - if (addr->a_family != route->rt_family) - return nl_error(EINVAL, "Address family mismatch"); - } else - route->rt_family = addr->a_family; + if (type > RTN_MAX) + return -NLE_RANGE; - if (route->rt_gateway) - nl_addr_put(route->rt_gateway); - - nl_addr_get(addr); - route->rt_gateway = addr; - route->ce_mask |= (ROUTE_ATTR_GATEWAY | ROUTE_ATTR_FAMILY); - - return 0; -} - -struct nl_addr *rtnl_route_get_gateway(struct rtnl_route *route) -{ - return route->rt_gateway; -} - -void rtnl_route_set_type(struct rtnl_route *route, int type) -{ route->rt_type = type; route->ce_mask |= ROUTE_ATTR_TYPE; + + return 0; } -int rtnl_route_get_type(struct rtnl_route *route) +uint8_t rtnl_route_get_type(struct rtnl_route *route) { - if (route->ce_mask & ROUTE_ATTR_TYPE) - return route->rt_type; - else - return RTN_UNICAST; + return route->rt_type; } -void rtnl_route_set_flags(struct rtnl_route *route, unsigned int flags) +void rtnl_route_set_flags(struct rtnl_route *route, uint32_t flags) { route->rt_flag_mask |= flags; route->rt_flags |= flags; route->ce_mask |= ROUTE_ATTR_FLAGS; } -void rtnl_route_unset_flags(struct rtnl_route *route, unsigned int flags) +void rtnl_route_unset_flags(struct rtnl_route *route, uint32_t flags) { route->rt_flag_mask |= flags; route->rt_flags &= ~flags; route->ce_mask |= ROUTE_ATTR_FLAGS; } -unsigned int rtnl_route_get_flags(struct rtnl_route *route) +uint32_t rtnl_route_get_flags(struct rtnl_route *route) { return route->rt_flags; } @@ -768,11 +668,16 @@ unsigned int rtnl_route_get_flags(struct rtnl_route *route) int rtnl_route_set_metric(struct rtnl_route *route, int metric, uint32_t value) { if (metric > RTAX_MAX || metric < 1) - return nl_error(EINVAL, "Metric out of range (1..%d)", - RTAX_MAX); + return -NLE_RANGE; route->rt_metrics[metric - 1] = value; - route->rt_metrics_mask |= (1 << (metric - 1)); + + if (!(route->rt_metrics_mask & (1 << (metric - 1)))) { + route->rt_nmetrics++; + route->rt_metrics_mask |= (1 << (metric - 1)); + } + + route->ce_mask |= ROUTE_ATTR_METRICS; return 0; } @@ -780,30 +685,35 @@ int rtnl_route_set_metric(struct rtnl_route *route, int metric, uint32_t value) int rtnl_route_unset_metric(struct rtnl_route *route, int metric) { if (metric > RTAX_MAX || metric < 1) - return nl_error(EINVAL, "Metric out of range (1..%d)", - RTAX_MAX); + return -NLE_RANGE; - route->rt_metrics_mask &= ~(1 << (metric - 1)); + if (route->rt_metrics_mask & (1 << (metric - 1))) { + route->rt_nmetrics--; + route->rt_metrics_mask &= ~(1 << (metric - 1)); + } return 0; } -unsigned int rtnl_route_get_metric(struct rtnl_route *route, int metric) +int rtnl_route_get_metric(struct rtnl_route *route, int metric, uint32_t *value) { if (metric > RTAX_MAX || metric < 1) - return UINT_MAX; + return -NLE_RANGE; if (!(route->rt_metrics_mask & (1 << (metric - 1)))) - return UINT_MAX; + return -NLE_OBJ_NOTFOUND; - return route->rt_metrics[metric - 1]; + if (value) + *value = route->rt_metrics[metric - 1]; + + return 0; } int rtnl_route_set_pref_src(struct rtnl_route *route, struct nl_addr *addr) { if (route->ce_mask & ROUTE_ATTR_FAMILY) { if (addr->a_family != route->rt_family) - return nl_error(EINVAL, "Address family mismatch"); + return -NLE_AF_MISMATCH; } else route->rt_family = addr->a_family; @@ -822,42 +732,27 @@ struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *route) return route->rt_pref_src; } -void rtnl_route_set_oif(struct rtnl_route *route, int ifindex) +void rtnl_route_set_iif(struct rtnl_route *route, int ifindex) { - route->rt_oif = ifindex; - route->ce_mask |= ROUTE_ATTR_OIF; -} - -int rtnl_route_get_oif(struct rtnl_route *route) -{ - if (route->ce_mask & ROUTE_ATTR_OIF) - return route->rt_oif; - else - return RTNL_LINK_NOT_FOUND; -} - -void rtnl_route_set_iif(struct rtnl_route *route, const char *name) -{ - strncpy(route->rt_iif, name, sizeof(route->rt_iif) - 1); + route->rt_iif = ifindex; route->ce_mask |= ROUTE_ATTR_IIF; } -char *rtnl_route_get_iif(struct rtnl_route *route) +int rtnl_route_get_iif(struct rtnl_route *route) { - if (route->ce_mask & ROUTE_ATTR_IIF) - return route->rt_iif; - else - return NULL; + return route->rt_iif; } void rtnl_route_add_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh) { nl_list_add_tail(&nh->rtnh_list, &route->rt_nexthops); + route->rt_nr_nh++; route->ce_mask |= ROUTE_ATTR_MULTIPATH; } -void rtnl_route_remove_nexthop(struct rtnl_nexthop *nh) +void rtnl_route_remove_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh) { + route->rt_nr_nh--; nl_list_del(&nh->rtnh_list); } @@ -866,44 +761,437 @@ struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *route) return &route->rt_nexthops; } -void rtnl_route_set_cacheinfo(struct rtnl_route *route, - struct rtnl_rtcacheinfo *ci) +int rtnl_route_get_nnexthops(struct rtnl_route *route) { - memcpy(&route->rt_cacheinfo, ci, sizeof(*ci)); - route->ce_mask |= ROUTE_ATTR_CACHEINFO; + return route->rt_nr_nh; } -uint32_t rtnl_route_get_mp_algo(struct rtnl_route *route) +void rtnl_route_foreach_nexthop(struct rtnl_route *r, + void (*cb)(struct rtnl_nexthop *, void *), + void *arg) { - if (route->ce_mask & ROUTE_ATTR_MP_ALGO) - return route->rt_mp_algo; - else - return IP_MP_ALG_NONE; + struct rtnl_nexthop *nh; + + if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + cb(nh, arg); + } + } } -void rtnl_route_set_mp_algo(struct rtnl_route *route, uint32_t algo) +struct rtnl_nexthop *rtnl_route_nexthop_n(struct rtnl_route *r, int n) { - route->rt_mp_algo = algo; - route->ce_mask |= ROUTE_ATTR_MP_ALGO; + struct rtnl_nexthop *nh; + int i; + + if (r->ce_mask & ROUTE_ATTR_MULTIPATH && r->rt_nr_nh > n) { + i = 0; + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + if (i == n) return nh; + i++; + } + } + return NULL; } /** @} */ +/** + * @name Utilities + * @{ + */ + +/** + * Guess scope of a route object. + * @arg route Route object. + * + * Guesses the scope of a route object, based on the following rules: + * @code + * 1) Local route -> local scope + * 2) At least one nexthop not directly connected -> universe scope + * 3) All others -> link scope + * @endcode + * + * @return Scope value. + */ +int rtnl_route_guess_scope(struct rtnl_route *route) +{ + if (route->rt_type == RTN_LOCAL) + return RT_SCOPE_HOST; + + if (!nl_list_empty(&route->rt_nexthops)) { + struct rtnl_nexthop *nh; + + /* + * Use scope uiniverse if there is at least one nexthop which + * is not directly connected + */ + nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) { + if (nh->rtnh_gateway) + return RT_SCOPE_UNIVERSE; + } + } + + return RT_SCOPE_LINK; +} + +/** @} */ + +static struct nla_policy route_policy[RTA_MAX+1] = { + [RTA_IIF] = { .type = NLA_U32 }, + [RTA_OIF] = { .type = NLA_U32 }, + [RTA_PRIORITY] = { .type = NLA_U32 }, + [RTA_FLOW] = { .type = NLA_U32 }, + [RTA_CACHEINFO] = { .minlen = sizeof(struct rta_cacheinfo) }, + [RTA_METRICS] = { .type = NLA_NESTED }, + [RTA_MULTIPATH] = { .type = NLA_NESTED }, +}; + +static int parse_multipath(struct rtnl_route *route, struct nlattr *attr) +{ + struct rtnl_nexthop *nh = NULL; + struct rtnexthop *rtnh = nla_data(attr); + size_t tlen = nla_len(attr); + int err; + + while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) { + nh = rtnl_route_nh_alloc(); + if (!nh) + return -NLE_NOMEM; + + rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops); + rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex); + rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags); + + if (rtnh->rtnh_len > sizeof(*rtnh)) { + struct nlattr *ntb[RTA_MAX + 1]; + + err = nla_parse(ntb, RTA_MAX, (struct nlattr *) + RTNH_DATA(rtnh), + rtnh->rtnh_len - sizeof(*rtnh), + route_policy); + if (err < 0) + goto errout; + + if (ntb[RTA_GATEWAY]) { + struct nl_addr *addr; + + addr = nl_addr_alloc_attr(ntb[RTA_GATEWAY], + route->rt_family); + if (!addr) { + err = -NLE_NOMEM; + goto errout; + } + + rtnl_route_nh_set_gateway(nh, addr); + nl_addr_put(addr); + } + + if (ntb[RTA_FLOW]) { + uint32_t realms; + + realms = nla_get_u32(ntb[RTA_FLOW]); + rtnl_route_nh_set_realms(nh, realms); + } + } + + rtnl_route_add_nexthop(route, nh); + tlen -= RTNH_ALIGN(rtnh->rtnh_len); + rtnh = RTNH_NEXT(rtnh); + } + + err = 0; +errout: + if (err && nh) + rtnl_route_nh_free(nh); + + return err; +} + +int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result) +{ + struct rtmsg *rtm; + struct rtnl_route *route; + struct nlattr *tb[RTA_MAX + 1]; + struct nl_addr *src = NULL, *dst = NULL, *addr; + struct rtnl_nexthop *old_nh = NULL; + int err, family; + + route = rtnl_route_alloc(); + if (!route) { + err = -NLE_NOMEM; + goto errout; + } + + route->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, route_policy); + if (err < 0) + goto errout; + + rtm = nlmsg_data(nlh); + route->rt_family = family = rtm->rtm_family; + route->rt_tos = rtm->rtm_tos; + route->rt_table = rtm->rtm_table; + route->rt_type = rtm->rtm_type; + route->rt_scope = rtm->rtm_scope; + route->rt_protocol = rtm->rtm_protocol; + route->rt_flags = rtm->rtm_flags; + + route->ce_mask |= ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | + ROUTE_ATTR_TABLE | ROUTE_ATTR_TYPE | + ROUTE_ATTR_SCOPE | ROUTE_ATTR_PROTOCOL | + ROUTE_ATTR_FLAGS; + + if (tb[RTA_DST]) { + if (!(dst = nl_addr_alloc_attr(tb[RTA_DST], family))) + goto errout_nomem; + } else { + if (!(dst = nl_addr_alloc(0))) + goto errout_nomem; + nl_addr_set_family(dst, rtm->rtm_family); + } + + nl_addr_set_prefixlen(dst, rtm->rtm_dst_len); + err = rtnl_route_set_dst(route, dst); + if (err < 0) + goto errout; + + nl_addr_put(dst); + + if (tb[RTA_SRC]) { + if (!(src = nl_addr_alloc_attr(tb[RTA_SRC], family))) + goto errout_nomem; + } else if (rtm->rtm_src_len) + if (!(src = nl_addr_alloc(0))) + goto errout_nomem; + + if (src) { + nl_addr_set_prefixlen(src, rtm->rtm_src_len); + rtnl_route_set_src(route, src); + nl_addr_put(src); + } + + if (tb[RTA_IIF]) + rtnl_route_set_iif(route, nla_get_u32(tb[RTA_IIF])); + + if (tb[RTA_PRIORITY]) + rtnl_route_set_priority(route, nla_get_u32(tb[RTA_PRIORITY])); + + if (tb[RTA_PREFSRC]) { + if (!(addr = nl_addr_alloc_attr(tb[RTA_PREFSRC], family))) + goto errout_nomem; + rtnl_route_set_pref_src(route, addr); + nl_addr_put(addr); + } + + if (tb[RTA_METRICS]) { + struct nlattr *mtb[RTAX_MAX + 1]; + int i; + + err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL); + if (err < 0) + goto errout; + + for (i = 1; i <= RTAX_MAX; i++) { + if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) { + uint32_t m = nla_get_u32(mtb[i]); + if (rtnl_route_set_metric(route, i, m) < 0) + goto errout; + } + } + } + + if (tb[RTA_MULTIPATH]) + if ((err = parse_multipath(route, tb[RTA_MULTIPATH])) < 0) + goto errout; + + if (tb[RTA_CACHEINFO]) { + nla_memcpy(&route->rt_cacheinfo, tb[RTA_CACHEINFO], + sizeof(route->rt_cacheinfo)); + route->ce_mask |= ROUTE_ATTR_CACHEINFO; + } + + if (tb[RTA_OIF]) { + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + goto errout; + + rtnl_route_nh_set_ifindex(old_nh, nla_get_u32(tb[RTA_OIF])); + } + + if (tb[RTA_GATEWAY]) { + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + goto errout; + + if (!(addr = nl_addr_alloc_attr(tb[RTA_GATEWAY], family))) + goto errout_nomem; + + rtnl_route_nh_set_gateway(old_nh, addr); + nl_addr_put(addr); + } + + if (tb[RTA_FLOW]) { + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + goto errout; + + rtnl_route_nh_set_realms(old_nh, nla_get_u32(tb[RTA_FLOW])); + } + + if (old_nh) { + if (route->rt_nr_nh == 0) { + /* If no nexthops have been provided via RTA_MULTIPATH + * we add it as regular nexthop to maintain backwards + * compatibility */ + rtnl_route_add_nexthop(route, old_nh); + } else { + /* Kernel supports new style nexthop configuration, + * verify that it is a duplicate and discard nexthop. */ + struct rtnl_nexthop *first; + + first = nl_list_first_entry(&route->rt_nexthops, + struct rtnl_nexthop, + rtnh_list); + if (!first) + BUG(); + + if (rtnl_route_nh_compare(old_nh, first, + old_nh->ce_mask, 0)) { + err = -NLE_INVAL; + goto errout; + } + + rtnl_route_nh_free(old_nh); + } + } + + *result = route; + return 0; + +errout: + rtnl_route_put(route); + return err; + +errout_nomem: + err = -NLE_NOMEM; + goto errout; +} + +int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route) +{ + int i; + struct nlattr *metrics; + struct rtmsg rtmsg = { + .rtm_family = route->rt_family, + .rtm_tos = route->rt_tos, + .rtm_table = route->rt_table, + .rtm_protocol = route->rt_protocol, + .rtm_scope = route->rt_scope, + .rtm_type = route->rt_type, + .rtm_flags = route->rt_flags, + }; + + if (route->rt_dst == NULL) + return -NLE_MISSING_ATTR; + + rtmsg.rtm_dst_len = nl_addr_get_prefixlen(route->rt_dst); + if (route->rt_src) + rtmsg.rtm_src_len = nl_addr_get_prefixlen(route->rt_src); + + + if (rtmsg.rtm_scope == RT_SCOPE_NOWHERE) + rtmsg.rtm_scope = rtnl_route_guess_scope(route); + + if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + /* Additional table attribute replacing the 8bit in the header, was + * required to allow more than 256 tables. */ + NLA_PUT_U32(msg, RTA_TABLE, route->rt_table); + + if (nl_addr_get_len(route->rt_dst)) + NLA_PUT_ADDR(msg, RTA_DST, route->rt_dst); + NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio); + + if (route->ce_mask & ROUTE_ATTR_SRC) + NLA_PUT_ADDR(msg, RTA_SRC, route->rt_src); + + if (route->ce_mask & ROUTE_ATTR_PREF_SRC) + NLA_PUT_ADDR(msg, RTA_PREFSRC, route->rt_pref_src); + + if (route->ce_mask & ROUTE_ATTR_IIF) + NLA_PUT_U32(msg, RTA_IIF, route->rt_iif); + + if (route->rt_nmetrics > 0) { + uint32_t val; + + metrics = nla_nest_start(msg, RTA_METRICS); + if (metrics == NULL) + goto nla_put_failure; + + for (i = 1; i <= RTAX_MAX; i++) { + if (!rtnl_route_get_metric(route, i, &val)) + NLA_PUT_U32(msg, i, val); + } + + nla_nest_end(msg, metrics); + } + + if (rtnl_route_get_nnexthops(route) > 0) { + struct nlattr *multipath; + struct rtnl_nexthop *nh; + + if (!(multipath = nla_nest_start(msg, RTA_MULTIPATH))) + goto nla_put_failure; + + nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) { + struct rtnexthop *rtnh; + + rtnh = nlmsg_reserve(msg, sizeof(*rtnh), NLMSG_ALIGNTO); + if (!rtnh) + goto nla_put_failure; + + rtnh->rtnh_flags = nh->rtnh_flags; + rtnh->rtnh_hops = nh->rtnh_weight; + rtnh->rtnh_ifindex = nh->rtnh_ifindex; + + if (nh->rtnh_gateway) + NLA_PUT_ADDR(msg, RTA_GATEWAY, + nh->rtnh_gateway); + + if (nh->rtnh_realms) + NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms); + + rtnh->rtnh_len = nlmsg_tail(msg->nm_nlh) - + (void *) rtnh; + } + + nla_nest_end(msg, multipath); + } + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +/** @cond SKIP */ struct nl_object_ops route_obj_ops = { .oo_name = "route/route", .oo_size = sizeof(struct rtnl_route), .oo_constructor = route_constructor, .oo_free_data = route_free_data, .oo_clone = route_clone, - .oo_dump[NL_DUMP_BRIEF] = route_dump_brief, - .oo_dump[NL_DUMP_FULL] = route_dump_full, - .oo_dump[NL_DUMP_STATS] = route_dump_stats, - .oo_dump[NL_DUMP_XML] = route_dump_xml, - .oo_dump[NL_DUMP_ENV] = route_dump_env, + .oo_dump = { + [NL_DUMP_LINE] = route_dump_line, + [NL_DUMP_DETAILS] = route_dump_details, + [NL_DUMP_STATS] = route_dump_stats, + [NL_DUMP_ENV] = route_dump_env, + }, .oo_compare = route_compare, .oo_attrs2str = route_attrs2str, .oo_id_attrs = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | ROUTE_ATTR_TABLE | ROUTE_ATTR_DST), }; +/** @endcond */ /** @} */ diff --git a/lib/route/route_utils.c b/lib/route/route_utils.c index a12d169..41ae65c 100644 --- a/lib/route/route_utils.c +++ b/lib/route/route_utils.c @@ -63,6 +63,11 @@ static void __init init_routing_table_names(void) add_routing_table_name(RT_TABLE_LOCAL, "local"); }; +static void __exit release_routing_table_names(void) +{ + __trans_list_clear(&table_names); +} + int rtnl_route_read_table_names(const char *path) { __trans_list_clear(&table_names); @@ -104,6 +109,11 @@ static void __init init_proto_names(void) add_proto_name(RTPROT_STATIC, "static"); }; +static void __exit release_proto_names(void) +{ + __trans_list_clear(&proto_names); +} + int rtnl_route_read_protocol_names(const char *path) { __trans_list_clear(&proto_names); @@ -157,27 +167,4 @@ int rtnl_route_str2metric(const char *name) /** @} */ -/** - * @name Nexthop Flags Translations - * @{ - */ - -static struct trans_tbl nh_flags[] = { - __ADD(RTNH_F_DEAD, dead) - __ADD(RTNH_F_PERVASIVE, pervasive) - __ADD(RTNH_F_ONLINK, onlink) -}; - -char * rtnl_route_nh_flags2str(int flags, char *buf, size_t len) -{ - return __flags2str(flags, buf, len, nh_flags, ARRAY_SIZE(nh_flags)); -} - -int rtnl_route_nh_str2flags(const char *name) -{ - return __str2flags(name, nh_flags, ARRAY_SIZE(nh_flags)); -} - -/** @} */ - /** @} */ diff --git a/lib/route/rtnl.c b/lib/route/rtnl.c index 81ddf94..2533674 100644 --- a/lib/route/rtnl.c +++ b/lib/route/rtnl.c @@ -6,12 +6,11 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** - * @ingroup nlfam - * @defgroup rtnl Routing Netlink + * @defgroup rtnl Routing Family * @{ */ @@ -27,7 +26,7 @@ /** * Send routing netlink request message - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg type Netlink message type. * @arg family Address family. * @arg flags Additional netlink message flags. @@ -37,13 +36,13 @@ * * @return 0 on success or a negative error code. */ -int nl_rtgen_request(struct nl_handle *handle, int type, int family, int flags) +int nl_rtgen_request(struct nl_sock *sk, int type, int family, int flags) { struct rtgenmsg gmsg = { .rtgen_family = family, }; - return nl_send_simple(handle, type, flags, &gmsg, sizeof(gmsg)); + return nl_send_simple(sk, type, flags, &gmsg, sizeof(gmsg)); } /** @} */ diff --git a/lib/route/rule.c b/lib/route/rule.c index 60defd7..126e96d 100644 --- a/lib/route/rule.c +++ b/lib/route/rule.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -60,15 +60,13 @@ static int rule_clone(struct nl_object *_dst, struct nl_object *_src) if (src->r_src) if (!(dst->r_src = nl_addr_clone(src->r_src))) - goto errout; + return -NLE_NOMEM; if (src->r_dst) if (!(dst->r_dst = nl_addr_clone(src->r_dst))) - goto errout; + return -NLE_NOMEM; return 0; -errout: - return nl_get_errno(); } static struct nla_policy rule_policy[RTA_MAX+1] = { @@ -85,11 +83,11 @@ static int rule_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct rtnl_rule *rule; struct rtmsg *r; struct nlattr *tb[RTA_MAX+1]; - int err = 1; + int err = 1, family; rule = rtnl_rule_alloc(); if (!rule) { - err = nl_errno(ENOMEM); + err = -NLE_NOMEM; goto errout; } @@ -100,14 +98,15 @@ static int rule_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, if (err < 0) goto errout; - rule->r_family = r->rtm_family; + rule->r_family = family = r->rtm_family; rule->r_type = r->rtm_type; rule->r_dsfield = r->rtm_tos; rule->r_src_len = r->rtm_src_len; rule->r_dst_len = r->rtm_dst_len; rule->r_table = r->rtm_table; rule->ce_mask = (RULE_ATTR_FAMILY | RULE_ATTR_TYPE | RULE_ATTR_DSFIELD | - RULE_ATTR_SRC_LEN | RULE_ATTR_DST_LEN |RULE_ATTR_TYPE); + RULE_ATTR_SRC_LEN | RULE_ATTR_DST_LEN |RULE_ATTR_TYPE | + RULE_ATTR_TABLE); if (tb[RTA_PRIORITY]) { rule->r_prio = nla_get_u32(tb[RTA_PRIORITY]); @@ -115,21 +114,15 @@ static int rule_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, } if (tb[RTA_SRC]) { - rule->r_src = nla_get_addr(tb[RTA_SRC], r->rtm_family); - if (!rule->r_src) { - err = nl_errno(ENOMEM); - goto errout; - } + if (!(rule->r_src = nl_addr_alloc_attr(tb[RTA_SRC], family))) + goto errout_enomem; nl_addr_set_prefixlen(rule->r_src, r->rtm_src_len); rule->ce_mask |= RULE_ATTR_SRC; } if (tb[RTA_DST]) { - rule->r_dst = nla_get_addr(tb[RTA_DST], r->rtm_family); - if (!rule->r_dst) { - err = nl_errno(ENOMEM); - goto errout; - } + if (!(rule->r_dst = nl_addr_alloc_attr(tb[RTA_DST], family))) + goto errout_enomem; nl_addr_set_prefixlen(rule->r_dst, r->rtm_dst_len); rule->ce_mask |= RULE_ATTR_DST; } @@ -150,216 +143,135 @@ static int rule_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, } if (tb[RTA_GATEWAY]) { - rule->r_srcmap = nla_get_addr(tb[RTA_GATEWAY], r->rtm_family); - if (!rule->r_srcmap) { - err = nl_errno(ENOMEM); - goto errout; - } + rule->r_srcmap = nl_addr_alloc_attr(tb[RTA_GATEWAY], family); + if (!rule->r_srcmap) + goto errout_enomem; rule->ce_mask |= RULE_ATTR_SRCMAP; } - err = pp->pp_cb((struct nl_object *) rule, pp); - if (err < 0) - goto errout; - - err = P_ACCEPT; + if (tb[RTA_TABLE]) { + rule->r_table = nla_get_u32(tb[RTA_TABLE]); + rule->ce_mask |= RULE_ATTR_TABLE; + } + err = pp->pp_cb((struct nl_object *) rule, pp); errout: rtnl_rule_put(rule); return err; + +errout_enomem: + err = -NLE_NOMEM; + goto errout; } -static int rule_request_update(struct nl_cache *c, struct nl_handle *h) +static int rule_request_update(struct nl_cache *c, struct nl_sock *h) { return nl_rtgen_request(h, RTM_GETRULE, AF_UNSPEC, NLM_F_DUMP); } -static int rule_dump_brief(struct nl_object *o, struct nl_dump_params *p) +static void rule_dump_line(struct nl_object *o, struct nl_dump_params *p) { struct rtnl_rule *r = (struct rtnl_rule *) o; char buf[128]; - if (r->ce_mask & RULE_ATTR_PRIO) - dp_dump(p, "%d:\t", r->r_prio); - else - dp_dump(p, "0:\t"); + nl_dump_line(p, "%8d ", (r->ce_mask & RULE_ATTR_PRIO) ? r->r_prio : 0); + nl_dump(p, "%s ", nl_af2str(r->r_family, buf, sizeof(buf))); if (r->ce_mask & RULE_ATTR_SRC) - dp_dump(p, "from %s ", + nl_dump(p, "from %s ", nl_addr2str(r->r_src, buf, sizeof(buf))); else if (r->ce_mask & RULE_ATTR_SRC_LEN && r->r_src_len) - dp_dump(p, "from 0/%d ", r->r_src_len); + nl_dump(p, "from 0/%d ", r->r_src_len); if (r->ce_mask & RULE_ATTR_DST) - dp_dump(p, "to %s ", + nl_dump(p, "to %s ", nl_addr2str(r->r_dst, buf, sizeof(buf))); else if (r->ce_mask & RULE_ATTR_DST_LEN && r->r_dst_len) - dp_dump(p, "to 0/%d ", r->r_dst_len); + nl_dump(p, "to 0/%d ", r->r_dst_len); if (r->ce_mask & RULE_ATTR_DSFIELD && r->r_dsfield) - dp_dump(p, "tos %d ", r->r_dsfield); + nl_dump(p, "tos %d ", r->r_dsfield); if (r->ce_mask & RULE_ATTR_MARK) - dp_dump(p, "mark %" PRIx64 , r->r_mark); + nl_dump(p, "mark %" PRIx64 , r->r_mark); if (r->ce_mask & RULE_ATTR_IIF) - dp_dump(p, "iif %s ", r->r_iif); + nl_dump(p, "iif %s ", r->r_iif); if (r->ce_mask & RULE_ATTR_TABLE) - dp_dump(p, "lookup %s ", + nl_dump(p, "lookup %s ", rtnl_route_table2str(r->r_table, buf, sizeof(buf))); if (r->ce_mask & RULE_ATTR_REALMS) - dp_dump(p, "realms %s ", + nl_dump(p, "realms %s ", rtnl_realms2str(r->r_realms, buf, sizeof(buf))); - dp_dump(p, "action %s\n", + nl_dump(p, "action %s\n", nl_rtntype2str(r->r_type, buf, sizeof(buf))); - - return 1; } -static int rule_dump_full(struct nl_object *obj, struct nl_dump_params *p) +static void rule_dump_details(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_rule *rule = (struct rtnl_rule *) obj; char buf[128]; - int line; - line = rule_dump_brief(obj, p); - - dp_dump_line(p, line++, " family %s", - nl_af2str(rule->r_family, buf, sizeof(buf))); + rule_dump_line(obj, p); if (rule->ce_mask & RULE_ATTR_SRCMAP) - dp_dump(p, " srcmap %s", + nl_dump_line(p, " srcmap %s\n", nl_addr2str(rule->r_srcmap, buf, sizeof(buf))); - - dp_dump(p, "\n"); - - return line; } -static int rule_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +static void rule_dump_stats(struct nl_object *obj, struct nl_dump_params *p) { - return rule_dump_full(obj, p); + rule_dump_details(obj, p); } -static int rule_dump_xml(struct nl_object *obj, struct nl_dump_params *p) +static void rule_dump_env(struct nl_object *obj, struct nl_dump_params *p) { struct rtnl_rule *rule = (struct rtnl_rule *) obj; char buf[128]; - int line = 0; - - dp_dump_line(p, line++, "<rule>\n"); - dp_dump_line(p, line++, " <priority>%u</priority>\n", - rule->r_prio); - dp_dump_line(p, line++, " <family>%s</family>\n", + nl_dump_line(p, "RULE_PRIORITY=%u\n", rule->r_prio); + nl_dump_line(p, "RULE_FAMILY=%s\n", nl_af2str(rule->r_family, buf, sizeof(buf))); if (rule->ce_mask & RULE_ATTR_DST) - dp_dump_line(p, line++, " <dst>%s</dst>\n", + nl_dump_line(p, "RULE_DST=%s\n", nl_addr2str(rule->r_dst, buf, sizeof(buf))); if (rule->ce_mask & RULE_ATTR_DST_LEN) - dp_dump_line(p, line++, " <dstlen>%u</dstlen>\n", - rule->r_dst_len); + nl_dump_line(p, "RULE_DSTLEN=%u\n", rule->r_dst_len); if (rule->ce_mask & RULE_ATTR_SRC) - dp_dump_line(p, line++, " <src>%s</src>\n", + nl_dump_line(p, "RULE_SRC=%s\n", nl_addr2str(rule->r_src, buf, sizeof(buf))); if (rule->ce_mask & RULE_ATTR_SRC_LEN) - dp_dump_line(p, line++, " <srclen>%u</srclen>\n", - rule->r_src_len); + nl_dump_line(p, "RULE_SRCLEN=%u\n", rule->r_src_len); if (rule->ce_mask & RULE_ATTR_IIF) - dp_dump_line(p, line++, " <iif>%s</iif>\n", rule->r_iif); + nl_dump_line(p, "RULE_IIF=%s\n", rule->r_iif); if (rule->ce_mask & RULE_ATTR_TABLE) - dp_dump_line(p, line++, " <table>%u</table>\n", - rule->r_table); + nl_dump_line(p, "RULE_TABLE=%u\n", rule->r_table); if (rule->ce_mask & RULE_ATTR_REALMS) - dp_dump_line(p, line++, " <realms>%u</realms>\n", - rule->r_realms); + nl_dump_line(p, "RULE_REALM=%u\n", rule->r_realms); if (rule->ce_mask & RULE_ATTR_MARK) - dp_dump_line(p, line++, " <mark>%" PRIx64 "</mark>\n", - rule->r_mark); + nl_dump_line(p, "RULE_MARK=0x%" PRIx64 "\n", rule->r_mark); if (rule->ce_mask & RULE_ATTR_DSFIELD) - dp_dump_line(p, line++, " <dsfield>%u</dsfield>\n", - rule->r_dsfield); + nl_dump_line(p, "RULE_DSFIELD=%u\n", rule->r_dsfield); if (rule->ce_mask & RULE_ATTR_TYPE) - dp_dump_line(p, line++, "<type>%s</type>\n", + nl_dump_line(p, "RULE_TYPE=%s\n", nl_rtntype2str(rule->r_type, buf, sizeof(buf))); if (rule->ce_mask & RULE_ATTR_SRCMAP) - dp_dump_line(p, line++, "<srcmap>%s</srcmap>\n", + nl_dump_line(p, "RULE_SRCMAP=%s\n", nl_addr2str(rule->r_srcmap, buf, sizeof(buf))); - - dp_dump_line(p, line++, "</rule>\n"); - - return line; -} - -static int rule_dump_env(struct nl_object *obj, struct nl_dump_params *p) -{ - struct rtnl_rule *rule = (struct rtnl_rule *) obj; - char buf[128]; - int line = 0; - - dp_dump_line(p, line++, "RULE_PRIORITY=%u\n", - rule->r_prio); - dp_dump_line(p, line++, "RULE_FAMILY=%s\n", - nl_af2str(rule->r_family, buf, sizeof(buf))); - - if (rule->ce_mask & RULE_ATTR_DST) - dp_dump_line(p, line++, "RULE_DST=%s\n", - nl_addr2str(rule->r_dst, buf, sizeof(buf))); - - if (rule->ce_mask & RULE_ATTR_DST_LEN) - dp_dump_line(p, line++, "RULE_DSTLEN=%u\n", - rule->r_dst_len); - - if (rule->ce_mask & RULE_ATTR_SRC) - dp_dump_line(p, line++, "RULE_SRC=%s\n", - nl_addr2str(rule->r_src, buf, sizeof(buf))); - - if (rule->ce_mask & RULE_ATTR_SRC_LEN) - dp_dump_line(p, line++, "RULE_SRCLEN=%u\n", - rule->r_src_len); - - if (rule->ce_mask & RULE_ATTR_IIF) - dp_dump_line(p, line++, "RULE_IIF=%s\n", rule->r_iif); - - if (rule->ce_mask & RULE_ATTR_TABLE) - dp_dump_line(p, line++, "RULE_TABLE=%u\n", - rule->r_table); - - if (rule->ce_mask & RULE_ATTR_REALMS) - dp_dump_line(p, line++, "RULE_REALM=%u\n", - rule->r_realms); - - if (rule->ce_mask & RULE_ATTR_MARK) - dp_dump_line(p, line++, "RULE_MARK=0x%" PRIx64 "\n", - rule->r_mark); - - if (rule->ce_mask & RULE_ATTR_DSFIELD) - dp_dump_line(p, line++, "RULE_DSFIELD=%u\n", - rule->r_dsfield); - - if (rule->ce_mask & RULE_ATTR_TYPE) - dp_dump_line(p, line++, "RULE_TYPE=%s\n", - nl_rtntype2str(rule->r_type, buf, sizeof(buf))); - - if (rule->ce_mask & RULE_ATTR_SRCMAP) - dp_dump_line(p, line++, "RULE_SRCMAP=%s\n", - nl_addr2str(rule->r_srcmap, buf, sizeof(buf))); - - return line; } static int rule_compare(struct nl_object *_a, struct nl_object *_b, @@ -434,51 +346,34 @@ void rtnl_rule_put(struct rtnl_rule *rule) */ /** - * Build a rule cache including all rules of the specified family currently configured in the kernel. - * @arg handle netlink handle - * @arg family address family + * Build a rule cache including all rules currently configured in the kernel. + * @arg sk Netlink socket. + * @arg family Address family or AF_UNSPEC. + * @arg result Pointer to store resulting cache. * * Allocates a new rule cache, initializes it properly and updates it - * to include all rules of the specified address family currently - * configured in the kernel. + * to include all rules currently configured in the kernel. * - * @note The caller is responsible for destroying and freeing the - * cache after using it. (nl_cache_destroy_and_free()) - * @return The new cache or NULL if an error occured. + * @return 0 on success or a negative error code. */ -struct nl_cache * rtnl_rule_alloc_cache_by_family(struct nl_handle *handle, - int family) +int rtnl_rule_alloc_cache(struct nl_sock *sock, int family, + struct nl_cache **result) { struct nl_cache * cache; + int err; - cache = nl_cache_alloc(&rtnl_rule_ops); - if (cache == NULL) - return NULL; + if (!(cache = nl_cache_alloc(&rtnl_rule_ops))) + return -NLE_NOMEM; - /* XXX RULE_CACHE_FAMILY(cache) = family; */ + cache->c_iarg1 = family; - if (handle && nl_cache_refill(handle, cache) < 0) { + if (sock && (err = nl_cache_refill(sock, cache)) < 0) { free(cache); - return NULL; + return err; } - return cache; -} - -/** - * Build a rule cache including all rules currently configured in the kernel. - * @arg handle netlink handle - * - * Allocates a new rule cache, initializes it properly and updates it - * to include all rules currently configured in the kernel. - * - * @note The caller is responsible for destroying and freeing the - * cache after using it. (nl_cache_destroy_and_free()) - * @return The new cache or NULL if an error occured. - */ -struct nl_cache * rtnl_rule_alloc_cache(struct nl_handle *handle) -{ - return rtnl_rule_alloc_cache_by_family(handle, AF_UNSPEC); + *result = cache; + return 0; } /** @} */ @@ -488,7 +383,8 @@ struct nl_cache * rtnl_rule_alloc_cache(struct nl_handle *handle) * @{ */ -static struct nl_msg *build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags) +static int build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags, + struct nl_msg **result) { struct nl_msg *msg; struct rtmsg rtm = { @@ -518,7 +414,7 @@ static struct nl_msg *build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags) msg = nlmsg_alloc_simple(cmd, flags); if (!msg) - goto nla_put_failure; + return -NLE_NOMEM; if (nlmsg_append(msg, &rtm, sizeof(rtm), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -541,11 +437,12 @@ static struct nl_msg *build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags) if (tmpl->ce_mask & RULE_ATTR_IIF) NLA_PUT_STRING(msg, RTA_IIF, tmpl->r_iif); - return msg; + *result = msg; + return 0; nla_put_failure: nlmsg_free(msg); - return NULL; + return -NLE_MSGSIZE; } /** @@ -561,14 +458,16 @@ nla_put_failure: * * @return The netlink message */ -struct nl_msg *rtnl_rule_build_add_request(struct rtnl_rule *tmpl, int flags) +int rtnl_rule_build_add_request(struct rtnl_rule *tmpl, int flags, + struct nl_msg **result) { - return build_rule_msg(tmpl, RTM_NEWRULE, NLM_F_CREATE | flags); + return build_rule_msg(tmpl, RTM_NEWRULE, NLM_F_CREATE | flags, + result); } /** * Add a new rule - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg tmpl template with requested changes * @arg flags additional netlink message flags * @@ -578,21 +477,20 @@ struct nl_msg *rtnl_rule_build_add_request(struct rtnl_rule *tmpl, int flags) * * @return 0 on sucess or a negative error if an error occured. */ -int rtnl_rule_add(struct nl_handle *handle, struct rtnl_rule *tmpl, int flags) +int rtnl_rule_add(struct nl_sock *sk, struct rtnl_rule *tmpl, int flags) { - int err; struct nl_msg *msg; + int err; - msg = rtnl_rule_build_add_request(tmpl, flags); - if (!msg) - return nl_errno(ENOMEM); + if ((err = rtnl_rule_build_add_request(tmpl, flags, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); if (err < 0) return err; - nlmsg_free(msg); - return nl_wait_for_ack(handle); + return wait_for_ack(sk); } /** @} */ @@ -615,14 +513,15 @@ int rtnl_rule_add(struct nl_handle *handle, struct rtnl_rule *tmpl, int flags) * * @return The netlink message */ -struct nl_msg *rtnl_rule_build_delete_request(struct rtnl_rule *rule, int flags) +int rtnl_rule_build_delete_request(struct rtnl_rule *rule, int flags, + struct nl_msg **result) { - return build_rule_msg(rule, RTM_DELRULE, flags); + return build_rule_msg(rule, RTM_DELRULE, flags, result); } /** * Delete a rule - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg rule rule to delete * @arg flags additional netlink message flags * @@ -632,22 +531,20 @@ struct nl_msg *rtnl_rule_build_delete_request(struct rtnl_rule *rule, int flags) * * @return 0 on sucess or a negative error if an error occured. */ -int rtnl_rule_delete(struct nl_handle *handle, struct rtnl_rule *rule, - int flags) +int rtnl_rule_delete(struct nl_sock *sk, struct rtnl_rule *rule, int flags) { - int err; struct nl_msg *msg; + int err; - msg = rtnl_rule_build_delete_request(rule, flags); - if (!msg) - return nl_errno(ENOMEM); + if ((err = rtnl_rule_build_delete_request(rule, flags, &msg)) < 0) + return err; - err = nl_send_auto_complete(handle, msg); + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); if (err < 0) return err; - nlmsg_free(msg); - return nl_wait_for_ack(handle); + return wait_for_ack(sk); } /** @} */ @@ -764,7 +661,7 @@ static inline int __assign_addr(struct rtnl_rule *rule, struct nl_addr **pos, { if (rule->ce_mask & RULE_ATTR_FAMILY) { if (new->a_family != rule->r_family) - return nl_error(EINVAL, "Address family mismatch"); + return -NLE_AF_MISMATCH; } else rule->r_family = new->a_family; @@ -811,7 +708,7 @@ struct nl_addr *rtnl_rule_get_dst(struct rtnl_rule *rule) int rtnl_rule_set_iif(struct rtnl_rule *rule, const char *dev) { if (strlen(dev) > IFNAMSIZ-1) - return nl_errno(ERANGE); + return -NLE_RANGE; strcpy(rule->r_iif, dev); rule->ce_mask |= RULE_ATTR_IIF; @@ -837,16 +734,16 @@ int rtnl_rule_get_action(struct rtnl_rule *rule) if (rule->ce_mask & RULE_ATTR_TYPE) return rule->r_type; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } -void rtnl_rule_set_realms(struct rtnl_rule *rule, realm_t realms) +void rtnl_rule_set_realms(struct rtnl_rule *rule, uint32_t realms) { rule->r_realms = realms; rule->ce_mask |= RULE_ATTR_REALMS; } -realm_t rtnl_rule_get_realms(struct rtnl_rule *rule) +uint32_t rtnl_rule_get_realms(struct rtnl_rule *rule) { if (rule->ce_mask & RULE_ATTR_REALMS) return rule->r_realms; @@ -861,11 +758,12 @@ static struct nl_object_ops rule_obj_ops = { .oo_size = sizeof(struct rtnl_rule), .oo_free_data = rule_free_data, .oo_clone = rule_clone, - .oo_dump[NL_DUMP_BRIEF] = rule_dump_brief, - .oo_dump[NL_DUMP_FULL] = rule_dump_full, - .oo_dump[NL_DUMP_STATS] = rule_dump_stats, - .oo_dump[NL_DUMP_XML] = rule_dump_xml, - .oo_dump[NL_DUMP_ENV] = rule_dump_env, + .oo_dump = { + [NL_DUMP_LINE] = rule_dump_line, + [NL_DUMP_DETAILS] = rule_dump_details, + [NL_DUMP_STATS] = rule_dump_stats, + [NL_DUMP_ENV] = rule_dump_env, + }, .oo_compare = rule_compare, .oo_attrs2str = rule_attrs2str, .oo_id_attrs = ~0, diff --git a/lib/route/sch/cbq.c b/lib/route/sch/cbq.c index 9808509..1aaa58d 100644 --- a/lib/route/sch/cbq.c +++ b/lib/route/sch/cbq.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ #include <netlink-local.h> @@ -99,7 +99,7 @@ static int cbq_msg_parser(struct rtnl_tca *tca) cbq = cbq_alloc(tca); if (!cbq) - return nl_errno(ENOMEM); + return -NLE_NOMEM; nla_memcpy(&cbq->cbq_lss, tb[TCA_CBQ_LSSOPT], sizeof(cbq->cbq_lss)); nla_memcpy(&cbq->cbq_rate, tb[TCA_CBQ_RATE], sizeof(cbq->cbq_rate)); @@ -133,7 +133,7 @@ static int cbq_clone(struct rtnl_tca *_dst, struct rtnl_tca *_src) struct rtnl_cbq *src = cbq_qdisc(_src); if (src && !cbq_alloc(_dst)) - return nl_errno(ENOMEM); + return -NLE_NOMEM; else return 0; } @@ -153,8 +153,7 @@ static int cbq_class_clone(struct rtnl_class *dst, struct rtnl_class *src) return cbq_clone((struct rtnl_tca *) dst, (struct rtnl_tca *) src); } -static int cbq_dump_brief(struct rtnl_tca *tca, struct nl_dump_params *p, - int line) +static void cbq_dump_line(struct rtnl_tca *tca, struct nl_dump_params *p) { struct rtnl_cbq *cbq; double r, rbit; @@ -162,32 +161,28 @@ static int cbq_dump_brief(struct rtnl_tca *tca, struct nl_dump_params *p, cbq = cbq_qdisc(tca); if (!cbq) - goto ignore; + return; r = nl_cancel_down_bytes(cbq->cbq_rate.rate, &ru); rbit = nl_cancel_down_bits(cbq->cbq_rate.rate * 8, &rubit); - dp_dump(p, " rate %.2f%s/s (%.0f%s) prio %u", + nl_dump(p, " rate %.2f%s/s (%.0f%s) prio %u", r, ru, rbit, rubit, cbq->cbq_wrr.priority); - -ignore: - return line; } -static int cbq_qdisc_dump_brief(struct rtnl_qdisc *qdisc, - struct nl_dump_params *p, int line) +static void cbq_qdisc_dump_line(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p) { - return cbq_dump_brief((struct rtnl_tca *) qdisc, p, line); + cbq_dump_line((struct rtnl_tca *) qdisc, p); } -static int cbq_class_dump_brief(struct rtnl_class *class, - struct nl_dump_params *p, int line) +static void cbq_class_dump_line(struct rtnl_class *class, + struct nl_dump_params *p) { - return cbq_dump_brief((struct rtnl_tca *) class, p, line); + cbq_dump_line((struct rtnl_tca *) class, p); } -static int cbq_dump_full(struct rtnl_tca *tca, struct nl_dump_params *p, - int line) +static void cbq_dump_details(struct rtnl_tca *tca, struct nl_dump_params *p) { struct rtnl_cbq *cbq; char *unit, buf[32]; @@ -196,18 +191,18 @@ static int cbq_dump_full(struct rtnl_tca *tca, struct nl_dump_params *p, cbq = cbq_qdisc(tca); if (!cbq) - goto ignore; + return; w = nl_cancel_down_bits(cbq->cbq_wrr.weight * 8, &unit); - dp_dump(p, "avgpkt %u mpu %u cell %u allot %u weight %.0f%s\n", + nl_dump(p, "avgpkt %u mpu %u cell %u allot %u weight %.0f%s\n", cbq->cbq_lss.avpkt, cbq->cbq_rate.mpu, 1 << cbq->cbq_rate.cell_log, cbq->cbq_wrr.allot, w, unit); el = cbq->cbq_lss.ewma_log; - dp_dump_line(p, line++, " minidle %uus maxidle %uus offtime " + nl_dump_line(p, " minidle %uus maxidle %uus offtime " "%uus level %u ewma_log %u\n", nl_ticks2us(cbq->cbq_lss.minidle >> el), nl_ticks2us(cbq->cbq_lss.maxidle >> el), @@ -215,60 +210,53 @@ static int cbq_dump_full(struct rtnl_tca *tca, struct nl_dump_params *p, cbq->cbq_lss.level, cbq->cbq_lss.ewma_log); - dp_dump_line(p, line++, " penalty %uus strategy %s ", + nl_dump_line(p, " penalty %uus strategy %s ", nl_ticks2us(cbq->cbq_ovl.penalty), nl_ovl_strategy2str(cbq->cbq_ovl.strategy, buf, sizeof(buf))); - dp_dump(p, "split %s defmap 0x%08x ", + nl_dump(p, "split %s defmap 0x%08x ", rtnl_tc_handle2str(cbq->cbq_fopt.split, buf, sizeof(buf)), cbq->cbq_fopt.defmap); - dp_dump(p, "police %s", + nl_dump(p, "police %s", nl_police2str(cbq->cbq_police.police, buf, sizeof(buf))); - -ignore: - return line; } -static int cbq_qdisc_dump_full(struct rtnl_qdisc *qdisc, - struct nl_dump_params *p, int line) +static void cbq_qdisc_dump_details(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p) { - return cbq_dump_full((struct rtnl_tca *) qdisc, p, line); + cbq_dump_details((struct rtnl_tca *) qdisc, p); } -static int cbq_class_dump_full(struct rtnl_class *class, - struct nl_dump_params *p, int line) +static void cbq_class_dump_details(struct rtnl_class *class, + struct nl_dump_params *p) { - return cbq_dump_full((struct rtnl_tca *) class, p, line); + cbq_dump_details((struct rtnl_tca *) class, p); } -static int cbq_dump_with_stats(struct rtnl_tca *tca, struct nl_dump_params *p, - int line) +static void cbq_dump_stats(struct rtnl_tca *tca, struct nl_dump_params *p) { struct tc_cbq_xstats *x = tca_xstats(tca); if (!x) - goto ignore; + return; - dp_dump_line(p, line++, " borrows overact " - " avgidle undertime\n"); - dp_dump_line(p, line++, " %10u %10u %10u %10u\n", + nl_dump_line(p, " borrows overact " + " avgidle undertime\n"); + nl_dump_line(p, " %10u %10u %10u %10u\n", x->borrows, x->overactions, x->avgidle, x->undertime); - -ignore: - return line; } -static int cbq_qdisc_dump_with_stats(struct rtnl_qdisc *qdisc, - struct nl_dump_params *p, int line) +static void cbq_qdisc_dump_stats(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p) { - return cbq_dump_with_stats((struct rtnl_tca *) qdisc, p, line); + cbq_dump_stats((struct rtnl_tca *) qdisc, p); } -static int cbq_class_dump_with_stats(struct rtnl_class *class, - struct nl_dump_params *p, int line) +static void cbq_class_dump_stats(struct rtnl_class *class, + struct nl_dump_params *p) { - return cbq_dump_with_stats((struct rtnl_tca *) class, p, line); + cbq_dump_stats((struct rtnl_tca *) class, p); } static struct rtnl_qdisc_ops cbq_qdisc_ops = { @@ -276,9 +264,11 @@ static struct rtnl_qdisc_ops cbq_qdisc_ops = { .qo_msg_parser = cbq_qdisc_msg_parser, .qo_free_data = cbq_qdisc_free_data, .qo_clone = cbq_qdisc_clone, - .qo_dump[NL_DUMP_BRIEF] = cbq_qdisc_dump_brief, - .qo_dump[NL_DUMP_FULL] = cbq_qdisc_dump_full, - .qo_dump[NL_DUMP_STATS] = cbq_qdisc_dump_with_stats, + .qo_dump = { + [NL_DUMP_LINE] = cbq_qdisc_dump_line, + [NL_DUMP_DETAILS] = cbq_qdisc_dump_details, + [NL_DUMP_STATS] = cbq_qdisc_dump_stats, + }, }; static struct rtnl_class_ops cbq_class_ops = { @@ -286,9 +276,11 @@ static struct rtnl_class_ops cbq_class_ops = { .co_msg_parser = cbq_class_msg_parser, .co_free_data = cbq_class_free_data, .co_clone = cbq_class_clone, - .co_dump[NL_DUMP_BRIEF] = cbq_class_dump_brief, - .co_dump[NL_DUMP_FULL] = cbq_class_dump_full, - .co_dump[NL_DUMP_STATS] = cbq_class_dump_with_stats, + .co_dump = { + [NL_DUMP_LINE] = cbq_class_dump_line, + [NL_DUMP_DETAILS] = cbq_class_dump_details, + [NL_DUMP_STATS] = cbq_class_dump_stats, + }, }; static void __init cbq_init(void) diff --git a/lib/route/sch/dsmark.c b/lib/route/sch/dsmark.c index 5ba6b92..61b0fea 100644 --- a/lib/route/sch/dsmark.c +++ b/lib/route/sch/dsmark.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -70,7 +70,7 @@ static int dsmark_qdisc_msg_parser(struct rtnl_qdisc *qdisc) dsmark = dsmark_qdisc_alloc(qdisc); if (!dsmark) - return nl_errno(ENOMEM); + return -NLE_NOMEM; if (tb[TCA_DSMARK_INDICES]) { dsmark->qdm_indices = nla_get_u16(tb[TCA_DSMARK_INDICES]); @@ -118,7 +118,7 @@ static int dsmark_class_msg_parser(struct rtnl_class *class) dsmark = dsmark_class_alloc(class); if (!dsmark) - return nl_errno(ENOMEM); + return -NLE_NOMEM; if (tb[TCA_DSMARK_MASK]) { dsmark->cdm_bmask = nla_get_u8(tb[TCA_DSMARK_MASK]); @@ -133,51 +133,43 @@ static int dsmark_class_msg_parser(struct rtnl_class *class) return 0; } -static int dsmark_qdisc_dump_brief(struct rtnl_qdisc *qdisc, - struct nl_dump_params *p, int line) +static void dsmark_qdisc_dump_line(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p) { struct rtnl_dsmark_qdisc *dsmark = dsmark_qdisc(qdisc); if (dsmark && (dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES)) - dp_dump(p, " indices 0x%04x", dsmark->qdm_indices); - - return line; + nl_dump(p, " indices 0x%04x", dsmark->qdm_indices); } -static int dsmark_qdisc_dump_full(struct rtnl_qdisc *qdisc, - struct nl_dump_params *p, int line) +static void dsmark_qdisc_dump_details(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p) { struct rtnl_dsmark_qdisc *dsmark = dsmark_qdisc(qdisc); if (!dsmark) - goto ignore; + return; if (dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX) - dp_dump(p, " default index 0x%04x", dsmark->qdm_default_index); + nl_dump(p, " default index 0x%04x", dsmark->qdm_default_index); if (dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX) - dp_dump(p, " set-tc-index"); - -ignore: - return line; + nl_dump(p, " set-tc-index"); } -static int dsmark_class_dump_brief(struct rtnl_class *class, - struct nl_dump_params *p, int line) +static void dsmark_class_dump_line(struct rtnl_class *class, + struct nl_dump_params *p) { struct rtnl_dsmark_class *dsmark = dsmark_class(class); if (!dsmark) - goto ignore; + return; if (dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE) - dp_dump(p, " value 0x%02x", dsmark->cdm_value); + nl_dump(p, " value 0x%02x", dsmark->cdm_value); if (dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK) - dp_dump(p, " mask 0x%02x", dsmark->cdm_bmask); - -ignore: - return line; + nl_dump(p, " mask 0x%02x", dsmark->cdm_bmask); } static struct nl_msg *dsmark_qdisc_get_opts(struct rtnl_qdisc *qdisc) @@ -251,7 +243,7 @@ int rtnl_class_dsmark_set_bitmask(struct rtnl_class *class, uint8_t mask) dsmark = dsmark_class(class); if (!dsmark) - return nl_errno(ENOMEM); + return -NLE_NOMEM; dsmark->cdm_bmask = mask; dsmark->cdm_mask |= SCH_DSMARK_ATTR_MASK; @@ -272,7 +264,7 @@ int rtnl_class_dsmark_get_bitmask(struct rtnl_class *class) if (dsmark && dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK) return dsmark->cdm_bmask; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @@ -287,7 +279,7 @@ int rtnl_class_dsmark_set_value(struct rtnl_class *class, uint8_t value) dsmark = dsmark_class(class); if (!dsmark) - return nl_errno(ENOMEM); + return -NLE_NOMEM; dsmark->cdm_value = value; dsmark->cdm_mask |= SCH_DSMARK_ATTR_VALUE; @@ -308,7 +300,7 @@ int rtnl_class_dsmark_get_value(struct rtnl_class *class) if (dsmark && dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE) return dsmark->cdm_value; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @} */ @@ -329,7 +321,7 @@ int rtnl_qdisc_dsmark_set_indices(struct rtnl_qdisc *qdisc, uint16_t indices) dsmark = dsmark_qdisc(qdisc); if (!dsmark) - return nl_errno(ENOMEM); + return -NLE_NOMEM; dsmark->qdm_indices = indices; dsmark->qdm_mask |= SCH_DSMARK_ATTR_INDICES; @@ -350,7 +342,7 @@ int rtnl_qdisc_dsmark_get_indices(struct rtnl_qdisc *qdisc) if (dsmark && dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES) return dsmark->qdm_indices; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @@ -366,7 +358,7 @@ int rtnl_qdisc_dsmark_set_default_index(struct rtnl_qdisc *qdisc, dsmark = dsmark_qdisc(qdisc); if (!dsmark) - return nl_errno(ENOMEM); + return -NLE_NOMEM; dsmark->qdm_default_index = default_index; dsmark->qdm_mask |= SCH_DSMARK_ATTR_DEFAULT_INDEX; @@ -387,7 +379,7 @@ int rtnl_qdisc_dsmark_get_default_index(struct rtnl_qdisc *qdisc) if (dsmark && dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX) return dsmark->qdm_default_index; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @@ -402,7 +394,7 @@ int rtnl_qdisc_dsmark_set_set_tc_index(struct rtnl_qdisc *qdisc, int flag) dsmark = dsmark_qdisc(qdisc); if (!dsmark) - return nl_errno(ENOMEM); + return -NLE_NOMEM; dsmark->qdm_set_tc_index = !!flag; dsmark->qdm_mask |= SCH_DSMARK_ATTR_SET_TC_INDEX; @@ -424,7 +416,7 @@ int rtnl_qdisc_dsmark_get_set_tc_index(struct rtnl_qdisc *qdisc) if (dsmark && dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX) return dsmark->qdm_set_tc_index; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @} */ @@ -432,15 +424,17 @@ int rtnl_qdisc_dsmark_get_set_tc_index(struct rtnl_qdisc *qdisc) static struct rtnl_qdisc_ops dsmark_qdisc_ops = { .qo_kind = "dsmark", .qo_msg_parser = dsmark_qdisc_msg_parser, - .qo_dump[NL_DUMP_BRIEF] = dsmark_qdisc_dump_brief, - .qo_dump[NL_DUMP_FULL] = dsmark_qdisc_dump_full, + .qo_dump = { + [NL_DUMP_LINE] = dsmark_qdisc_dump_line, + [NL_DUMP_DETAILS] = dsmark_qdisc_dump_details, + }, .qo_get_opts = dsmark_qdisc_get_opts, }; static struct rtnl_class_ops dsmark_class_ops = { .co_kind = "dsmark", .co_msg_parser = dsmark_class_msg_parser, - .co_dump[NL_DUMP_BRIEF] = dsmark_class_dump_brief, + .co_dump[NL_DUMP_LINE] = dsmark_class_dump_line, .co_get_opts = dsmark_class_get_opts, }; diff --git a/lib/route/sch/fifo.c b/lib/route/sch/fifo.c index 4f8d202..464af30 100644 --- a/lib/route/sch/fifo.c +++ b/lib/route/sch/fifo.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -60,11 +60,11 @@ static int fifo_msg_parser(struct rtnl_qdisc *qdisc) struct tc_fifo_qopt *opt; if (qdisc->q_opts->d_size < sizeof(struct tc_fifo_qopt)) - return nl_error(EINVAL, "FIFO options size mismatch"); + return -NLE_INVAL; fifo = fifo_alloc(qdisc); if (!fifo) - return nl_errno(ENOMEM); + return -NLE_NOMEM; opt = (struct tc_fifo_qopt *) qdisc->q_opts->d_data; fifo->qf_limit = opt->limit; @@ -78,19 +78,15 @@ static void fifo_free_data(struct rtnl_qdisc *qdisc) free(qdisc->q_subdata); } -static int pfifo_dump_brief(struct rtnl_qdisc *qdisc, - struct nl_dump_params *p, int line) +static void pfifo_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) { struct rtnl_fifo *fifo = fifo_qdisc(qdisc); if (fifo) - dp_dump(p, " limit %u packets", fifo->qf_limit); - - return line; + nl_dump(p, " limit %u packets", fifo->qf_limit); } -static int bfifo_dump_brief(struct rtnl_qdisc *qdisc, - struct nl_dump_params *p, int line) +static void bfifo_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) { struct rtnl_fifo *fifo = fifo_qdisc(qdisc); @@ -99,10 +95,8 @@ static int bfifo_dump_brief(struct rtnl_qdisc *qdisc, double r; r = nl_cancel_down_bytes(fifo->qf_limit, &unit); - dp_dump(p, " limit %.1f%s", r, unit); + nl_dump(p, " limit %.1f%s", r, unit); } - - return line; } static struct nl_msg *fifo_get_opts(struct rtnl_qdisc *qdisc) @@ -148,7 +142,7 @@ int rtnl_qdisc_fifo_set_limit(struct rtnl_qdisc *qdisc, int limit) fifo = fifo_alloc(qdisc); if (!fifo) - return nl_errno(ENOMEM); + return -NLE_NOMEM; fifo->qf_limit = limit; fifo->qf_mask |= SCH_FIFO_ATTR_LIMIT; @@ -169,7 +163,7 @@ int rtnl_qdisc_fifo_get_limit(struct rtnl_qdisc *qdisc) if (fifo && fifo->qf_mask & SCH_FIFO_ATTR_LIMIT) return fifo->qf_limit; else - return nl_errno(ENOMEM); + return -NLE_NOATTR; } /** @} */ @@ -178,7 +172,7 @@ static struct rtnl_qdisc_ops pfifo_ops = { .qo_kind = "pfifo", .qo_msg_parser = fifo_msg_parser, .qo_free_data = fifo_free_data, - .qo_dump[NL_DUMP_BRIEF] = pfifo_dump_brief, + .qo_dump[NL_DUMP_LINE] = pfifo_dump_line, .qo_get_opts = fifo_get_opts, }; @@ -186,7 +180,7 @@ static struct rtnl_qdisc_ops bfifo_ops = { .qo_kind = "bfifo", .qo_msg_parser = fifo_msg_parser, .qo_free_data = fifo_free_data, - .qo_dump[NL_DUMP_BRIEF] = bfifo_dump_brief, + .qo_dump[NL_DUMP_LINE] = bfifo_dump_line, .qo_get_opts = fifo_get_opts, }; diff --git a/lib/route/sch/htb.c b/lib/route/sch/htb.c index 6de87b3..a167136 100644 --- a/lib/route/sch/htb.c +++ b/lib/route/sch/htb.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com> * Copyright (c) 2005-2006 Siemens AG Oesterreich */ @@ -136,34 +136,31 @@ static void htb_class_free_data(struct rtnl_class *class) free(class->c_subdata); } -static int htb_qdisc_dump_brief(struct rtnl_qdisc *qdisc, - struct nl_dump_params *p, int line) +static void htb_qdisc_dump_line(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p) { struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata; if (d == NULL) - goto ignore; + return; if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) - dp_dump(p, " r2q %u", d->qh_rate2quantum); + nl_dump(p, " r2q %u", d->qh_rate2quantum); if (d->qh_mask & SCH_HTB_HAS_DEFCLS) { char buf[32]; - dp_dump(p, " default %s", + nl_dump(p, " default %s", rtnl_tc_handle2str(d->qh_defcls, buf, sizeof(buf))); } - -ignore: - return line; } -static int htb_class_dump_brief(struct rtnl_class *class, - struct nl_dump_params *p, int line) +static void htb_class_dump_line(struct rtnl_class *class, + struct nl_dump_params *p) { struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; if (d == NULL) - goto ignore; + return; if (d->ch_mask & SCH_HTB_HAS_RATE) { double r, rbit; @@ -172,21 +169,18 @@ static int htb_class_dump_brief(struct rtnl_class *class, r = nl_cancel_down_bytes(d->ch_rate.rs_rate, &ru); rbit = nl_cancel_down_bits(d->ch_rate.rs_rate*8, &rubit); - dp_dump(p, " rate %.2f%s/s (%.0f%s) log %u", + nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u", r, ru, rbit, rubit, 1<<d->ch_rate.rs_cell_log); } - -ignore: - return line; } -static int htb_class_dump_full(struct rtnl_class *class, - struct nl_dump_params *p, int line) +static void htb_class_dump_details(struct rtnl_class *class, + struct nl_dump_params *p) { struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; if (d == NULL) - goto ignore; + return; /* line 1 */ if (d->ch_mask & SCH_HTB_HAS_CEIL) { @@ -196,22 +190,22 @@ static int htb_class_dump_full(struct rtnl_class *class, r = nl_cancel_down_bytes(d->ch_ceil.rs_rate, &ru); rbit = nl_cancel_down_bits(d->ch_ceil.rs_rate*8, &rubit); - dp_dump(p, " ceil %.2f%s/s (%.0f%s) log %u", + nl_dump(p, " ceil %.2f%s/s (%.0f%s) log %u", r, ru, rbit, rubit, 1<<d->ch_ceil.rs_cell_log); } if (d->ch_mask & SCH_HTB_HAS_PRIO) - dp_dump(p, " prio %u", d->ch_prio); + nl_dump(p, " prio %u", d->ch_prio); if (d->ch_mask & SCH_HTB_HAS_MTU) - dp_dump(p, " mtu %u", d->ch_mtu); + nl_dump(p, " mtu %u", d->ch_mtu); if (d->ch_mask & SCH_HTB_HAS_RBUFFER) { double b; char *bu; b = nl_cancel_down_bytes(d->ch_rbuffer, &bu); - dp_dump(p, " rbuffer %.2f%s", b, bu); + nl_dump(p, " rbuffer %.2f%s", b, bu); } if (d->ch_mask & SCH_HTB_HAS_CBUFFER) { @@ -219,20 +213,17 @@ static int htb_class_dump_full(struct rtnl_class *class, char *bu; b = nl_cancel_down_bytes(d->ch_cbuffer, &bu); - dp_dump(p, " cbuffer %.2f%s", b, bu); + nl_dump(p, " cbuffer %.2f%s", b, bu); } if (d->ch_mask & SCH_HTB_HAS_QUANTUM) - dp_dump(p, " quantum %u", d->ch_quantum); + nl_dump(p, " quantum %u", d->ch_quantum); if (d->ch_mask & SCH_HTB_HAS_OVERHEAD) - dp_dump(p, " overhead %u", d->ch_overhead); + nl_dump(p, " overhead %u", d->ch_overhead); if (d->ch_mask & SCH_HTB_HAS_MPU) - dp_dump(p, " mpu %u", d->ch_mpu); - -ignore: - return line; + nl_dump(p, " mpu %u", d->ch_mpu); } static struct nl_msg *htb_qdisc_get_opts(struct rtnl_qdisc *qdisc) @@ -525,7 +516,7 @@ static struct rtnl_qdisc_ops htb_qdisc_ops = { .qo_kind = "htb", .qo_msg_parser = htb_qdisc_msg_parser, .qo_free_data = htb_qdisc_free_data, - .qo_dump[NL_DUMP_BRIEF] = htb_qdisc_dump_brief, + .qo_dump[NL_DUMP_LINE] = htb_qdisc_dump_line, .qo_get_opts = htb_qdisc_get_opts, }; @@ -533,8 +524,10 @@ static struct rtnl_class_ops htb_class_ops = { .co_kind = "htb", .co_msg_parser = htb_class_msg_parser, .co_free_data = htb_class_free_data, - .co_dump[NL_DUMP_BRIEF] = htb_class_dump_brief, - .co_dump[NL_DUMP_FULL] = htb_class_dump_full, + .co_dump = { + [NL_DUMP_LINE] = htb_class_dump_line, + [NL_DUMP_DETAILS] = htb_class_dump_details, + }, .co_get_opts = htb_class_get_opts, }; diff --git a/lib/route/sch/netem.c b/lib/route/sch/netem.c index e8b8913..18878a7 100644 --- a/lib/route/sch/netem.c +++ b/lib/route/sch/netem.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -27,17 +27,20 @@ #include <netlink/route/sch/netem.h> /** @cond SKIP */ -#define SCH_NETEM_ATTR_LATENCY 0x001 -#define SCH_NETEM_ATTR_LIMIT 0x002 -#define SCH_NETEM_ATTR_LOSS 0x004 -#define SCH_NETEM_ATTR_GAP 0x008 -#define SCH_NETEM_ATTR_DUPLICATE 0x010 -#define SCH_NETEM_ATTR_JITTER 0x020 -#define SCH_NETEM_ATTR_DELAY_CORR 0x040 -#define SCH_NETEM_ATTR_LOSS_CORR 0x080 -#define SCH_NETEM_ATTR_DUP_CORR 0x100 -#define SCH_NETEM_ATTR_RO_PROB 0x200 -#define SCH_NETEM_ATTR_RO_CORR 0x400 +#define SCH_NETEM_ATTR_LATENCY 0x0001 +#define SCH_NETEM_ATTR_LIMIT 0x0002 +#define SCH_NETEM_ATTR_LOSS 0x0004 +#define SCH_NETEM_ATTR_GAP 0x0008 +#define SCH_NETEM_ATTR_DUPLICATE 0x0010 +#define SCH_NETEM_ATTR_JITTER 0x0020 +#define SCH_NETEM_ATTR_DELAY_CORR 0x0040 +#define SCH_NETEM_ATTR_LOSS_CORR 0x0080 +#define SCH_NETEM_ATTR_DUP_CORR 0x0100 +#define SCH_NETEM_ATTR_RO_PROB 0x0200 +#define SCH_NETEM_ATTR_RO_CORR 0x0400 +#define SCH_NETEM_ATTR_CORRUPT_PROB 0x0800 +#define SCH_NETEM_ATTR_CORRUPT_CORR 0x1000 +#define SCH_NETEM_ATTR_DIST 0x2000 /** @endcond */ static inline struct rtnl_netem *netem_qdisc(struct rtnl_qdisc *qdisc) @@ -56,6 +59,7 @@ static inline struct rtnl_netem *netem_alloc(struct rtnl_qdisc *qdisc) static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = { [TCA_NETEM_CORR] = { .minlen = sizeof(struct tc_netem_corr) }, [TCA_NETEM_REORDER] = { .minlen = sizeof(struct tc_netem_reorder) }, + [TCA_NETEM_CORRUPT] = { .minlen = sizeof(struct tc_netem_corrupt) }, }; static int netem_msg_parser(struct rtnl_qdisc *qdisc) @@ -65,11 +69,11 @@ static int netem_msg_parser(struct rtnl_qdisc *qdisc) struct tc_netem_qopt *opts; if (qdisc->q_opts->d_size < sizeof(*opts)) - return nl_error(EINVAL, "Netem specific options size mismatch"); + return -NLE_INVAL; netem = netem_alloc(qdisc); if (!netem) - return nl_errno(ENOMEM); + return -NLE_NOMEM; opts = (struct tc_netem_qopt *) qdisc->q_opts->d_data; netem->qnm_latency = opts->latency; @@ -89,7 +93,7 @@ static int netem_msg_parser(struct rtnl_qdisc *qdisc) struct nlattr *tb[TCA_NETEM_MAX+1]; err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *) - qdisc->q_opts->d_data + sizeof(*opts), + (qdisc->q_opts->d_data + sizeof(*opts)), len, netem_policy); if (err < 0) { free(netem); @@ -106,7 +110,7 @@ static int netem_msg_parser(struct rtnl_qdisc *qdisc) netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR | SCH_NETEM_ATTR_LOSS_CORR | - SCH_NETEM_ATTR_DELAY_CORR); + SCH_NETEM_ATTR_DUP_CORR); } if (tb[TCA_NETEM_REORDER]) { @@ -119,6 +123,21 @@ static int netem_msg_parser(struct rtnl_qdisc *qdisc) netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB | SCH_NETEM_ATTR_RO_CORR); } + + if (tb[TCA_NETEM_CORRUPT]) { + struct tc_netem_corrupt corrupt; + + nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT], sizeof(corrupt)); + netem->qnm_crpt.nmcr_probability = corrupt.probability; + netem->qnm_crpt.nmcr_correlation = corrupt.correlation; + + netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB | + SCH_NETEM_ATTR_CORRUPT_CORR); + } + + /* sch_netem does not currently dump TCA_NETEM_DELAY_DIST */ + netem->qnm_dist.dist_data = NULL; + netem->qnm_dist.dist_size = 0; } return 0; @@ -126,29 +145,164 @@ static int netem_msg_parser(struct rtnl_qdisc *qdisc) static void netem_free_data(struct rtnl_qdisc *qdisc) { - free(qdisc->q_subdata); + struct rtnl_netem *netem; + + if ( ! qdisc ) return; + + netem = netem_qdisc(qdisc); + if ( ! netem ) return; + + if ( netem->qnm_dist.dist_data ) + free(netem->qnm_dist.dist_data); + + netem = NULL; + + free (qdisc->q_subdata); } -static int netem_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, - int line) +static void netem_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) { struct rtnl_netem *netem = netem_qdisc(qdisc); if (netem) - dp_dump(p, "limit %d", netem->qnm_limit); - - return line; + nl_dump(p, "limit %d", netem->qnm_limit); } -static int netem_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, - int line) +int netem_build_msg(struct rtnl_qdisc *qdisc, struct nl_msg *msg) { - return line; -} + int err = 0; + struct tc_netem_qopt opts; + struct tc_netem_corr cor; + struct tc_netem_reorder reorder; + struct tc_netem_corrupt corrupt; + struct rtnl_netem *netem; + + unsigned char set_correlation = 0, set_reorder = 0, + set_corrupt = 0, set_dist = 0; -static struct nl_msg *netem_get_opts(struct rtnl_qdisc *qdisc) -{ - return NULL; + memset(&opts, 0, sizeof(opts)); + memset(&cor, 0, sizeof(cor)); + memset(&reorder, 0, sizeof(reorder)); + memset(&corrupt, 0, sizeof(corrupt)); + + netem = netem_qdisc(qdisc); + if (!netem || !msg) + return EFAULT; + + msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST; + + if ( netem->qnm_ro.nmro_probability != 0 ) { + if (netem->qnm_latency == 0) { + return -NLE_MISSING_ATTR; + } + if (netem->qnm_gap == 0) netem->qnm_gap = 1; + } + else if ( netem->qnm_gap ) { + return -NLE_MISSING_ATTR; + } + + if ( netem->qnm_corr.nmc_delay != 0 ) { + if ( netem->qnm_latency == 0 || netem->qnm_jitter == 0) { + return -NLE_MISSING_ATTR; + } + set_correlation = 1; + } + + if ( netem->qnm_corr.nmc_loss != 0 ) { + if ( netem->qnm_loss == 0 ) { + return -NLE_MISSING_ATTR; + } + set_correlation = 1; + } + + if ( netem->qnm_corr.nmc_duplicate != 0 ) { + if ( netem->qnm_duplicate == 0 ) { + return -NLE_MISSING_ATTR; + } + set_correlation = 1; + } + + if ( netem->qnm_ro.nmro_probability != 0 ) set_reorder = 1; + else if ( netem->qnm_ro.nmro_correlation != 0 ) { + return -NLE_MISSING_ATTR; + } + + if ( netem->qnm_crpt.nmcr_probability != 0 ) set_corrupt = 1; + else if ( netem->qnm_crpt.nmcr_correlation != 0 ) { + return -NLE_MISSING_ATTR; + } + + if ( netem->qnm_dist.dist_data && netem->qnm_dist.dist_size ) { + if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) { + return -NLE_MISSING_ATTR; + } + else { + /* Resize to accomodate the large distribution table */ + int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size * + sizeof(netem->qnm_dist.dist_data[0]); + + msg->nm_nlh = (struct nlmsghdr *) realloc(msg->nm_nlh, new_msg_len); + if ( msg->nm_nlh == NULL ) + return -NLE_NOMEM; + msg->nm_size = new_msg_len; + set_dist = 1; + } + } + + opts.latency = netem->qnm_latency; + opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000; + opts.loss = netem->qnm_loss; + opts.gap = netem->qnm_gap; + opts.duplicate = netem->qnm_duplicate; + opts.jitter = netem->qnm_jitter; + + NLA_PUT(msg, TCA_OPTIONS, sizeof(opts), &opts); + + if ( set_correlation ) { + cor.delay_corr = netem->qnm_corr.nmc_delay; + cor.loss_corr = netem->qnm_corr.nmc_loss; + cor.dup_corr = netem->qnm_corr.nmc_duplicate; + + NLA_PUT(msg, TCA_NETEM_CORR, sizeof(cor), &cor); + } + + if ( set_reorder ) { + reorder.probability = netem->qnm_ro.nmro_probability; + reorder.correlation = netem->qnm_ro.nmro_correlation; + + NLA_PUT(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder); + } + + if ( set_corrupt ) { + corrupt.probability = netem->qnm_crpt.nmcr_probability; + corrupt.correlation = netem->qnm_crpt.nmcr_correlation; + + NLA_PUT(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt); + } + + if ( set_dist ) { + NLA_PUT(msg, TCA_NETEM_DELAY_DIST, + netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]), + netem->qnm_dist.dist_data); + } + + /* Length specified in the TCA_OPTIONS section must span the entire + * remainder of the message. That's just the way that sch_netem expects it. + * Maybe there's a more succinct way to do this at a higher level. + */ + struct nlattr* head = (struct nlattr *)(NLMSG_DATA(msg->nm_nlh) + + NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO); + + struct nlattr* tail = (struct nlattr *)(((void *) (msg->nm_nlh)) + + NLMSG_ALIGN(msg->nm_nlh->nlmsg_len)); + + int old_len = head->nla_len; + head->nla_len = (void *)tail - (void *)head; + msg->nm_nlh->nlmsg_len += (head->nla_len - old_len); + + return err; +nla_put_failure: + return -NLE_MSGSIZE; } /** @@ -168,7 +322,7 @@ int rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit) netem = netem_alloc(qdisc); if (!netem) - return nl_errno(ENOMEM); + return -NLE_NOMEM; netem->qnm_limit = limit; netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT; @@ -189,7 +343,7 @@ int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc) if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT)) return netem->qnm_limit; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @} */ @@ -211,7 +365,7 @@ int rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap) netem = netem_alloc(qdisc); if (!netem) - return nl_errno(ENOMEM); + return -NLE_NOMEM; netem->qnm_gap = gap; netem->qnm_mask |= SCH_NETEM_ATTR_GAP; @@ -232,7 +386,7 @@ int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc) if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_GAP)) return netem->qnm_gap; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @@ -247,7 +401,7 @@ int rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob) netem = netem_alloc(qdisc); if (!netem) - return nl_errno(ENOMEM); + return -NLE_NOMEM; netem->qnm_ro.nmro_probability = prob; netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB; @@ -268,7 +422,7 @@ int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc) if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB)) return netem->qnm_ro.nmro_probability; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @@ -283,7 +437,7 @@ int rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob) netem = netem_alloc(qdisc); if (!netem) - return nl_errno(ENOMEM); + return -NLE_NOMEM; netem->qnm_ro.nmro_correlation = prob; netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR; @@ -304,7 +458,86 @@ int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc) if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR)) return netem->qnm_ro.nmro_correlation; else - return nl_errno(ENOENT); + return -NLE_NOATTR; +} + +/** @} */ + +/** + * @name Corruption + * @{ + */ + +/** + * Set corruption probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New corruption probability. + * @return 0 on success or a negative error code. + */ +int rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return -NLE_NOMEM; + + netem->qnm_crpt.nmcr_probability = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB; + + return 0; +} + +/** + * Get corruption probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Corruption probability or a negative error code. + */ +int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB)) + return netem->qnm_crpt.nmcr_probability; + else + return -NLE_NOATTR; +} + +/** + * Set corruption correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New corruption correlation probability. + * @return 0 on success or a negative error code. + */ +int rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return -NLE_NOMEM; + + netem->qnm_crpt.nmcr_correlation = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR; + + return 0; +} + +/** + * Get corruption correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Corruption correlation probability or a negative error code. + */ +int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR)) + return netem->qnm_crpt.nmcr_correlation; + else + return -NLE_NOATTR; } /** @} */ @@ -326,7 +559,7 @@ int rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob) netem = netem_alloc(qdisc); if (!netem) - return nl_errno(ENOMEM); + return -NLE_NOMEM; netem->qnm_loss = prob; netem->qnm_mask |= SCH_NETEM_ATTR_LOSS; @@ -347,7 +580,7 @@ int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc) if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS)) return netem->qnm_loss; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @@ -362,7 +595,7 @@ int rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob) netem = netem_alloc(qdisc); if (!netem) - return nl_errno(ENOMEM); + return -NLE_NOMEM; netem->qnm_corr.nmc_loss = prob; netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR; @@ -383,7 +616,7 @@ int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc) if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR)) return netem->qnm_corr.nmc_loss; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @} */ @@ -405,7 +638,7 @@ int rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob) netem = netem_alloc(qdisc); if (!netem) - return nl_errno(ENOMEM); + return -NLE_NOMEM; netem->qnm_duplicate = prob; netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE; @@ -426,7 +659,7 @@ int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc) if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE)) return netem->qnm_duplicate; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @@ -441,7 +674,7 @@ int rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob) netem = netem_alloc(qdisc); if (!netem) - return nl_errno(ENOMEM); + return -NLE_NOMEM; netem->qnm_corr.nmc_duplicate = prob; netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR; @@ -462,7 +695,7 @@ int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc) if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR)) return netem->qnm_corr.nmc_duplicate; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @} */ @@ -484,7 +717,7 @@ int rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay) netem = netem_alloc(qdisc); if (!netem) - return nl_errno(ENOMEM); + return -NLE_NOMEM; netem->qnm_latency = nl_us2ticks(delay); netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY; @@ -505,7 +738,7 @@ int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc) if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY)) return nl_ticks2us(netem->qnm_latency); else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @@ -520,7 +753,7 @@ int rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter) netem = netem_alloc(qdisc); if (!netem) - return nl_errno(ENOMEM); + return -NLE_NOMEM; netem->qnm_jitter = nl_us2ticks(jitter); netem->qnm_mask |= SCH_NETEM_ATTR_JITTER; @@ -541,7 +774,7 @@ int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc) if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_JITTER)) return nl_ticks2us(netem->qnm_jitter); else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @@ -555,7 +788,7 @@ int rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob) netem = netem_alloc(qdisc); if (!netem) - return nl_errno(ENOMEM); + return -NLE_NOMEM; netem->qnm_corr.nmc_delay = prob; netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR; @@ -576,7 +809,110 @@ int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc) if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR)) return netem->qnm_corr.nmc_delay; else - return nl_errno(ENOENT); + return -NLE_NOATTR; +} + +/** + * Get the size of the distribution table. + * @arg qdisc Netem qdisc. + * @return Distribution table size or a negative error code. + */ +int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST)) + return netem->qnm_dist.dist_size; + else + return -NLE_NOATTR; +} + +/** + * Get a pointer to the distribution table. + * @arg qdisc Netem qdisc. + * @arg dist_ptr The pointer to set. + * @return Negative error code on failure or 0 on success. + */ +int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST)) { + *dist_ptr = netem->qnm_dist.dist_data; + return 0; + } + else + return -NLE_NOATTR; +} + +/** + * Set the delay distribution. Latency/jitter must be set before applying. + * @arg qdisc Netem qdisc. + * @arg dist_type The name of the distribution (type, file, path/file). + * @return 0 on success, error code on failure. + */ +int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) { + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return -NLE_NOMEM; + + FILE *f = NULL; + int i, n = 0; + size_t len = 2048; + char *line; + char name[NAME_MAX]; + char dist_suffix[] = ".dist"; + + /* If the given filename already ends in .dist, don't append it later */ + char *test_suffix = strstr(dist_type, dist_suffix); + if (test_suffix != NULL && strlen(test_suffix) == 5) + strcpy(dist_suffix, ""); + + /* Check several locations for the dist file */ + char *test_path[] = { "", "./", "/usr/lib/tc/", "/usr/local/lib/tc/" }; + + for (i = 0; i < sizeof(test_path) && f == NULL; i++) { + snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix); + f = fopen(name, "r"); + } + + if ( f == NULL ) + return -nl_syserr2nlerr(errno); + + netem->qnm_dist.dist_data = (int16_t *) calloc (MAXDIST, sizeof(int16_t)); + + line = (char *) calloc (sizeof(char), len + 1); + + while (getline(&line, &len, f) != -1) { + char *p, *endp; + + if (*line == '\n' || *line == '#') + continue; + + for (p = line; ; p = endp) { + long x = strtol(p, &endp, 0); + if (endp == p) break; + + if (n >= MAXDIST) { + free(line); + fclose(f); + return -NLE_INVAL; + } + netem->qnm_dist.dist_data[n++] = x; + } + } + + free(line); + + netem->qnm_dist.dist_size = n; + netem->qnm_mask |= SCH_NETEM_ATTR_DIST; + + fclose(f); + return 0; } /** @} */ @@ -585,9 +921,9 @@ static struct rtnl_qdisc_ops netem_ops = { .qo_kind = "netem", .qo_msg_parser = netem_msg_parser, .qo_free_data = netem_free_data, - .qo_dump[NL_DUMP_BRIEF] = netem_dump_brief, - .qo_dump[NL_DUMP_FULL] = netem_dump_full, - .qo_get_opts = netem_get_opts, + .qo_dump[NL_DUMP_LINE] = netem_dump_line, + .qo_get_opts = 0, + .qo_build_msg = netem_build_msg }; static void __init netem_init(void) diff --git a/lib/route/sch/prio.c b/lib/route/sch/prio.c index 4e3d624..4c9ebcf 100644 --- a/lib/route/sch/prio.c +++ b/lib/route/sch/prio.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -58,11 +58,11 @@ static int prio_msg_parser(struct rtnl_qdisc *qdisc) struct tc_prio_qopt *opt; if (qdisc->q_opts->d_size < sizeof(*opt)) - return nl_error(EINVAL, "prio specific option size mismatch"); + return -NLE_INVAL; prio = prio_alloc(qdisc); if (!prio) - return nl_errno(ENOMEM); + return -NLE_NOMEM; opt = (struct tc_prio_qopt *) qdisc->q_opts->d_data; prio->qp_bands = opt->bands; @@ -77,55 +77,48 @@ static void prio_free_data(struct rtnl_qdisc *qdisc) free(qdisc->q_subdata); } -static int prio_dump_brief(struct rtnl_qdisc *qdisc, - struct nl_dump_params *p, int line) +static void prio_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) { struct rtnl_prio *prio = prio_qdisc(qdisc); if (prio) - dp_dump(p, " bands %u", prio->qp_bands); - - return line; + nl_dump(p, " bands %u", prio->qp_bands); } -static int prio_dump_full(struct rtnl_qdisc *qdisc, - struct nl_dump_params *p, int line) +static void prio_dump_details(struct rtnl_qdisc *qdisc,struct nl_dump_params *p) { struct rtnl_prio *prio = prio_qdisc(qdisc); int i, hp; if (!prio) - goto ignore; + return; - dp_dump(p, "priomap ["); + nl_dump(p, "priomap ["); for (i = 0; i <= TC_PRIO_MAX; i++) - dp_dump(p, "%u%s", prio->qp_priomap[i], + nl_dump(p, "%u%s", prio->qp_priomap[i], i < TC_PRIO_MAX ? " " : ""); - dp_dump(p, "]\n"); - dp_new_line(p, line++); + nl_dump(p, "]\n"); + nl_new_line(p); hp = (((TC_PRIO_MAX/2) + 1) & ~1); for (i = 0; i < hp; i++) { char a[32]; - dp_dump(p, " %18s => %u", + nl_dump(p, " %18s => %u", rtnl_prio2str(i, a, sizeof(a)), prio->qp_priomap[i]); if (hp+i <= TC_PRIO_MAX) { - dp_dump(p, " %18s => %u", + nl_dump(p, " %18s => %u", rtnl_prio2str(hp+i, a, sizeof(a)), prio->qp_priomap[hp+i]); if (i < (hp - 1)) { - dp_dump(p, "\n"); - dp_new_line(p, line++); + nl_dump(p, "\n"); + nl_new_line(p); } } } - -ignore: - return line; } static struct nl_msg *prio_get_opts(struct rtnl_qdisc *qdisc) @@ -173,7 +166,7 @@ int rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *qdisc, int bands) prio = prio_alloc(qdisc); if (!prio) - return nl_errno(ENOMEM); + return -NLE_NOMEM; prio->qp_bands = bands; prio->qp_mask |= SCH_PRIO_ATTR_BANDS; @@ -194,7 +187,7 @@ int rtnl_qdisc_prio_get_bands(struct rtnl_qdisc *qdisc) if (prio && prio->qp_mask & SCH_PRIO_ATTR_BANDS) return prio->qp_bands; else - return nl_errno(ENOMEM); + return -NLE_NOMEM; } /** @@ -212,18 +205,17 @@ int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[], prio = prio_alloc(qdisc); if (!prio) - return nl_errno(ENOMEM); + return -NLE_NOMEM; if (!(prio->qp_mask & SCH_PRIO_ATTR_BANDS)) - return nl_error(EINVAL, "Set number of bands first"); + return -NLE_MISSING_ATTR; if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX+1)) - return nl_error(ERANGE, "priomap length out of bounds"); + return -NLE_RANGE; for (i = 0; i <= TC_PRIO_MAX; i++) { if (priomap[i] > prio->qp_bands) - return nl_error(ERANGE, "priomap element %d " \ - "out of bounds, increase bands number"); + return -NLE_RANGE; } memcpy(prio->qp_priomap, priomap, len); @@ -245,10 +237,8 @@ uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *qdisc) prio = prio_qdisc(qdisc); if (prio && prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP) return prio->qp_priomap; - else { - nl_errno(ENOENT); + else return NULL; - } } /** @} */ @@ -303,8 +293,10 @@ static struct rtnl_qdisc_ops prio_ops = { .qo_kind = "prio", .qo_msg_parser = prio_msg_parser, .qo_free_data = prio_free_data, - .qo_dump[NL_DUMP_BRIEF] = prio_dump_brief, - .qo_dump[NL_DUMP_FULL] = prio_dump_full, + .qo_dump = { + [NL_DUMP_LINE] = prio_dump_line, + [NL_DUMP_DETAILS] = prio_dump_details, + }, .qo_get_opts = prio_get_opts, }; @@ -312,8 +304,10 @@ static struct rtnl_qdisc_ops pfifo_fast_ops = { .qo_kind = "pfifo_fast", .qo_msg_parser = prio_msg_parser, .qo_free_data = prio_free_data, - .qo_dump[NL_DUMP_BRIEF] = prio_dump_brief, - .qo_dump[NL_DUMP_FULL] = prio_dump_full, + .qo_dump = { + [NL_DUMP_LINE] = prio_dump_line, + [NL_DUMP_DETAILS] = prio_dump_details, + }, .qo_get_opts = prio_get_opts, }; diff --git a/lib/route/sch/red.c b/lib/route/sch/red.c index a31c358..e4cac79 100644 --- a/lib/route/sch/red.c +++ b/lib/route/sch/red.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -66,11 +66,11 @@ static int red_msg_parser(struct rtnl_qdisc *qdisc) return err; if (!tb[TCA_RED_PARMS]) - return nl_error(EINVAL, "Missing TCA_RED_PARMS"); + return -NLE_MISSING_ATTR; red = red_alloc(qdisc); if (!red) - return nl_errno(ENOMEM); + return -NLE_NOMEM; opts = nla_data(tb[TCA_RED_PARMS]); @@ -89,40 +89,31 @@ static int red_msg_parser(struct rtnl_qdisc *qdisc) return 0; } -static int red_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, - int line) +static void red_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) { struct rtnl_red *red = red_qdisc(qdisc); if (red) { /* XXX: limit, min, max, flags */ } - - return line; } -static int red_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, - int line) +static void red_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) { struct rtnl_red *red = red_qdisc(qdisc); if (red) { /* XXX: wlog, plog, scell_log */ } - - return line; } -static int red_dump_stats(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, - int line) +static void red_dump_stats(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) { struct rtnl_red *red = red_qdisc(qdisc); if (red) { /* XXX: xstats */ } - - return line; } static struct nl_msg *red_get_opts(struct rtnl_qdisc *qdisc) @@ -171,7 +162,7 @@ int rtnl_red_set_limit(struct rtnl_qdisc *qdisc, int limit) red = red_alloc(qdisc); if (!red) - return nl_errno(ENOMEM); + return -NLE_NOMEM; red->qr_limit = limit; red->qr_mask |= RED_ATTR_LIMIT; @@ -192,7 +183,7 @@ int rtnl_red_get_limit(struct rtnl_qdisc *qdisc) if (red && (red->qr_mask & RED_ATTR_LIMIT)) return red->qr_limit; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @} */ @@ -200,9 +191,11 @@ int rtnl_red_get_limit(struct rtnl_qdisc *qdisc) static struct rtnl_qdisc_ops red_ops = { .qo_kind = "red", .qo_msg_parser = red_msg_parser, - .qo_dump[NL_DUMP_BRIEF] = red_dump_brief, - .qo_dump[NL_DUMP_FULL] = red_dump_full, - .qo_dump[NL_DUMP_STATS] = red_dump_stats, + .qo_dump = { + [NL_DUMP_LINE] = red_dump_line, + [NL_DUMP_DETAILS] = red_dump_details, + [NL_DUMP_STATS] = red_dump_stats, + }, .qo_get_opts = red_get_opts, }; diff --git a/lib/route/sch/sfq.c b/lib/route/sch/sfq.c index d530c0f..4b47356 100644 --- a/lib/route/sch/sfq.c +++ b/lib/route/sch/sfq.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -61,11 +61,11 @@ static int sfq_msg_parser(struct rtnl_qdisc *qdisc) return 0; if (qdisc->q_opts->d_size < sizeof(*opts)) - return nl_error(EINVAL, "SFQ specific options size mismatch"); + return -NLE_INVAL; sfq = sfq_alloc(qdisc); if (!sfq) - return nl_errno(ENOMEM); + return -NLE_NOMEM; opts = (struct tc_sfq_qopt *) qdisc->q_opts->d_data; @@ -87,29 +87,22 @@ static void sfq_free_data(struct rtnl_qdisc *qdisc) free(qdisc->q_subdata); } -static int sfq_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, - int line) +static void sfq_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) { struct rtnl_sfq *sfq = sfq_qdisc(qdisc); if (sfq) - dp_dump(p, " quantum %u perturb %us", - sfq->qs_quantum, + nl_dump(p, " quantum %u perturb %us", sfq->qs_quantum, nl_ticks2us(sfq->qs_perturb * nl_get_hz())); - - return line; } -static int sfq_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, - int line) +static void sfq_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) { struct rtnl_sfq *sfq = sfq_qdisc(qdisc); if (sfq) - dp_dump(p, "limit %u divisor %u", + nl_dump(p, "limit %u divisor %u", sfq->qs_limit, sfq->qs_divisor); - - return line; } static struct nl_msg *sfq_get_opts(struct rtnl_qdisc *qdisc) @@ -157,7 +150,7 @@ int rtnl_sfq_set_quantum(struct rtnl_qdisc *qdisc, int quantum) sfq = sfq_alloc(qdisc); if (!sfq) - return nl_errno(ENOMEM); + return -NLE_NOMEM; sfq->qs_quantum = quantum; sfq->qs_mask |= SCH_SFQ_ATTR_QUANTUM; @@ -178,7 +171,7 @@ int rtnl_sfq_get_quantum(struct rtnl_qdisc *qdisc) if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_QUANTUM) return sfq->qs_quantum; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @@ -193,7 +186,7 @@ int rtnl_sfq_set_limit(struct rtnl_qdisc *qdisc, int limit) sfq = sfq_alloc(qdisc); if (!sfq) - return nl_errno(ENOMEM); + return -NLE_NOMEM; sfq->qs_limit = limit; sfq->qs_mask |= SCH_SFQ_ATTR_LIMIT; @@ -214,7 +207,7 @@ int rtnl_sfq_get_limit(struct rtnl_qdisc *qdisc) if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_LIMIT) return sfq->qs_limit; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @@ -230,7 +223,7 @@ int rtnl_sfq_set_perturb(struct rtnl_qdisc *qdisc, int perturb) sfq = sfq_alloc(qdisc); if (!sfq) - return nl_errno(ENOMEM); + return -NLE_NOMEM; sfq->qs_perturb = perturb; sfq->qs_mask |= SCH_SFQ_ATTR_PERTURB; @@ -251,7 +244,7 @@ int rtnl_sfq_get_perturb(struct rtnl_qdisc *qdisc) if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_PERTURB) return sfq->qs_perturb; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @@ -267,7 +260,7 @@ int rtnl_sfq_get_divisor(struct rtnl_qdisc *qdisc) if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_DIVISOR) return sfq->qs_divisor; else - return nl_errno(ENOENT); + return -NLE_NOATTR; } /** @} */ @@ -276,8 +269,10 @@ static struct rtnl_qdisc_ops sfq_ops = { .qo_kind = "sfq", .qo_msg_parser = sfq_msg_parser, .qo_free_data = sfq_free_data, - .qo_dump[NL_DUMP_BRIEF] = sfq_dump_brief, - .qo_dump[NL_DUMP_FULL] = sfq_dump_full, + .qo_dump = { + [NL_DUMP_LINE] = sfq_dump_line, + [NL_DUMP_DETAILS] = sfq_dump_details, + }, .qo_get_opts = sfq_get_opts, }; diff --git a/lib/route/sch/tbf.c b/lib/route/sch/tbf.c index 04d1689..eccaf70 100644 --- a/lib/route/sch/tbf.c +++ b/lib/route/sch/tbf.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -62,9 +62,9 @@ static int tbf_msg_parser(struct rtnl_qdisc *q) if (err < 0) return err; - tbf = tbf_qdisc(q); + tbf = tbf_alloc(q); if (!tbf) - return nl_errno(ENOMEM); + return -NLE_NOMEM; if (tb[TCA_TBF_PARMS]) { struct tc_tbf_qopt opts; @@ -93,34 +93,34 @@ static int tbf_msg_parser(struct rtnl_qdisc *q) return 0; } -static int tbf_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, - int line) +static void tbf_free_data(struct rtnl_qdisc *qdisc) +{ + free(qdisc->q_subdata); +} + +static void tbf_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) { double r, rbit, lim; char *ru, *rubit, *limu; struct rtnl_tbf *tbf = tbf_qdisc(qdisc); if (!tbf) - goto ignore; + return; r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru); rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit); lim = nl_cancel_down_bytes(tbf->qt_limit, &limu); - dp_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s", + nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s", r, ru, rbit, rubit, lim, limu); - -ignore: - return line; } -static int tbf_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, - int line) +static void tbf_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) { struct rtnl_tbf *tbf = tbf_qdisc(qdisc); if (!tbf) - goto ignore; + return; if (1) { char *bu, *cu; @@ -128,7 +128,7 @@ static int tbf_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log, &cu); - dp_dump(p, "mpu %u rate-bucket-size %1.f%s " + nl_dump(p, "mpu %u rate-bucket-size %1.f%s " "rate-cell-size %.1f%s\n", tbf->qt_mpu, bs, bu, cl, cu); @@ -144,14 +144,11 @@ static int tbf_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log, &clu); - dp_dump_line(p, line++, " peak-rate %.2f%s/s (%.0f%s) " - "bucket-size %.1f%s cell-size %.1f%s", - "latency %.1f%s", + nl_dump_line(p, " peak-rate %.2f%s/s (%.0f%s) " + "bucket-size %.1f%s cell-size %.1f%s" + "latency %.1f%s", pr, pru, prb, prbu, bs, bsu, cl, clu); } - -ignore: - return line; } static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc) @@ -226,7 +223,7 @@ int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit) tbf = tbf_alloc(qdisc); if (!tbf) - return nl_errno(ENOMEM); + return -NLE_NOMEM; tbf->qt_limit = limit; tbf->qt_mask |= TBF_ATTR_LIMIT; @@ -270,11 +267,10 @@ int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency) tbf = tbf_alloc(qdisc); if (!tbf) - return nl_errno(ENOMEM); + return -NLE_NOMEM; if (!(tbf->qt_mask & TBF_ATTR_RATE)) - return nl_error(EINVAL, "The rate must be specified before " - "limit can be calculated based on latency."); + return -NLE_MISSING_ATTR; limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket); @@ -301,8 +297,8 @@ int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc) tbf = tbf_qdisc(qdisc); if (tbf && (tbf->qt_mask & TBF_ATTR_LIMIT)) return tbf->qt_limit; - return - nl_errno(ENOENT); + else + return -NLE_NOATTR; } /** @@ -317,7 +313,7 @@ int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *qdisc, int mpu) tbf = tbf_alloc(qdisc); if (!tbf) - return nl_errno(ENOMEM); + return -NLE_NOMEM; tbf->qt_mpu = mpu; tbf->qt_mask |= TBF_ATTR_MPU; @@ -337,8 +333,8 @@ int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *qdisc) tbf = tbf_qdisc(qdisc); if (tbf && (tbf->qt_mask & TBF_ATTR_MPU)) return tbf->qt_mpu; - return - nl_errno(ENOENT); + else + return -NLE_NOATTR; } static inline int calc_cell_log(int cell, int bucket) @@ -374,7 +370,7 @@ int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket, tbf = tbf_alloc(qdisc); if (!tbf) - return nl_errno(ENOMEM); + return -NLE_NOMEM; cell_log = calc_cell_log(cell, bucket); if (cell_log < 0) @@ -453,7 +449,7 @@ int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket, tbf = tbf_alloc(qdisc); if (!tbf) - return nl_errno(ENOMEM); + return -NLE_NOMEM; cell_log = calc_cell_log(cell, bucket); if (cell_log < 0) @@ -522,8 +518,11 @@ int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc) static struct rtnl_qdisc_ops tbf_qdisc_ops = { .qo_kind = "tbf", .qo_msg_parser = tbf_msg_parser, - .qo_dump[NL_DUMP_BRIEF] = tbf_dump_brief, - .qo_dump[NL_DUMP_FULL] = tbf_dump_full, + .qo_dump = { + [NL_DUMP_LINE] = tbf_dump_line, + [NL_DUMP_DETAILS] = tbf_dump_details, + }, + .qo_free_data = tbf_free_data, .qo_get_opts = tbf_get_opts, }; diff --git a/lib/route/tc.c b/lib/route/tc.c index 1351fa2..97faef4 100644 --- a/lib/route/tc.c +++ b/lib/route/tc.c @@ -6,7 +6,7 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** @@ -66,7 +66,7 @@ int tca_msg_parser(struct nlmsghdr *n, struct rtnl_tca *g) return err; if (tb[TCA_KIND] == NULL) - return nl_error(EINVAL, "Missing tca kind TLV"); + return -NLE_MISSING_ATTR; nla_strlcpy(g->tc_kind, tb[TCA_KIND], TCKINDSIZ); @@ -81,9 +81,9 @@ int tca_msg_parser(struct nlmsghdr *n, struct rtnl_tca *g) TCA_ATTR_PARENT | TCA_ATTR_INFO | TCA_ATTR_KIND); if (tb[TCA_OPTIONS]) { - g->tc_opts = nla_get_data(tb[TCA_OPTIONS]); + g->tc_opts = nl_data_alloc_attr(tb[TCA_OPTIONS]); if (!g->tc_opts) - return nl_errno(ENOMEM); + return -NLE_NOMEM; g->ce_mask |= TCA_ATTR_OPTS; } @@ -126,9 +126,9 @@ int tca_msg_parser(struct nlmsghdr *n, struct rtnl_tca *g) g->ce_mask |= TCA_ATTR_STATS; if (tbs[TCA_STATS_APP]) { - g->tc_xstats = nla_get_data(tbs[TCA_STATS_APP]); + g->tc_xstats = nl_data_alloc_attr(tbs[TCA_STATS_APP]); if (g->tc_xstats == NULL) - return -ENOMEM; + return -NLE_NOMEM; } else goto compat_xstats; } else { @@ -149,9 +149,9 @@ int tca_msg_parser(struct nlmsghdr *n, struct rtnl_tca *g) compat_xstats: if (tb[TCA_XSTATS]) { - g->tc_xstats = nla_get_data(tb[TCA_XSTATS]); + g->tc_xstats = nl_data_alloc_attr(tb[TCA_XSTATS]); if (g->tc_xstats == NULL) - return -ENOMEM; + return -NLE_NOMEM; g->ce_mask |= TCA_ATTR_XSTATS; } } @@ -171,58 +171,53 @@ int tca_clone(struct rtnl_tca *dst, struct rtnl_tca *src) if (src->tc_opts) { dst->tc_opts = nl_data_clone(src->tc_opts); if (!dst->tc_opts) - goto errout; + return -NLE_NOMEM; } if (src->tc_xstats) { dst->tc_xstats = nl_data_clone(src->tc_xstats); if (!dst->tc_xstats) - goto errout; + return -NLE_NOMEM; } return 0; -errout: - return nl_get_errno(); } -int tca_dump_brief(struct rtnl_tca *g, const char *type, - struct nl_dump_params *p, int line) +void tca_dump_line(struct rtnl_tca *g, const char *type, + struct nl_dump_params *p) { char handle[32], parent[32]; struct nl_cache *link_cache; link_cache = nl_cache_mngt_require("route/link"); - dp_dump(p, "%s %s ", g->tc_kind, type); + nl_dump_line(p, "%s %s ", g->tc_kind, type); if (link_cache) { char buf[32]; - dp_dump(p, "dev %s ", + nl_dump(p, "dev %s ", rtnl_link_i2name(link_cache, g->tc_ifindex, buf, sizeof(buf))); } else - dp_dump(p, "dev %u ", g->tc_ifindex); + nl_dump(p, "dev %u ", g->tc_ifindex); - dp_dump(p, "handle %s parent %s", + nl_dump(p, "handle %s parent %s", rtnl_tc_handle2str(g->tc_handle, handle, sizeof(handle)), rtnl_tc_handle2str(g->tc_parent, parent, sizeof(parent))); - - return 1; } -int tca_dump_full(struct rtnl_tca *g, struct nl_dump_params *p, int line) +void tca_dump_details(struct rtnl_tca *g, struct nl_dump_params *p) { - dp_dump_line(p, line++, " "); - return line; + nl_dump_line(p, " "); } -int tca_dump_stats(struct rtnl_tca *g, struct nl_dump_params *p, int line) +void tca_dump_stats(struct rtnl_tca *g, struct nl_dump_params *p) { char *unit, fmt[64]; float res; strcpy(fmt, " %7.2f %s %10u %10u %10u %10u %10u\n"); - dp_dump_line(p, line++, + nl_dump_line(p, " Stats: bytes packets drops overlimits" \ " qlen backlog\n"); @@ -230,7 +225,7 @@ int tca_dump_stats(struct rtnl_tca *g, struct nl_dump_params *p, int line) if (*unit == 'B') fmt[11] = '9'; - dp_dump_line(p, line++, fmt, res, unit, + nl_dump_line(p, fmt, res, unit, g->tc_stats[RTNL_TC_PACKETS], g->tc_stats[RTNL_TC_DROPS], g->tc_stats[RTNL_TC_OVERLIMITS], @@ -244,9 +239,7 @@ int tca_dump_stats(struct rtnl_tca *g, struct nl_dump_params *p, int line) if (*unit == 'B') fmt[11] = '9'; - dp_dump_line(p, line++, fmt, res, unit, g->tc_stats[RTNL_TC_RATE_PPS]); - - return line; + nl_dump_line(p, fmt, res, unit, g->tc_stats[RTNL_TC_RATE_PPS]); } int tca_compare(struct nl_object *_a, struct nl_object *_b, @@ -276,10 +269,7 @@ void tca_set_ifindex(struct rtnl_tca *t, int ifindex) int tca_get_ifindex(struct rtnl_tca *t) { - if (t->ce_mask & TCA_ATTR_IFINDEX) - return t->tc_ifindex; - else - return RTNL_LINK_NOT_FOUND; + return t->tc_ifindex; } void tca_set_handle(struct rtnl_tca *t, uint32_t handle) @@ -332,7 +322,8 @@ uint64_t tca_get_stat(struct rtnl_tca *t, int id) return t->tc_stats[id]; } -struct nl_msg *tca_build_msg(struct rtnl_tca *tca, int type, int flags) +int tca_build_msg(struct rtnl_tca *tca, int type, int flags, + struct nl_msg **result) { struct nl_msg *msg; struct tcmsg tchdr = { @@ -344,7 +335,7 @@ struct nl_msg *tca_build_msg(struct rtnl_tca *tca, int type, int flags) msg = nlmsg_alloc_simple(type, flags); if (!msg) - goto nla_put_failure; + return -NLE_NOMEM; if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -352,11 +343,12 @@ struct nl_msg *tca_build_msg(struct rtnl_tca *tca, int type, int flags) if (tca->ce_mask & TCA_ATTR_KIND) NLA_PUT_STRING(msg, TCA_KIND, tca->tc_kind); - return msg; + *result = msg; + return 0; nla_put_failure: nlmsg_free(msg); - return NULL; + return -NLE_MSGSIZE; } /** @endcond */ @@ -425,7 +417,7 @@ int rtnl_tc_calc_cell_log(int cell_size) if ((1 << i) == cell_size) return i; - return nl_errno(EINVAL); + return -NLE_INVAL; } @@ -546,13 +538,13 @@ int rtnl_tc_str2handle(const char *name, uint32_t *res) /* :YYYY */ h = 0; if (':' != *colon) - return -EINVAL; + return -NLE_INVAL; } if (':' == *colon) { /* check if we would lose bits */ if (TC_H_MAJ(h)) - return -ERANGE; + return -NLE_RANGE; h <<= 16; if ('\0' == colon[1]) { @@ -564,10 +556,10 @@ int rtnl_tc_str2handle(const char *name, uint32_t *res) /* check if we overlap with major part */ if (TC_H_MAJ(l)) - return -ERANGE; + return -NLE_RANGE; if ('\0' != *end) - return -EINVAL; + return -NLE_INVAL; *res = (h | l); } @@ -575,7 +567,7 @@ int rtnl_tc_str2handle(const char *name, uint32_t *res) /* XXXXYYYY */ *res = h; } else - return -EINVAL; + return -NLE_INVAL; return 0; } diff --git a/lib/socket.c b/lib/socket.c index aae8f54..8083bbb 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -1,91 +1,17 @@ /* - * lib/socket.c Netlink Socket Handle + * lib/socket.c Netlink Socket * * 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) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** - * @ingroup nl + * @ingroup core * @defgroup socket Socket - * @brief Handle representing a netlink socket. - * - * The socket is represented in a structure called the netlink handle, - * besides the socket, it stores various settings and values related - * to the socket. Every socket handle has a mandatory association with - * a set of callbacks which can be used to modify the behaviour when - * sending/receiving data from the socket. - * - * @par Socket Attributes - * - \b Local \b Port: The local port is a netlink port identifying the - * local endpoint. It is used as source address for outgoing messages - * and will be addressed in replies. It must therefore be unique among - * all userspace applications. When the socket handle is allocated, a - * unique port number is generated automatically in the form of 22 bits - * Process Identifier + 10 bits Arbitary Number. Therefore the library - * is capable of generating 1024 unique local port numbers for every - * process. If more sockets are required, the application has to manage - * port numbers itself using nl_socket_set_local_port(). - * - \b Group \b Subscriptions: A socket can subscribe to any number of - * multicast groups. It will then receive a copy of all messages sent - * to one of the groups. This method is mainly used for event notification. - * Prior to kernel 2.6.14, the group subscription was done via bitmask - * which limited to a total number of groups of 32. With 2.6.14 a new - * method was added based on continous identifiers which supports an - * arbitary number of groups. Both methods are supported, see - * nl_join_groups() respectively nl_socket_add_membership() and - * nl_socket_drop_membership(). - * - \b Peer \b Port: The peer port is a netlink port identifying the - * peer's endpoint. If no peer port is specified, the kernel will try to - * autobind to a socket of the specified netlink family automatically. - * This is very common as typically only one listening socket exists - * on the kernel side. The peer port can be modified using - * nl_socket_set_peer_port(). - * - \b Peer \b Groups: - * - \b File \b Descriptor: The file descriptor of the socket, it can be - * accessed via nl_socket_get_fd() to change socket options or monitor - * activity using poll()/select(). - * - \b Protocol: Once connected, the socket is bound to stick to one - * netlink family. This field is invisible, it is maintained automatically. - * (See nl_connect()) - * - \b Next \b Sequence \b Number: Next available sequence number to be used - * for the next message being sent out. (Initial value: UNIX time when the - * socket was allocated.) Sequence numbers can be used via - * nl_socket_use_seq(). - * - \b Expected \b Sequence \b Number: Expected sequence number in the next - * message received from the socket. (Initial value: Equal to next sequence - * number.) - * - \b Callbacks \b Configuration: - * - * @par 1) Creating the netlink handle - * @code - * struct nl_handle *handle; - * - * // Allocate and initialize a new netlink handle - * handle = nl_handle_alloc(); - * - * // Use nl_socket_get_fd() to fetch the file description, for example to - * // put a socket into non-blocking i/o mode. - * fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK); - * @endcode - * - * @par 2) Group Subscriptions - * @code - * // Event notifications are typically sent to multicast addresses which - * // represented by groups. Join a group to f.e. receive link notifications. - * nl_socket_add_membership(handle, RTNLGRP_LINK); - * @endcode - * - * @par 6) Cleaning up - * @code - * // Finally destroy the netlink handle - * nl_handle_destroy(handle); - * @endcode - * * @{ */ @@ -153,7 +79,7 @@ static void release_local_port(uint32_t port) return; nr = port >> 22; - used_ports_map[nr / 32] &= ~((nr % 32) + 1); + used_ports_map[nr / 32] &= ~(1 << nr % 32); } /** @@ -161,83 +87,78 @@ static void release_local_port(uint32_t port) * @{ */ -static struct nl_handle *__alloc_handle(struct nl_cb *cb) +static struct nl_sock *__alloc_socket(struct nl_cb *cb) { - struct nl_handle *handle; + struct nl_sock *sk; - handle = calloc(1, sizeof(*handle)); - if (!handle) { - nl_errno(ENOMEM); + sk = calloc(1, sizeof(*sk)); + if (!sk) return NULL; - } - handle->h_fd = -1; - handle->h_cb = cb; - handle->h_local.nl_family = AF_NETLINK; - handle->h_peer.nl_family = AF_NETLINK; - handle->h_seq_expect = handle->h_seq_next = time(0); - handle->h_local.nl_pid = generate_local_port(); - if (handle->h_local.nl_pid == UINT_MAX) { - nl_handle_destroy(handle); - nl_error(ENOBUFS, "Out of local ports"); + sk->s_fd = -1; + sk->s_cb = 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); + sk->s_local.nl_pid = generate_local_port(); + if (sk->s_local.nl_pid == UINT_MAX) { + nl_socket_free(sk); return NULL; } - return handle; + return sk; } /** - * Allocate new netlink socket handle. + * Allocate new netlink socket * - * @return Newly allocated netlink socket handle or NULL. + * @return Newly allocated netlink socket or NULL. */ -struct nl_handle *nl_handle_alloc(void) +struct nl_sock *nl_socket_alloc(void) { struct nl_cb *cb; cb = nl_cb_alloc(default_cb); - if (!cb) { - nl_errno(ENOMEM); + if (!cb) return NULL; - } - return __alloc_handle(cb); + return __alloc_socket(cb); } /** - * Allocate new socket handle with custom callbacks + * Allocate new socket with custom callbacks * @arg cb Callback handler * * The reference to the callback handler is taken into account - * automatically, it is released again upon calling nl_handle_destroy(). + * automatically, it is released again upon calling nl_socket_free(). * *@return Newly allocted socket handle or NULL. */ -struct nl_handle *nl_handle_alloc_cb(struct nl_cb *cb) +struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb) { if (cb == NULL) BUG(); - return __alloc_handle(nl_cb_get(cb)); + return __alloc_socket(nl_cb_get(cb)); } /** - * Destroy netlink handle. - * @arg handle Netlink handle. + * Free a netlink socket. + * @arg sk Netlink socket. */ -void nl_handle_destroy(struct nl_handle *handle) +void nl_socket_free(struct nl_sock *sk) { - if (!handle) + if (!sk) return; - if (handle->h_fd >= 0) - close(handle->h_fd); + if (sk->s_fd >= 0) + close(sk->s_fd); - if (!(handle->h_flags & NL_OWN_PORT)) - release_local_port(handle->h_local.nl_pid); + if (!(sk->s_flags & NL_OWN_PORT)) + release_local_port(sk->s_local.nl_pid); - nl_cb_put(handle->h_cb); - free(handle); + nl_cb_put(sk->s_cb); + free(sk); } /** @} */ @@ -255,33 +176,60 @@ static int noop_seq_check(struct nl_msg *msg, void *arg) /** * Disable sequence number checking. - * @arg handle Netlink handle. + * @arg sk Netlink socket. * - * Disables checking of sequence numbers on the netlink handle. This is + * Disables checking of sequence numbers on the netlink socket This is * required to allow messages to be processed which were not requested by * a preceding request message, e.g. netlink events. * * @note This function modifies the NL_CB_SEQ_CHECK configuration in * the callback handle associated with the socket. */ -void nl_disable_sequence_check(struct nl_handle *handle) +void nl_socket_disable_seq_check(struct nl_sock *sk) { - nl_cb_set(handle->h_cb, NL_CB_SEQ_CHECK, + nl_cb_set(sk->s_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, noop_seq_check, NULL); } /** * Use next sequence number - * @arg handle Netlink handle + * @arg sk Netlink socket. * * Uses the next available sequence number and increases the counter * by one for subsequent calls. * * @return Unique serial sequence number */ -unsigned int nl_socket_use_seq(struct nl_handle *handle) +unsigned int nl_socket_use_seq(struct nl_sock *sk) +{ + return sk->s_seq_next++; +} + +/** + * Disable automatic request for ACK + * @arg sk Netlink socket. + * + * The default behaviour of a socket is to request an ACK for + * each message sent to allow for the caller to synchronize to + * the completion of the netlink operation. This function + * disables this behaviour and will result in requests being + * sent which will not have the NLM_F_ACK flag set automatically. + * However, it is still possible for the caller to set the + * NLM_F_ACK flag explicitely. + */ +void nl_socket_disable_auto_ack(struct nl_sock *sk) +{ + sk->s_flags |= NL_NO_AUTO_ACK; +} + +/** + * Enable automatic request for ACK (default) + * @arg sk Netlink socket. + * @see nl_socket_disable_auto_ack + */ +void nl_socket_enable_auto_ack(struct nl_sock *sk) { - return handle->h_seq_next++; + sk->s_flags &= ~NL_NO_AUTO_ACK; } /** @} */ @@ -291,31 +239,31 @@ unsigned int nl_socket_use_seq(struct nl_handle *handle) * @{ */ -uint32_t nl_socket_get_local_port(struct nl_handle *handle) +uint32_t nl_socket_get_local_port(struct nl_sock *sk) { - return handle->h_local.nl_pid; + return sk->s_local.nl_pid; } /** * Set local port of socket - * @arg handle Netlink handle + * @arg sk Netlink socket. * @arg port Local port identifier * * Assigns a local port identifier to the socket. If port is 0 * a unique port identifier will be generated automatically. */ -void nl_socket_set_local_port(struct nl_handle *handle, uint32_t port) +void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port) { if (port == 0) { port = generate_local_port(); - handle->h_flags &= ~NL_OWN_PORT; + sk->s_flags &= ~NL_OWN_PORT; } else { - if (!(handle->h_flags & NL_OWN_PORT)) - release_local_port(handle->h_local.nl_pid); - handle->h_flags |= NL_OWN_PORT; + if (!(sk->s_flags & NL_OWN_PORT)) + release_local_port(sk->s_local.nl_pid); + sk->s_flags |= NL_OWN_PORT; } - handle->h_local.nl_pid = port; + sk->s_local.nl_pid = port; } /** @} */ @@ -326,13 +274,14 @@ void nl_socket_set_local_port(struct nl_handle *handle, uint32_t port) */ /** - * Join a group - * @arg handle Netlink handle + * Join groups + * @arg sk Netlink socket * @arg group Group identifier * - * Joins the specified group using the modern socket option which + * Joins the specified groups using the modern socket option which * is available since kernel version 2.6.14. It allows joining an - * almost arbitary number of groups without limitation. + * almost arbitary number of groups without limitation. The list + * of groups has to be terminated by 0 (%NFNLGRP_NONE). * * Make sure to use the correct group definitions as the older * bitmask definitions for nl_join_groups() are likely to still @@ -340,61 +289,95 @@ void nl_socket_set_local_port(struct nl_handle *handle, uint32_t port) * * @return 0 on sucess or a negative error code. */ -int nl_socket_add_membership(struct nl_handle *handle, int group) +int nl_socket_add_memberships(struct nl_sock *sk, int group, ...) { int err; + va_list ap; - if (handle->h_fd == -1) - return nl_error(EBADFD, "Socket not connected"); + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; - err = setsockopt(handle->h_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, - &group, sizeof(group)); - if (err < 0) - return nl_error(errno, "setsockopt(NETLINK_ADD_MEMBERSHIP) " - "failed"); + va_start(ap, group); + + while (group != 0) { + if (group < 0) + return -NLE_INVAL; + + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &group, sizeof(group)); + if (err < 0) + return -nl_syserr2nlerr(errno); + + group = va_arg(ap, int); + } + + va_end(ap); return 0; } +int nl_socket_add_membership(struct nl_sock *sk, int group) +{ + return nl_socket_add_memberships(sk, group, 0); +} + /** - * Leave a group - * @arg handle Netlink handle + * Leave groups + * @arg sk Netlink socket * @arg group Group identifier * - * Leaves the specified group using the modern socket option - * which is available since kernel version 2.6.14. + * Leaves the specified groups using the modern socket option + * which is available since kernel version 2.6.14. The list of groups + * has to terminated by 0 (%NFNLGRP_NONE). * * @see nl_socket_add_membership * @return 0 on success or a negative error code. */ -int nl_socket_drop_membership(struct nl_handle *handle, int group) +int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...) { int err; + va_list ap; - if (handle->h_fd == -1) - return nl_error(EBADFD, "Socket not connected"); + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; - err = setsockopt(handle->h_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, - &group, sizeof(group)); - if (err < 0) - return nl_error(errno, "setsockopt(NETLINK_DROP_MEMBERSHIP) " - "failed"); + va_start(ap, group); + + while (group != 0) { + if (group < 0) + return -NLE_INVAL; + + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, + &group, sizeof(group)); + if (err < 0) + return -nl_syserr2nlerr(errno); + + group = va_arg(ap, int); + } + + va_end(ap); return 0; } +int nl_socket_drop_membership(struct nl_sock *sk, int group) +{ + return nl_socket_drop_memberships(sk, group, 0); +} + + /** * Join multicast groups (deprecated) - * @arg handle Netlink handle. + * @arg sk Netlink socket. * @arg groups Bitmask of groups to join. * * This function defines the old way of joining multicast group which * has to be done prior to calling nl_connect(). It works on any kernel * version but is very limited as only 32 groups can be joined. */ -void nl_join_groups(struct nl_handle *handle, int groups) +void nl_join_groups(struct nl_sock *sk, int groups) { - handle->h_local.nl_groups |= groups; + sk->s_local.nl_groups |= groups; } @@ -405,14 +388,14 @@ void nl_join_groups(struct nl_handle *handle, int groups) * @{ */ -uint32_t nl_socket_get_peer_port(struct nl_handle *handle) +uint32_t nl_socket_get_peer_port(struct nl_sock *sk) { - return handle->h_peer.nl_pid; + return sk->s_peer.nl_pid; } -void nl_socket_set_peer_port(struct nl_handle *handle, uint32_t port) +void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port) { - handle->h_peer.nl_pid = port; + sk->s_peer.nl_pid = port; } /** @} */ @@ -422,44 +405,44 @@ void nl_socket_set_peer_port(struct nl_handle *handle, uint32_t port) * @{ */ -int nl_socket_get_fd(struct nl_handle *handle) +int nl_socket_get_fd(struct nl_sock *sk) { - return handle->h_fd; + return sk->s_fd; } /** - * Set file descriptor of socket handle to non-blocking state - * @arg handle Netlink socket + * Set file descriptor of socket to non-blocking state + * @arg sk Netlink socket. * * @return 0 on success or a negative error code. */ -int nl_socket_set_nonblocking(struct nl_handle *handle) +int nl_socket_set_nonblocking(struct nl_sock *sk) { - if (handle->h_fd == -1) - return nl_error(EBADFD, "Socket not connected"); + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; - if (fcntl(handle->h_fd, F_SETFL, O_NONBLOCK) < 0) - return nl_error(errno, "fcntl(F_SETFL, O_NONBLOCK) failed"); + if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) + return -nl_syserr2nlerr(errno); return 0; } /** * Enable use of MSG_PEEK when reading from socket - * @arg handle Netlink socket + * @arg sk Netlink socket. */ -void nl_socket_enable_msg_peek(struct nl_handle *handle) +void nl_socket_enable_msg_peek(struct nl_sock *sk) { - handle->h_flags |= NL_MSG_PEEK; + sk->s_flags |= NL_MSG_PEEK; } /** * Disable use of MSG_PEEK when reading from socket - * @arg handle Netlink socket + * @arg sk Netlink socket. */ -void nl_socket_disable_msg_peek(struct nl_handle *handle) +void nl_socket_disable_msg_peek(struct nl_sock *sk) { - handle->h_flags &= ~NL_MSG_PEEK; + sk->s_flags &= ~NL_MSG_PEEK; } /** @} */ @@ -469,20 +452,20 @@ void nl_socket_disable_msg_peek(struct nl_handle *handle) * @{ */ -struct nl_cb *nl_socket_get_cb(struct nl_handle *handle) +struct nl_cb *nl_socket_get_cb(struct nl_sock *sk) { - return nl_cb_get(handle->h_cb); + return nl_cb_get(sk->s_cb); } -void nl_socket_set_cb(struct nl_handle *handle, struct nl_cb *cb) +void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb) { - nl_cb_put(handle->h_cb); - handle->h_cb = nl_cb_get(cb); + nl_cb_put(sk->s_cb); + sk->s_cb = nl_cb_get(cb); } /** * Modify the callback handler associated to the socket - * @arg handle netlink handle + * @arg sk Netlink socket. * @arg type which type callback to set * @arg kind kind of callback * @arg func callback function @@ -490,11 +473,11 @@ void nl_socket_set_cb(struct nl_handle *handle, struct nl_cb *cb) * * @see nl_cb_set */ -int nl_socket_modify_cb(struct nl_handle *handle, enum nl_cb_type type, +int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type, enum nl_cb_kind kind, nl_recvmsg_msg_cb_t func, void *arg) { - return nl_cb_set(handle->h_cb, type, kind, func, arg); + return nl_cb_set(sk->s_cb, type, kind, func, arg); } /** @} */ @@ -505,19 +488,19 @@ int nl_socket_modify_cb(struct nl_handle *handle, enum nl_cb_type type, */ /** - * Set socket buffer size of netlink handle. - * @arg handle Netlink handle. + * Set socket buffer size of netlink socket. + * @arg sk Netlink socket. * @arg rxbuf New receive socket buffer size in bytes. * @arg txbuf New transmit socket buffer size in bytes. * - * Sets the socket buffer size of a netlink handle to the specified + * Sets the socket buffer size of a netlink socket to the specified * values \c rxbuf and \c txbuf. Providing a value of \c 0 assumes a * good default value. * * @note It is not required to call this function prior to nl_connect(). * @return 0 on sucess or a negative error code. */ -int nl_set_buffer_size(struct nl_handle *handle, int rxbuf, int txbuf) +int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf) { int err; @@ -527,69 +510,69 @@ int nl_set_buffer_size(struct nl_handle *handle, int rxbuf, int txbuf) if (txbuf <= 0) txbuf = 32768; - if (handle->h_fd == -1) - return nl_error(EBADFD, "Socket not connected"); + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; - err = setsockopt(handle->h_fd, SOL_SOCKET, SO_SNDBUF, + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF, &txbuf, sizeof(txbuf)); if (err < 0) - return nl_error(errno, "setsockopt(SO_SNDBUF) failed"); + return -nl_syserr2nlerr(errno); - err = setsockopt(handle->h_fd, SOL_SOCKET, SO_RCVBUF, + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF, &rxbuf, sizeof(rxbuf)); if (err < 0) - return nl_error(errno, "setsockopt(SO_RCVBUF) failed"); + return -nl_syserr2nlerr(errno); - handle->h_flags |= NL_SOCK_BUFSIZE_SET; + sk->s_flags |= NL_SOCK_BUFSIZE_SET; return 0; } /** - * Enable/disable credential passing on netlink handle. - * @arg handle Netlink handle + * Enable/disable credential passing on netlink socket. + * @arg sk Netlink socket. * @arg state New state (0 - disabled, 1 - enabled) * * @return 0 on success or a negative error code */ -int nl_set_passcred(struct nl_handle *handle, int state) +int nl_socket_set_passcred(struct nl_sock *sk, int state) { int err; - if (handle->h_fd == -1) - return nl_error(EBADFD, "Socket not connected"); + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; - err = setsockopt(handle->h_fd, SOL_SOCKET, SO_PASSCRED, + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED, &state, sizeof(state)); if (err < 0) - return nl_error(errno, "setsockopt(SO_PASSCRED) failed"); + return -nl_syserr2nlerr(errno); if (state) - handle->h_flags |= NL_SOCK_PASSCRED; + sk->s_flags |= NL_SOCK_PASSCRED; else - handle->h_flags &= ~NL_SOCK_PASSCRED; + sk->s_flags &= ~NL_SOCK_PASSCRED; return 0; } /** * Enable/disable receival of additional packet information - * @arg handle Netlink handle + * @arg sk Netlink socket. * @arg state New state (0 - disabled, 1 - enabled) * * @return 0 on success or a negative error code */ -int nl_socket_recv_pktinfo(struct nl_handle *handle, int state) +int nl_socket_recv_pktinfo(struct nl_sock *sk, int state) { int err; - if (handle->h_fd == -1) - return nl_error(EBADFD, "Socket not connected"); + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; - err = setsockopt(handle->h_fd, SOL_NETLINK, NETLINK_PKTINFO, + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_PKTINFO, &state, sizeof(state)); if (err < 0) - return nl_error(errno, "setsockopt(NETLINK_PKTINFO) failed"); + return -nl_syserr2nlerr(errno); return 0; } diff --git a/lib/utils.c b/lib/utils.c index b5b457a..e1fdae1 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -6,10 +6,11 @@ * License as published by the Free Software Foundation version 2.1 * of the License. * - * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> + * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> */ /** + * @ingroup core * @defgroup utils Utilities * @{ */ @@ -25,7 +26,7 @@ int nl_debug = 0; struct nl_dump_params nl_debug_dp = { - .dp_type = NL_DUMP_FULL, + .dp_type = NL_DUMP_DETAILS, }; static void __init nl_debug_init(void) @@ -41,48 +42,6 @@ static void __init nl_debug_init(void) nl_debug_dp.dp_fd = stderr; } -/** - * @name Error Code Helpers - * @{ - */ - -static char *errbuf; -static int nlerrno; - -/** @cond SKIP */ -int __nl_error(int err, const char *file, unsigned int line, const char *func, - const char *fmt, ...) -{ - char *user_err; - va_list args; - - if (errbuf) { - free(errbuf); - errbuf = NULL; - } - - nlerrno = err; - - if (fmt) { - va_start(args, fmt); - vasprintf(&user_err, fmt, args); - va_end(args); - } - -#ifdef VERBOSE_ERRORS - asprintf(&errbuf, "%s:%u:%s: %s (errno = %s)", - file, line, func, fmt ? user_err : "", strerror(err)); -#else - asprintf(&errbuf, "%s (errno = %s)", - fmt ? user_err : "", strerror(err)); -#endif - - if (fmt) - free(user_err); - - return -err; -} - int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *)) { FILE *fd; @@ -90,8 +49,7 @@ int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *)) fd = fopen(path, "r"); if (fd == NULL) - return nl_error(errno, "Unable to open file %s for reading", - path); + return -nl_syserr2nlerr(errno); while (fgets(buf, sizeof(buf), fd)) { int goodlen, err; @@ -103,17 +61,17 @@ int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *)) num = strtol(buf, &end, 0); if (end == buf) - return nl_error(EINVAL, "Parsing error"); + return -NLE_INVAL; if (num == LONG_MIN || num == LONG_MAX) - return nl_error(errno, "Number of out range"); + return -NLE_RANGE; while (*end == ' ' || *end == '\t') end++; goodlen = strcspn(end, "#\r\n\t "); if (goodlen == 0) - return nl_error(EINVAL, "Empty string"); + return -NLE_INVAL; end[goodlen] = '\0'; @@ -127,49 +85,6 @@ int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *)) return 0; } -/** @endcond */ - -int nl_get_errno(void) -{ - return nlerrno; -} - - -/** - * Return error message for an error code - * @return error message - */ -char *nl_geterror(void) -{ - if (errbuf) - return errbuf; - - if (nlerrno) - return strerror(nlerrno); - - return "Sucess\n"; -} - -/** - * Print a libnl error message - * @arg s error message prefix - * - * Prints the error message of the call that failed last. - * - * If s is not NULL and *s is not a null byte the argument - * string is printed, followed by a colon and a blank. Then - * the error message and a new-line. - */ -void nl_perror(const char *s) -{ - if (s && *s) - fprintf(stderr, "%s: %s\n", s, nl_geterror()); - else - fprintf(stderr, "%s\n", nl_geterror()); -} - -/** @} */ - /** * @name Unit Pretty-Printing * @{ @@ -285,7 +200,7 @@ long nl_size2int(const char *str) char *p; long l = strtol(str, &p, 0); if (p == str) - return -1; + return -NLE_INVAL; if (*p) { if (!strcasecmp(p, "kb") || !strcasecmp(p, "k")) @@ -303,7 +218,7 @@ long nl_size2int(const char *str) else if (!strcasecmp(p, "bit")) l /= 8; else if (strcasecmp(p, "b") != 0) - return -1; + return -NLE_INVAL; } return l; @@ -328,16 +243,16 @@ long nl_prob2int(const char *str) double d = strtod(str, &p); if (p == str) - return -1; + return -NLE_INVAL; if (d > 1.0) d /= 100.0f; if (d > 1.0f || d < 0.0f) - return -1; + return -NLE_RANGE; if (*p && strcmp(p, "%") != 0) - return -1; + return -NLE_INVAL; return rint(d * NL_PROB_MAX); } @@ -370,7 +285,7 @@ static void __init get_psched_settings(void) { char name[FILENAME_MAX]; FILE *fd; - int got_hz = 0, got_tick = 0; + int got_hz = 0; if (getenv("HZ")) { long hz = strtol(getenv("HZ"), NULL, 0); @@ -386,28 +301,25 @@ static void __init get_psched_settings(void) if (getenv("TICKS_PER_USEC")) { double t = strtod(getenv("TICKS_PER_USEC"), NULL); - ticks_per_usec = t; - got_tick = 1; } + else { + if (getenv("PROC_NET_PSCHED")) + snprintf(name, sizeof(name), "%s", getenv("PROC_NET_PSCHED")); + else if (getenv("PROC_ROOT")) + snprintf(name, sizeof(name), "%s/net/psched", + getenv("PROC_ROOT")); + else + strncpy(name, "/proc/net/psched", sizeof(name) - 1); - - if (getenv("PROC_NET_PSCHED")) - snprintf(name, sizeof(name), "%s", getenv("PROC_NET_PSCHED")); - else if (getenv("PROC_ROOT")) - snprintf(name, sizeof(name), "%s/net/psched", - getenv("PROC_ROOT")); - else - strncpy(name, "/proc/net/psched", sizeof(name) - 1); - - if ((fd = fopen(name, "r"))) { - uint32_t tick, us, nom; - int r = fscanf(fd, "%08x%08x%08x%*08x", &tick, &us, &nom); - - if (4 == r && nom == 1000000 && !got_tick) + if ((fd = fopen(name, "r"))) { + uint32_t tick, us; + /* the file contains 4 hexadecimals, but we just use + the first two of them */ + fscanf(fd, "%08x %08x", &tick, &us); ticks_per_usec = (double)tick/(double)us; - - fclose(fd); + fclose(fd); + } } } @@ -442,25 +354,40 @@ uint32_t nl_ticks2us(uint32_t ticks) return ticks / ticks_per_usec; } -long nl_time2int(const char *str) +int nl_str2msec(const char *str, uint64_t *result) { + uint64_t total = 0, l; + int plen; char *p; - long l = strtol(str, &p, 0); - if (p == str) - return -1; - if (*p) { - if (!strcasecmp(p, "min") == 0 || !strcasecmp(p, "m")) - l *= 60; - else if (!strcasecmp(p, "hour") || !strcasecmp(p, "h")) - l *= 60*60; - else if (!strcasecmp(p, "day") || !strcasecmp(p, "d")) - l *= 60*60*24; - else if (strcasecmp(p, "s") != 0) - return -1; - } + do { + l = strtoul(str, &p, 0); + if (p == str) + return -NLE_INVAL; + else if (*p) { + plen = strcspn(p, " \t"); + + if (!plen) + total += l; + else if (!strncasecmp(p, "sec", plen)) + total += (l * 1000); + else if (!strncasecmp(p, "min", plen)) + total += (l * 1000*60); + else if (!strncasecmp(p, "hour", plen)) + total += (l * 1000*60*60); + else if (!strncasecmp(p, "day", plen)) + total += (l * 1000*60*60*24); + else + return -NLE_INVAL; + + str = p + plen; + } else + total += l; + } while (*str && *p); + + *result = total; - return l; + return 0; } /** @@ -505,6 +432,47 @@ char * nl_msec2str(uint64_t msec, char *buf, size_t len) /** @} */ /** + * @name Netlink Family Translations + * @{ + */ + +static struct trans_tbl nlfamilies[] = { + __ADD(NETLINK_ROUTE,route) + __ADD(NETLINK_USERSOCK,usersock) + __ADD(NETLINK_FIREWALL,firewall) + __ADD(NETLINK_INET_DIAG,inetdiag) + __ADD(NETLINK_NFLOG,nflog) + __ADD(NETLINK_XFRM,xfrm) + __ADD(NETLINK_SELINUX,selinux) + __ADD(NETLINK_ISCSI,iscsi) + __ADD(NETLINK_AUDIT,audit) + __ADD(NETLINK_FIB_LOOKUP,fib_lookup) + __ADD(NETLINK_CONNECTOR,connector) + __ADD(NETLINK_NETFILTER,netfilter) + __ADD(NETLINK_IP6_FW,ip6_fw) + __ADD(NETLINK_DNRTMSG,dnrtmsg) + __ADD(NETLINK_KOBJECT_UEVENT,kobject_uevent) + __ADD(NETLINK_GENERIC,generic) + __ADD(NETLINK_SCSITRANSPORT,scsitransport) + __ADD(NETLINK_ECRYPTFS,ecryptfs) +}; + +char * nl_nlfamily2str(int family, char *buf, size_t size) +{ + return __type2str(family, buf, size, nlfamilies, + ARRAY_SIZE(nlfamilies)); +} + +int nl_str2nlfamily(const char *name) +{ + return __str2type(name, nlfamilies, ARRAY_SIZE(nlfamilies)); +} + +/** + * @} + */ + +/** * @name Link Layer Protocol Translations * @{ */ @@ -699,7 +667,7 @@ int nl_str2ip_proto(const char *name) l = strtoul(name, &end, 0); if (l == ULONG_MAX || *end != '\0') - return -1; + return -NLE_OBJ_NOTFOUND; return (int) l; } @@ -714,7 +682,6 @@ int nl_str2ip_proto(const char *name) /** * Handle a new line while dumping * @arg params Dumping parameters - * @arg line Number of lines dumped already. * * This function must be called before dumping any onto a * new line. It will ensure proper prefixing as specified @@ -722,8 +689,10 @@ int nl_str2ip_proto(const char *name) * * @note This function will NOT dump any newlines itself */ -void nl_new_line(struct nl_dump_params *params, int line) +void nl_new_line(struct nl_dump_params *params) { + params->dp_line++; + if (params->dp_prefix) { int i; for (i = 0; i < params->dp_prefix; i++) { @@ -737,9 +706,27 @@ void nl_new_line(struct nl_dump_params *params, int line) } if (params->dp_nl_cb) - params->dp_nl_cb(params, line); + params->dp_nl_cb(params, params->dp_line); } +static void dump_one(struct nl_dump_params *parms, const char *fmt, + va_list args) +{ + if (parms->dp_fd) + vfprintf(parms->dp_fd, fmt, args); + else if (parms->dp_buf || parms->dp_cb) { + char *buf = NULL; + vasprintf(&buf, fmt, args); + if (parms->dp_cb) + parms->dp_cb(parms, buf); + else + strncat(parms->dp_buf, buf, + parms->dp_buflen - strlen(parms->dp_buf) - 1); + free(buf); + } +} + + /** * Dump a formatted character string * @arg params Dumping parameters @@ -754,10 +741,195 @@ void nl_dump(struct nl_dump_params *params, const char *fmt, ...) va_list args; va_start(args, fmt); - __dp_dump(params, fmt, args); + dump_one(params, fmt, args); va_end(args); } +void nl_dump_line(struct nl_dump_params *parms, const char *fmt, ...) +{ + va_list args; + + nl_new_line(parms); + + va_start(args, fmt); + dump_one(parms, fmt, args); + va_end(args); +} + + /** @} */ +/** @cond SKIP */ + +int __trans_list_add(int i, const char *a, struct nl_list_head *head) +{ + struct trans_list *tl; + + tl = calloc(1, sizeof(*tl)); + if (!tl) + return -NLE_NOMEM; + + tl->i = i; + tl->a = strdup(a); + + nl_list_add_tail(&tl->list, head); + + return 0; +} + +void __trans_list_clear(struct nl_list_head *head) +{ + struct trans_list *tl, *next; + + nl_list_for_each_entry_safe(tl, next, head, list) { + free(tl->a); + free(tl); + } +} + +char *__type2str(int type, char *buf, size_t len, struct trans_tbl *tbl, + size_t tbl_len) +{ + int i; + for (i = 0; i < tbl_len; i++) { + if (tbl[i].i == type) { + snprintf(buf, len, "%s", tbl[i].a); + return buf; + } + } + + snprintf(buf, len, "0x%x", type); + return buf; +} + +char *__list_type2str(int type, char *buf, size_t len, + struct nl_list_head *head) +{ + struct trans_list *tl; + + nl_list_for_each_entry(tl, head, list) { + if (tl->i == type) { + snprintf(buf, len, "%s", tl->a); + return buf; + } + } + + snprintf(buf, len, "0x%x", type); + return buf; +} + +char *__flags2str(int flags, char *buf, size_t len, + struct trans_tbl *tbl, size_t tbl_len) +{ + int i; + int tmp = flags; + + memset(buf, 0, len); + + for (i = 0; i < tbl_len; i++) { + if (tbl[i].i & tmp) { + tmp &= ~tbl[i].i; + strncat(buf, tbl[i].a, len - strlen(buf) - 1); + if ((tmp & flags)) + strncat(buf, ",", len - strlen(buf) - 1); + } + } + + return buf; +} + +int __str2type(const char *buf, struct trans_tbl *tbl, size_t tbl_len) +{ + unsigned long l; + char *end; + int i; + + if (*buf == '\0') + return -NLE_INVAL; + + for (i = 0; i < tbl_len; i++) + if (!strcasecmp(tbl[i].a, buf)) + return tbl[i].i; + + l = strtoul(buf, &end, 0); + if (l == ULONG_MAX || *end != '\0') + return -NLE_OBJ_NOTFOUND; + + return (int) l; +} + +int __list_str2type(const char *buf, struct nl_list_head *head) +{ + struct trans_list *tl; + unsigned long l; + char *end; + + if (*buf == '\0') + return -NLE_INVAL; + + nl_list_for_each_entry(tl, head, list) { + if (!strcasecmp(tl->a, buf)) + return tl->i; + } + + l = strtoul(buf, &end, 0); + if (l == ULONG_MAX || *end != '\0') + return -NLE_OBJ_NOTFOUND; + + return (int) l; +} + +int __str2flags(const char *buf, struct trans_tbl *tbl, size_t tbl_len) +{ + int i, flags = 0, len; + char *p = (char *) buf, *t; + + for (;;) { + if (*p == ' ') + p++; + + t = strchr(p, ','); + len = t ? t - p : strlen(p); + for (i = 0; i < tbl_len; i++) + if (!strncasecmp(tbl[i].a, p, len)) + flags |= tbl[i].i; + + if (!t) + return flags; + + p = ++t; + } + + return 0; +} + +void dump_from_ops(struct nl_object *obj, struct nl_dump_params *params) +{ + int type = params->dp_type; + + if (type < 0 || type > NL_DUMP_MAX) + BUG(); + + params->dp_line = 0; + + if (params->dp_dump_msgtype) { +#if 0 + /* XXX */ + char buf[64]; + + dp_dump_line(params, 0, "%s ", + nl_cache_mngt_type2name(obj->ce_ops, + obj->ce_ops->co_protocol, + obj->ce_msgtype, + buf, sizeof(buf))); +#endif + params->dp_pre_dump = 1; + } + + if (obj->ce_ops->oo_dump[type]) + obj->ce_ops->oo_dump[type](obj, params); +} + +/** @endcond */ + /** @} */ |