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

📄 tcp_ipv6.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *	TCP over IPv6 *	Linux INET6 implementation * *	Authors: *	Pedro Roque		<roque@di.fc.ul.pt> * *	$Id: tcp_ipv6.c,v 1.144 2002/02/01 22:01:04 davem Exp $ * *	Based on: *	linux/net/ipv4/tcp.c *	linux/net/ipv4/tcp_input.c *	linux/net/ipv4/tcp_output.c * *	Fixes: *	Hideaki YOSHIFUJI	:	sin6_scope_id support *	YOSHIFUJI Hideaki @USAGI and:	Support IPV6_V6ONLY socket option, which *	Alexey Kuznetsov		allow both IPv4 and IPv6 sockets to bind *					a single port at the same time. *	YOSHIFUJI Hideaki @USAGI:	convert /proc/net/tcp6 to seq_file. * *	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. */#include <linux/module.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/net.h>#include <linux/jiffies.h>#include <linux/in.h>#include <linux/in6.h>#include <linux/netdevice.h>#include <linux/init.h>#include <linux/jhash.h>#include <linux/ipsec.h>#include <linux/times.h>#include <linux/ipv6.h>#include <linux/icmpv6.h>#include <linux/random.h>#include <net/tcp.h>#include <net/ndisc.h>#include <net/inet6_hashtables.h>#include <net/inet6_connection_sock.h>#include <net/ipv6.h>#include <net/transp_v6.h>#include <net/addrconf.h>#include <net/ip6_route.h>#include <net/ip6_checksum.h>#include <net/inet_ecn.h>#include <net/protocol.h>#include <net/xfrm.h>#include <net/snmp.h>#include <net/dsfield.h>#include <net/timewait_sock.h>#include <net/netdma.h>#include <asm/uaccess.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/crypto.h>#include <linux/scatterlist.h>/* Socket used for sending RSTs and ACKs */static struct socket *tcp6_socket;static void	tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb);static void	tcp_v6_reqsk_send_ack(struct sk_buff *skb, struct request_sock *req);static void	tcp_v6_send_check(struct sock *sk, int len,				  struct sk_buff *skb);static int	tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);static struct inet_connection_sock_af_ops ipv6_mapped;static struct inet_connection_sock_af_ops ipv6_specific;#ifdef CONFIG_TCP_MD5SIGstatic struct tcp_sock_af_ops tcp_sock_ipv6_specific;static struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific;#endifstatic int tcp_v6_get_port(struct sock *sk, unsigned short snum){	return inet_csk_get_port(&tcp_hashinfo, sk, snum,				 inet6_csk_bind_conflict);}static void tcp_v6_hash(struct sock *sk){	if (sk->sk_state != TCP_CLOSE) {		if (inet_csk(sk)->icsk_af_ops == &ipv6_mapped) {			tcp_prot.hash(sk);			return;		}		local_bh_disable();		__inet6_hash(&tcp_hashinfo, sk);		local_bh_enable();	}}static __inline__ __sum16 tcp_v6_check(struct tcphdr *th, int len,				   struct in6_addr *saddr,				   struct in6_addr *daddr,				   __wsum base){	return csum_ipv6_magic(saddr, daddr, len, IPPROTO_TCP, base);}static __u32 tcp_v6_init_sequence(struct sk_buff *skb){	return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32,					    ipv6_hdr(skb)->saddr.s6_addr32,					    tcp_hdr(skb)->dest,					    tcp_hdr(skb)->source);}static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,			  int addr_len){	struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;	struct inet_sock *inet = inet_sk(sk);	struct inet_connection_sock *icsk = inet_csk(sk);	struct ipv6_pinfo *np = inet6_sk(sk);	struct tcp_sock *tp = tcp_sk(sk);	struct in6_addr *saddr = NULL, *final_p = NULL, final;	struct flowi fl;	struct dst_entry *dst;	int addr_type;	int err;	if (addr_len < SIN6_LEN_RFC2133)		return -EINVAL;	if (usin->sin6_family != AF_INET6)		return(-EAFNOSUPPORT);	memset(&fl, 0, sizeof(fl));	if (np->sndflow) {		fl.fl6_flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;		IP6_ECN_flow_init(fl.fl6_flowlabel);		if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {			struct ip6_flowlabel *flowlabel;			flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);			if (flowlabel == NULL)				return -EINVAL;			ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);			fl6_sock_release(flowlabel);		}	}	/*	 *	connect() to INADDR_ANY means loopback (BSD'ism).	 */	if(ipv6_addr_any(&usin->sin6_addr))		usin->sin6_addr.s6_addr[15] = 0x1;	addr_type = ipv6_addr_type(&usin->sin6_addr);	if(addr_type & IPV6_ADDR_MULTICAST)		return -ENETUNREACH;	if (addr_type&IPV6_ADDR_LINKLOCAL) {		if (addr_len >= sizeof(struct sockaddr_in6) &&		    usin->sin6_scope_id) {			/* If interface is set while binding, indices			 * must coincide.			 */			if (sk->sk_bound_dev_if &&			    sk->sk_bound_dev_if != usin->sin6_scope_id)				return -EINVAL;			sk->sk_bound_dev_if = usin->sin6_scope_id;		}		/* Connect to link-local address requires an interface */		if (!sk->sk_bound_dev_if)			return -EINVAL;	}	if (tp->rx_opt.ts_recent_stamp &&	    !ipv6_addr_equal(&np->daddr, &usin->sin6_addr)) {		tp->rx_opt.ts_recent = 0;		tp->rx_opt.ts_recent_stamp = 0;		tp->write_seq = 0;	}	ipv6_addr_copy(&np->daddr, &usin->sin6_addr);	np->flow_label = fl.fl6_flowlabel;	/*	 *	TCP over IPv4	 */	if (addr_type == IPV6_ADDR_MAPPED) {		u32 exthdrlen = icsk->icsk_ext_hdr_len;		struct sockaddr_in sin;		SOCK_DEBUG(sk, "connect: ipv4 mapped\n");		if (__ipv6_only_sock(sk))			return -ENETUNREACH;		sin.sin_family = AF_INET;		sin.sin_port = usin->sin6_port;		sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];		icsk->icsk_af_ops = &ipv6_mapped;		sk->sk_backlog_rcv = tcp_v4_do_rcv;#ifdef CONFIG_TCP_MD5SIG		tp->af_specific = &tcp_sock_ipv6_mapped_specific;#endif		err = tcp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));		if (err) {			icsk->icsk_ext_hdr_len = exthdrlen;			icsk->icsk_af_ops = &ipv6_specific;			sk->sk_backlog_rcv = tcp_v6_do_rcv;#ifdef CONFIG_TCP_MD5SIG			tp->af_specific = &tcp_sock_ipv6_specific;#endif			goto failure;		} else {			ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000FFFF),				      inet->saddr);			ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000FFFF),				      inet->rcv_saddr);		}		return err;	}	if (!ipv6_addr_any(&np->rcv_saddr))		saddr = &np->rcv_saddr;	fl.proto = IPPROTO_TCP;	ipv6_addr_copy(&fl.fl6_dst, &np->daddr);	ipv6_addr_copy(&fl.fl6_src,		       (saddr ? saddr : &np->saddr));	fl.oif = sk->sk_bound_dev_if;	fl.fl_ip_dport = usin->sin6_port;	fl.fl_ip_sport = inet->sport;	if (np->opt && np->opt->srcrt) {		struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;		ipv6_addr_copy(&final, &fl.fl6_dst);		ipv6_addr_copy(&fl.fl6_dst, rt0->addr);		final_p = &final;	}	security_sk_classify_flow(sk, &fl);	err = ip6_dst_lookup(sk, &dst, &fl);	if (err)		goto failure;	if (final_p)		ipv6_addr_copy(&fl.fl6_dst, final_p);	if ((err = __xfrm_lookup(&dst, &fl, sk, 1)) < 0) {		if (err == -EREMOTE)			err = ip6_dst_blackhole(sk, &dst, &fl);		if (err < 0)			goto failure;	}	if (saddr == NULL) {		saddr = &fl.fl6_src;		ipv6_addr_copy(&np->rcv_saddr, saddr);	}	/* set the source address */	ipv6_addr_copy(&np->saddr, saddr);	inet->rcv_saddr = LOOPBACK4_IPV6;	sk->sk_gso_type = SKB_GSO_TCPV6;	__ip6_dst_store(sk, dst, NULL, NULL);	icsk->icsk_ext_hdr_len = 0;	if (np->opt)		icsk->icsk_ext_hdr_len = (np->opt->opt_flen +					  np->opt->opt_nflen);	tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);	inet->dport = usin->sin6_port;	tcp_set_state(sk, TCP_SYN_SENT);	err = inet6_hash_connect(&tcp_death_row, sk);	if (err)		goto late_failure;	if (!tp->write_seq)		tp->write_seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32,							     np->daddr.s6_addr32,							     inet->sport,							     inet->dport);	err = tcp_connect(sk);	if (err)		goto late_failure;	return 0;late_failure:	tcp_set_state(sk, TCP_CLOSE);	__sk_dst_reset(sk);failure:	inet->dport = 0;	sk->sk_route_caps = 0;	return err;}static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,		int type, int code, int offset, __be32 info){	struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data;	const struct tcphdr *th = (struct tcphdr *)(skb->data+offset);	struct ipv6_pinfo *np;	struct sock *sk;	int err;	struct tcp_sock *tp;	__u32 seq;	sk = inet6_lookup(&tcp_hashinfo, &hdr->daddr, th->dest, &hdr->saddr,			  th->source, skb->dev->ifindex);	if (sk == NULL) {		ICMP6_INC_STATS_BH(__in6_dev_get(skb->dev), ICMP6_MIB_INERRORS);		return;	}	if (sk->sk_state == TCP_TIME_WAIT) {		inet_twsk_put(inet_twsk(sk));		return;	}	bh_lock_sock(sk);	if (sock_owned_by_user(sk))		NET_INC_STATS_BH(LINUX_MIB_LOCKDROPPEDICMPS);	if (sk->sk_state == TCP_CLOSE)		goto out;	tp = tcp_sk(sk);	seq = ntohl(th->seq);	if (sk->sk_state != TCP_LISTEN &&	    !between(seq, tp->snd_una, tp->snd_nxt)) {		NET_INC_STATS_BH(LINUX_MIB_OUTOFWINDOWICMPS);		goto out;	}	np = inet6_sk(sk);	if (type == ICMPV6_PKT_TOOBIG) {		struct dst_entry *dst = NULL;		if (sock_owned_by_user(sk))			goto out;		if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))			goto out;		/* icmp should have updated the destination cache entry */		dst = __sk_dst_check(sk, np->dst_cookie);		if (dst == NULL) {			struct inet_sock *inet = inet_sk(sk);			struct flowi fl;			/* BUGGG_FUTURE: Again, it is not clear how			   to handle rthdr case. Ignore this complexity			   for now.			 */			memset(&fl, 0, sizeof(fl));			fl.proto = IPPROTO_TCP;			ipv6_addr_copy(&fl.fl6_dst, &np->daddr);			ipv6_addr_copy(&fl.fl6_src, &np->saddr);			fl.oif = sk->sk_bound_dev_if;			fl.fl_ip_dport = inet->dport;			fl.fl_ip_sport = inet->sport;			security_skb_classify_flow(skb, &fl);			if ((err = ip6_dst_lookup(sk, &dst, &fl))) {				sk->sk_err_soft = -err;				goto out;			}			if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {				sk->sk_err_soft = -err;				goto out;			}		} else			dst_hold(dst);		if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst)) {			tcp_sync_mss(sk, dst_mtu(dst));			tcp_simple_retransmit(sk);		} /* else let the usual retransmit timer handle it */		dst_release(dst);		goto out;	}	icmpv6_err_convert(type, code, &err);	/* Might be for an request_sock */	switch (sk->sk_state) {		struct request_sock *req, **prev;	case TCP_LISTEN:		if (sock_owned_by_user(sk))			goto out;		req = inet6_csk_search_req(sk, &prev, th->dest, &hdr->daddr,					   &hdr->saddr, inet6_iif(skb));		if (!req)			goto out;		/* ICMPs are not backlogged, hence we cannot get		 * an established socket here.		 */		BUG_TRAP(req->sk == NULL);		if (seq != tcp_rsk(req)->snt_isn) {			NET_INC_STATS_BH(LINUX_MIB_OUTOFWINDOWICMPS);			goto out;		}		inet_csk_reqsk_queue_drop(sk, req, prev);		goto out;	case TCP_SYN_SENT:	case TCP_SYN_RECV:  /* Cannot happen.			       It can, it SYNs are crossed. --ANK */		if (!sock_owned_by_user(sk)) {			sk->sk_err = err;			sk->sk_error_report(sk);		/* Wake people up to see the error (see connect in sock.c) */			tcp_done(sk);		} else			sk->sk_err_soft = err;		goto out;	}	if (!sock_owned_by_user(sk) && np->recverr) {		sk->sk_err = err;		sk->sk_error_report(sk);	} else		sk->sk_err_soft = err;out:	bh_unlock_sock(sk);	sock_put(sk);}static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,			      struct dst_entry *dst){	struct inet6_request_sock *treq = inet6_rsk(req);	struct ipv6_pinfo *np = inet6_sk(sk);	struct sk_buff * skb;	struct ipv6_txoptions *opt = NULL;	struct in6_addr * final_p = NULL, final;	struct flowi fl;	int err = -1;	memset(&fl, 0, sizeof(fl));	fl.proto = IPPROTO_TCP;	ipv6_addr_copy(&fl.fl6_dst, &treq->rmt_addr);	ipv6_addr_copy(&fl.fl6_src, &treq->loc_addr);	fl.fl6_flowlabel = 0;	fl.oif = treq->iif;	fl.fl_ip_dport = inet_rsk(req)->rmt_port;	fl.fl_ip_sport = inet_sk(sk)->sport;	security_req_classify_flow(req, &fl);	if (dst == NULL) {		opt = np->opt;		if (opt && opt->srcrt) {			struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;			ipv6_addr_copy(&final, &fl.fl6_dst);			ipv6_addr_copy(&fl.fl6_dst, rt0->addr);			final_p = &final;		}		err = ip6_dst_lookup(sk, &dst, &fl);		if (err)			goto done;		if (final_p)			ipv6_addr_copy(&fl.fl6_dst, final_p);		if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)			goto done;	}	skb = tcp_make_synack(sk, dst, req);	if (skb) {		struct tcphdr *th = tcp_hdr(skb);		th->check = tcp_v6_check(th, skb->len,					 &treq->loc_addr, &treq->rmt_addr,					 csum_partial((char *)th, skb->len, skb->csum));		ipv6_addr_copy(&fl.fl6_dst, &treq->rmt_addr);		err = ip6_xmit(sk, skb, &fl, opt, 0);		err = net_xmit_eval(err);	}done:	if (opt && opt != np->opt)		sock_kfree_s(sk, opt, opt->tot_len);	dst_release(dst);	return err;}static void tcp_v6_reqsk_destructor(struct request_sock *req){	if (inet6_rsk(req)->pktopts)		kfree_skb(inet6_rsk(req)->pktopts);}#ifdef CONFIG_TCP_MD5SIGstatic struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk,						   struct in6_addr *addr){	struct tcp_sock *tp = tcp_sk(sk);	int i;	BUG_ON(tp == NULL);	if (!tp->md5sig_info || !tp->md5sig_info->entries6)		return NULL;	for (i = 0; i < tp->md5sig_info->entries6; i++) {		if (ipv6_addr_cmp(&tp->md5sig_info->keys6[i].addr, addr) == 0)			return &tp->md5sig_info->keys6[i].base;	}	return NULL;

⌨️ 快捷键说明

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