📄 ip6_flowlabel.c
字号:
ipv6_addr_copy(&fl->dst, &freq->flr_dst); atomic_set(&fl->users, 1); switch (fl->share) { case IPV6_FL_S_EXCL: case IPV6_FL_S_ANY: break; case IPV6_FL_S_PROCESS: fl->owner = current->pid; break; case IPV6_FL_S_USER: fl->owner = current->euid; break; default: err = -EINVAL; goto done; } return fl;done: fl_free(fl); *err_p = err; return NULL;}static int mem_check(struct sock *sk){ struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_fl_socklist *sfl; int room = FL_MAX_SIZE - atomic_read(&fl_size); int count = 0; if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK) return 0; for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) count++; if (room <= 0 || ((count >= FL_MAX_PER_SOCK || (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) && !capable(CAP_NET_ADMIN))) return -ENOBUFS; return 0;}static int ipv6_hdr_cmp(struct ipv6_opt_hdr *h1, struct ipv6_opt_hdr *h2){ if (h1 == h2) return 0; if (h1 == NULL || h2 == NULL) return 1; if (h1->hdrlen != h2->hdrlen) return 1; return memcmp(h1+1, h2+1, ((h1->hdrlen+1)<<3) - sizeof(*h1));}static int ipv6_opt_cmp(struct ipv6_txoptions *o1, struct ipv6_txoptions *o2){ if (o1 == o2) return 0; if (o1 == NULL || o2 == NULL) return 1; if (o1->opt_nflen != o2->opt_nflen) return 1; if (ipv6_hdr_cmp(o1->hopopt, o2->hopopt)) return 1; if (ipv6_hdr_cmp(o1->dst0opt, o2->dst0opt)) return 1; if (ipv6_hdr_cmp((struct ipv6_opt_hdr *)o1->srcrt, (struct ipv6_opt_hdr *)o2->srcrt)) return 1; return 0;}static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl, struct ip6_flowlabel *fl){ write_lock_bh(&ip6_sk_fl_lock); sfl->fl = fl; sfl->next = np->ipv6_fl_list; np->ipv6_fl_list = sfl; write_unlock_bh(&ip6_sk_fl_lock);}int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen){ int err; struct ipv6_pinfo *np = inet6_sk(sk); struct in6_flowlabel_req freq; struct ipv6_fl_socklist *sfl1=NULL; struct ipv6_fl_socklist *sfl, **sflp; struct ip6_flowlabel *fl, *fl1 = NULL; if (optlen < sizeof(freq)) return -EINVAL; if (copy_from_user(&freq, optval, sizeof(freq))) return -EFAULT; switch (freq.flr_action) { case IPV6_FL_A_PUT: write_lock_bh(&ip6_sk_fl_lock); for (sflp = &np->ipv6_fl_list; (sfl=*sflp)!=NULL; sflp = &sfl->next) { if (sfl->fl->label == freq.flr_label) { if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK)) np->flow_label &= ~IPV6_FLOWLABEL_MASK; *sflp = sfl->next; write_unlock_bh(&ip6_sk_fl_lock); fl_release(sfl->fl); kfree(sfl); return 0; } } write_unlock_bh(&ip6_sk_fl_lock); return -ESRCH; case IPV6_FL_A_RENEW: read_lock_bh(&ip6_sk_fl_lock); for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) { if (sfl->fl->label == freq.flr_label) { err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires); read_unlock_bh(&ip6_sk_fl_lock); return err; } } read_unlock_bh(&ip6_sk_fl_lock); if (freq.flr_share == IPV6_FL_S_NONE && capable(CAP_NET_ADMIN)) { fl = fl_lookup(freq.flr_label); if (fl) { err = fl6_renew(fl, freq.flr_linger, freq.flr_expires); fl_release(fl); return err; } } return -ESRCH; case IPV6_FL_A_GET: if (freq.flr_label & ~IPV6_FLOWLABEL_MASK) return -EINVAL; fl = fl_create(&freq, optval, optlen, &err); if (fl == NULL) return err; sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL); if (freq.flr_label) { err = -EEXIST; read_lock_bh(&ip6_sk_fl_lock); for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) { if (sfl->fl->label == freq.flr_label) { if (freq.flr_flags&IPV6_FL_F_EXCL) { read_unlock_bh(&ip6_sk_fl_lock); goto done; } fl1 = sfl->fl; atomic_inc(&fl1->users); break; } } read_unlock_bh(&ip6_sk_fl_lock); if (fl1 == NULL) fl1 = fl_lookup(freq.flr_label); if (fl1) {recheck: err = -EEXIST; if (freq.flr_flags&IPV6_FL_F_EXCL) goto release; err = -EPERM; if (fl1->share == IPV6_FL_S_EXCL || fl1->share != fl->share || fl1->owner != fl->owner) goto release; err = -EINVAL; if (!ipv6_addr_equal(&fl1->dst, &fl->dst) || ipv6_opt_cmp(fl1->opt, fl->opt)) goto release; err = -ENOMEM; if (sfl1 == NULL) goto release; if (fl->linger > fl1->linger) fl1->linger = fl->linger; if ((long)(fl->expires - fl1->expires) > 0) fl1->expires = fl->expires; fl_link(np, sfl1, fl1); fl_free(fl); return 0;release: fl_release(fl1); goto done; } } err = -ENOENT; if (!(freq.flr_flags&IPV6_FL_F_CREATE)) goto done; err = -ENOMEM; if (sfl1 == NULL || (err = mem_check(sk)) != 0) goto done; fl1 = fl_intern(fl, freq.flr_label); if (fl1 != NULL) goto recheck; if (!freq.flr_label) { if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label, &fl->label, sizeof(fl->label))) { /* Intentionally ignore fault. */ } } fl_link(np, sfl1, fl); return 0; default: return -EINVAL; }done: fl_free(fl); kfree(sfl1); return err;}#ifdef CONFIG_PROC_FSstruct ip6fl_iter_state { int bucket;};#define ip6fl_seq_private(seq) ((struct ip6fl_iter_state *)(seq)->private)static struct ip6_flowlabel *ip6fl_get_first(struct seq_file *seq){ struct ip6_flowlabel *fl = NULL; struct ip6fl_iter_state *state = ip6fl_seq_private(seq); for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) { if (fl_ht[state->bucket]) { fl = fl_ht[state->bucket]; break; } } return fl;}static struct ip6_flowlabel *ip6fl_get_next(struct seq_file *seq, struct ip6_flowlabel *fl){ struct ip6fl_iter_state *state = ip6fl_seq_private(seq); fl = fl->next; while (!fl) { if (++state->bucket <= FL_HASH_MASK) fl = fl_ht[state->bucket]; else break; } return fl;}static struct ip6_flowlabel *ip6fl_get_idx(struct seq_file *seq, loff_t pos){ struct ip6_flowlabel *fl = ip6fl_get_first(seq); if (fl) while (pos && (fl = ip6fl_get_next(seq, fl)) != NULL) --pos; return pos ? NULL : fl;}static void *ip6fl_seq_start(struct seq_file *seq, loff_t *pos){ read_lock_bh(&ip6_fl_lock); return *pos ? ip6fl_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;}static void *ip6fl_seq_next(struct seq_file *seq, void *v, loff_t *pos){ struct ip6_flowlabel *fl; if (v == SEQ_START_TOKEN) fl = ip6fl_get_first(seq); else fl = ip6fl_get_next(seq, v); ++*pos; return fl;}static void ip6fl_seq_stop(struct seq_file *seq, void *v){ read_unlock_bh(&ip6_fl_lock);}static int ip6fl_seq_show(struct seq_file *seq, void *v){ if (v == SEQ_START_TOKEN) seq_printf(seq, "%-5s %-1s %-6s %-6s %-6s %-8s %-32s %s\n", "Label", "S", "Owner", "Users", "Linger", "Expires", "Dst", "Opt"); else { struct ip6_flowlabel *fl = v; seq_printf(seq, "%05X %-1d %-6d %-6d %-6ld %-8ld " NIP6_SEQFMT " %-4d\n", (unsigned)ntohl(fl->label), fl->share, (unsigned)fl->owner, atomic_read(&fl->users), fl->linger/HZ, (long)(fl->expires - jiffies)/HZ, NIP6(fl->dst), fl->opt ? fl->opt->opt_nflen : 0); } return 0;}static const struct seq_operations ip6fl_seq_ops = { .start = ip6fl_seq_start, .next = ip6fl_seq_next, .stop = ip6fl_seq_stop, .show = ip6fl_seq_show,};static int ip6fl_seq_open(struct inode *inode, struct file *file){ return seq_open_private(file, &ip6fl_seq_ops, sizeof(struct ip6fl_iter_state));}static const struct file_operations ip6fl_seq_fops = { .owner = THIS_MODULE, .open = ip6fl_seq_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_private,};#endifvoid ip6_flowlabel_init(void){#ifdef CONFIG_PROC_FS proc_net_fops_create(&init_net, "ip6_flowlabel", S_IRUGO, &ip6fl_seq_fops);#endif}void ip6_flowlabel_cleanup(void){ del_timer(&ip6_fl_gc_timer);#ifdef CONFIG_PROC_FS proc_net_remove(&init_net, "ip6_flowlabel");#endif}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -