| /* |
| * 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-2008 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(sk); |
| * |
| * // 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(sk, neigh, NLM_F_CREATE); |
| * |
| * // 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(sk, 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_add(sk, neigh, NLM_F_REPLACE); |
| * |
| * // 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))) |
| return -NLE_NOMEM; |
| |
| if (src->n_dst) |
| if (!(dst->n_dst = nl_addr_clone(src->n_dst))) |
| return -NLE_NOMEM; |
| |
| return 0; |
| } |
| |
| 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_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 = -NLE_NOMEM; |
| 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 = 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 = 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; |
| } |
| |
| 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); |
| errout: |
| rtnl_neigh_put(neigh); |
| return err; |
| } |
| |
| 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 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; |
| struct nl_cache *link_cache; |
| char state[128], flags[64]; |
| |
| link_cache = nl_cache_mngt_require("route/link"); |
| |
| nl_dump_line(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst))); |
| |
| if (link_cache) |
| nl_dump(p, "dev %s ", |
| rtnl_link_i2name(link_cache, n->n_ifindex, |
| state, sizeof(state))); |
| else |
| nl_dump(p, "dev %d ", n->n_ifindex); |
| |
| if (n->ce_mask & NEIGH_ATTR_LLADDR) |
| 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]) |
| nl_dump(p, "<%s", state); |
| if (flags[0]) |
| nl_dump(p, "%s%s", state[0] ? "," : "<", flags); |
| if (state[0] || flags[0]) |
| nl_dump(p, ">"); |
| nl_dump(p, "\n"); |
| } |
| |
| 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(); |
| |
| neigh_dump_line(a, p); |
| |
| 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); |
| } |
| |
| static void neigh_dump_stats(struct nl_object *a, struct nl_dump_params *p) |
| { |
| neigh_dump_details(a, 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]; |
| |
| nl_dump_line(p, "NEIGH_FAMILY=%s\n", |
| nl_af2str(neigh->n_family, buf, sizeof(buf))); |
| |
| if (neigh->ce_mask & NEIGH_ATTR_LLADDR) |
| nl_dump_line(p, "NEIGHT_LLADDR=%s\n", |
| nl_addr2str(neigh->n_lladdr, buf, sizeof(buf))); |
| |
| if (neigh->ce_mask & NEIGH_ATTR_DST) |
| 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; |
| |
| nl_dump_line(p, "NEIGH_IFINDEX=%u\n", neigh->n_ifindex); |
| |
| link_cache = nl_cache_mngt_require("route/link"); |
| if (link_cache) |
| 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) |
| nl_dump_line(p, "NEIGH_PROBES=%u\n", neigh->n_probes); |
| |
| if (neigh->ce_mask & NEIGH_ATTR_TYPE) |
| 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]) |
| nl_dump_line(p, "NEIGH_FLAGS=%s\n", buf); |
| |
| rtnl_neigh_state2str(neigh->n_state, buf, sizeof(buf)); |
| if (buf[0]) |
| nl_dump_line(p, "NEIGH_STATE=%s\n", buf); |
| } |
| |
| /** |
| * @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 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. |
| * |
| * @return 0 on success or a negative error code. |
| */ |
| int rtnl_neigh_alloc_cache(struct nl_sock *sock, struct nl_cache **result) |
| { |
| return nl_cache_alloc_and_fill(&rtnl_neigh_ops, sock, result); |
| } |
| |
| /** |
| * 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 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_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 -NLE_NOMEM; |
| |
| 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); |
| |
| *result = msg; |
| return 0; |
| |
| nla_put_failure: |
| nlmsg_free(msg); |
| 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 |
| * 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 0 on success or a negative error code. |
| */ |
| int rtnl_neigh_build_add_request(struct rtnl_neigh *tmpl, int flags, |
| struct nl_msg **result) |
| { |
| return build_neigh_msg(tmpl, RTM_NEWNEIGH, flags, result); |
| } |
| |
| /** |
| * Add a new neighbour |
| * @arg sk Netlink socket. |
| * @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_sock *sk, struct rtnl_neigh *tmpl, int flags) |
| { |
| int err; |
| struct nl_msg *msg; |
| |
| if ((err = rtnl_neigh_build_add_request(tmpl, flags, &msg)) < 0) |
| return err; |
| |
| err = nl_send_auto_complete(sk, msg); |
| nlmsg_free(msg); |
| if (err < 0) |
| return err; |
| |
| return wait_for_ack(sk); |
| } |
| |
| /** @} */ |
| |
| /** |
| * @name Neighbour Deletion |
| * @{ |
| */ |
| |
| /** |
| * 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 |
| * 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 0 on success or a negative error code. |
| */ |
| int rtnl_neigh_build_delete_request(struct rtnl_neigh *neigh, int flags, |
| struct nl_msg **result) |
| { |
| return build_neigh_msg(neigh, RTM_DELNEIGH, flags, result); |
| } |
| |
| /** |
| * Delete a neighbour |
| * @arg sk Netlink socket. |
| * @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_sock *sk, struct rtnl_neigh *neigh, |
| int flags) |
| { |
| struct nl_msg *msg; |
| int err; |
| |
| if ((err = rtnl_neigh_build_delete_request(neigh, flags, &msg)) < 0) |
| return err; |
| |
| err = nl_send_auto_complete(sk, msg); |
| nlmsg_free(msg); |
| if (err < 0) |
| return err; |
| |
| return wait_for_ack(sk); |
| } |
| |
| /** @} */ |
| |
| /** |
| * @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) |
| { |
| return neigh->n_ifindex; |
| } |
| |
| 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 -NLE_AF_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; |
| } |
| |
| 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; |
| 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_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_IFINDEX | 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); |
| } |
| |
| /** @} */ |