📄 neighbour.c
字号:
read_lock_bh(&tbl->lock); ndtmsg->ndtm_family = tbl->family; ndtmsg->ndtm_pad1 = 0; ndtmsg->ndtm_pad2 = 0; NLA_PUT_STRING(skb, NDTA_NAME, tbl->id); NLA_PUT_MSECS(skb, NDTA_GC_INTERVAL, tbl->gc_interval); NLA_PUT_U32(skb, NDTA_THRESH1, tbl->gc_thresh1); NLA_PUT_U32(skb, NDTA_THRESH2, tbl->gc_thresh2); NLA_PUT_U32(skb, NDTA_THRESH3, tbl->gc_thresh3); { unsigned long now = jiffies; unsigned int flush_delta = now - tbl->last_flush; unsigned int rand_delta = now - tbl->last_rand; struct ndt_config ndc = { .ndtc_key_len = tbl->key_len, .ndtc_entry_size = tbl->entry_size, .ndtc_entries = atomic_read(&tbl->entries), .ndtc_last_flush = jiffies_to_msecs(flush_delta), .ndtc_last_rand = jiffies_to_msecs(rand_delta), .ndtc_hash_rnd = tbl->hash_rnd, .ndtc_hash_mask = tbl->hash_mask, .ndtc_hash_chain_gc = tbl->hash_chain_gc, .ndtc_proxy_qlen = tbl->proxy_queue.qlen, }; NLA_PUT(skb, NDTA_CONFIG, sizeof(ndc), &ndc); } { int cpu; struct ndt_stats ndst; memset(&ndst, 0, sizeof(ndst)); for_each_possible_cpu(cpu) { struct neigh_statistics *st; st = per_cpu_ptr(tbl->stats, cpu); ndst.ndts_allocs += st->allocs; ndst.ndts_destroys += st->destroys; ndst.ndts_hash_grows += st->hash_grows; ndst.ndts_res_failed += st->res_failed; ndst.ndts_lookups += st->lookups; ndst.ndts_hits += st->hits; ndst.ndts_rcv_probes_mcast += st->rcv_probes_mcast; ndst.ndts_rcv_probes_ucast += st->rcv_probes_ucast; ndst.ndts_periodic_gc_runs += st->periodic_gc_runs; ndst.ndts_forced_gc_runs += st->forced_gc_runs; } NLA_PUT(skb, NDTA_STATS, sizeof(ndst), &ndst); } BUG_ON(tbl->parms.dev); if (neightbl_fill_parms(skb, &tbl->parms) < 0) goto nla_put_failure; read_unlock_bh(&tbl->lock); return nlmsg_end(skb, nlh);nla_put_failure: read_unlock_bh(&tbl->lock); nlmsg_cancel(skb, nlh); return -EMSGSIZE;}static int neightbl_fill_param_info(struct sk_buff *skb, struct neigh_table *tbl, struct neigh_parms *parms, u32 pid, u32 seq, int type, unsigned int flags){ struct ndtmsg *ndtmsg; struct nlmsghdr *nlh; nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags); if (nlh == NULL) return -EMSGSIZE; ndtmsg = nlmsg_data(nlh); read_lock_bh(&tbl->lock); ndtmsg->ndtm_family = tbl->family; ndtmsg->ndtm_pad1 = 0; ndtmsg->ndtm_pad2 = 0; if (nla_put_string(skb, NDTA_NAME, tbl->id) < 0 || neightbl_fill_parms(skb, parms) < 0) goto errout; read_unlock_bh(&tbl->lock); return nlmsg_end(skb, nlh);errout: read_unlock_bh(&tbl->lock); nlmsg_cancel(skb, nlh); return -EMSGSIZE;}static inline struct neigh_parms *lookup_neigh_params(struct neigh_table *tbl, int ifindex){ struct neigh_parms *p; for (p = &tbl->parms; p; p = p->next) if ((p->dev && p->dev->ifindex == ifindex) || (!p->dev && !ifindex)) return p; return NULL;}static const struct nla_policy nl_neightbl_policy[NDTA_MAX+1] = { [NDTA_NAME] = { .type = NLA_STRING }, [NDTA_THRESH1] = { .type = NLA_U32 }, [NDTA_THRESH2] = { .type = NLA_U32 }, [NDTA_THRESH3] = { .type = NLA_U32 }, [NDTA_GC_INTERVAL] = { .type = NLA_U64 }, [NDTA_PARMS] = { .type = NLA_NESTED },};static const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = { [NDTPA_IFINDEX] = { .type = NLA_U32 }, [NDTPA_QUEUE_LEN] = { .type = NLA_U32 }, [NDTPA_PROXY_QLEN] = { .type = NLA_U32 }, [NDTPA_APP_PROBES] = { .type = NLA_U32 }, [NDTPA_UCAST_PROBES] = { .type = NLA_U32 }, [NDTPA_MCAST_PROBES] = { .type = NLA_U32 }, [NDTPA_BASE_REACHABLE_TIME] = { .type = NLA_U64 }, [NDTPA_GC_STALETIME] = { .type = NLA_U64 }, [NDTPA_DELAY_PROBE_TIME] = { .type = NLA_U64 }, [NDTPA_RETRANS_TIME] = { .type = NLA_U64 }, [NDTPA_ANYCAST_DELAY] = { .type = NLA_U64 }, [NDTPA_PROXY_DELAY] = { .type = NLA_U64 }, [NDTPA_LOCKTIME] = { .type = NLA_U64 },};static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg){ struct neigh_table *tbl; struct ndtmsg *ndtmsg; struct nlattr *tb[NDTA_MAX+1]; int err; err = nlmsg_parse(nlh, sizeof(*ndtmsg), tb, NDTA_MAX, nl_neightbl_policy); if (err < 0) goto errout; if (tb[NDTA_NAME] == NULL) { err = -EINVAL; goto errout; } ndtmsg = nlmsg_data(nlh); read_lock(&neigh_tbl_lock); for (tbl = neigh_tables; tbl; tbl = tbl->next) { if (ndtmsg->ndtm_family && tbl->family != ndtmsg->ndtm_family) continue; if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0) break; } if (tbl == NULL) { err = -ENOENT; goto errout_locked; } /* * We acquire tbl->lock to be nice to the periodic timers and * make sure they always see a consistent set of values. */ write_lock_bh(&tbl->lock); if (tb[NDTA_PARMS]) { struct nlattr *tbp[NDTPA_MAX+1]; struct neigh_parms *p; int i, ifindex = 0; err = nla_parse_nested(tbp, NDTPA_MAX, tb[NDTA_PARMS], nl_ntbl_parm_policy); if (err < 0) goto errout_tbl_lock; if (tbp[NDTPA_IFINDEX]) ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]); p = lookup_neigh_params(tbl, ifindex); if (p == NULL) { err = -ENOENT; goto errout_tbl_lock; } for (i = 1; i <= NDTPA_MAX; i++) { if (tbp[i] == NULL) continue; switch (i) { case NDTPA_QUEUE_LEN: p->queue_len = nla_get_u32(tbp[i]); break; case NDTPA_PROXY_QLEN: p->proxy_qlen = nla_get_u32(tbp[i]); break; case NDTPA_APP_PROBES: p->app_probes = nla_get_u32(tbp[i]); break; case NDTPA_UCAST_PROBES: p->ucast_probes = nla_get_u32(tbp[i]); break; case NDTPA_MCAST_PROBES: p->mcast_probes = nla_get_u32(tbp[i]); break; case NDTPA_BASE_REACHABLE_TIME: p->base_reachable_time = nla_get_msecs(tbp[i]); break; case NDTPA_GC_STALETIME: p->gc_staletime = nla_get_msecs(tbp[i]); break; case NDTPA_DELAY_PROBE_TIME: p->delay_probe_time = nla_get_msecs(tbp[i]); break; case NDTPA_RETRANS_TIME: p->retrans_time = nla_get_msecs(tbp[i]); break; case NDTPA_ANYCAST_DELAY: p->anycast_delay = nla_get_msecs(tbp[i]); break; case NDTPA_PROXY_DELAY: p->proxy_delay = nla_get_msecs(tbp[i]); break; case NDTPA_LOCKTIME: p->locktime = nla_get_msecs(tbp[i]); break; } } } if (tb[NDTA_THRESH1]) tbl->gc_thresh1 = nla_get_u32(tb[NDTA_THRESH1]); if (tb[NDTA_THRESH2]) tbl->gc_thresh2 = nla_get_u32(tb[NDTA_THRESH2]); if (tb[NDTA_THRESH3]) tbl->gc_thresh3 = nla_get_u32(tb[NDTA_THRESH3]); if (tb[NDTA_GC_INTERVAL]) tbl->gc_interval = nla_get_msecs(tb[NDTA_GC_INTERVAL]); err = 0;errout_tbl_lock: write_unlock_bh(&tbl->lock);errout_locked: read_unlock(&neigh_tbl_lock);errout: return err;}static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb){ int family, tidx, nidx = 0; int tbl_skip = cb->args[0]; int neigh_skip = cb->args[1]; struct neigh_table *tbl; family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family; read_lock(&neigh_tbl_lock); for (tbl = neigh_tables, tidx = 0; tbl; tbl = tbl->next, tidx++) { struct neigh_parms *p; if (tidx < tbl_skip || (family && tbl->family != family)) continue; if (neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWNEIGHTBL, NLM_F_MULTI) <= 0) break; for (nidx = 0, p = tbl->parms.next; p; p = p->next, nidx++) { if (nidx < neigh_skip) continue; if (neightbl_fill_param_info(skb, tbl, p, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWNEIGHTBL, NLM_F_MULTI) <= 0) goto out; } neigh_skip = 0; }out: read_unlock(&neigh_tbl_lock); cb->args[0] = tidx; cb->args[1] = nidx; return skb->len;}static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh, u32 pid, u32 seq, int type, unsigned int flags){ unsigned long now = jiffies; struct nda_cacheinfo ci; struct nlmsghdr *nlh; struct ndmsg *ndm; nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags); if (nlh == NULL) return -EMSGSIZE; ndm = nlmsg_data(nlh); ndm->ndm_family = neigh->ops->family; ndm->ndm_pad1 = 0; ndm->ndm_pad2 = 0; ndm->ndm_flags = neigh->flags; ndm->ndm_type = neigh->type; ndm->ndm_ifindex = neigh->dev->ifindex; NLA_PUT(skb, NDA_DST, neigh->tbl->key_len, neigh->primary_key); read_lock_bh(&neigh->lock); ndm->ndm_state = neigh->nud_state; if ((neigh->nud_state & NUD_VALID) && nla_put(skb, NDA_LLADDR, neigh->dev->addr_len, neigh->ha) < 0) { read_unlock_bh(&neigh->lock); goto nla_put_failure; } ci.ndm_used = now - neigh->used; ci.ndm_confirmed = now - neigh->confirmed; ci.ndm_updated = now - neigh->updated; ci.ndm_refcnt = atomic_read(&neigh->refcnt) - 1; read_unlock_bh(&neigh->lock); NLA_PUT_U32(skb, NDA_PROBES, atomic_read(&neigh->probes)); NLA_PUT(skb, NDA_CACHEINFO, sizeof(ci), &ci); return nlmsg_end(skb, nlh);nla_put_failure: nlmsg_cancel(skb, nlh); return -EMSGSIZE;}static void neigh_update_notify(struct neighbour *neigh){ call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh); __neigh_notify(neigh, RTM_NEWNEIGH, 0);}static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, struct netlink_callback *cb){ struct neighbour *n; int rc, h, s_h = cb->args[1]; int idx, s_idx = idx = cb->args[2]; read_lock_bh(&tbl->lock); for (h = 0; h <= tbl->hash_mask; h++) { if (h < s_h) continue; if (h > s_h) s_idx = 0; for (n = tbl->hash_buckets[h], idx = 0; n; n = n->next, idx++) { if (idx < s_idx) continue; if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWNEIGH, NLM_F_MULTI) <= 0) { read_unlock_bh(&tbl->lock); rc = -1; goto out; } } } read_unlock_bh(&tbl->lock); rc = skb->len;out: cb->args[1] = h; cb->args[2] = idx; return rc;}static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb){ struct neigh_table *tbl; int t, family, s_t; read_lock(&neigh_tbl_lock); family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family; s_t = cb->args[0]; for (tbl = neigh_tables, t = 0; tbl; tbl = tbl->next, t++) { if (t < s_t || (family && tbl->family != family)) continue; if (t > s_t) memset(&cb->args[1], 0, sizeof(cb->args) - sizeof(cb->args[0])); if (neigh_dump_table(tbl, skb, cb) < 0) break; } read_unlock(&neigh_tbl_lock); cb->args[0] = t; return skb->len;}void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie){ int chain; read_lock_bh(&tbl->lock); for (chain = 0; chain <= tbl->hash_mask; chain++) { struct neighbour *n; for (n = tbl->hash_buckets[chain]; n; n = n->next) cb(n, cookie); } read_unlock_bh(&tbl->lock);}EXPORT_SYMBOL(neigh_for_each);/* The tbl->lock must be held as a writer and BH disabled. */void __neigh_for_each_release(struct neigh_table *tbl, int (*cb)(struct neighbour *)){ int chain; for (chain = 0; chain <= tbl->hash_mask; chain++) { struct neighbour *n, **np; np = &tbl->hash_buckets[chain]; while ((n = *np) != NULL) { int release; write_lock(&n->lock); release = cb(n); if (release) { *np = n->next; n->dead = 1; } else np = &n->next; write_unlock(&n->lock); if (release) neigh_cleanup_and_release(n); } }}EXPORT_SYMBOL(__neigh_for_each_release);#ifdef CONFIG_PROC_FSstatic struct neighbour *neigh_get_first(struct seq_file *seq){ struct neigh_seq_state *state = seq->private; struct neigh_table *tbl = state->tbl; struct neighbour *n = NULL; int bucket = state->bucket; state->flags &= ~NEIGH_SEQ_IS_PNEIGH; for (bucket = 0; bucket <= tbl->hash_mask; bucket++) { n = tbl->hash_buckets[bucket]; while (n) { if (state->neigh_sub_iter) { loff_t fakep = 0; void *v; v = state->neigh_sub_iter(state, n, &fakep); if (!v) goto next; } if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) break; if (n->nud_state & ~NUD_NOARP) break; next: n = n->next; } if (n) break; } state->bucket = bucket; return n;}static struct neighbour *neigh_get_next(struct seq_file *seq, struct neighbour *n, loff_t *pos){ struct neigh_seq_state *state = seq->private; struct neigh_table *tbl = state->tbl; if (state->neigh_sub_iter) { void *v = state->neigh_sub_iter(state, n, pos); if (v) return n; } n = n->next; while (1) { while (n) { if (state->neigh_sub_iter) { void *v = state->neigh_sub_iter(state, n, pos); if (v) return n; goto next; } if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) break; if (n->nud_state & ~NUD_NOARP) break; next: n = n->next; } if (n) break; if (++state->bucket > tbl->hash_mask) break; n = tbl->hash_buckets[state->bucket]; } if (n && pos) --(*pos); return n;}static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos){ struct neighbour *n = neigh_get_first(seq); if (n) { while (*pos) { n = neigh_get_next(seq, n, pos); if (!n) break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -