transport.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 865 行 · 第 1/2 页

C
865
字号
/* transport.c: Rx Transport routines * * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * 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/sched.h>#include <linux/slab.h>#include <linux/module.h>#include <rxrpc/transport.h>#include <rxrpc/peer.h>#include <rxrpc/connection.h>#include <rxrpc/call.h>#include <rxrpc/message.h>#include <rxrpc/krxiod.h>#include <rxrpc/krxsecd.h>#include <linux/udp.h>#include <linux/in.h>#include <linux/in6.h>#include <linux/icmp.h>#include <net/sock.h>#include <net/ip.h>#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)#include <linux/ipv6.h>	/* this should _really_ be in errqueue.h.. */#endif#include <linux/errqueue.h>#include <asm/uaccess.h>#include <asm/checksum.h>#include "internal.h"struct errormsg {	struct cmsghdr			cmsg;		/* control message header */	struct sock_extended_err	ee;		/* extended error information */	struct sockaddr_in		icmp_src;	/* ICMP packet source address */};static spinlock_t rxrpc_transports_lock = SPIN_LOCK_UNLOCKED;static struct list_head rxrpc_transports = LIST_HEAD_INIT(rxrpc_transports);__RXACCT_DECL(atomic_t rxrpc_transport_count);LIST_HEAD(rxrpc_proc_transports);DECLARE_RWSEM(rxrpc_proc_transports_sem);static void rxrpc_data_ready(struct sock *sk, int count);static void rxrpc_error_report(struct sock *sk);static int rxrpc_trans_receive_new_call(struct rxrpc_transport *trans,					struct list_head *msgq);static void rxrpc_trans_receive_error_report(struct rxrpc_transport *trans);/*****************************************************************************//* * create a new transport endpoint using the specified UDP port */int rxrpc_create_transport(unsigned short port,			   struct rxrpc_transport **_trans){	struct rxrpc_transport *trans;	struct sockaddr_in sin;	mm_segment_t oldfs;	struct sock *sock;	int ret, opt;	_enter("%hu", port);	trans = kmalloc(sizeof(struct rxrpc_transport), GFP_KERNEL);	if (!trans)		return -ENOMEM;	memset(trans, 0, sizeof(struct rxrpc_transport));	atomic_set(&trans->usage, 1);	INIT_LIST_HEAD(&trans->services);	INIT_LIST_HEAD(&trans->link);	INIT_LIST_HEAD(&trans->krxiodq_link);	spin_lock_init(&trans->lock);	INIT_LIST_HEAD(&trans->peer_active);	INIT_LIST_HEAD(&trans->peer_graveyard);	spin_lock_init(&trans->peer_gylock);	init_waitqueue_head(&trans->peer_gy_waitq);	rwlock_init(&trans->peer_lock);	atomic_set(&trans->peer_count, 0);	trans->port = port;	/* create a UDP socket to be my actual transport endpoint */	ret = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &trans->socket);	if (ret < 0)		goto error;	/* use the specified port */	if (port) {		memset(&sin, 0, sizeof(sin));		sin.sin_family = AF_INET;		sin.sin_port = htons(port);		ret = trans->socket->ops->bind(trans->socket,					       (struct sockaddr *) &sin,					       sizeof(sin));		if (ret < 0)			goto error;	}	opt = 1;	oldfs = get_fs();	set_fs(KERNEL_DS);	ret = trans->socket->ops->setsockopt(trans->socket, SOL_IP, IP_RECVERR,					     (char *) &opt, sizeof(opt));	set_fs(oldfs);	spin_lock(&rxrpc_transports_lock);	list_add(&trans->link, &rxrpc_transports);	spin_unlock(&rxrpc_transports_lock);	/* set the socket up */	sock = trans->socket->sk;	sock->sk_user_data	= trans;	sock->sk_data_ready	= rxrpc_data_ready;	sock->sk_error_report	= rxrpc_error_report;	down_write(&rxrpc_proc_transports_sem);	list_add_tail(&trans->proc_link, &rxrpc_proc_transports);	up_write(&rxrpc_proc_transports_sem);	__RXACCT(atomic_inc(&rxrpc_transport_count));	*_trans = trans;	_leave(" = 0 (%p)", trans);	return 0; error:	/* finish cleaning up the transport (not really needed here, but...) */	if (trans->socket)		trans->socket->ops->shutdown(trans->socket, 2);	/* close the socket */	if (trans->socket) {		trans->socket->sk->sk_user_data = NULL;		sock_release(trans->socket);		trans->socket = NULL;	}	kfree(trans);	_leave(" = %d", ret);	return ret;} /* end rxrpc_create_transport() *//*****************************************************************************//* * clear the connections on a transport endpoint */void rxrpc_clear_transport(struct rxrpc_transport *trans){	//struct rxrpc_connection *conn;} /* end rxrpc_clear_transport() *//*****************************************************************************//* * destroy a transport endpoint */void rxrpc_put_transport(struct rxrpc_transport *trans){	_enter("%p{u=%d p=%hu}",	       trans, atomic_read(&trans->usage), trans->port);	BUG_ON(atomic_read(&trans->usage) <= 0);	/* to prevent a race, the decrement and the dequeue must be	 * effectively atomic */	spin_lock(&rxrpc_transports_lock);	if (likely(!atomic_dec_and_test(&trans->usage))) {		spin_unlock(&rxrpc_transports_lock);		_leave("");		return;	}	list_del(&trans->link);	spin_unlock(&rxrpc_transports_lock);	/* finish cleaning up the transport */	if (trans->socket)		trans->socket->ops->shutdown(trans->socket, 2);	rxrpc_krxsecd_clear_transport(trans);	rxrpc_krxiod_dequeue_transport(trans);	/* discard all peer information */	rxrpc_peer_clearall(trans);	down_write(&rxrpc_proc_transports_sem);	list_del(&trans->proc_link);	up_write(&rxrpc_proc_transports_sem);	__RXACCT(atomic_dec(&rxrpc_transport_count));	/* close the socket */	if (trans->socket) {		trans->socket->sk->sk_user_data = NULL;		sock_release(trans->socket);		trans->socket = NULL;	}	kfree(trans);	_leave("");} /* end rxrpc_put_transport() *//*****************************************************************************//* * add a service to a transport to be listened upon */int rxrpc_add_service(struct rxrpc_transport *trans,		      struct rxrpc_service *newsrv){	struct rxrpc_service *srv;	struct list_head *_p;	int ret = -EEXIST;	_enter("%p{%hu},%p{%hu}",	       trans, trans->port, newsrv, newsrv->service_id);	/* verify that the service ID is not already present */	spin_lock(&trans->lock);	list_for_each(_p, &trans->services) {		srv = list_entry(_p, struct rxrpc_service, link);		if (srv->service_id == newsrv->service_id)			goto out;	}	/* okay - add the transport to the list */	list_add_tail(&newsrv->link, &trans->services);	rxrpc_get_transport(trans);	ret = 0; out:	spin_unlock(&trans->lock);	_leave("= %d", ret);	return ret;} /* end rxrpc_add_service() *//*****************************************************************************//* * remove a service from a transport */void rxrpc_del_service(struct rxrpc_transport *trans, struct rxrpc_service *srv){	_enter("%p{%hu},%p{%hu}", trans, trans->port, srv, srv->service_id);	spin_lock(&trans->lock);	list_del(&srv->link);	spin_unlock(&trans->lock);	rxrpc_put_transport(trans);	_leave("");} /* end rxrpc_del_service() *//*****************************************************************************//* * INET callback when data has been received on the socket. */static void rxrpc_data_ready(struct sock *sk, int count){	struct rxrpc_transport *trans;	_enter("%p{t=%p},%d", sk, sk->sk_user_data, count);	/* queue the transport for attention by krxiod */	trans = (struct rxrpc_transport *) sk->sk_user_data;	if (trans)		rxrpc_krxiod_queue_transport(trans);	/* wake up anyone waiting on the socket */	if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))		wake_up_interruptible(sk->sk_sleep);	_leave("");} /* end rxrpc_data_ready() *//*****************************************************************************//* * INET callback when an ICMP error packet is received * - sk->err is error (EHOSTUNREACH, EPROTO or EMSGSIZE) */static void rxrpc_error_report(struct sock *sk){	struct rxrpc_transport *trans;	_enter("%p{t=%p}", sk, sk->sk_user_data);	/* queue the transport for attention by krxiod */	trans = (struct rxrpc_transport *) sk->sk_user_data;	if (trans) {		trans->error_rcvd = 1;		rxrpc_krxiod_queue_transport(trans);	}	/* wake up anyone waiting on the socket */	if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))		wake_up_interruptible(sk->sk_sleep);	_leave("");} /* end rxrpc_error_report() *//*****************************************************************************//* * split a message up, allocating message records and filling them in * from the contents of a socket buffer */static int rxrpc_incoming_msg(struct rxrpc_transport *trans,			      struct sk_buff *pkt,			      struct list_head *msgq){	struct rxrpc_message *msg;	int ret;	_enter("");	msg = kmalloc(sizeof(struct rxrpc_message), GFP_KERNEL);	if (!msg) {		_leave(" = -ENOMEM");		return -ENOMEM;	}	memset(msg, 0, sizeof(*msg));	atomic_set(&msg->usage, 1);	list_add_tail(&msg->link,msgq);	/* dig out the Rx routing parameters */	if (skb_copy_bits(pkt, sizeof(struct udphdr),			  &msg->hdr, sizeof(msg->hdr)) < 0) {		ret = -EBADMSG;		goto error;	}	msg->trans = trans;	msg->state = RXRPC_MSG_RECEIVED;	msg->stamp = pkt->stamp;	if (msg->stamp.tv_sec == 0) {		do_gettimeofday(&msg->stamp); 		if (pkt->sk) 			sock_enable_timestamp(pkt->sk);	} 	msg->seq = ntohl(msg->hdr.seq);	/* attach the packet */	skb_get(pkt);	msg->pkt = pkt;	msg->offset = sizeof(struct udphdr) + sizeof(struct rxrpc_header);	msg->dsize = msg->pkt->len - msg->offset;	_net("Rx Received packet from %s (%08x;%08x,%1x,%d,%s,%02x,%d,%d)",	     msg->hdr.flags & RXRPC_CLIENT_INITIATED ? "client" : "server",	     ntohl(msg->hdr.epoch),	     (ntohl(msg->hdr.cid) & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT,	     ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK,	     ntohl(msg->hdr.callNumber),	     rxrpc_pkts[msg->hdr.type],	     msg->hdr.flags,	     ntohs(msg->hdr.serviceId),	     msg->hdr.securityIndex);	__RXACCT(atomic_inc(&rxrpc_message_count));	/* split off jumbo packets */	while (msg->hdr.type == RXRPC_PACKET_TYPE_DATA &&	       msg->hdr.flags & RXRPC_JUMBO_PACKET	       ) {		struct rxrpc_jumbo_header jumbo;		struct rxrpc_message *jumbomsg = msg;		_debug("split jumbo packet");		/* quick sanity check */		ret = -EBADMSG;		if (msg->dsize <		    RXRPC_JUMBO_DATALEN + sizeof(struct rxrpc_jumbo_header))			goto error;		if (msg->hdr.flags & RXRPC_LAST_PACKET)			goto error;		/* dig out the secondary header */		if (skb_copy_bits(pkt, msg->offset + RXRPC_JUMBO_DATALEN,				  &jumbo, sizeof(jumbo)) < 0)			goto error;		/* allocate a new message record */		ret = -ENOMEM;		msg = kmalloc(sizeof(struct rxrpc_message), GFP_KERNEL);		if (!msg)			goto error;		memcpy(msg, jumbomsg, sizeof(*msg));		list_add_tail(&msg->link, msgq);		/* adjust the jumbo packet */		jumbomsg->dsize = RXRPC_JUMBO_DATALEN;		/* attach the packet here too */		skb_get(pkt);		/* adjust the parameters */		msg->seq++;		msg->hdr.seq = htonl(msg->seq);		msg->hdr.serial = htonl(ntohl(msg->hdr.serial) + 1);		msg->offset += RXRPC_JUMBO_DATALEN +			sizeof(struct rxrpc_jumbo_header);		msg->dsize -= RXRPC_JUMBO_DATALEN +			sizeof(struct rxrpc_jumbo_header);		msg->hdr.flags = jumbo.flags;		msg->hdr._rsvd = jumbo._rsvd;		_net("Rx Split jumbo packet from %s"		     " (%08x;%08x,%1x,%d,%s,%02x,%d,%d)",		     msg->hdr.flags & RXRPC_CLIENT_INITIATED ? "client" : "server",		     ntohl(msg->hdr.epoch),		     (ntohl(msg->hdr.cid) & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT,		     ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK,		     ntohl(msg->hdr.callNumber),		     rxrpc_pkts[msg->hdr.type],		     msg->hdr.flags,		     ntohs(msg->hdr.serviceId),		     msg->hdr.securityIndex);		__RXACCT(atomic_inc(&rxrpc_message_count));	}

⌨️ 快捷键说明

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