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

📄 tcp_ipv4.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	bh_unlock_sock(sk);	sock_put(sk);}/* This routine computes an IPv4 TCP checksum. */void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb){	struct inet_sock *inet = inet_sk(sk);	struct tcphdr *th = tcp_hdr(skb);	if (skb->ip_summed == CHECKSUM_PARTIAL) {		th->check = ~tcp_v4_check(len, inet->saddr,					  inet->daddr, 0);		skb->csum_start = skb_transport_header(skb) - skb->head;		skb->csum_offset = offsetof(struct tcphdr, check);	} else {		th->check = tcp_v4_check(len, inet->saddr, inet->daddr,					 csum_partial((char *)th,						      th->doff << 2,						      skb->csum));	}}int tcp_v4_gso_send_check(struct sk_buff *skb){	const struct iphdr *iph;	struct tcphdr *th;	if (!pskb_may_pull(skb, sizeof(*th)))		return -EINVAL;	iph = ip_hdr(skb);	th = tcp_hdr(skb);	th->check = 0;	th->check = ~tcp_v4_check(skb->len, iph->saddr, iph->daddr, 0);	skb->csum_start = skb_transport_header(skb) - skb->head;	skb->csum_offset = offsetof(struct tcphdr, check);	skb->ip_summed = CHECKSUM_PARTIAL;	return 0;}/* *	This routine will send an RST to the other tcp. * *	Someone asks: why I NEVER use socket parameters (TOS, TTL etc.) *		      for reset. *	Answer: if a packet caused RST, it is not for a socket *		existing in our system, if it is matched to a socket, *		it is just duplicate segment or bug in other side's TCP. *		So that we build reply only basing on parameters *		arrived with segment. *	Exception: precedence violation. We do not implement it in any case. */static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb){	struct tcphdr *th = tcp_hdr(skb);	struct {		struct tcphdr th;#ifdef CONFIG_TCP_MD5SIG		__be32 opt[(TCPOLEN_MD5SIG_ALIGNED >> 2)];#endif	} rep;	struct ip_reply_arg arg;#ifdef CONFIG_TCP_MD5SIG	struct tcp_md5sig_key *key;#endif	/* Never send a reset in response to a reset. */	if (th->rst)		return;	if (((struct rtable *)skb->dst)->rt_type != RTN_LOCAL)		return;	/* Swap the send and the receive. */	memset(&rep, 0, sizeof(rep));	rep.th.dest   = th->source;	rep.th.source = th->dest;	rep.th.doff   = sizeof(struct tcphdr) / 4;	rep.th.rst    = 1;	if (th->ack) {		rep.th.seq = th->ack_seq;	} else {		rep.th.ack = 1;		rep.th.ack_seq = htonl(ntohl(th->seq) + th->syn + th->fin +				       skb->len - (th->doff << 2));	}	memset(&arg, 0, sizeof(arg));	arg.iov[0].iov_base = (unsigned char *)&rep;	arg.iov[0].iov_len  = sizeof(rep.th);#ifdef CONFIG_TCP_MD5SIG	key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr) : NULL;	if (key) {		rep.opt[0] = htonl((TCPOPT_NOP << 24) |				   (TCPOPT_NOP << 16) |				   (TCPOPT_MD5SIG << 8) |				   TCPOLEN_MD5SIG);		/* Update length and the length the header thinks exists */		arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED;		rep.th.doff = arg.iov[0].iov_len / 4;		tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[1],					key,					ip_hdr(skb)->daddr,					ip_hdr(skb)->saddr,					&rep.th, IPPROTO_TCP,					arg.iov[0].iov_len);	}#endif	arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,				      ip_hdr(skb)->saddr, /* XXX */				      sizeof(struct tcphdr), IPPROTO_TCP, 0);	arg.csumoffset = offsetof(struct tcphdr, check) / 2;	ip_send_reply(tcp_socket->sk, skb, &arg, arg.iov[0].iov_len);	TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);	TCP_INC_STATS_BH(TCP_MIB_OUTRSTS);}/* The code following below sending ACKs in SYN-RECV and TIME-WAIT states   outside socket context is ugly, certainly. What can I do? */static void tcp_v4_send_ack(struct tcp_timewait_sock *twsk,			    struct sk_buff *skb, u32 seq, u32 ack,			    u32 win, u32 ts){	struct tcphdr *th = tcp_hdr(skb);	struct {		struct tcphdr th;		__be32 opt[(TCPOLEN_TSTAMP_ALIGNED >> 2)#ifdef CONFIG_TCP_MD5SIG			   + (TCPOLEN_MD5SIG_ALIGNED >> 2)#endif			];	} rep;	struct ip_reply_arg arg;#ifdef CONFIG_TCP_MD5SIG	struct tcp_md5sig_key *key;	struct tcp_md5sig_key tw_key;#endif	memset(&rep.th, 0, sizeof(struct tcphdr));	memset(&arg, 0, sizeof(arg));	arg.iov[0].iov_base = (unsigned char *)&rep;	arg.iov[0].iov_len  = sizeof(rep.th);	if (ts) {		rep.opt[0] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |				   (TCPOPT_TIMESTAMP << 8) |				   TCPOLEN_TIMESTAMP);		rep.opt[1] = htonl(tcp_time_stamp);		rep.opt[2] = htonl(ts);		arg.iov[0].iov_len += TCPOLEN_TSTAMP_ALIGNED;	}	/* Swap the send and the receive. */	rep.th.dest    = th->source;	rep.th.source  = th->dest;	rep.th.doff    = arg.iov[0].iov_len / 4;	rep.th.seq     = htonl(seq);	rep.th.ack_seq = htonl(ack);	rep.th.ack     = 1;	rep.th.window  = htons(win);#ifdef CONFIG_TCP_MD5SIG	/*	 * The SKB holds an imcoming packet, but may not have a valid ->sk	 * pointer. This is especially the case when we're dealing with a	 * TIME_WAIT ack, because the sk structure is long gone, and only	 * the tcp_timewait_sock remains. So the md5 key is stashed in that	 * structure, and we use it in preference.  I believe that (twsk ||	 * skb->sk) holds true, but we program defensively.	 */	if (!twsk && skb->sk) {		key = tcp_v4_md5_do_lookup(skb->sk, ip_hdr(skb)->daddr);	} else if (twsk && twsk->tw_md5_keylen) {		tw_key.key = twsk->tw_md5_key;		tw_key.keylen = twsk->tw_md5_keylen;		key = &tw_key;	} else		key = NULL;	if (key) {		int offset = (ts) ? 3 : 0;		rep.opt[offset++] = htonl((TCPOPT_NOP << 24) |					  (TCPOPT_NOP << 16) |					  (TCPOPT_MD5SIG << 8) |					  TCPOLEN_MD5SIG);		arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED;		rep.th.doff = arg.iov[0].iov_len/4;		tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[offset],					key,					ip_hdr(skb)->daddr,					ip_hdr(skb)->saddr,					&rep.th, IPPROTO_TCP,					arg.iov[0].iov_len);	}#endif	arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,				      ip_hdr(skb)->saddr, /* XXX */				      arg.iov[0].iov_len, IPPROTO_TCP, 0);	arg.csumoffset = offsetof(struct tcphdr, check) / 2;	if (twsk)		arg.bound_dev_if = twsk->tw_sk.tw_bound_dev_if;	ip_send_reply(tcp_socket->sk, skb, &arg, arg.iov[0].iov_len);	TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);}static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb){	struct inet_timewait_sock *tw = inet_twsk(sk);	struct tcp_timewait_sock *tcptw = tcp_twsk(sk);	tcp_v4_send_ack(tcptw, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,			tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,			tcptw->tw_ts_recent);	inet_twsk_put(tw);}static void tcp_v4_reqsk_send_ack(struct sk_buff *skb,				  struct request_sock *req){	tcp_v4_send_ack(NULL, skb, tcp_rsk(req)->snt_isn + 1,			tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd,			req->ts_recent);}/* *	Send a SYN-ACK after having received an ACK. *	This still operates on a request_sock only, not on a big *	socket. */static int tcp_v4_send_synack(struct sock *sk, struct request_sock *req,			      struct dst_entry *dst){	const struct inet_request_sock *ireq = inet_rsk(req);	int err = -1;	struct sk_buff * skb;	/* First, grab a route. */	if (!dst && (dst = inet_csk_route_req(sk, req)) == NULL)		goto out;	skb = tcp_make_synack(sk, dst, req);	if (skb) {		struct tcphdr *th = tcp_hdr(skb);		th->check = tcp_v4_check(skb->len,					 ireq->loc_addr,					 ireq->rmt_addr,					 csum_partial((char *)th, skb->len,						      skb->csum));		err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr,					    ireq->rmt_addr,					    ireq->opt);		err = net_xmit_eval(err);	}out:	dst_release(dst);	return err;}/* *	IPv4 request_sock destructor. */static void tcp_v4_reqsk_destructor(struct request_sock *req){	kfree(inet_rsk(req)->opt);}#ifdef CONFIG_SYN_COOKIESstatic void syn_flood_warning(struct sk_buff *skb){	static unsigned long warntime;	if (time_after(jiffies, (warntime + HZ * 60))) {		warntime = jiffies;		printk(KERN_INFO		       "possible SYN flooding on port %d. Sending cookies.\n",		       ntohs(tcp_hdr(skb)->dest));	}}#endif/* * Save and compile IPv4 options into the request_sock if needed. */static struct ip_options *tcp_v4_save_options(struct sock *sk,					      struct sk_buff *skb){	struct ip_options *opt = &(IPCB(skb)->opt);	struct ip_options *dopt = NULL;	if (opt && opt->optlen) {		int opt_size = optlength(opt);		dopt = kmalloc(opt_size, GFP_ATOMIC);		if (dopt) {			if (ip_options_echo(dopt, skb)) {				kfree(dopt);				dopt = NULL;			}		}	}	return dopt;}#ifdef CONFIG_TCP_MD5SIG/* * RFC2385 MD5 checksumming requires a mapping of * IP address->MD5 Key. * We need to maintain these in the sk structure. *//* Find the Key structure for an address.  */static struct tcp_md5sig_key *			tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr){	struct tcp_sock *tp = tcp_sk(sk);	int i;	if (!tp->md5sig_info || !tp->md5sig_info->entries4)		return NULL;	for (i = 0; i < tp->md5sig_info->entries4; i++) {		if (tp->md5sig_info->keys4[i].addr == addr)			return &tp->md5sig_info->keys4[i].base;	}	return NULL;}struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,					 struct sock *addr_sk){	return tcp_v4_md5_do_lookup(sk, inet_sk(addr_sk)->daddr);}EXPORT_SYMBOL(tcp_v4_md5_lookup);static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk,						      struct request_sock *req){	return tcp_v4_md5_do_lookup(sk, inet_rsk(req)->rmt_addr);}/* This can be called on a newly created socket, from other files */int tcp_v4_md5_do_add(struct sock *sk, __be32 addr,		      u8 *newkey, u8 newkeylen){	/* Add Key to the list */	struct tcp_md5sig_key *key;	struct tcp_sock *tp = tcp_sk(sk);	struct tcp4_md5sig_key *keys;	key = tcp_v4_md5_do_lookup(sk, addr);	if (key) {		/* Pre-existing entry - just update that one. */		kfree(key->key);		key->key = newkey;		key->keylen = newkeylen;	} else {		struct tcp_md5sig_info *md5sig;		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;		}		md5sig = tp->md5sig_info;		if (md5sig->alloced4 == md5sig->entries4) {			keys = kmalloc((sizeof(*keys) *					(md5sig->entries4 + 1)), GFP_ATOMIC);			if (!keys) {				kfree(newkey);				tcp_free_md5sig_pool();				return -ENOMEM;			}			if (md5sig->entries4)				memcpy(keys, md5sig->keys4,				       sizeof(*keys) * md5sig->entries4);			/* Free old key list, and reference new one */			kfree(md5sig->keys4);			md5sig->keys4 = keys;			md5sig->alloced4++;		}		md5sig->entries4++;		md5sig->keys4[md5sig->entries4 - 1].addr        = addr;		md5sig->keys4[md5sig->entries4 - 1].base.key    = newkey;		md5sig->keys4[md5sig->entries4 - 1].base.keylen = newkeylen;	}	return 0;}EXPORT_SYMBOL(tcp_v4_md5_do_add);static int tcp_v4_md5_add_func(struct sock *sk, struct sock *addr_sk,			       u8 *newkey, u8 newkeylen){	return tcp_v4_md5_do_add(sk, inet_sk(addr_sk)->daddr,				 newkey, newkeylen);}int tcp_v4_md5_do_del(struct sock *sk, __be32 addr){	struct tcp_sock *tp = tcp_sk(sk);	int i;	for (i = 0; i < tp->md5sig_info->entries4; i++) {		if (tp->md5sig_info->keys4[i].addr == addr) {			/* Free the key */			kfree(tp->md5sig_info->keys4[i].base.key);			tp->md5sig_info->entries4--;			if (tp->md5sig_info->entries4 == 0) {				kfree(tp->md5sig_info->keys4);				tp->md5sig_info->keys4 = NULL;				tp->md5sig_info->alloced4 = 0;			} else if (tp->md5sig_info->entries4 != i) {				/* Need to do some manipulation */				memmove(&tp->md5sig_info->keys4[i],					&tp->md5sig_info->keys4[i+1],					(tp->md5sig_info->entries4 - i) *					 sizeof(struct tcp4_md5sig_key));			}			tcp_free_md5sig_pool();			return 0;		}	}	return -ENOENT;}EXPORT_SYMBOL(tcp_v4_md5_do_del);static void tcp_v4_clear_md5_list(struct sock *sk){	struct tcp_sock *tp = tcp_sk(sk);	/* Free each key, then the set of key keys,	 * the crypto element, and then decrement our	 * hold on the last resort crypto.	 */	if (tp->md5sig_info->entries4) {		int i;		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();	}	if (tp->md5sig_info->keys4) {		kfree(tp->md5sig_info->keys4);		tp->md5sig_info->keys4 = NULL;		tp->md5sig_info->alloced4  = 0;	}}static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,				 int optlen){	struct tcp_md5sig cmd;	struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpm_addr;	u8 *newkey;	if (optlen < sizeof(cmd))		return -EINVAL;	if (copy_from_user(&cmd, optval, sizeof(cmd)))		return -EFAULT;	if (sin->sin_family != AF_INET)		return -EINVAL;	if (!cmd.tcpm_key || !cmd.tcpm_keylen) {		if (!tcp_sk(sk)->md5sig_info)

⌨️ 快捷键说明

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