📄 dn_table.c
字号:
dz = table->dh_zones[z]; if (!dz && !(dz = dn_new_zone(table, z))) return -ENOBUFS; dz_key_0(key); if (rta->rta_dst) { dn_address dst; memcpy(&dst, rta->rta_dst, 2); if (dst & ~DZ_MASK(dz)) return -EINVAL; key = dz_key(dst, dz); } if ((fi = dn_fib_create_info(r, rta, n, &err)) == NULL) return err; if (dz->dz_nent > (dz->dz_divisor << 2) && dz->dz_divisor > DN_MAX_DIVISOR && (z==16 || (1<<z) > dz->dz_divisor)) dn_rehash_zone(dz); fp = dn_chain_p(key, dz); DN_FIB_SCAN(f, fp) { if (dn_key_leq(key, f->fn_key)) break; } del_fp = NULL; if (f && (f->fn_state & DN_S_ZOMBIE) && dn_key_eq(f->fn_key, key)) { del_fp = fp; fp = &f->fn_next; f = *fp; goto create; } DN_FIB_SCAN_KEY(f, fp, key) { if (fi->fib_priority <= DN_FIB_INFO(f)->fib_priority) break; } if (f && dn_key_eq(f->fn_key, key) && fi->fib_priority == DN_FIB_INFO(f)->fib_priority) { struct dn_fib_node **ins_fp; err = -EEXIST; if (n->nlmsg_flags & NLM_F_EXCL) goto out; if (n->nlmsg_flags & NLM_F_REPLACE) { del_fp = fp; fp = &f->fn_next; f = *fp; goto replace; } ins_fp = fp; err = -EEXIST; DN_FIB_SCAN_KEY(f, fp, key) { if (fi->fib_priority != DN_FIB_INFO(f)->fib_priority) break; if (f->fn_type == type && f->fn_scope == r->rtm_scope && DN_FIB_INFO(f) == fi) goto out; } if (!(n->nlmsg_flags & NLM_F_APPEND)) { fp = ins_fp; f = *fp; } }create: err = -ENOENT; if (!(n->nlmsg_flags & NLM_F_CREATE)) goto out;replace: err = -ENOBUFS; new_f = kmem_cache_alloc(dn_hash_kmem, SLAB_KERNEL); if (new_f == NULL) goto out; memset(new_f, 0, sizeof(struct dn_fib_node)); new_f->fn_key = key; new_f->fn_type = type; new_f->fn_scope = r->rtm_scope; DN_FIB_INFO(new_f) = fi; new_f->fn_next = f; write_lock_bh(&dn_fib_tables_lock); *fp = new_f; write_unlock_bh(&dn_fib_tables_lock); dz->dz_nent++; if (del_fp) { f = *del_fp; write_lock_bh(&dn_fib_tables_lock); *del_fp = f->fn_next; write_unlock_bh(&dn_fib_tables_lock); if (!(f->fn_state & DN_S_ZOMBIE)) dn_rtmsg_fib(RTM_DELROUTE, f, z, tb->n, n, req); if (f->fn_state & DN_S_ACCESSED) dn_rt_cache_flush(-1); dn_free_node(f); dz->dz_nent--; } else { dn_rt_cache_flush(-1); } dn_rtmsg_fib(RTM_NEWROUTE, new_f, z, tb->n, n, req); return 0;out: dn_fib_release_info(fi); return err;}static int dn_fib_table_delete(struct dn_fib_table *tb, struct rtmsg *r, struct dn_kern_rta *rta, struct nlmsghdr *n, struct netlink_skb_parms *req){ struct dn_hash *table = (struct dn_hash*)tb->data; struct dn_fib_node **fp, **del_fp, *f; int z = r->rtm_dst_len; struct dn_zone *dz; dn_fib_key_t key; int matched; if (z > 16) return -EINVAL; if ((dz = table->dh_zones[z]) == NULL) return -ESRCH; dz_key_0(key); if (rta->rta_dst) { dn_address dst; memcpy(&dst, rta->rta_dst, 2); if (dst & ~DZ_MASK(dz)) return -EINVAL; key = dz_key(dst, dz); } fp = dn_chain_p(key, dz); DN_FIB_SCAN(f, fp) { if (dn_key_eq(f->fn_key, key)) break; if (dn_key_leq(key, f->fn_key)) return -ESRCH; } matched = 0; del_fp = NULL; DN_FIB_SCAN_KEY(f, fp, key) { struct dn_fib_info *fi = DN_FIB_INFO(f); if (f->fn_state & DN_S_ZOMBIE) return -ESRCH; matched++; if (del_fp == NULL && (!r->rtm_type || f->fn_type == r->rtm_type) && (r->rtm_scope == RT_SCOPE_NOWHERE || f->fn_scope == r->rtm_scope) && (!r->rtm_protocol || fi->fib_protocol == r->rtm_protocol) && dn_fib_nh_match(r, n, rta, fi) == 0) del_fp = fp; } if (del_fp) { f = *del_fp; dn_rtmsg_fib(RTM_DELROUTE, f, z, tb->n, n, req); if (matched != 1) { write_lock_bh(&dn_fib_tables_lock); *del_fp = f->fn_next; write_unlock_bh(&dn_fib_tables_lock); if (f->fn_state & DN_S_ACCESSED) dn_rt_cache_flush(-1); dn_free_node(f); dz->dz_nent--; } else { f->fn_state |= DN_S_ZOMBIE; if (f->fn_state & DN_S_ACCESSED) { f->fn_state &= ~DN_S_ACCESSED; dn_rt_cache_flush(-1); } if (++dn_fib_hash_zombies > 128) dn_fib_flush(); } return 0; } return -ESRCH;}static __inline__ int dn_flush_list(struct dn_fib_node **fp, int z, struct dn_hash *table){ int found = 0; struct dn_fib_node *f; while((f = *fp) != NULL) { struct dn_fib_info *fi = DN_FIB_INFO(f); if (fi && ((f->fn_state & DN_S_ZOMBIE) || (fi->fib_flags & RTNH_F_DEAD))) { write_lock_bh(&dn_fib_tables_lock); *fp = f->fn_next; write_unlock_bh(&dn_fib_tables_lock); dn_free_node(f); found++; continue; } fp = &f->fn_next; } return found;}static int dn_fib_table_flush(struct dn_fib_table *tb){ struct dn_hash *table = (struct dn_hash *)tb->data; struct dn_zone *dz; int found = 0; dn_fib_hash_zombies = 0; for(dz = table->dh_zone_list; dz; dz = dz->dz_next) { int i; int tmp = 0; for(i = dz->dz_divisor-1; i >= 0; i--) tmp += dn_flush_list(&dz->dz_hash[i], dz->dz_order, table); dz->dz_nent -= tmp; found += tmp; } return found;}static int dn_fib_table_lookup(struct dn_fib_table *tb, const struct dn_fib_key *key, struct dn_fib_res *res){ int err; struct dn_zone *dz; struct dn_hash *t = (struct dn_hash *)tb->data; read_lock(&dn_fib_tables_lock); for(dz = t->dh_zone_list; dz; dz = dz->dz_next) { struct dn_fib_node *f; dn_fib_key_t k = dz_key(key->dst, dz); for(f = dz_chain(k, dz); f; f = f->fn_next) { if (!dn_key_leq(k, f->fn_key)) break; else continue; f->fn_state |= DN_S_ACCESSED; if (f->fn_state&DN_S_ZOMBIE) continue; if (f->fn_scope < key->scope) continue; err = dn_fib_semantic_match(f->fn_type, DN_FIB_INFO(f), key, res); if (err == 0) { res->type = f->fn_type; res->scope = f->fn_scope; res->prefixlen = dz->dz_order; goto out; } if (err < 0) goto out; } } err = 1;out: read_unlock(&dn_fib_tables_lock); return err;}#ifdef CONFIG_PROC_FSstatic unsigned dn_fib_flag_trans(int type, int dead, u16 mask, struct dn_fib_info *fi){ static unsigned type2flags[RTN_MAX+1] = { 0, 0, 0, 0, 0, 0, 0, RTF_REJECT, RTF_REJECT, 0, 0, 0 }; unsigned flags = type2flags[type]; if (fi && fi->fib_nh->nh_gw) flags |= RTF_GATEWAY; if (mask == 0xFFFF) flags |= RTF_HOST; if (dead) flags |= RTF_UP; return flags;}static void dn_fib_node_get_info(int type, int dead, struct dn_fib_info *fi, u16 prefix, u16 mask, char *buffer){ int len; unsigned flags = dn_fib_flag_trans(type, dead, mask, fi); if (fi) { len = sprintf(buffer, "%s\t%04x\t%04x\t%04x\t%d\t%u\t%d\t%04x\t%d\t%u\t%u", fi->fib_dev ? fi->fib_dev->name : "*", prefix, fi->fib_nh->nh_gw, flags, 0, 0, fi->fib_priority, mask, 0, 0, 0); } else { len = sprintf(buffer, "*\t%04x\t%04x\t%04x\t%d\t%u\t%d\t%04x\t%d\t%u\t%u", prefix, 0, flags, 0, 0, 0, mask, 0, 0, 0); } memset(buffer+len, ' ', 127-len); buffer[127] = '\n';}static int dn_fib_table_get_info(struct dn_fib_table *tb, char *buffer, int first, int count){ struct dn_hash *table = (struct dn_hash *)tb->data; struct dn_zone *dz; int pos = 0; int n = 0; read_lock(&dn_fib_tables_lock); for(dz = table->dh_zone_list; dz; dz = dz->dz_next) { int i; struct dn_fib_node *f; int maxslot = dz->dz_divisor; struct dn_fib_node **fp = dz->dz_hash; if (dz->dz_nent == 0) continue; if (pos + dz->dz_nent < first) { pos += dz->dz_nent; continue; } for(i = 0; i < maxslot; i++, fp++) { for(f = *fp; f ; f = f->fn_next) { if (++pos <= first) continue; dn_fib_node_get_info(f->fn_type, f->fn_state & DN_S_ZOMBIE, DN_FIB_INFO(f), dz_prefix(f->fn_key, dz), DZ_MASK(dz), buffer); buffer += 128; if (++n >= count) goto out; } } }out: read_unlock(&dn_fib_tables_lock); return n;}#endif /* CONFIG_PROC_FS */struct dn_fib_table *dn_fib_get_table(int n, int create){ struct dn_fib_table *t; if (n < DN_MIN_TABLE) return NULL; if (n > DN_NUM_TABLES) return NULL; if (dn_fib_tables[n]) return dn_fib_tables[n]; if (!create) return NULL; if (in_interrupt() && net_ratelimit()) { printk(KERN_DEBUG "DECnet: BUG! Attempt to create routing table from interrupt\n"); return NULL; } if ((t = kmalloc(sizeof(struct dn_fib_table), GFP_KERNEL)) == NULL) return NULL; memset(t, 0, sizeof(struct dn_fib_table)); t->n = n; t->insert = dn_fib_table_insert; t->delete = dn_fib_table_delete; t->lookup = dn_fib_table_lookup; t->flush = dn_fib_table_flush;#ifdef CONFIG_PROC_FS t->get_info = dn_fib_table_get_info;#endif#ifdef CONFIG_RTNETLINK t->dump = dn_fib_table_dump;#endif dn_fib_tables[n] = t; return t;}static void dn_fib_del_tree(int n){ struct dn_fib_table *t; write_lock(&dn_fib_tables_lock); t = dn_fib_tables[n]; dn_fib_tables[n] = NULL; write_unlock(&dn_fib_tables_lock); if (t) { kfree(t); }}struct dn_fib_table *dn_fib_empty_table(void){ int id; for(id = DN_MIN_TABLE; id <= DN_NUM_TABLES; id++) if (dn_fib_tables[id] == NULL) return dn_fib_get_table(id, 1); return NULL;}void __init dn_fib_table_init(void){ dn_hash_kmem = kmem_cache_create("dn_fib_info_cache", sizeof(struct dn_fib_info), 0, SLAB_HWCACHE_ALIGN, NULL, NULL);}void __exit dn_fib_table_cleanup(void){ int i; for (i = 0; i < DN_NUM_TABLES + 1; ++i) dn_fib_del_tree(i); return;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -