/* * lib/route/neigh.c Neighbours * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation version 2.1 * of the License. * * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch> */ /** * @ingroup rtnl * @defgroup neigh Neighbours * @brief * * The neighbour table establishes bindings between protocol addresses and * link layer addresses for hosts sharing the same physical link. This * module allows you to access and manipulate the content of these tables. * * @par Neighbour States * @code * NUD_INCOMPLETE * NUD_REACHABLE * NUD_STALE * NUD_DELAY * NUD_PROBE * NUD_FAILED * NUD_NOARP * NUD_PERMANENT * @endcode * * @par Neighbour Flags * @code * NTF_PROXY * NTF_ROUTER * @endcode * * @par Neighbour Identification * A neighbour is uniquely identified by the attributes listed below, whenever * you refer to an existing neighbour all of the attributes must be set. * Neighbours from caches automatically have all required attributes set. * - interface index (rtnl_neigh_set_ifindex()) * - destination address (rtnl_neigh_set_dst()) * * @par Changeable Attributes * \anchor neigh_changeable * - state (rtnl_neigh_set_state()) * - link layer address (rtnl_neigh_set_lladdr()) * * @par Required Caches for Dumping * In order to dump neighbour attributes you must provide the following * caches via nl_cache_provide() * - link cache holding all links * * @par TODO * - Document proxy settings * - Document states and their influence * * @par 1) Retrieving information about configured neighbours * @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); * * // Neighbours can then be looked up by the interface and destination * // address: * struct rtnl_neigh *neigh = rtnl_neigh_get(cache, ifindex, dst_addr); * * // After successful usage, the object must be given back to the cache * rtnl_neigh_put(neigh); * @endcode * * @par 2) Adding new neighbours * @code * // Allocate an empty neighbour handle to be filled out with the attributes * // of the new neighbour. * struct rtnl_neigh *neigh = rtnl_neigh_alloc(); * * // Fill out the attributes of the new neighbour * rtnl_neigh_set_ifindex(neigh, ifindex); * rtnl_neigh_set_dst(neigh, dst_addr); * rtnl_neigh_set_state(neigh, rtnl_neigh_str2state("permanent")); * * // 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_neigh_build_add_request() * // to be sent out using nl_send_auto_complete(). * rtnl_neigh_add(nl_handle, neigh, NLM_F_REPLACE); * * // Free the memory * rtnl_neigh_put(neigh); * @endcode * * @par 3) Deleting an existing neighbour * @code * // Allocate an empty neighbour object to be filled out with the attributes * // matching the neighbour to be deleted. Alternatively a fully equipped * // neighbour object out of a cache can be used instead. * struct rtnl_neigh *neigh = rtnl_neigh_alloc(); * * // Neighbours are uniquely identified by their interface index and * // destination address, you may fill out other attributes but they * // will have no influence. * rtnl_neigh_set_ifindex(neigh, ifindex); * rtnl_neigh_set_dst(neigh, dst_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_neigh_build_delete_request() * // to be sent out using nl_send_auto_complete(). * rtnl_neigh_delete(handle, neigh, 0); * * // Free the memory * rtnl_neigh_put(neigh); * @endcode * * @par 4) Changing neighbour attributes * @code * // Allocate an empty neighbour object to be filled out with the attributes * // matching the neighbour to be changed and the new parameters. Alternatively * // a fully equipped modified neighbour object out of a cache can be used. * struct rtnl_neigh *neigh = rtnl_neigh_alloc(); * * // Identify the neighbour to be changed by its interface index and * // destination address * rtnl_neigh_set_ifindex(neigh, ifindex); * rtnl_neigh_set_dst(neigh, dst_addr); * * // The link layer address may be modified, if so it is wise to change * // its state to "permanent" in order to avoid having it overwritten. * rtnl_neigh_set_lladdr(neigh, lladdr); * * // Secondly the state can be modified allowing normal neighbours to be * // converted into permanent entries or to manually confirm a neighbour. * rtnl_neigh_set_state(neigh, state); * * // 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_neigh_build_change_request() * // to be sent out using nl_send_auto_complete(). * rtnl_neigh_change(handle, neigh, 0); * * // Free the memory * rtnl_neigh_put(neigh); * @endcode * @{ */ #include <netlink-local.h> #include <netlink/netlink.h> #include <netlink/utils.h> #include <netlink/route/rtnl.h> #include <netlink/route/neighbour.h> #include <netlink/route/link.h> /** @cond SKIP */ #define NEIGH_ATTR_FLAGS 0x01 #define NEIGH_ATTR_STATE 0x02 #define NEIGH_ATTR_LLADDR 0x04 #define NEIGH_ATTR_DST 0x08 #define NEIGH_ATTR_CACHEINFO 0x10 #define NEIGH_ATTR_IFINDEX 0x20 #define NEIGH_ATTR_FAMILY 0x40 #define NEIGH_ATTR_TYPE 0x80 #define NEIGH_ATTR_PROBES 0x100 static struct nl_cache_ops rtnl_neigh_ops; static struct nl_object_ops neigh_obj_ops; /** @endcond */ static void neigh_free_data(struct nl_object *c) { struct rtnl_neigh *neigh = nl_object_priv(c); if (!neigh) return; nl_addr_put(neigh->n_lladdr); nl_addr_put(neigh->n_dst); } static int neigh_clone(struct nl_object *_dst, struct nl_object *_src) { struct rtnl_neigh *dst = nl_object_priv(_dst); struct rtnl_neigh *src = nl_object_priv(_src); if (src->n_lladdr) if (!(dst->n_lladdr = nl_addr_clone(src->n_lladdr))) goto errout; if (src->n_dst) if (!(dst->n_dst = nl_addr_clone(src->n_dst))) goto errout; return 0; errout: return nl_get_errno(); } static int neigh_compare(struct nl_object *_a, struct nl_object *_b, uint32_t attrs, int flags) { struct rtnl_neigh *a = (struct rtnl_neigh *) _a; struct rtnl_neigh *b = (struct rtnl_neigh *) _b; int diff = 0; #define NEIGH_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NEIGH_ATTR_##ATTR, a, b, EXPR) diff |= NEIGH_DIFF(IFINDEX, a->n_ifindex != b->n_ifindex); diff |= NEIGH_DIFF(FAMILY, a->n_family != b->n_family); diff |= NEIGH_DIFF(TYPE, a->n_type != b->n_type); 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) { diff |= NEIGH_DIFF(STATE, (a->n_state ^ b->n_state) & b->n_state_mask); diff |= NEIGH_DIFF(FLAGS, (a->n_flags ^ b->n_flags) & b->n_flag_mask); } else { diff |= NEIGH_DIFF(STATE, a->n_state != b->n_state); diff |= NEIGH_DIFF(FLAGS, a->n_flags != b->n_flags); } #undef NEIGH_DIFF return diff; } static struct trans_tbl neigh_attrs[] = { __ADD(NEIGH_ATTR_FLAGS, flags) __ADD(NEIGH_ATTR_STATE, state) __ADD(NEIGH_ATTR_LLADDR, lladdr) __ADD(NEIGH_ATTR_DST, dst) __ADD(NEIGH_ATTR_CACHEINFO, cacheinfo) __ADD(NEIGH_ATTR_IFINDEX, ifindex) __ADD(NEIGH_ATTR_FAMILY, family) __ADD(NEIGH_ATTR_TYPE, type) __ADD(NEIGH_ATTR_PROBES, probes) }; static char *neigh_attrs2str(int attrs, char *buf, size_t len) { return __flags2str(attrs, buf, len, neigh_attrs, ARRAY_SIZE(neigh_attrs)); } static struct nla_policy neigh_policy[NDA_MAX+1] = { [NDA_CACHEINFO] = { .minlen = sizeof(struct nda_cacheinfo) }, [NDA_PROBES] = { .type = NLA_U32 }, }; static int neigh_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct nlmsghdr *n, struct nl_parser_param *pp) { struct rtnl_neigh *neigh; struct nlattr *tb[NDA_MAX + 1]; struct ndmsg *nm; int err; neigh = rtnl_neigh_alloc(); if (!neigh) { err = nl_errno(ENOMEM); goto errout; } neigh->ce_msgtype = n->nlmsg_type; nm = nlmsg_data(n); err = nlmsg_parse(n, sizeof(*nm), tb, NDA_MAX, neigh_policy); if (err < 0) goto errout; neigh->n_family = nm->ndm_family; neigh->n_ifindex = nm->ndm_ifindex; neigh->n_state = nm->ndm_state; neigh->n_flags = nm->ndm_flags; neigh->n_type = nm->ndm_type; neigh->ce_mask |= (NEIGH_ATTR_FAMILY | NEIGH_ATTR_IFINDEX | NEIGH_ATTR_STATE | NEIGH_ATTR_FLAGS | NEIGH_ATTR_TYPE); if (tb[NDA_LLADDR]) { neigh->n_lladdr = nla_get_addr(tb[NDA_LLADDR], AF_UNSPEC); if (!neigh->n_lladdr) 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) goto errout; neigh->ce_mask |= NEIGH_ATTR_DST; } if (tb[NDA_CACHEINFO]) { struct nda_cacheinfo *ci = nla_data(tb[NDA_CACHEINFO]); neigh->n_cacheinfo.nci_confirmed = ci->ndm_confirmed; neigh->n_cacheinfo.nci_used = ci->ndm_used; neigh->n_cacheinfo.nci_updated = ci->ndm_updated; neigh->n_cacheinfo.nci_refcnt = ci->ndm_refcnt; neigh->ce_mask |= NEIGH_ATTR_CACHEINFO; } if (tb[NDA_PROBES]) { neigh->n_probes = nla_get_u32(tb[NDA_PROBES]); neigh->ce_mask |= NEIGH_ATTR_PROBES; } 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) { 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) { char dst[INET6_ADDRSTRLEN+5], lladdr[INET6_ADDRSTRLEN+5]; struct rtnl_neigh *n = (struct rtnl_neigh *) a; struct nl_cache *link_cache; char state[128], flags[64]; link_cache = nl_cache_mngt_require("route/link"); dp_dump(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst))); if (link_cache) dp_dump(p, "dev %s ", rtnl_link_i2name(link_cache, n->n_ifindex, state, sizeof(state))); else dp_dump(p, "dev %d ", n->n_ifindex); if (n->ce_mask & NEIGH_ATTR_LLADDR) dp_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); if (flags[0]) dp_dump(p, "%s%s", state[0] ? "," : "<", flags); if (state[0] || flags[0]) dp_dump(p, ">"); dp_dump(p, "\n"); return 1; } static int neigh_dump_full(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); dp_dump_line(p, line++, " 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) { 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; } static int 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_af2str(neigh->n_family, buf, sizeof(buf))); if (neigh->ce_mask & NEIGH_ATTR_LLADDR) dp_dump_line(p, line++, "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_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); link_cache = nl_cache_mngt_require("route/link"); if (link_cache) dp_dump_line(p, line++, "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); if (neigh->ce_mask & NEIGH_ATTR_TYPE) dp_dump_line(p, line++, "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); rtnl_neigh_state2str(neigh->n_state, buf, sizeof(buf)); if (buf[0]) dp_dump_line(p, line++, "NEIGH_STATE=%s\n", buf); return line; } /** * @name Neighbour Object Allocation/Freeage * @{ */ struct rtnl_neigh *rtnl_neigh_alloc(void) { return (struct rtnl_neigh *) nl_object_alloc(&neigh_obj_ops); } void rtnl_neigh_put(struct rtnl_neigh *neigh) { nl_object_put((struct nl_object *) neigh); } /** @} */ /** * @name Neighbour Cache Managament * @{ */ /** * Build a neighbour cache including all neighbours currently configured in the kernel. * @arg handle netlink handle * * 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. */ struct nl_cache *rtnl_neigh_alloc_cache(struct nl_handle *handle) { 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; } /** * Look up a neighbour by interface index and destination address * @arg cache neighbour cache * @arg ifindex interface index the neighbour is on * @arg dst destination address of the neighbour * @return neighbour handle or NULL if no match was found. */ struct rtnl_neigh * rtnl_neigh_get(struct nl_cache *cache, int ifindex, struct nl_addr *dst) { struct rtnl_neigh *neigh; nl_list_for_each_entry(neigh, &cache->c_items, ce_list) { if (neigh->n_ifindex == ifindex && !nl_addr_cmp(neigh->n_dst, dst)) { nl_object_get((struct nl_object *) neigh); return neigh; } } return NULL; } /** @} */ /** * @name Neighbour Addition * @{ */ static struct nl_msg * build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, int flags) { 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_STATE) nhdr.ndm_state = tmpl->n_state; msg = nlmsg_alloc_simple(cmd, flags); if (!msg) return NULL; if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0) goto nla_put_failure; NLA_PUT_ADDR(msg, NDA_DST, tmpl->n_dst); if (tmpl->ce_mask & NEIGH_ATTR_LLADDR) NLA_PUT_ADDR(msg, NDA_LLADDR, tmpl->n_lladdr); return msg; nla_put_failure: nlmsg_free(msg); return NULL; } /** * Build netlink request message to add a new neighbour * @arg tmpl template with data of new neighbour * @arg flags additional netlink message flags * * Builds a new netlink message requesting a addition of a new * neighbour. 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. \a tmpl must contain the attributes of the new * neighbour set via \c rtnl_neigh_set_* functions. * * The following attributes must be set in the template: * - Interface index (rtnl_neigh_set_ifindex()) * - State (rtnl_neigh_set_state()) * - Destination address (rtnl_neigh_set_dst()) * - Link layer address (rtnl_neigh_set_lladdr()) * * @return The netlink message */ struct nl_msg * rtnl_neigh_build_add_request(struct rtnl_neigh *tmpl, int flags) { return build_neigh_msg(tmpl, RTM_NEWNEIGH, NLM_F_CREATE | flags); } /** * Add a new neighbour * @arg handle netlink handle * @arg tmpl template with requested changes * @arg flags additional netlink message flags * * Builds a netlink message by calling rtnl_neigh_build_add_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. * * The following attributes must be set in the template: * - Interface index (rtnl_neigh_set_ifindex()) * - State (rtnl_neigh_set_state()) * - Destination address (rtnl_neigh_set_dst()) * - Link layer address (rtnl_neigh_set_lladdr()) * * @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 err; struct nl_msg *msg; msg = rtnl_neigh_build_add_request(tmpl, 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); } /** @} */ /** * @name Neighbour Deletion * @{ */ /** * Build a netlink request message to delete a neighbour * @arg neigh neighbour to delete * @arg flags additional netlink message flags * * Builds a new netlink message requesting a deletion of a neighbour. * 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. \a neigh must point to an existing * neighbour. * * @return The netlink message */ struct nl_msg *rtnl_neigh_build_delete_request(struct rtnl_neigh *neigh, int flags) { return build_neigh_msg(neigh, RTM_DELNEIGH, flags); } /** * Delete a neighbour * @arg handle netlink handle * @arg neigh neighbour to delete * @arg flags additional netlink message flags * * Builds a netlink message by calling rtnl_neigh_build_delete_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. */ int rtnl_neigh_delete(struct nl_handle *handle, struct rtnl_neigh *neigh, int flags) { int err; struct nl_msg *msg; msg = rtnl_neigh_build_delete_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); } /** @} */ /** * @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); } /** @} */ /** * @name Neighbour States Translations * @{ */ static struct trans_tbl neigh_states[] = { __ADD(NUD_INCOMPLETE, incomplete) __ADD(NUD_REACHABLE, reachable) __ADD(NUD_STALE, stale) __ADD(NUD_DELAY, delay) __ADD(NUD_PROBE, probe) __ADD(NUD_FAILED, failed) __ADD(NUD_NOARP, norarp) __ADD(NUD_PERMANENT, permanent) }; char * rtnl_neigh_state2str(int state, char *buf, size_t len) { return __flags2str(state, buf, len, neigh_states, ARRAY_SIZE(neigh_states)); } int rtnl_neigh_str2state(const char *name) { return __str2type(name, neigh_states, ARRAY_SIZE(neigh_states)); } /** @} */ /** * @name Neighbour Flags Translations * @{ */ static struct trans_tbl neigh_flags[] = { __ADD(NTF_PROXY, proxy) __ADD(NTF_ROUTER, router) }; char * rtnl_neigh_flags2str(int flags, char *buf, size_t len) { return __flags2str(flags, buf, len, neigh_flags, ARRAY_SIZE(neigh_flags)); } int rtnl_neigh_str2flag(const char *name) { return __str2type(name, neigh_flags, ARRAY_SIZE(neigh_flags)); } /** @} */ /** * @name Attributes * @{ */ void rtnl_neigh_set_state(struct rtnl_neigh *neigh, int state) { neigh->n_state_mask |= state; neigh->n_state |= state; neigh->ce_mask |= NEIGH_ATTR_STATE; } int rtnl_neigh_get_state(struct rtnl_neigh *neigh) { if (neigh->ce_mask & NEIGH_ATTR_STATE) return neigh->n_state; else return -1; } void rtnl_neigh_unset_state(struct rtnl_neigh *neigh, int state) { neigh->n_state_mask |= state; neigh->n_state &= ~state; neigh->ce_mask |= NEIGH_ATTR_STATE; } void rtnl_neigh_set_flags(struct rtnl_neigh *neigh, unsigned int flags) { neigh->n_flag_mask |= flags; neigh->n_flags |= flags; neigh->ce_mask |= NEIGH_ATTR_FLAGS; } unsigned int rtnl_neigh_get_flags(struct rtnl_neigh *neigh) { return neigh->n_flags; } void rtnl_neigh_unset_flags(struct rtnl_neigh *neigh, unsigned int flags) { neigh->n_flag_mask |= flags; neigh->n_flags &= ~flags; neigh->ce_mask |= NEIGH_ATTR_FLAGS; } void rtnl_neigh_set_ifindex(struct rtnl_neigh *neigh, int ifindex) { neigh->n_ifindex = ifindex; neigh->ce_mask |= NEIGH_ATTR_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; } static inline int __assign_addr(struct rtnl_neigh *neigh, struct nl_addr **pos, struct nl_addr *new, int flag, int nocheck) { if (!nocheck) { if (neigh->ce_mask & NEIGH_ATTR_FAMILY) { if (new->a_family != neigh->n_family) return nl_error(EINVAL, "Address family mismatch"); } else { neigh->n_family = new->a_family; neigh->ce_mask |= NEIGH_ATTR_FAMILY; } } if (*pos) nl_addr_put(*pos); nl_addr_get(new); *pos = new; neigh->ce_mask |= flag; return 0; } void rtnl_neigh_set_lladdr(struct rtnl_neigh *neigh, struct nl_addr *addr) { __assign_addr(neigh, &neigh->n_lladdr, addr, NEIGH_ATTR_LLADDR, 1); } struct nl_addr *rtnl_neigh_get_lladdr(struct rtnl_neigh *neigh) { if (neigh->ce_mask & NEIGH_ATTR_LLADDR) return neigh->n_lladdr; else return NULL; } int rtnl_neigh_set_dst(struct rtnl_neigh *neigh, struct nl_addr *addr) { return __assign_addr(neigh, &neigh->n_dst, addr, NEIGH_ATTR_DST, 0); } struct nl_addr *rtnl_neigh_get_dst(struct rtnl_neigh *neigh) { if (neigh->ce_mask & NEIGH_ATTR_DST) return neigh->n_dst; else return NULL; } void rtnl_neigh_set_family(struct rtnl_neigh *neigh, int family) { neigh->n_family = family; neigh->ce_mask |= NEIGH_ATTR_FAMILY; } void rtnl_neigh_set_type(struct rtnl_neigh *neigh, int type) { neigh->n_type = type; neigh->ce_mask = NEIGH_ATTR_TYPE; } int rtnl_neigh_get_type(struct rtnl_neigh *neigh) { if (neigh->ce_mask & NEIGH_ATTR_TYPE) return neigh->n_type; else return -1; } /** @} */ static struct nl_object_ops neigh_obj_ops = { .oo_name = "route/neigh", .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_compare = neigh_compare, .oo_attrs2str = neigh_attrs2str, .oo_id_attrs = (NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY), }; static struct nl_af_group neigh_groups[] = { { AF_UNSPEC, RTNLGRP_NEIGH }, { END_OF_GROUP_LIST }, }; static struct nl_cache_ops rtnl_neigh_ops = { .co_name = "route/neigh", .co_hdrsize = sizeof(struct ndmsg), .co_msgtypes = { { RTM_NEWNEIGH, NL_ACT_NEW, "new" }, { RTM_DELNEIGH, NL_ACT_DEL, "del" }, { RTM_GETNEIGH, NL_ACT_GET, "get" }, END_OF_MSGTYPES_LIST, }, .co_protocol = NETLINK_ROUTE, .co_groups = neigh_groups, .co_request_update = neigh_request_update, .co_msg_parser = neigh_msg_parser, .co_obj_ops = &neigh_obj_ops, }; static void __init neigh_init(void) { nl_cache_mngt_register(&rtnl_neigh_ops); } static void __exit neigh_exit(void) { nl_cache_mngt_unregister(&rtnl_neigh_ops); } /** @} */