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

📄 udp.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
		lock_sock(sk);		if (likely(up->pending)) {			if (unlikely(up->pending != AF_INET)) {				release_sock(sk);				return -EINVAL;			}			goto do_append_data;		}		release_sock(sk);	}	ulen += sizeof(struct udphdr);	/*	 *	Get and verify the address.	 */	if (msg->msg_name) {		struct sockaddr_in * usin = (struct sockaddr_in*)msg->msg_name;		if (msg->msg_namelen < sizeof(*usin))			return -EINVAL;		if (usin->sin_family != AF_INET) {			if (usin->sin_family != AF_UNSPEC)				return -EAFNOSUPPORT;		}		daddr = usin->sin_addr.s_addr;		dport = usin->sin_port;		if (dport == 0)			return -EINVAL;	} else {		if (sk->sk_state != TCP_ESTABLISHED)			return -EDESTADDRREQ;		daddr = inet->daddr;		dport = inet->dport;		/* Open fast path for connected socket.		   Route will not be used, if at least one option is set.		 */		connected = 1;	}	ipc.addr = inet->saddr;	ipc.oif = sk->sk_bound_dev_if;	if (msg->msg_controllen) {		err = ip_cmsg_send(msg, &ipc);		if (err)			return err;		if (ipc.opt)			free = 1;		connected = 0;	}	if (!ipc.opt)		ipc.opt = inet->opt;	saddr = ipc.addr;	ipc.addr = faddr = daddr;	if (ipc.opt && ipc.opt->srr) {		if (!daddr)			return -EINVAL;		faddr = ipc.opt->faddr;		connected = 0;	}	tos = RT_TOS(inet->tos);	if (sock_flag(sk, SOCK_LOCALROUTE) ||	    (msg->msg_flags & MSG_DONTROUTE) ||	    (ipc.opt && ipc.opt->is_strictroute)) {		tos |= RTO_ONLINK;		connected = 0;	}	if (MULTICAST(daddr)) {		if (!ipc.oif)			ipc.oif = inet->mc_index;		if (!saddr)			saddr = inet->mc_addr;		connected = 0;	}	if (connected)		rt = (struct rtable*)sk_dst_check(sk, 0);	if (rt == NULL) {		struct flowi fl = { .oif = ipc.oif,				    .nl_u = { .ip4_u =					      { .daddr = faddr,						.saddr = saddr,						.tos = tos } },				    .proto = sk->sk_protocol,				    .uli_u = { .ports =					       { .sport = inet->sport,						 .dport = dport } } };		security_sk_classify_flow(sk, &fl);		err = ip_route_output_flow(&rt, &fl, sk, 1);		if (err) {			if (err == -ENETUNREACH)				IP_INC_STATS_BH(IPSTATS_MIB_OUTNOROUTES);			goto out;		}		err = -EACCES;		if ((rt->rt_flags & RTCF_BROADCAST) &&		    !sock_flag(sk, SOCK_BROADCAST))			goto out;		if (connected)			sk_dst_set(sk, dst_clone(&rt->u.dst));	}	if (msg->msg_flags&MSG_CONFIRM)		goto do_confirm;back_from_confirm:	saddr = rt->rt_src;	if (!ipc.addr)		daddr = ipc.addr = rt->rt_dst;	lock_sock(sk);	if (unlikely(up->pending)) {		/* The socket is already corked while preparing it. */		/* ... which is an evident application bug. --ANK */		release_sock(sk);		LIMIT_NETDEBUG(KERN_DEBUG "udp cork app bug 2\n");		err = -EINVAL;		goto out;	}	/*	 *	Now cork the socket to pend data.	 */	inet->cork.fl.fl4_dst = daddr;	inet->cork.fl.fl_ip_dport = dport;	inet->cork.fl.fl4_src = saddr;	inet->cork.fl.fl_ip_sport = inet->sport;	up->pending = AF_INET;do_append_data:	up->len += ulen;	getfrag  =  is_udplite ?  udplite_getfrag : ip_generic_getfrag;	err = ip_append_data(sk, getfrag, msg->msg_iov, ulen,			sizeof(struct udphdr), &ipc, rt,			corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);	if (err)		udp_flush_pending_frames(sk);	else if (!corkreq)		err = udp_push_pending_frames(sk);	else if (unlikely(skb_queue_empty(&sk->sk_write_queue)))		up->pending = 0;	release_sock(sk);out:	ip_rt_put(rt);	if (free)		kfree(ipc.opt);	if (!err)		return len;	/*	 * ENOBUFS = no kernel mem, SOCK_NOSPACE = no sndbuf space.  Reporting	 * ENOBUFS might not be good (it's not tunable per se), but otherwise	 * we don't have a good statistic (IpOutDiscards but it can be too many	 * things).  We could add another new stat but at least for now that	 * seems like overkill.	 */	if (err == -ENOBUFS || test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) {		UDP_INC_STATS_USER(UDP_MIB_SNDBUFERRORS, is_udplite);	}	return err;do_confirm:	dst_confirm(&rt->u.dst);	if (!(msg->msg_flags&MSG_PROBE) || len)		goto back_from_confirm;	err = 0;	goto out;}int udp_sendpage(struct sock *sk, struct page *page, int offset,		 size_t size, int flags){	struct udp_sock *up = udp_sk(sk);	int ret;	if (!up->pending) {		struct msghdr msg = {	.msg_flags = flags|MSG_MORE };		/* Call udp_sendmsg to specify destination address which		 * sendpage interface can't pass.		 * This will succeed only when the socket is connected.		 */		ret = udp_sendmsg(NULL, sk, &msg, 0);		if (ret < 0)			return ret;	}	lock_sock(sk);	if (unlikely(!up->pending)) {		release_sock(sk);		LIMIT_NETDEBUG(KERN_DEBUG "udp cork app bug 3\n");		return -EINVAL;	}	ret = ip_append_page(sk, page, offset, size, flags);	if (ret == -EOPNOTSUPP) {		release_sock(sk);		return sock_no_sendpage(sk->sk_socket, page, offset,					size, flags);	}	if (ret < 0) {		udp_flush_pending_frames(sk);		goto out;	}	up->len += size;	if (!(up->corkflag || (flags&MSG_MORE)))		ret = udp_push_pending_frames(sk);	if (!ret)		ret = size;out:	release_sock(sk);	return ret;}/* *	IOCTL requests applicable to the UDP protocol */int udp_ioctl(struct sock *sk, int cmd, unsigned long arg){	switch (cmd) {	case SIOCOUTQ:	{		int amount = atomic_read(&sk->sk_wmem_alloc);		return put_user(amount, (int __user *)arg);	}	case SIOCINQ:	{		struct sk_buff *skb;		unsigned long amount;		amount = 0;		spin_lock_bh(&sk->sk_receive_queue.lock);		skb = skb_peek(&sk->sk_receive_queue);		if (skb != NULL) {			/*			 * We will only return the amount			 * of this packet since that is all			 * that will be read.			 */			amount = skb->len - sizeof(struct udphdr);		}		spin_unlock_bh(&sk->sk_receive_queue.lock);		return put_user(amount, (int __user *)arg);	}	default:		return -ENOIOCTLCMD;	}	return 0;}/* * 	This should be easy, if there is something there we * 	return it, otherwise we block. */int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,		size_t len, int noblock, int flags, int *addr_len){	struct inet_sock *inet = inet_sk(sk);	struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;	struct sk_buff *skb;	unsigned int ulen, copied;	int err;	int is_udplite = IS_UDPLITE(sk);	/*	 *	Check any passed addresses	 */	if (addr_len)		*addr_len=sizeof(*sin);	if (flags & MSG_ERRQUEUE)		return ip_recv_error(sk, msg, len);try_again:	skb = skb_recv_datagram(sk, flags, noblock, &err);	if (!skb)		goto out;	ulen = skb->len - sizeof(struct udphdr);	copied = len;	if (copied > ulen)		copied = ulen;	else if (copied < ulen)		msg->msg_flags |= MSG_TRUNC;	/*	 * If checksum is needed at all, try to do it while copying the	 * data.  If the data is truncated, or if we only want a partial	 * coverage checksum (UDP-Lite), do it before the copy.	 */	if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) {		if (udp_lib_checksum_complete(skb))			goto csum_copy_err;	}	if (skb_csum_unnecessary(skb))		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),					      msg->msg_iov, copied       );	else {		err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov);		if (err == -EINVAL)			goto csum_copy_err;	}	if (err)		goto out_free;	sock_recv_timestamp(msg, sk, skb);	/* Copy the address. */	if (sin)	{		sin->sin_family = AF_INET;		sin->sin_port = udp_hdr(skb)->source;		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));	}	if (inet->cmsg_flags)		ip_cmsg_recv(msg, skb);	err = copied;	if (flags & MSG_TRUNC)		err = ulen;out_free:	skb_free_datagram(sk, skb);out:	return err;csum_copy_err:	UDP_INC_STATS_BH(UDP_MIB_INERRORS, is_udplite);	skb_kill_datagram(sk, skb, flags);	if (noblock)		return -EAGAIN;	goto try_again;}int udp_disconnect(struct sock *sk, int flags){	struct inet_sock *inet = inet_sk(sk);	/*	 *	1003.1g - break association.	 */	sk->sk_state = TCP_CLOSE;	inet->daddr = 0;	inet->dport = 0;	sk->sk_bound_dev_if = 0;	if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))		inet_reset_saddr(sk);	if (!(sk->sk_userlocks & SOCK_BINDPORT_LOCK)) {		sk->sk_prot->unhash(sk);		inet->sport = 0;	}	sk_dst_reset(sk);	return 0;}/* returns: *  -1: error *   0: success *  >0: "udp encap" protocol resubmission * * Note that in the success and error cases, the skb is assumed to * have either been requeued or freed. */int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb){	struct udp_sock *up = udp_sk(sk);	int rc;	/*	 *	Charge it to the socket, dropping if the queue is full.	 */	if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))		goto drop;	nf_reset(skb);	if (up->encap_type) {		/*		 * This is an encapsulation socket so pass the skb to		 * the socket's udp_encap_rcv() hook. Otherwise, just		 * fall through and pass this up the UDP socket.		 * up->encap_rcv() returns the following value:		 * =0 if skb was successfully passed to the encap		 *    handler or was discarded by it.		 * >0 if skb should be passed on to UDP.		 * <0 if skb should be resubmitted as proto -N		 */		/* if we're overly short, let UDP handle it */		if (skb->len > sizeof(struct udphdr) &&		    up->encap_rcv != NULL) {			int ret;			ret = (*up->encap_rcv)(sk, skb);			if (ret <= 0) {				UDP_INC_STATS_BH(UDP_MIB_INDATAGRAMS, up->pcflag);				return -ret;			}		}		/* FALLTHROUGH -- it's a UDP Packet */	}	/*	 * 	UDP-Lite specific tests, ignored on UDP sockets	 */	if ((up->pcflag & UDPLITE_RECV_CC)  &&  UDP_SKB_CB(skb)->partial_cov) {		/*		 * MIB statistics other than incrementing the error count are		 * disabled for the following two types of errors: these depend		 * on the application settings, not on the functioning of the		 * protocol stack as such.		 *		 * RFC 3828 here recommends (sec 3.3): "There should also be a		 * way ... to ... at least let the receiving application block		 * delivery of packets with coverage values less than a value		 * provided by the application."		 */		if (up->pcrlen == 0) {          /* full coverage was set  */			LIMIT_NETDEBUG(KERN_WARNING "UDPLITE: partial coverage "				"%d while full coverage %d requested\n",				UDP_SKB_CB(skb)->cscov, skb->len);			goto drop;		}		/* The next case involves violating the min. coverage requested		 * by the receiver. This is subtle: if receiver wants x and x is		 * greater than the buffersize/MTU then receiver will complain		 * that it wants x while sender emits packets of smaller size y.		 * Therefore the above ...()->partial_cov statement is essential.		 */		if (UDP_SKB_CB(skb)->cscov  <  up->pcrlen) {			LIMIT_NETDEBUG(KERN_WARNING				"UDPLITE: coverage %d too small, need min %d\n",				UDP_SKB_CB(skb)->cscov, up->pcrlen);			goto drop;		}	}	if (sk->sk_filter) {		if (udp_lib_checksum_complete(skb))			goto drop;	}	if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) {		/* Note that an ENOMEM error is charged twice */		if (rc == -ENOMEM)			UDP_INC_STATS_BH(UDP_MIB_RCVBUFERRORS, up->pcflag);		goto drop;	}	UDP_INC_STATS_BH(UDP_MIB_INDATAGRAMS, up->pcflag);	return 0;drop:	UDP_INC_STATS_BH(UDP_MIB_INERRORS, up->pcflag);	kfree_skb(skb);	return -1;}/* *	Multicasts and broadcasts go to each listener. * *	Note: called only from the BH handler context, *	so we don't need to lock the hashes. */static int __udp4_lib_mcast_deliver(struct sk_buff *skb,				    struct udphdr  *uh,				    __be32 saddr, __be32 daddr,				    struct hlist_head udptable[]){	struct sock *sk;	int dif;	read_lock(&udp_hash_lock);	sk = sk_head(&udptable[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)]);	dif = skb->dev->ifindex;	sk = udp_v4_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif);	if (sk) {		struct sock *sknext = NULL;		do {			struct sk_buff *skb1 = skb;			sknext = udp_v4_mcast_next(sk_next(sk), uh->dest, daddr,						   uh->source, saddr, dif);			if (sknext)				skb1 = skb_clone(skb, GFP_ATOMIC);			if (skb1) {				int ret = udp_queue_rcv_skb(sk, skb1);				if (ret > 0)					/* we should probably re-process instead					 * of dropping packets here. */					kfree_skb(skb1);			}			sk = sknext;		} while (sknext);	} else		kfree_skb(skb);	read_unlock(&udp_hash_lock);	return 0;}/* Initialize UDP checksum. If exited with zero value (success), * CHECKSUM_UNNECESSARY means, that no more checks are required. * Otherwise, csum completion requires chacksumming packet body, * including udp header and folding it to skb->csum. */static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh,				 int proto){	const struct iphdr *iph;	int err;	UDP_SKB_CB(skb)->partial_cov = 0;	UDP_SKB_CB(skb)->cscov = skb->len;	if (proto == IPPROTO_UDPLITE) {		err = udplite_checksum_init(skb, uh);		if (err)			return err;	}	iph = ip_hdr(skb);	if (uh->check == 0) {		skb->ip_summed = CHECKSUM_UNNECESSARY;	} else if (skb->ip_summed == CHECKSUM_COMPLETE) {	       if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,				      proto, skb->csum))			skb->ip_summed = CHECKSUM_UNNECESSARY;	}	if (!skb_csum_unnecessary(skb))		skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,

⌨️ 快捷键说明

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