diff options
Diffstat (limited to 'lib/route')
42 files changed, 4031 insertions, 2799 deletions
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; } |