📄 neighbour.c
字号:
neigh->ha, NULL, skb->len); read_unlock_bh(&neigh->lock); } if (err >= 0) rc = neigh->ops->queue_xmit(skb); else goto out_kfree_skb; }out: return rc;discard: NEIGH_PRINTK1("neigh_resolve_output: dst=%p neigh=%p\n", dst, dst ? dst->neighbour : NULL);out_kfree_skb: rc = -EINVAL; kfree_skb(skb); goto out;}/* As fast as possible without hh cache */int neigh_connected_output(struct sk_buff *skb){ int err; struct dst_entry *dst = skb->dst; struct neighbour *neigh = dst->neighbour; struct net_device *dev = neigh->dev; __skb_pull(skb, skb->nh.raw - skb->data); read_lock_bh(&neigh->lock); err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len); read_unlock_bh(&neigh->lock); if (err >= 0) err = neigh->ops->queue_xmit(skb); else { err = -EINVAL; kfree_skb(skb); } return err;}static void neigh_proxy_process(unsigned long arg){ struct neigh_table *tbl = (struct neigh_table *)arg; long sched_next = 0; unsigned long now = jiffies; struct sk_buff *skb; spin_lock(&tbl->proxy_queue.lock); skb = tbl->proxy_queue.next; while (skb != (struct sk_buff *)&tbl->proxy_queue) { struct sk_buff *back = skb; long tdif = back->stamp.tv_usec - now; skb = skb->next; if (tdif <= 0) { struct net_device *dev = back->dev; __skb_unlink(back, &tbl->proxy_queue); if (tbl->proxy_redo && netif_running(dev)) tbl->proxy_redo(back); else kfree_skb(back); dev_put(dev); } else if (!sched_next || tdif < sched_next) sched_next = tdif; } del_timer(&tbl->proxy_timer); if (sched_next) mod_timer(&tbl->proxy_timer, jiffies + sched_next); spin_unlock(&tbl->proxy_queue.lock);}void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, struct sk_buff *skb){ unsigned long now = jiffies; unsigned long sched_next = now + (net_random() % p->proxy_delay); if (tbl->proxy_queue.qlen > p->proxy_qlen) { kfree_skb(skb); return; } skb->stamp.tv_sec = LOCALLY_ENQUEUED; skb->stamp.tv_usec = sched_next; spin_lock(&tbl->proxy_queue.lock); if (del_timer(&tbl->proxy_timer)) { if (time_before(tbl->proxy_timer.expires, sched_next)) sched_next = tbl->proxy_timer.expires; } dst_release(skb->dst); skb->dst = NULL; dev_hold(skb->dev); __skb_queue_tail(&tbl->proxy_queue, skb); mod_timer(&tbl->proxy_timer, sched_next); spin_unlock(&tbl->proxy_queue.lock);}struct neigh_parms *neigh_parms_alloc(struct net_device *dev, struct neigh_table *tbl){ struct neigh_parms *p = kmalloc(sizeof(*p), GFP_KERNEL); if (p) { memcpy(p, &tbl->parms, sizeof(*p)); p->tbl = tbl; atomic_set(&p->refcnt, 1); INIT_RCU_HEAD(&p->rcu_head); p->reachable_time = neigh_rand_reach_time(p->base_reachable_time); if (dev && dev->neigh_setup && dev->neigh_setup(dev, p)) { kfree(p); return NULL; } p->sysctl_table = NULL; write_lock_bh(&tbl->lock); p->next = tbl->parms.next; tbl->parms.next = p; write_unlock_bh(&tbl->lock); } return p;}static void neigh_rcu_free_parms(struct rcu_head *head){ struct neigh_parms *parms = container_of(head, struct neigh_parms, rcu_head); neigh_parms_put(parms);}void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms){ struct neigh_parms **p; if (!parms || parms == &tbl->parms) return; write_lock_bh(&tbl->lock); for (p = &tbl->parms.next; *p; p = &(*p)->next) { if (*p == parms) { *p = parms->next; parms->dead = 1; write_unlock_bh(&tbl->lock); call_rcu(&parms->rcu_head, neigh_rcu_free_parms); return; } } write_unlock_bh(&tbl->lock); NEIGH_PRINTK1("neigh_parms_release: not found\n");}void neigh_parms_destroy(struct neigh_parms *parms){ kfree(parms);}void neigh_table_init(struct neigh_table *tbl){ unsigned long now = jiffies; unsigned long phsize; atomic_set(&tbl->parms.refcnt, 1); INIT_RCU_HEAD(&tbl->parms.rcu_head); tbl->parms.reachable_time = neigh_rand_reach_time(tbl->parms.base_reachable_time); if (!tbl->kmem_cachep) tbl->kmem_cachep = kmem_cache_create(tbl->id, tbl->entry_size, 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if (!tbl->kmem_cachep) panic("cannot create neighbour cache"); tbl->stats = alloc_percpu(struct neigh_statistics); if (!tbl->stats) panic("cannot create neighbour cache statistics"); #ifdef CONFIG_PROC_FS tbl->pde = create_proc_entry(tbl->id, 0, proc_net_stat); if (!tbl->pde) panic("cannot create neighbour proc dir entry"); tbl->pde->proc_fops = &neigh_stat_seq_fops; tbl->pde->data = tbl;#endif tbl->hash_mask = 1; tbl->hash_buckets = neigh_hash_alloc(tbl->hash_mask + 1); phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *); tbl->phash_buckets = kmalloc(phsize, GFP_KERNEL); if (!tbl->hash_buckets || !tbl->phash_buckets) panic("cannot allocate neighbour cache hashes"); memset(tbl->phash_buckets, 0, phsize); get_random_bytes(&tbl->hash_rnd, sizeof(tbl->hash_rnd)); tbl->lock = RW_LOCK_UNLOCKED; init_timer(&tbl->gc_timer); tbl->gc_timer.data = (unsigned long)tbl; tbl->gc_timer.function = neigh_periodic_timer; tbl->gc_timer.expires = now + 1; add_timer(&tbl->gc_timer); init_timer(&tbl->proxy_timer); tbl->proxy_timer.data = (unsigned long)tbl; tbl->proxy_timer.function = neigh_proxy_process; skb_queue_head_init(&tbl->proxy_queue); tbl->last_flush = now; tbl->last_rand = now + tbl->parms.reachable_time * 20; write_lock(&neigh_tbl_lock); tbl->next = neigh_tables; neigh_tables = tbl; write_unlock(&neigh_tbl_lock);}int neigh_table_clear(struct neigh_table *tbl){ struct neigh_table **tp; /* It is not clean... Fix it to unload IPv6 module safely */ del_timer_sync(&tbl->gc_timer); del_timer_sync(&tbl->proxy_timer); pneigh_queue_purge(&tbl->proxy_queue); neigh_ifdown(tbl, NULL); if (tbl->entries) printk(KERN_CRIT "neighbour leakage\n"); write_lock(&neigh_tbl_lock); for (tp = &neigh_tables; *tp; tp = &(*tp)->next) { if (*tp == tbl) { *tp = tbl->next; break; } } write_unlock(&neigh_tbl_lock); neigh_hash_free(tbl->hash_buckets, tbl->hash_mask + 1); tbl->hash_buckets = NULL; kfree(tbl->phash_buckets); tbl->phash_buckets = NULL; return 0;}int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg){ struct ndmsg *ndm = NLMSG_DATA(nlh); struct rtattr **nda = arg; struct neigh_table *tbl; struct net_device *dev = NULL; int err = -ENODEV; if (ndm->ndm_ifindex && (dev = dev_get_by_index(ndm->ndm_ifindex)) == NULL) goto out; read_lock(&neigh_tbl_lock); for (tbl = neigh_tables; tbl; tbl = tbl->next) { struct neighbour *n; if (tbl->family != ndm->ndm_family) continue; read_unlock(&neigh_tbl_lock); err = -EINVAL; if (!nda[NDA_DST - 1] || nda[NDA_DST - 1]->rta_len != RTA_LENGTH(tbl->key_len)) goto out_dev_put; if (ndm->ndm_flags & NTF_PROXY) { err = pneigh_delete(tbl, RTA_DATA(nda[NDA_DST - 1]), dev); goto out_dev_put; } if (!dev) goto out; n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST - 1]), dev); if (n) { err = neigh_update(n, NULL, NUD_FAILED, NEIGH_UPDATE_F_OVERRIDE| NEIGH_UPDATE_F_ADMIN); neigh_release(n); } goto out_dev_put; } read_unlock(&neigh_tbl_lock); err = -EADDRNOTAVAIL;out_dev_put: if (dev) dev_put(dev);out: return err;}int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg){ struct ndmsg *ndm = NLMSG_DATA(nlh); struct rtattr **nda = arg; struct neigh_table *tbl; struct net_device *dev = NULL; int err = -ENODEV; if (ndm->ndm_ifindex && (dev = dev_get_by_index(ndm->ndm_ifindex)) == NULL) goto out; read_lock(&neigh_tbl_lock); for (tbl = neigh_tables; tbl; tbl = tbl->next) { int override = 1; struct neighbour *n; if (tbl->family != ndm->ndm_family) continue; read_unlock(&neigh_tbl_lock); err = -EINVAL; if (!nda[NDA_DST - 1] || nda[NDA_DST - 1]->rta_len != RTA_LENGTH(tbl->key_len)) goto out_dev_put; if (ndm->ndm_flags & NTF_PROXY) { err = -ENOBUFS; if (pneigh_lookup(tbl, RTA_DATA(nda[NDA_DST - 1]), dev, 1)) err = 0; goto out_dev_put; } err = -EINVAL; if (!dev) goto out; if (nda[NDA_LLADDR - 1] && nda[NDA_LLADDR - 1]->rta_len != RTA_LENGTH(dev->addr_len)) goto out_dev_put; err = 0; n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST - 1]), dev); if (n) { if (nlh->nlmsg_flags & NLM_F_EXCL) err = -EEXIST; override = nlh->nlmsg_flags & NLM_F_REPLACE; } else if (!(nlh->nlmsg_flags & NLM_F_CREATE)) err = -ENOENT; else { n = __neigh_lookup_errno(tbl, RTA_DATA(nda[NDA_DST - 1]), dev); if (IS_ERR(n)) { err = PTR_ERR(n); n = NULL; } } if (!err) { err = neigh_update(n, nda[NDA_LLADDR - 1] ? RTA_DATA(nda[NDA_LLADDR - 1]) : NULL, ndm->ndm_state, (override ? NEIGH_UPDATE_F_OVERRIDE : 0) | NEIGH_UPDATE_F_ADMIN); } if (n) neigh_release(n); goto out_dev_put; } read_unlock(&neigh_tbl_lock); err = -EADDRNOTAVAIL;out_dev_put: if (dev) dev_put(dev);out: return err;}static int neigh_fill_info(struct sk_buff *skb, struct neighbour *n, u32 pid, u32 seq, int event){ unsigned long now = jiffies; unsigned char *b = skb->tail; struct nda_cacheinfo ci; int locked = 0; struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct ndmsg)); struct ndmsg *ndm = NLMSG_DATA(nlh); ndm->ndm_family = n->ops->family; ndm->ndm_flags = n->flags; ndm->ndm_type = n->type; ndm->ndm_ifindex = n->dev->ifindex; RTA_PUT(skb, NDA_DST, n->tbl->key_len, n->primary_key); read_lock_bh(&n->lock); locked = 1; ndm->ndm_state = n->nud_state; if (n->nud_state & NUD_VALID) RTA_PUT(skb, NDA_LLADDR, n->dev->addr_len, n->ha); ci.ndm_used = now - n->used; ci.ndm_confirmed = now - n->confirmed; ci.ndm_updated = now - n->updated; ci.ndm_refcnt = atomic_read(&n->refcnt) - 1; read_unlock_bh(&n->lock); locked = 0; RTA_PUT(skb, NDA_CACHEINFO, sizeof(ci), &ci); nlh->nlmsg_len = skb->tail - b; return skb->len;nlmsg_failure:rtattr_failure: if (locked) read_unlock_bh(&n->lock); skb_trim(skb, b - skb->data); return -1;}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]; for (h = 0; h <= tbl->hash_mask; h++) { if (h < s_h) continue; if (h > s_h) s_idx = 0; read_lock_bh(&tbl->lock); 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) <= 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;}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_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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -