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 + -
显示快捷键?