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

📄 tcp_ipv6.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
}static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk,						struct sock *addr_sk){	return tcp_v6_md5_do_lookup(sk, &inet6_sk(addr_sk)->daddr);}static struct tcp_md5sig_key *tcp_v6_reqsk_md5_lookup(struct sock *sk,						      struct request_sock *req){	return tcp_v6_md5_do_lookup(sk, &inet6_rsk(req)->rmt_addr);}static int tcp_v6_md5_do_add(struct sock *sk, struct in6_addr *peer,			     char *newkey, u8 newkeylen){	/* Add key to the list */	struct tcp_md5sig_key *key;	struct tcp_sock *tp = tcp_sk(sk);	struct tcp6_md5sig_key *keys;	key = tcp_v6_md5_do_lookup(sk, peer);	if (key) {		/* modify existing entry - just update that one */		kfree(key->key);		key->key = newkey;		key->keylen = newkeylen;	} else {		/* reallocate new list if current one is full. */		if (!tp->md5sig_info) {			tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info), GFP_ATOMIC);			if (!tp->md5sig_info) {				kfree(newkey);				return -ENOMEM;			}			sk->sk_route_caps &= ~NETIF_F_GSO_MASK;		}		if (tcp_alloc_md5sig_pool() == NULL) {			kfree(newkey);			return -ENOMEM;		}		if (tp->md5sig_info->alloced6 == tp->md5sig_info->entries6) {			keys = kmalloc((sizeof (tp->md5sig_info->keys6[0]) *				       (tp->md5sig_info->entries6 + 1)), GFP_ATOMIC);			if (!keys) {				tcp_free_md5sig_pool();				kfree(newkey);				return -ENOMEM;			}			if (tp->md5sig_info->entries6)				memmove(keys, tp->md5sig_info->keys6,					(sizeof (tp->md5sig_info->keys6[0]) *					 tp->md5sig_info->entries6));			kfree(tp->md5sig_info->keys6);			tp->md5sig_info->keys6 = keys;			tp->md5sig_info->alloced6++;		}		ipv6_addr_copy(&tp->md5sig_info->keys6[tp->md5sig_info->entries6].addr,			       peer);		tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.key = newkey;		tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.keylen = newkeylen;		tp->md5sig_info->entries6++;	}	return 0;}static int tcp_v6_md5_add_func(struct sock *sk, struct sock *addr_sk,			       u8 *newkey, __u8 newkeylen){	return tcp_v6_md5_do_add(sk, &inet6_sk(addr_sk)->daddr,				 newkey, newkeylen);}static int tcp_v6_md5_do_del(struct sock *sk, struct in6_addr *peer){	struct tcp_sock *tp = tcp_sk(sk);	int i;	for (i = 0; i < tp->md5sig_info->entries6; i++) {		if (ipv6_addr_cmp(&tp->md5sig_info->keys6[i].addr, peer) == 0) {			/* Free the key */			kfree(tp->md5sig_info->keys6[i].base.key);			tp->md5sig_info->entries6--;			if (tp->md5sig_info->entries6 == 0) {				kfree(tp->md5sig_info->keys6);				tp->md5sig_info->keys6 = NULL;				tp->md5sig_info->alloced6 = 0;			} else {				/* shrink the database */				if (tp->md5sig_info->entries6 != i)					memmove(&tp->md5sig_info->keys6[i],						&tp->md5sig_info->keys6[i+1],						(tp->md5sig_info->entries6 - i)						* sizeof (tp->md5sig_info->keys6[0]));			}			tcp_free_md5sig_pool();			return 0;		}	}	return -ENOENT;}static void tcp_v6_clear_md5_list (struct sock *sk){	struct tcp_sock *tp = tcp_sk(sk);	int i;	if (tp->md5sig_info->entries6) {		for (i = 0; i < tp->md5sig_info->entries6; i++)			kfree(tp->md5sig_info->keys6[i].base.key);		tp->md5sig_info->entries6 = 0;		tcp_free_md5sig_pool();	}	kfree(tp->md5sig_info->keys6);	tp->md5sig_info->keys6 = NULL;	tp->md5sig_info->alloced6 = 0;	if (tp->md5sig_info->entries4) {		for (i = 0; i < tp->md5sig_info->entries4; i++)			kfree(tp->md5sig_info->keys4[i].base.key);		tp->md5sig_info->entries4 = 0;		tcp_free_md5sig_pool();	}	kfree(tp->md5sig_info->keys4);	tp->md5sig_info->keys4 = NULL;	tp->md5sig_info->alloced4 = 0;}static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,				  int optlen){	struct tcp_md5sig cmd;	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr;	u8 *newkey;	if (optlen < sizeof(cmd))		return -EINVAL;	if (copy_from_user(&cmd, optval, sizeof(cmd)))		return -EFAULT;	if (sin6->sin6_family != AF_INET6)		return -EINVAL;	if (!cmd.tcpm_keylen) {		if (!tcp_sk(sk)->md5sig_info)			return -ENOENT;		if (ipv6_addr_v4mapped(&sin6->sin6_addr))			return tcp_v4_md5_do_del(sk, sin6->sin6_addr.s6_addr32[3]);		return tcp_v6_md5_do_del(sk, &sin6->sin6_addr);	}	if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)		return -EINVAL;	if (!tcp_sk(sk)->md5sig_info) {		struct tcp_sock *tp = tcp_sk(sk);		struct tcp_md5sig_info *p;		p = kzalloc(sizeof(struct tcp_md5sig_info), GFP_KERNEL);		if (!p)			return -ENOMEM;		tp->md5sig_info = p;		sk->sk_route_caps &= ~NETIF_F_GSO_MASK;	}	newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);	if (!newkey)		return -ENOMEM;	if (ipv6_addr_v4mapped(&sin6->sin6_addr)) {		return tcp_v4_md5_do_add(sk, sin6->sin6_addr.s6_addr32[3],					 newkey, cmd.tcpm_keylen);	}	return tcp_v6_md5_do_add(sk, &sin6->sin6_addr, newkey, cmd.tcpm_keylen);}static int tcp_v6_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,				   struct in6_addr *saddr,				   struct in6_addr *daddr,				   struct tcphdr *th, int protocol,				   int tcplen){	struct scatterlist sg[4];	__u16 data_len;	int block = 0;	__sum16 cksum;	struct tcp_md5sig_pool *hp;	struct tcp6_pseudohdr *bp;	struct hash_desc *desc;	int err;	unsigned int nbytes = 0;	hp = tcp_get_md5sig_pool();	if (!hp) {		printk(KERN_WARNING "%s(): hash pool not found...\n", __FUNCTION__);		goto clear_hash_noput;	}	bp = &hp->md5_blk.ip6;	desc = &hp->md5_desc;	/* 1. TCP pseudo-header (RFC2460) */	ipv6_addr_copy(&bp->saddr, saddr);	ipv6_addr_copy(&bp->daddr, daddr);	bp->len = htonl(tcplen);	bp->protocol = htonl(protocol);	sg_init_table(sg, 4);	sg_set_buf(&sg[block++], bp, sizeof(*bp));	nbytes += sizeof(*bp);	/* 2. TCP header, excluding options */	cksum = th->check;	th->check = 0;	sg_set_buf(&sg[block++], th, sizeof(*th));	nbytes += sizeof(*th);	/* 3. TCP segment data (if any) */	data_len = tcplen - (th->doff << 2);	if (data_len > 0) {		u8 *data = (u8 *)th + (th->doff << 2);		sg_set_buf(&sg[block++], data, data_len);		nbytes += data_len;	}	/* 4. shared key */	sg_set_buf(&sg[block++], key->key, key->keylen);	nbytes += key->keylen;	sg_mark_end(&sg[block - 1]);	/* Now store the hash into the packet */	err = crypto_hash_init(desc);	if (err) {		printk(KERN_WARNING "%s(): hash_init failed\n", __FUNCTION__);		goto clear_hash;	}	err = crypto_hash_update(desc, sg, nbytes);	if (err) {		printk(KERN_WARNING "%s(): hash_update failed\n", __FUNCTION__);		goto clear_hash;	}	err = crypto_hash_final(desc, md5_hash);	if (err) {		printk(KERN_WARNING "%s(): hash_final failed\n", __FUNCTION__);		goto clear_hash;	}	/* Reset header, and free up the crypto */	tcp_put_md5sig_pool();	th->check = cksum;out:	return 0;clear_hash:	tcp_put_md5sig_pool();clear_hash_noput:	memset(md5_hash, 0, 16);	goto out;}static int tcp_v6_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,				struct sock *sk,				struct dst_entry *dst,				struct request_sock *req,				struct tcphdr *th, int protocol,				int tcplen){	struct in6_addr *saddr, *daddr;	if (sk) {		saddr = &inet6_sk(sk)->saddr;		daddr = &inet6_sk(sk)->daddr;	} else {		saddr = &inet6_rsk(req)->loc_addr;		daddr = &inet6_rsk(req)->rmt_addr;	}	return tcp_v6_do_calc_md5_hash(md5_hash, key,				       saddr, daddr,				       th, protocol, tcplen);}static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb){	__u8 *hash_location = NULL;	struct tcp_md5sig_key *hash_expected;	struct ipv6hdr *ip6h = ipv6_hdr(skb);	struct tcphdr *th = tcp_hdr(skb);	int length = (th->doff << 2) - sizeof (*th);	int genhash;	u8 *ptr;	u8 newhash[16];	hash_expected = tcp_v6_md5_do_lookup(sk, &ip6h->saddr);	/* If the TCP option is too short, we can short cut */	if (length < TCPOLEN_MD5SIG)		return hash_expected ? 1 : 0;	/* parse options */	ptr = (u8*)(th + 1);	while (length > 0) {		int opcode = *ptr++;		int opsize;		switch(opcode) {		case TCPOPT_EOL:			goto done_opts;		case TCPOPT_NOP:			length--;			continue;		default:			opsize = *ptr++;			if (opsize < 2 || opsize > length)				goto done_opts;			if (opcode == TCPOPT_MD5SIG) {				hash_location = ptr;				goto done_opts;			}		}		ptr += opsize - 2;		length -= opsize;	}done_opts:	/* do we have a hash as expected? */	if (!hash_expected) {		if (!hash_location)			return 0;		if (net_ratelimit()) {			printk(KERN_INFO "MD5 Hash NOT expected but found "			       "(" NIP6_FMT ", %u)->"			       "(" NIP6_FMT ", %u)\n",			       NIP6(ip6h->saddr), ntohs(th->source),			       NIP6(ip6h->daddr), ntohs(th->dest));		}		return 1;	}	if (!hash_location) {		if (net_ratelimit()) {			printk(KERN_INFO "MD5 Hash expected but NOT found "			       "(" NIP6_FMT ", %u)->"			       "(" NIP6_FMT ", %u)\n",			       NIP6(ip6h->saddr), ntohs(th->source),			       NIP6(ip6h->daddr), ntohs(th->dest));		}		return 1;	}	/* check the signature */	genhash = tcp_v6_do_calc_md5_hash(newhash,					  hash_expected,					  &ip6h->saddr, &ip6h->daddr,					  th, sk->sk_protocol,					  skb->len);	if (genhash || memcmp(hash_location, newhash, 16) != 0) {		if (net_ratelimit()) {			printk(KERN_INFO "MD5 Hash %s for "			       "(" NIP6_FMT ", %u)->"			       "(" NIP6_FMT ", %u)\n",			       genhash ? "failed" : "mismatch",			       NIP6(ip6h->saddr), ntohs(th->source),			       NIP6(ip6h->daddr), ntohs(th->dest));		}		return 1;	}	return 0;}#endifstatic struct request_sock_ops tcp6_request_sock_ops __read_mostly = {	.family		=	AF_INET6,	.obj_size	=	sizeof(struct tcp6_request_sock),	.rtx_syn_ack	=	tcp_v6_send_synack,	.send_ack	=	tcp_v6_reqsk_send_ack,	.destructor	=	tcp_v6_reqsk_destructor,	.send_reset	=	tcp_v6_send_reset};#ifdef CONFIG_TCP_MD5SIGstatic struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {	.md5_lookup	=	tcp_v6_reqsk_md5_lookup,};#endifstatic struct timewait_sock_ops tcp6_timewait_sock_ops = {	.twsk_obj_size	= sizeof(struct tcp6_timewait_sock),	.twsk_unique	= tcp_twsk_unique,	.twsk_destructor= tcp_twsk_destructor,};static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb){	struct ipv6_pinfo *np = inet6_sk(sk);	struct tcphdr *th = tcp_hdr(skb);	if (skb->ip_summed == CHECKSUM_PARTIAL) {		th->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,  0);		skb->csum_start = skb_transport_header(skb) - skb->head;		skb->csum_offset = offsetof(struct tcphdr, check);	} else {		th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,					    csum_partial((char *)th, th->doff<<2,							 skb->csum));	}}static int tcp_v6_gso_send_check(struct sk_buff *skb){	struct ipv6hdr *ipv6h;	struct tcphdr *th;	if (!pskb_may_pull(skb, sizeof(*th)))		return -EINVAL;	ipv6h = ipv6_hdr(skb);	th = tcp_hdr(skb);	th->check = 0;	th->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,				     IPPROTO_TCP, 0);	skb->csum_start = skb_transport_header(skb) - skb->head;	skb->csum_offset = offsetof(struct tcphdr, check);	skb->ip_summed = CHECKSUM_PARTIAL;	return 0;}static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb){	struct tcphdr *th = tcp_hdr(skb), *t1;	struct sk_buff *buff;	struct flowi fl;	int tot_len = sizeof(*th);#ifdef CONFIG_TCP_MD5SIG	struct tcp_md5sig_key *key;#endif	if (th->rst)		return;	if (!ipv6_unicast_destination(skb))		return;#ifdef CONFIG_TCP_MD5SIG	if (sk)		key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr);	else		key = NULL;	if (key)		tot_len += TCPOLEN_MD5SIG_ALIGNED;#endif	/*	 * We need to grab some memory, and put together an RST,	 * and then put it into the queue to be sent.	 */	buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) + tot_len,			 GFP_ATOMIC);	if (buff == NULL)		return;	skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr) + tot_len);	t1 = (struct tcphdr *) skb_push(buff, tot_len);	/* Swap the send and the receive. */	memset(t1, 0, sizeof(*t1));	t1->dest = th->source;	t1->source = th->dest;	t1->doff = tot_len / 4;	t1->rst = 1;	if(th->ack) {		t1->seq = th->ack_seq;	} else {		t1->ack = 1;		t1->ack_seq = htonl(ntohl(th->seq) + th->syn + th->fin				    + skb->len - (th->doff<<2));	}#ifdef CONFIG_TCP_MD5SIG	if (key) {		__be32 *opt = (__be32*)(t1 + 1);		opt[0] = htonl((TCPOPT_NOP << 24) |			       (TCPOPT_NOP << 16) |			       (TCPOPT_MD5SIG << 8) |			       TCPOLEN_MD5SIG);		tcp_v6_do_calc_md5_hash((__u8 *)&opt[1], key,					&ipv6_hdr(skb)->daddr,					&ipv6_hdr(skb)->saddr,					t1, IPPROTO_TCP, tot_len);	}#endif	buff->csum = csum_partial((char *)t1, sizeof(*t1), 0);	memset(&fl, 0, sizeof(fl));	ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr);	ipv6_addr_copy(&fl.fl6_src, &ipv6_hdr(skb)->daddr);	t1->check = csum_ipv6_magic(&fl.fl6_src, &fl.fl6_dst,				    sizeof(*t1), IPPROTO_TCP,				    buff->csum);	fl.proto = IPPROTO_TCP;	fl.oif = inet6_iif(skb);	fl.fl_ip_dport = t1->dest;	fl.fl_ip_sport = t1->source;	security_skb_classify_flow(skb, &fl);	/* sk = NULL, but it is safe for now. RST socket required. */	if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {		if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) {			ip6_xmit(tcp6_socket->sk, buff, &fl, NULL, 0);			TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);			TCP_INC_STATS_BH(TCP_MIB_OUTRSTS);			return;		}	}	kfree_skb(buff);}static void tcp_v6_send_ack(struct tcp_timewait_sock *tw,			    struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts){	struct tcphdr *th = tcp_hdr(skb), *t1;	struct sk_buff *buff;	struct flowi fl;	int tot_len = sizeof(struct tcphdr);	__be32 *topt;#ifdef CONFIG_TCP_MD5SIG

⌨️ 快捷键说明

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