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

📄 af_key.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * net/key/af_key.c	An implementation of PF_KEYv2 sockets. * *		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:	Maxim Giryaev	<gem@asplinux.ru> *		David S. Miller	<davem@redhat.com> *		Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> *		Kunihiro Ishiguro <kunihiro@ipinfusion.com> *		Kazunori MIYAZAWA / USAGI Project <miyazawa@linux-ipv6.org> *		Derek Atkins <derek@ihtfp.com> */#include <linux/capability.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/socket.h>#include <linux/pfkeyv2.h>#include <linux/ipsec.h>#include <linux/skbuff.h>#include <linux/rtnetlink.h>#include <linux/in.h>#include <linux/in6.h>#include <linux/proc_fs.h>#include <linux/init.h>#include <net/net_namespace.h>#include <net/xfrm.h>#include <net/sock.h>#define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x))#define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x))/* List of all pfkey sockets. */static HLIST_HEAD(pfkey_table);static DECLARE_WAIT_QUEUE_HEAD(pfkey_table_wait);static DEFINE_RWLOCK(pfkey_table_lock);static atomic_t pfkey_table_users = ATOMIC_INIT(0);static atomic_t pfkey_socks_nr = ATOMIC_INIT(0);struct pfkey_sock {	/* struct sock must be the first member of struct pfkey_sock */	struct sock	sk;	int		registered;	int		promisc;};static inline struct pfkey_sock *pfkey_sk(struct sock *sk){	return (struct pfkey_sock *)sk;}static void pfkey_sock_destruct(struct sock *sk){	skb_queue_purge(&sk->sk_receive_queue);	if (!sock_flag(sk, SOCK_DEAD)) {		printk("Attempt to release alive pfkey socket: %p\n", sk);		return;	}	BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));	BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));	atomic_dec(&pfkey_socks_nr);}static void pfkey_table_grab(void){	write_lock_bh(&pfkey_table_lock);	if (atomic_read(&pfkey_table_users)) {		DECLARE_WAITQUEUE(wait, current);		add_wait_queue_exclusive(&pfkey_table_wait, &wait);		for(;;) {			set_current_state(TASK_UNINTERRUPTIBLE);			if (atomic_read(&pfkey_table_users) == 0)				break;			write_unlock_bh(&pfkey_table_lock);			schedule();			write_lock_bh(&pfkey_table_lock);		}		__set_current_state(TASK_RUNNING);		remove_wait_queue(&pfkey_table_wait, &wait);	}}static __inline__ void pfkey_table_ungrab(void){	write_unlock_bh(&pfkey_table_lock);	wake_up(&pfkey_table_wait);}static __inline__ void pfkey_lock_table(void){	/* read_lock() synchronizes us to pfkey_table_grab */	read_lock(&pfkey_table_lock);	atomic_inc(&pfkey_table_users);	read_unlock(&pfkey_table_lock);}static __inline__ void pfkey_unlock_table(void){	if (atomic_dec_and_test(&pfkey_table_users))		wake_up(&pfkey_table_wait);}static const struct proto_ops pfkey_ops;static void pfkey_insert(struct sock *sk){	pfkey_table_grab();	sk_add_node(sk, &pfkey_table);	pfkey_table_ungrab();}static void pfkey_remove(struct sock *sk){	pfkey_table_grab();	sk_del_node_init(sk);	pfkey_table_ungrab();}static struct proto key_proto = {	.name	  = "KEY",	.owner	  = THIS_MODULE,	.obj_size = sizeof(struct pfkey_sock),};static int pfkey_create(struct net *net, struct socket *sock, int protocol){	struct sock *sk;	int err;	if (net != &init_net)		return -EAFNOSUPPORT;	if (!capable(CAP_NET_ADMIN))		return -EPERM;	if (sock->type != SOCK_RAW)		return -ESOCKTNOSUPPORT;	if (protocol != PF_KEY_V2)		return -EPROTONOSUPPORT;	err = -ENOMEM;	sk = sk_alloc(net, PF_KEY, GFP_KERNEL, &key_proto);	if (sk == NULL)		goto out;	sock->ops = &pfkey_ops;	sock_init_data(sock, sk);	sk->sk_family = PF_KEY;	sk->sk_destruct = pfkey_sock_destruct;	atomic_inc(&pfkey_socks_nr);	pfkey_insert(sk);	return 0;out:	return err;}static int pfkey_release(struct socket *sock){	struct sock *sk = sock->sk;	if (!sk)		return 0;	pfkey_remove(sk);	sock_orphan(sk);	sock->sk = NULL;	skb_queue_purge(&sk->sk_write_queue);	sock_put(sk);	return 0;}static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2,			       gfp_t allocation, struct sock *sk){	int err = -ENOBUFS;	sock_hold(sk);	if (*skb2 == NULL) {		if (atomic_read(&skb->users) != 1) {			*skb2 = skb_clone(skb, allocation);		} else {			*skb2 = skb;			atomic_inc(&skb->users);		}	}	if (*skb2 != NULL) {		if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) {			skb_orphan(*skb2);			skb_set_owner_r(*skb2, sk);			skb_queue_tail(&sk->sk_receive_queue, *skb2);			sk->sk_data_ready(sk, (*skb2)->len);			*skb2 = NULL;			err = 0;		}	}	sock_put(sk);	return err;}/* Send SKB to all pfkey sockets matching selected criteria.  */#define BROADCAST_ALL		0#define BROADCAST_ONE		1#define BROADCAST_REGISTERED	2#define BROADCAST_PROMISC_ONLY	4static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,			   int broadcast_flags, struct sock *one_sk){	struct sock *sk;	struct hlist_node *node;	struct sk_buff *skb2 = NULL;	int err = -ESRCH;	/* XXX Do we need something like netlink_overrun?  I think	 * XXX PF_KEY socket apps will not mind current behavior.	 */	if (!skb)		return -ENOMEM;	pfkey_lock_table();	sk_for_each(sk, node, &pfkey_table) {		struct pfkey_sock *pfk = pfkey_sk(sk);		int err2;		/* Yes, it means that if you are meant to receive this		 * pfkey message you receive it twice as promiscuous		 * socket.		 */		if (pfk->promisc)			pfkey_broadcast_one(skb, &skb2, allocation, sk);		/* the exact target will be processed later */		if (sk == one_sk)			continue;		if (broadcast_flags != BROADCAST_ALL) {			if (broadcast_flags & BROADCAST_PROMISC_ONLY)				continue;			if ((broadcast_flags & BROADCAST_REGISTERED) &&			    !pfk->registered)				continue;			if (broadcast_flags & BROADCAST_ONE)				continue;		}		err2 = pfkey_broadcast_one(skb, &skb2, allocation, sk);		/* Error is cleare after succecful sending to at least one		 * registered KM */		if ((broadcast_flags & BROADCAST_REGISTERED) && err)			err = err2;	}	pfkey_unlock_table();	if (one_sk != NULL)		err = pfkey_broadcast_one(skb, &skb2, allocation, one_sk);	if (skb2)		kfree_skb(skb2);	kfree_skb(skb);	return err;}static inline void pfkey_hdr_dup(struct sadb_msg *new, struct sadb_msg *orig){	*new = *orig;}static int pfkey_error(struct sadb_msg *orig, int err, struct sock *sk){	struct sk_buff *skb = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_KERNEL);	struct sadb_msg *hdr;	if (!skb)		return -ENOBUFS;	/* Woe be to the platform trying to support PFKEY yet	 * having normal errnos outside the 1-255 range, inclusive.	 */	err = -err;	if (err == ERESTARTSYS ||	    err == ERESTARTNOHAND ||	    err == ERESTARTNOINTR)		err = EINTR;	if (err >= 512)		err = EINVAL;	BUG_ON(err <= 0 || err >= 256);	hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));	pfkey_hdr_dup(hdr, orig);	hdr->sadb_msg_errno = (uint8_t) err;	hdr->sadb_msg_len = (sizeof(struct sadb_msg) /			     sizeof(uint64_t));	pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ONE, sk);	return 0;}static u8 sadb_ext_min_len[] = {	[SADB_EXT_RESERVED]		= (u8) 0,	[SADB_EXT_SA]			= (u8) sizeof(struct sadb_sa),	[SADB_EXT_LIFETIME_CURRENT]	= (u8) sizeof(struct sadb_lifetime),	[SADB_EXT_LIFETIME_HARD]	= (u8) sizeof(struct sadb_lifetime),	[SADB_EXT_LIFETIME_SOFT]	= (u8) sizeof(struct sadb_lifetime),	[SADB_EXT_ADDRESS_SRC]		= (u8) sizeof(struct sadb_address),	[SADB_EXT_ADDRESS_DST]		= (u8) sizeof(struct sadb_address),	[SADB_EXT_ADDRESS_PROXY]	= (u8) sizeof(struct sadb_address),	[SADB_EXT_KEY_AUTH]		= (u8) sizeof(struct sadb_key),	[SADB_EXT_KEY_ENCRYPT]		= (u8) sizeof(struct sadb_key),	[SADB_EXT_IDENTITY_SRC]		= (u8) sizeof(struct sadb_ident),	[SADB_EXT_IDENTITY_DST]		= (u8) sizeof(struct sadb_ident),	[SADB_EXT_SENSITIVITY]		= (u8) sizeof(struct sadb_sens),	[SADB_EXT_PROPOSAL]		= (u8) sizeof(struct sadb_prop),	[SADB_EXT_SUPPORTED_AUTH]	= (u8) sizeof(struct sadb_supported),	[SADB_EXT_SUPPORTED_ENCRYPT]	= (u8) sizeof(struct sadb_supported),	[SADB_EXT_SPIRANGE]		= (u8) sizeof(struct sadb_spirange),	[SADB_X_EXT_KMPRIVATE]		= (u8) sizeof(struct sadb_x_kmprivate),	[SADB_X_EXT_POLICY]		= (u8) sizeof(struct sadb_x_policy),	[SADB_X_EXT_SA2]		= (u8) sizeof(struct sadb_x_sa2),	[SADB_X_EXT_NAT_T_TYPE]		= (u8) sizeof(struct sadb_x_nat_t_type),	[SADB_X_EXT_NAT_T_SPORT]	= (u8) sizeof(struct sadb_x_nat_t_port),	[SADB_X_EXT_NAT_T_DPORT]	= (u8) sizeof(struct sadb_x_nat_t_port),	[SADB_X_EXT_NAT_T_OA]		= (u8) sizeof(struct sadb_address),	[SADB_X_EXT_SEC_CTX]		= (u8) sizeof(struct sadb_x_sec_ctx),};/* Verify sadb_address_{len,prefixlen} against sa_family.  */static int verify_address_len(void *p){	struct sadb_address *sp = p;	struct sockaddr *addr = (struct sockaddr *)(sp + 1);	struct sockaddr_in *sin;#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)	struct sockaddr_in6 *sin6;#endif	int len;	switch (addr->sa_family) {	case AF_INET:		len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin), sizeof(uint64_t));		if (sp->sadb_address_len != len ||		    sp->sadb_address_prefixlen > 32)			return -EINVAL;		break;#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)	case AF_INET6:		len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin6), sizeof(uint64_t));		if (sp->sadb_address_len != len ||		    sp->sadb_address_prefixlen > 128)			return -EINVAL;		break;#endif	default:		/* It is user using kernel to keep track of security		 * associations for another protocol, such as		 * OSPF/RSVP/RIPV2/MIP.  It is user's job to verify		 * lengths.		 *		 * XXX Actually, association/policy database is not yet		 * XXX able to cope with arbitrary sockaddr families.		 * XXX When it can, remove this -EINVAL.  -DaveM		 */		return -EINVAL;		break;	}	return 0;}static inline int pfkey_sec_ctx_len(struct sadb_x_sec_ctx *sec_ctx){	return DIV_ROUND_UP(sizeof(struct sadb_x_sec_ctx) +			    sec_ctx->sadb_x_ctx_len,			    sizeof(uint64_t));}static inline int verify_sec_ctx_len(void *p){	struct sadb_x_sec_ctx *sec_ctx = (struct sadb_x_sec_ctx *)p;	int len = sec_ctx->sadb_x_ctx_len;	if (len > PAGE_SIZE)		return -EINVAL;	len = pfkey_sec_ctx_len(sec_ctx);	if (sec_ctx->sadb_x_sec_len != len)		return -EINVAL;	return 0;}static inline struct xfrm_user_sec_ctx *pfkey_sadb2xfrm_user_sec_ctx(struct sadb_x_sec_ctx *sec_ctx){	struct xfrm_user_sec_ctx *uctx = NULL;	int ctx_size = sec_ctx->sadb_x_ctx_len;	uctx = kmalloc((sizeof(*uctx)+ctx_size), GFP_KERNEL);	if (!uctx)		return NULL;	uctx->len = pfkey_sec_ctx_len(sec_ctx);	uctx->exttype = sec_ctx->sadb_x_sec_exttype;	uctx->ctx_doi = sec_ctx->sadb_x_ctx_doi;	uctx->ctx_alg = sec_ctx->sadb_x_ctx_alg;	uctx->ctx_len = sec_ctx->sadb_x_ctx_len;	memcpy(uctx + 1, sec_ctx + 1,	       uctx->ctx_len);	return uctx;}static int present_and_same_family(struct sadb_address *src,				   struct sadb_address *dst){	struct sockaddr *s_addr, *d_addr;	if (!src || !dst)		return 0;	s_addr = (struct sockaddr *)(src + 1);	d_addr = (struct sockaddr *)(dst + 1);	if (s_addr->sa_family != d_addr->sa_family)		return 0;	if (s_addr->sa_family != AF_INET#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)	    && s_addr->sa_family != AF_INET6#endif		)		return 0;	return 1;}static int parse_exthdrs(struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs){	char *p = (char *) hdr;	int len = skb->len;	len -= sizeof(*hdr);	p += sizeof(*hdr);	while (len > 0) {		struct sadb_ext *ehdr = (struct sadb_ext *) p;		uint16_t ext_type;		int ext_len;		ext_len  = ehdr->sadb_ext_len;		ext_len *= sizeof(uint64_t);		ext_type = ehdr->sadb_ext_type;		if (ext_len < sizeof(uint64_t) ||		    ext_len > len ||		    ext_type == SADB_EXT_RESERVED)			return -EINVAL;		if (ext_type <= SADB_EXT_MAX) {			int min = (int) sadb_ext_min_len[ext_type];			if (ext_len < min)				return -EINVAL;			if (ext_hdrs[ext_type-1] != NULL)				return -EINVAL;

⌨️ 快捷键说明

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