⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ip6_flowlabel.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	ip6_flowlabel.c		IPv6 flowlabel manager. * *	This program is free software; you can redistribute it and/or *      modify it under the terms of the GNU General Public License *      as published by the Free Software Foundation; either version *      2 of the License, or (at your option) any later version. * *	Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> */#include <linux/capability.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/net.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/in6.h>#include <linux/route.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <net/net_namespace.h>#include <net/sock.h>#include <net/ipv6.h>#include <net/ndisc.h>#include <net/protocol.h>#include <net/ip6_route.h>#include <net/addrconf.h>#include <net/rawv6.h>#include <net/icmp.h>#include <net/transp_v6.h>#include <asm/uaccess.h>#define FL_MIN_LINGER	6	/* Minimal linger. It is set to 6sec specified				   in old IPv6 RFC. Well, it was reasonable value.				 */#define FL_MAX_LINGER	60	/* Maximal linger timeout *//* FL hash table */#define FL_MAX_PER_SOCK	32#define FL_MAX_SIZE	4096#define FL_HASH_MASK	255#define FL_HASH(l)	(ntohl(l)&FL_HASH_MASK)static atomic_t fl_size = ATOMIC_INIT(0);static struct ip6_flowlabel *fl_ht[FL_HASH_MASK+1];static void ip6_fl_gc(unsigned long dummy);static DEFINE_TIMER(ip6_fl_gc_timer, ip6_fl_gc, 0, 0);/* FL hash table lock: it protects only of GC */static DEFINE_RWLOCK(ip6_fl_lock);/* Big socket sock */static DEFINE_RWLOCK(ip6_sk_fl_lock);static __inline__ struct ip6_flowlabel * __fl_lookup(__be32 label){	struct ip6_flowlabel *fl;	for (fl=fl_ht[FL_HASH(label)]; fl; fl = fl->next) {		if (fl->label == label)			return fl;	}	return NULL;}static struct ip6_flowlabel * fl_lookup(__be32 label){	struct ip6_flowlabel *fl;	read_lock_bh(&ip6_fl_lock);	fl = __fl_lookup(label);	if (fl)		atomic_inc(&fl->users);	read_unlock_bh(&ip6_fl_lock);	return fl;}static void fl_free(struct ip6_flowlabel *fl){	if (fl)		kfree(fl->opt);	kfree(fl);}static void fl_release(struct ip6_flowlabel *fl){	write_lock_bh(&ip6_fl_lock);	fl->lastuse = jiffies;	if (atomic_dec_and_test(&fl->users)) {		unsigned long ttd = fl->lastuse + fl->linger;		if (time_after(ttd, fl->expires))			fl->expires = ttd;		ttd = fl->expires;		if (fl->opt && fl->share == IPV6_FL_S_EXCL) {			struct ipv6_txoptions *opt = fl->opt;			fl->opt = NULL;			kfree(opt);		}		if (!timer_pending(&ip6_fl_gc_timer) ||		    time_after(ip6_fl_gc_timer.expires, ttd))			mod_timer(&ip6_fl_gc_timer, ttd);	}	write_unlock_bh(&ip6_fl_lock);}static void ip6_fl_gc(unsigned long dummy){	int i;	unsigned long now = jiffies;	unsigned long sched = 0;	write_lock(&ip6_fl_lock);	for (i=0; i<=FL_HASH_MASK; i++) {		struct ip6_flowlabel *fl, **flp;		flp = &fl_ht[i];		while ((fl=*flp) != NULL) {			if (atomic_read(&fl->users) == 0) {				unsigned long ttd = fl->lastuse + fl->linger;				if (time_after(ttd, fl->expires))					fl->expires = ttd;				ttd = fl->expires;				if (time_after_eq(now, ttd)) {					*flp = fl->next;					fl_free(fl);					atomic_dec(&fl_size);					continue;				}				if (!sched || time_before(ttd, sched))					sched = ttd;			}			flp = &fl->next;		}	}	if (!sched && atomic_read(&fl_size))		sched = now + FL_MAX_LINGER;	if (sched) {		ip6_fl_gc_timer.expires = sched;		add_timer(&ip6_fl_gc_timer);	}	write_unlock(&ip6_fl_lock);}static struct ip6_flowlabel *fl_intern(struct ip6_flowlabel *fl, __be32 label){	struct ip6_flowlabel *lfl;	fl->label = label & IPV6_FLOWLABEL_MASK;	write_lock_bh(&ip6_fl_lock);	if (label == 0) {		for (;;) {			fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK;			if (fl->label) {				lfl = __fl_lookup(fl->label);				if (lfl == NULL)					break;			}		}	} else {		/*		 * we dropper the ip6_fl_lock, so this entry could reappear		 * and we need to recheck with it.		 *		 * OTOH no need to search the active socket first, like it is		 * done in ipv6_flowlabel_opt - sock is locked, so new entry		 * with the same label can only appear on another sock		 */		lfl = __fl_lookup(fl->label);		if (lfl != NULL) {			atomic_inc(&lfl->users);			write_unlock_bh(&ip6_fl_lock);			return lfl;		}	}	fl->lastuse = jiffies;	fl->next = fl_ht[FL_HASH(fl->label)];	fl_ht[FL_HASH(fl->label)] = fl;	atomic_inc(&fl_size);	write_unlock_bh(&ip6_fl_lock);	return NULL;}/* Socket flowlabel lists */struct ip6_flowlabel * fl6_sock_lookup(struct sock *sk, __be32 label){	struct ipv6_fl_socklist *sfl;	struct ipv6_pinfo *np = inet6_sk(sk);	label &= IPV6_FLOWLABEL_MASK;	read_lock_bh(&ip6_sk_fl_lock);	for (sfl=np->ipv6_fl_list; sfl; sfl = sfl->next) {		struct ip6_flowlabel *fl = sfl->fl;		if (fl->label == label) {			fl->lastuse = jiffies;			atomic_inc(&fl->users);			read_unlock_bh(&ip6_sk_fl_lock);			return fl;		}	}	read_unlock_bh(&ip6_sk_fl_lock);	return NULL;}EXPORT_SYMBOL_GPL(fl6_sock_lookup);void fl6_free_socklist(struct sock *sk){	struct ipv6_pinfo *np = inet6_sk(sk);	struct ipv6_fl_socklist *sfl;	while ((sfl = np->ipv6_fl_list) != NULL) {		np->ipv6_fl_list = sfl->next;		fl_release(sfl->fl);		kfree(sfl);	}}/* Service routines *//*   It is the only difficult place. flowlabel enforces equal headers   before and including routing header, however user may supply options   following rthdr. */struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,					 struct ip6_flowlabel * fl,					 struct ipv6_txoptions * fopt){	struct ipv6_txoptions * fl_opt = fl->opt;	if (fopt == NULL || fopt->opt_flen == 0)		return fl_opt;	if (fl_opt != NULL) {		opt_space->hopopt = fl_opt->hopopt;		opt_space->dst0opt = fl_opt->dst0opt;		opt_space->srcrt = fl_opt->srcrt;		opt_space->opt_nflen = fl_opt->opt_nflen;	} else {		if (fopt->opt_nflen == 0)			return fopt;		opt_space->hopopt = NULL;		opt_space->dst0opt = NULL;		opt_space->srcrt = NULL;		opt_space->opt_nflen = 0;	}	opt_space->dst1opt = fopt->dst1opt;	opt_space->opt_flen = fopt->opt_flen;	return opt_space;}static unsigned long check_linger(unsigned long ttl){	if (ttl < FL_MIN_LINGER)		return FL_MIN_LINGER*HZ;	if (ttl > FL_MAX_LINGER && !capable(CAP_NET_ADMIN))		return 0;	return ttl*HZ;}static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned long expires){	linger = check_linger(linger);	if (!linger)		return -EPERM;	expires = check_linger(expires);	if (!expires)		return -EPERM;	fl->lastuse = jiffies;	if (time_before(fl->linger, linger))		fl->linger = linger;	if (time_before(expires, fl->linger))		expires = fl->linger;	if (time_before(fl->expires, fl->lastuse + expires))		fl->expires = fl->lastuse + expires;	return 0;}static struct ip6_flowlabel *fl_create(struct in6_flowlabel_req *freq, char __user *optval, int optlen, int *err_p){	struct ip6_flowlabel *fl;	int olen;	int addr_type;	int err;	err = -ENOMEM;	fl = kzalloc(sizeof(*fl), GFP_KERNEL);	if (fl == NULL)		goto done;	olen = optlen - CMSG_ALIGN(sizeof(*freq));	if (olen > 0) {		struct msghdr msg;		struct flowi flowi;		int junk;		err = -ENOMEM;		fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL);		if (fl->opt == NULL)			goto done;		memset(fl->opt, 0, sizeof(*fl->opt));		fl->opt->tot_len = sizeof(*fl->opt) + olen;		err = -EFAULT;		if (copy_from_user(fl->opt+1, optval+CMSG_ALIGN(sizeof(*freq)), olen))			goto done;		msg.msg_controllen = olen;		msg.msg_control = (void*)(fl->opt+1);		flowi.oif = 0;		err = datagram_send_ctl(&msg, &flowi, fl->opt, &junk, &junk);		if (err)			goto done;		err = -EINVAL;		if (fl->opt->opt_flen)			goto done;		if (fl->opt->opt_nflen == 0) {			kfree(fl->opt);			fl->opt = NULL;		}	}	fl->expires = jiffies;	err = fl6_renew(fl, freq->flr_linger, freq->flr_expires);	if (err)		goto done;	fl->share = freq->flr_share;	addr_type = ipv6_addr_type(&freq->flr_dst);	if ((addr_type&IPV6_ADDR_MAPPED)	    || addr_type == IPV6_ADDR_ANY) {		err = -EINVAL;		goto done;	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -