After Al Viro (finally) succeeded in removing the sched.h #include in module.h recently, it makes sense again to remove other superfluous sched.h includes. There are quite a lot of files which include it but don't actually need anything defined in there. Presumably these includes were once needed for macros that used to live in sched.h, but moved to other header files in the course of cleaning it up. To ease the pain, this time I did not fiddle with any header files and only removed #includes from .c-files, which tend to cause less trouble. Compile tested against 2.6.20-rc2 and 2.6.20-rc2-mm2 (with offsets) on alpha, arm, i386, ia64, mips, powerpc, and x86_64 with allnoconfig, defconfig, allmodconfig, and allyesconfig as well as a few randconfigs on x86_64 and all configs in arch/arm/configs on arm. I also checked that no new warnings were introduced by the patch (actually, some warnings are removed that were emitted by unnecessarily included header files). Signed-off-by: Tim Schmielau <tim@physik3.uni-rostock.de> Acked-by: Russell King <rmk+kernel@arm.linux.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
846 lines
21 KiB
C
846 lines
21 KiB
C
/* 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/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 <linux/skbuff.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 "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 DEFINE_SPINLOCK(rxrpc_transports_lock);
|
|
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 = kzalloc(sizeof(struct rxrpc_transport), GFP_KERNEL);
|
|
if (!trans)
|
|
return -ENOMEM;
|
|
|
|
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() */
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* 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 = kzalloc(sizeof(struct rxrpc_message), GFP_KERNEL);
|
|
if (!msg) {
|
|
_leave(" = -ENOMEM");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
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;
|
|
skb_get_timestamp(pkt, &msg->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 = kmemdup(jumbomsg, sizeof(struct rxrpc_message), GFP_KERNEL);
|
|
if (!msg)
|
|
goto error;
|
|
|
|
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));
|
|
}
|
|
|
|
_leave(" = 0 #%d", atomic_read(&rxrpc_message_count));
|
|
return 0;
|
|
|
|
error:
|
|
while (!list_empty(msgq)) {
|
|
msg = list_entry(msgq->next, struct rxrpc_message, link);
|
|
list_del_init(&msg->link);
|
|
|
|
rxrpc_put_message(msg);
|
|
}
|
|
|
|
_leave(" = %d", ret);
|
|
return ret;
|
|
} /* end rxrpc_incoming_msg() */
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* accept a new call
|
|
* - called from krxiod in process context
|
|
*/
|
|
void rxrpc_trans_receive_packet(struct rxrpc_transport *trans)
|
|
{
|
|
struct rxrpc_message *msg;
|
|
struct rxrpc_peer *peer;
|
|
struct sk_buff *pkt;
|
|
int ret;
|
|
__be32 addr;
|
|
__be16 port;
|
|
|
|
LIST_HEAD(msgq);
|
|
|
|
_enter("%p{%d}", trans, trans->port);
|
|
|
|
for (;;) {
|
|
/* deal with outstanting errors first */
|
|
if (trans->error_rcvd)
|
|
rxrpc_trans_receive_error_report(trans);
|
|
|
|
/* attempt to receive a packet */
|
|
pkt = skb_recv_datagram(trans->socket->sk, 0, 1, &ret);
|
|
if (!pkt) {
|
|
if (ret == -EAGAIN) {
|
|
_leave(" EAGAIN");
|
|
return;
|
|
}
|
|
|
|
/* an icmp error may have occurred */
|
|
rxrpc_krxiod_queue_transport(trans);
|
|
_leave(" error %d\n", ret);
|
|
return;
|
|
}
|
|
|
|
/* we'll probably need to checksum it (didn't call
|
|
* sock_recvmsg) */
|
|
if (skb_checksum_complete(pkt)) {
|
|
kfree_skb(pkt);
|
|
rxrpc_krxiod_queue_transport(trans);
|
|
_leave(" CSUM failed");
|
|
return;
|
|
}
|
|
|
|
addr = pkt->nh.iph->saddr;
|
|
port = pkt->h.uh->source;
|
|
|
|
_net("Rx Received UDP packet from %08x:%04hu",
|
|
ntohl(addr), ntohs(port));
|
|
|
|
/* unmarshall the Rx parameters and split jumbo packets */
|
|
ret = rxrpc_incoming_msg(trans, pkt, &msgq);
|
|
if (ret < 0) {
|
|
kfree_skb(pkt);
|
|
rxrpc_krxiod_queue_transport(trans);
|
|
_leave(" bad packet");
|
|
return;
|
|
}
|
|
|
|
BUG_ON(list_empty(&msgq));
|
|
|
|
msg = list_entry(msgq.next, struct rxrpc_message, link);
|
|
|
|
/* locate the record for the peer from which it
|
|
* originated */
|
|
ret = rxrpc_peer_lookup(trans, addr, &peer);
|
|
if (ret < 0) {
|
|
kdebug("Rx No connections from that peer");
|
|
rxrpc_trans_immediate_abort(trans, msg, -EINVAL);
|
|
goto finished_msg;
|
|
}
|
|
|
|
/* try and find a matching connection */
|
|
ret = rxrpc_connection_lookup(peer, msg, &msg->conn);
|
|
if (ret < 0) {
|
|
kdebug("Rx Unknown Connection");
|
|
rxrpc_trans_immediate_abort(trans, msg, -EINVAL);
|
|
rxrpc_put_peer(peer);
|
|
goto finished_msg;
|
|
}
|
|
rxrpc_put_peer(peer);
|
|
|
|
/* deal with the first packet of a new call */
|
|
if (msg->hdr.flags & RXRPC_CLIENT_INITIATED &&
|
|
msg->hdr.type == RXRPC_PACKET_TYPE_DATA &&
|
|
ntohl(msg->hdr.seq) == 1
|
|
) {
|
|
_debug("Rx New server call");
|
|
rxrpc_trans_receive_new_call(trans, &msgq);
|
|
goto finished_msg;
|
|
}
|
|
|
|
/* deal with subsequent packet(s) of call */
|
|
_debug("Rx Call packet");
|
|
while (!list_empty(&msgq)) {
|
|
msg = list_entry(msgq.next, struct rxrpc_message, link);
|
|
list_del_init(&msg->link);
|
|
|
|
ret = rxrpc_conn_receive_call_packet(msg->conn, NULL, msg);
|
|
if (ret < 0) {
|
|
rxrpc_trans_immediate_abort(trans, msg, ret);
|
|
rxrpc_put_message(msg);
|
|
goto finished_msg;
|
|
}
|
|
|
|
rxrpc_put_message(msg);
|
|
}
|
|
|
|
goto finished_msg;
|
|
|
|
/* dispose of the packets */
|
|
finished_msg:
|
|
while (!list_empty(&msgq)) {
|
|
msg = list_entry(msgq.next, struct rxrpc_message, link);
|
|
list_del_init(&msg->link);
|
|
|
|
rxrpc_put_message(msg);
|
|
}
|
|
kfree_skb(pkt);
|
|
}
|
|
|
|
_leave("");
|
|
|
|
} /* end rxrpc_trans_receive_packet() */
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* accept a new call from a client trying to connect to one of my services
|
|
* - called in process context
|
|
*/
|
|
static int rxrpc_trans_receive_new_call(struct rxrpc_transport *trans,
|
|
struct list_head *msgq)
|
|
{
|
|
struct rxrpc_message *msg;
|
|
|
|
_enter("");
|
|
|
|
/* only bother with the first packet */
|
|
msg = list_entry(msgq->next, struct rxrpc_message, link);
|
|
list_del_init(&msg->link);
|
|
rxrpc_krxsecd_queue_incoming_call(msg);
|
|
rxrpc_put_message(msg);
|
|
|
|
_leave(" = 0");
|
|
|
|
return 0;
|
|
} /* end rxrpc_trans_receive_new_call() */
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* perform an immediate abort without connection or call structures
|
|
*/
|
|
int rxrpc_trans_immediate_abort(struct rxrpc_transport *trans,
|
|
struct rxrpc_message *msg,
|
|
int error)
|
|
{
|
|
struct rxrpc_header ahdr;
|
|
struct sockaddr_in sin;
|
|
struct msghdr msghdr;
|
|
struct kvec iov[2];
|
|
__be32 _error;
|
|
int len, ret;
|
|
|
|
_enter("%p,%p,%d", trans, msg, error);
|
|
|
|
/* don't abort an abort packet */
|
|
if (msg->hdr.type == RXRPC_PACKET_TYPE_ABORT) {
|
|
_leave(" = 0");
|
|
return 0;
|
|
}
|
|
|
|
_error = htonl(-error);
|
|
|
|
/* set up the message to be transmitted */
|
|
memcpy(&ahdr, &msg->hdr, sizeof(ahdr));
|
|
ahdr.epoch = msg->hdr.epoch;
|
|
ahdr.serial = htonl(1);
|
|
ahdr.seq = 0;
|
|
ahdr.type = RXRPC_PACKET_TYPE_ABORT;
|
|
ahdr.flags = RXRPC_LAST_PACKET;
|
|
ahdr.flags |= ~msg->hdr.flags & RXRPC_CLIENT_INITIATED;
|
|
|
|
iov[0].iov_len = sizeof(ahdr);
|
|
iov[0].iov_base = &ahdr;
|
|
iov[1].iov_len = sizeof(_error);
|
|
iov[1].iov_base = &_error;
|
|
|
|
len = sizeof(ahdr) + sizeof(_error);
|
|
|
|
memset(&sin,0,sizeof(sin));
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_port = msg->pkt->h.uh->source;
|
|
sin.sin_addr.s_addr = msg->pkt->nh.iph->saddr;
|
|
|
|
msghdr.msg_name = &sin;
|
|
msghdr.msg_namelen = sizeof(sin);
|
|
msghdr.msg_control = NULL;
|
|
msghdr.msg_controllen = 0;
|
|
msghdr.msg_flags = MSG_DONTWAIT;
|
|
|
|
_net("Sending message type %d of %d bytes to %08x:%d",
|
|
ahdr.type,
|
|
len,
|
|
ntohl(sin.sin_addr.s_addr),
|
|
ntohs(sin.sin_port));
|
|
|
|
/* send the message */
|
|
ret = kernel_sendmsg(trans->socket, &msghdr, iov, 2, len);
|
|
|
|
_leave(" = %d", ret);
|
|
return ret;
|
|
} /* end rxrpc_trans_immediate_abort() */
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* receive an ICMP error report and percolate it to all connections
|
|
* heading to the affected host or port
|
|
*/
|
|
static void rxrpc_trans_receive_error_report(struct rxrpc_transport *trans)
|
|
{
|
|
struct rxrpc_connection *conn;
|
|
struct sockaddr_in sin;
|
|
struct rxrpc_peer *peer;
|
|
struct list_head connq, *_p;
|
|
struct errormsg emsg;
|
|
struct msghdr msg;
|
|
__be16 port;
|
|
int local, err;
|
|
|
|
_enter("%p", trans);
|
|
|
|
for (;;) {
|
|
trans->error_rcvd = 0;
|
|
|
|
/* try and receive an error message */
|
|
msg.msg_name = &sin;
|
|
msg.msg_namelen = sizeof(sin);
|
|
msg.msg_control = &emsg;
|
|
msg.msg_controllen = sizeof(emsg);
|
|
msg.msg_flags = 0;
|
|
|
|
err = kernel_recvmsg(trans->socket, &msg, NULL, 0, 0,
|
|
MSG_ERRQUEUE | MSG_DONTWAIT | MSG_TRUNC);
|
|
|
|
if (err == -EAGAIN) {
|
|
_leave("");
|
|
return;
|
|
}
|
|
|
|
if (err < 0) {
|
|
printk("%s: unable to recv an error report: %d\n",
|
|
__FUNCTION__, err);
|
|
_leave("");
|
|
return;
|
|
}
|
|
|
|
msg.msg_controllen = (char *) msg.msg_control - (char *) &emsg;
|
|
|
|
if (msg.msg_controllen < sizeof(emsg.cmsg) ||
|
|
msg.msg_namelen < sizeof(sin)) {
|
|
printk("%s: short control message"
|
|
" (nlen=%u clen=%Zu fl=%x)\n",
|
|
__FUNCTION__,
|
|
msg.msg_namelen,
|
|
msg.msg_controllen,
|
|
msg.msg_flags);
|
|
continue;
|
|
}
|
|
|
|
_net("Rx Received control message"
|
|
" { len=%Zu level=%u type=%u }",
|
|
emsg.cmsg.cmsg_len,
|
|
emsg.cmsg.cmsg_level,
|
|
emsg.cmsg.cmsg_type);
|
|
|
|
if (sin.sin_family != AF_INET) {
|
|
printk("Rx Ignoring error report with non-INET address"
|
|
" (fam=%u)",
|
|
sin.sin_family);
|
|
continue;
|
|
}
|
|
|
|
_net("Rx Received message pertaining to host addr=%x port=%hu",
|
|
ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port));
|
|
|
|
if (emsg.cmsg.cmsg_level != SOL_IP ||
|
|
emsg.cmsg.cmsg_type != IP_RECVERR) {
|
|
printk("Rx Ignoring unknown error report"
|
|
" { level=%u type=%u }",
|
|
emsg.cmsg.cmsg_level,
|
|
emsg.cmsg.cmsg_type);
|
|
continue;
|
|
}
|
|
|
|
if (msg.msg_controllen < sizeof(emsg.cmsg) + sizeof(emsg.ee)) {
|
|
printk("%s: short error message (%Zu)\n",
|
|
__FUNCTION__, msg.msg_controllen);
|
|
_leave("");
|
|
return;
|
|
}
|
|
|
|
port = sin.sin_port;
|
|
|
|
switch (emsg.ee.ee_origin) {
|
|
case SO_EE_ORIGIN_ICMP:
|
|
local = 0;
|
|
switch (emsg.ee.ee_type) {
|
|
case ICMP_DEST_UNREACH:
|
|
switch (emsg.ee.ee_code) {
|
|
case ICMP_NET_UNREACH:
|
|
_net("Rx Received ICMP Network Unreachable");
|
|
port = 0;
|
|
err = -ENETUNREACH;
|
|
break;
|
|
case ICMP_HOST_UNREACH:
|
|
_net("Rx Received ICMP Host Unreachable");
|
|
port = 0;
|
|
err = -EHOSTUNREACH;
|
|
break;
|
|
case ICMP_PORT_UNREACH:
|
|
_net("Rx Received ICMP Port Unreachable");
|
|
err = -ECONNREFUSED;
|
|
break;
|
|
case ICMP_NET_UNKNOWN:
|
|
_net("Rx Received ICMP Unknown Network");
|
|
port = 0;
|
|
err = -ENETUNREACH;
|
|
break;
|
|
case ICMP_HOST_UNKNOWN:
|
|
_net("Rx Received ICMP Unknown Host");
|
|
port = 0;
|
|
err = -EHOSTUNREACH;
|
|
break;
|
|
default:
|
|
_net("Rx Received ICMP DestUnreach { code=%u }",
|
|
emsg.ee.ee_code);
|
|
err = emsg.ee.ee_errno;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ICMP_TIME_EXCEEDED:
|
|
_net("Rx Received ICMP TTL Exceeded");
|
|
err = emsg.ee.ee_errno;
|
|
break;
|
|
|
|
default:
|
|
_proto("Rx Received ICMP error { type=%u code=%u }",
|
|
emsg.ee.ee_type, emsg.ee.ee_code);
|
|
err = emsg.ee.ee_errno;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SO_EE_ORIGIN_LOCAL:
|
|
_proto("Rx Received local error { error=%d }",
|
|
emsg.ee.ee_errno);
|
|
local = 1;
|
|
err = emsg.ee.ee_errno;
|
|
break;
|
|
|
|
case SO_EE_ORIGIN_NONE:
|
|
case SO_EE_ORIGIN_ICMP6:
|
|
default:
|
|
_proto("Rx Received error report { orig=%u }",
|
|
emsg.ee.ee_origin);
|
|
local = 0;
|
|
err = emsg.ee.ee_errno;
|
|
break;
|
|
}
|
|
|
|
/* find all the connections between this transport and the
|
|
* affected destination */
|
|
INIT_LIST_HEAD(&connq);
|
|
|
|
if (rxrpc_peer_lookup(trans, sin.sin_addr.s_addr,
|
|
&peer) == 0) {
|
|
read_lock(&peer->conn_lock);
|
|
list_for_each(_p, &peer->conn_active) {
|
|
conn = list_entry(_p, struct rxrpc_connection,
|
|
link);
|
|
if (port && conn->addr.sin_port != port)
|
|
continue;
|
|
if (!list_empty(&conn->err_link))
|
|
continue;
|
|
|
|
rxrpc_get_connection(conn);
|
|
list_add_tail(&conn->err_link, &connq);
|
|
}
|
|
read_unlock(&peer->conn_lock);
|
|
|
|
/* service all those connections */
|
|
while (!list_empty(&connq)) {
|
|
conn = list_entry(connq.next,
|
|
struct rxrpc_connection,
|
|
err_link);
|
|
list_del(&conn->err_link);
|
|
|
|
rxrpc_conn_handle_error(conn, local, err);
|
|
|
|
rxrpc_put_connection(conn);
|
|
}
|
|
|
|
rxrpc_put_peer(peer);
|
|
}
|
|
}
|
|
|
|
_leave("");
|
|
return;
|
|
} /* end rxrpc_trans_receive_error_report() */
|