You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both
Provide AF_RXRPC sockets that can be used to talk to AFS servers, or serve answers to AFS clients. KerberosIV security is fully supported. The patches and some example test programs can be found in: http://people.redhat.com/~dhowells/rxrpc/ This will eventually replace the old implementation of kernel-only RxRPC currently resident in net/rxrpc/. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
e19dff1fdd
commit
17926a7932
663
Documentation/networking/rxrpc.txt
Normal file
663
Documentation/networking/rxrpc.txt
Normal file
File diff suppressed because it is too large
Load Diff
22
include/keys/rxrpc-type.h
Normal file
22
include/keys/rxrpc-type.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/* RxRPC key type
|
||||
*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#ifndef _KEYS_RXRPC_TYPE_H
|
||||
#define _KEYS_RXRPC_TYPE_H
|
||||
|
||||
#include <linux/key.h>
|
||||
|
||||
/*
|
||||
* key type for AF_RXRPC keys
|
||||
*/
|
||||
extern struct key_type key_type_rxrpc;
|
||||
|
||||
#endif /* _KEYS_USER_TYPE_H */
|
||||
@@ -24,7 +24,7 @@
|
||||
struct poll_table_struct;
|
||||
struct inode;
|
||||
|
||||
#define NPROTO 33 /* should be enough for now.. */
|
||||
#define NPROTO 34 /* should be enough for now.. */
|
||||
|
||||
#define SYS_SOCKET 1 /* sys_socket(2) */
|
||||
#define SYS_BIND 2 /* sys_bind(2) */
|
||||
|
||||
62
include/linux/rxrpc.h
Normal file
62
include/linux/rxrpc.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* AF_RXRPC parameters
|
||||
*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_RXRPC_H
|
||||
#define _LINUX_RXRPC_H
|
||||
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
|
||||
/*
|
||||
* RxRPC socket address
|
||||
*/
|
||||
struct sockaddr_rxrpc {
|
||||
sa_family_t srx_family; /* address family */
|
||||
u16 srx_service; /* service desired */
|
||||
u16 transport_type; /* type of transport socket (SOCK_DGRAM) */
|
||||
u16 transport_len; /* length of transport address */
|
||||
union {
|
||||
sa_family_t family; /* transport address family */
|
||||
struct sockaddr_in sin; /* IPv4 transport address */
|
||||
struct sockaddr_in6 sin6; /* IPv6 transport address */
|
||||
} transport;
|
||||
};
|
||||
|
||||
/*
|
||||
* RxRPC socket options
|
||||
*/
|
||||
#define RXRPC_SECURITY_KEY 1 /* [clnt] set client security key */
|
||||
#define RXRPC_SECURITY_KEYRING 2 /* [srvr] set ring of server security keys */
|
||||
#define RXRPC_EXCLUSIVE_CONNECTION 3 /* [clnt] use exclusive RxRPC connection */
|
||||
#define RXRPC_MIN_SECURITY_LEVEL 4 /* minimum security level */
|
||||
|
||||
/*
|
||||
* RxRPC control messages
|
||||
* - terminal messages mean that a user call ID tag can be recycled
|
||||
*/
|
||||
#define RXRPC_USER_CALL_ID 1 /* user call ID specifier */
|
||||
#define RXRPC_ABORT 2 /* abort request / notification [terminal] */
|
||||
#define RXRPC_ACK 3 /* [Server] RPC op final ACK received [terminal] */
|
||||
#define RXRPC_NET_ERROR 5 /* network error received [terminal] */
|
||||
#define RXRPC_BUSY 6 /* server busy received [terminal] */
|
||||
#define RXRPC_LOCAL_ERROR 7 /* local error generated [terminal] */
|
||||
#define RXRPC_NEW_CALL 8 /* [Server] new incoming call notification */
|
||||
#define RXRPC_ACCEPT 9 /* [Server] accept request */
|
||||
|
||||
/*
|
||||
* RxRPC security levels
|
||||
*/
|
||||
#define RXRPC_SECURITY_PLAIN 0 /* plain secure-checksummed packets only */
|
||||
#define RXRPC_SECURITY_AUTH 1 /* authenticated packets */
|
||||
#define RXRPC_SECURITY_ENCRYPT 2 /* encrypted packets */
|
||||
|
||||
|
||||
#endif /* _LINUX_RXRPC_H */
|
||||
@@ -188,7 +188,8 @@ struct ucred {
|
||||
#define AF_TIPC 30 /* TIPC sockets */
|
||||
#define AF_BLUETOOTH 31 /* Bluetooth sockets */
|
||||
#define AF_IUCV 32 /* IUCV sockets */
|
||||
#define AF_MAX 33 /* For now.. */
|
||||
#define AF_RXRPC 33 /* RxRPC sockets */
|
||||
#define AF_MAX 34 /* For now.. */
|
||||
|
||||
/* Protocol families, same as address families. */
|
||||
#define PF_UNSPEC AF_UNSPEC
|
||||
@@ -222,6 +223,7 @@ struct ucred {
|
||||
#define PF_TIPC AF_TIPC
|
||||
#define PF_BLUETOOTH AF_BLUETOOTH
|
||||
#define PF_IUCV AF_IUCV
|
||||
#define PF_RXRPC AF_RXRPC
|
||||
#define PF_MAX AF_MAX
|
||||
|
||||
/* Maximum queue length specifiable by listen. */
|
||||
@@ -284,6 +286,7 @@ struct ucred {
|
||||
#define SOL_DCCP 269
|
||||
#define SOL_NETLINK 270
|
||||
#define SOL_TIPC 271
|
||||
#define SOL_RXRPC 272
|
||||
|
||||
/* IPX options */
|
||||
#define IPX_TYPE 1
|
||||
|
||||
17
include/net/af_rxrpc.h
Normal file
17
include/net/af_rxrpc.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/* RxRPC definitions
|
||||
*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef _NET_RXRPC_H
|
||||
#define _NET_RXRPC_H
|
||||
|
||||
#include <linux/rxrpc.h>
|
||||
|
||||
#endif /* _NET_RXRPC_H */
|
||||
@@ -33,7 +33,8 @@ struct rxrpc_header
|
||||
#define RXRPC_MAXCALLS 4 /* max active calls per conn */
|
||||
#define RXRPC_CHANNELMASK (RXRPC_MAXCALLS-1) /* mask for channel ID */
|
||||
#define RXRPC_CIDMASK (~RXRPC_CHANNELMASK) /* mask for connection ID */
|
||||
#define RXRPC_CIDSHIFT 2 /* shift for connection ID */
|
||||
#define RXRPC_CIDSHIFT ilog2(RXRPC_MAXCALLS) /* shift for connection ID */
|
||||
#define RXRPC_CID_INC (1 << RXRPC_CIDSHIFT) /* connection ID increment */
|
||||
|
||||
__be32 callNumber; /* call ID (0 for connection-level packets) */
|
||||
#define RXRPC_PROCESS_MAXCALLS (1<<2) /* maximum number of active calls per conn (power of 2) */
|
||||
@@ -62,7 +63,10 @@ struct rxrpc_header
|
||||
|
||||
uint8_t userStatus; /* app-layer defined status */
|
||||
uint8_t securityIndex; /* security protocol ID */
|
||||
__be16 _rsvd; /* reserved (used by kerberos security as cksum) */
|
||||
union {
|
||||
__be16 _rsvd; /* reserved */
|
||||
__be16 cksum; /* kerberos security checksum */
|
||||
};
|
||||
__be16 serviceId; /* service ID */
|
||||
|
||||
} __attribute__((packed));
|
||||
@@ -124,4 +128,81 @@ struct rxrpc_ackpacket
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* ACK packets can have a further piece of information tagged on the end
|
||||
*/
|
||||
struct rxrpc_ackinfo {
|
||||
__be32 rxMTU; /* maximum Rx MTU size (bytes) [AFS 3.3] */
|
||||
__be32 maxMTU; /* maximum interface MTU size (bytes) [AFS 3.3] */
|
||||
__be32 rwind; /* Rx window size (packets) [AFS 3.4] */
|
||||
__be32 jumbo_max; /* max packets to stick into a jumbo packet [AFS 3.5] */
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* Kerberos security type-2 challenge packet
|
||||
*/
|
||||
struct rxkad_challenge {
|
||||
__be32 version; /* version of this challenge type */
|
||||
__be32 nonce; /* encrypted random number */
|
||||
__be32 min_level; /* minimum security level */
|
||||
__be32 __padding; /* padding to 8-byte boundary */
|
||||
} __attribute__((packed));
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* Kerberos security type-2 response packet
|
||||
*/
|
||||
struct rxkad_response {
|
||||
__be32 version; /* version of this reponse type */
|
||||
__be32 __pad;
|
||||
|
||||
/* encrypted bit of the response */
|
||||
struct {
|
||||
__be32 epoch; /* current epoch */
|
||||
__be32 cid; /* parent connection ID */
|
||||
__be32 checksum; /* checksum */
|
||||
__be32 securityIndex; /* security type */
|
||||
__be32 call_id[4]; /* encrypted call IDs */
|
||||
__be32 inc_nonce; /* challenge nonce + 1 */
|
||||
__be32 level; /* desired level */
|
||||
} encrypted;
|
||||
|
||||
__be32 kvno; /* Kerberos key version number */
|
||||
__be32 ticket_len; /* Kerberos ticket length */
|
||||
} __attribute__((packed));
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* RxRPC-level abort codes
|
||||
*/
|
||||
#define RX_CALL_DEAD -1 /* call/conn has been inactive and is shut down */
|
||||
#define RX_INVALID_OPERATION -2 /* invalid operation requested / attempted */
|
||||
#define RX_CALL_TIMEOUT -3 /* call timeout exceeded */
|
||||
#define RX_EOF -4 /* unexpected end of data on read op */
|
||||
#define RX_PROTOCOL_ERROR -5 /* low-level protocol error */
|
||||
#define RX_USER_ABORT -6 /* generic user abort */
|
||||
#define RX_ADDRINUSE -7 /* UDP port in use */
|
||||
#define RX_DEBUGI_BADTYPE -8 /* bad debugging packet type */
|
||||
|
||||
/*
|
||||
* Rx kerberos security abort codes
|
||||
* - unfortunately we have no generalised security abort codes to say things
|
||||
* like "unsupported security", so we have to use these instead and hope the
|
||||
* other side understands
|
||||
*/
|
||||
#define RXKADINCONSISTENCY 19270400 /* security module structure inconsistent */
|
||||
#define RXKADPACKETSHORT 19270401 /* packet too short for security challenge */
|
||||
#define RXKADLEVELFAIL 19270402 /* security level negotiation failed */
|
||||
#define RXKADTICKETLEN 19270403 /* ticket length too short or too long */
|
||||
#define RXKADOUTOFSEQUENCE 19270404 /* packet had bad sequence number */
|
||||
#define RXKADNOAUTH 19270405 /* caller not authorised */
|
||||
#define RXKADBADKEY 19270406 /* illegal key: bad parity or weak */
|
||||
#define RXKADBADTICKET 19270407 /* security object was passed a bad ticket */
|
||||
#define RXKADUNKNOWNKEY 19270408 /* ticket contained unknown key version number */
|
||||
#define RXKADEXPIRED 19270409 /* authentication expired */
|
||||
#define RXKADSEALEDINCON 19270410 /* sealed data inconsistent */
|
||||
#define RXKADDATALEN 19270411 /* user data too long */
|
||||
#define RXKADILLEGALLEVEL 19270412 /* caller not authorised to use encrypted conns */
|
||||
|
||||
#endif /* _LINUX_RXRPC_PACKET_H */
|
||||
|
||||
@@ -212,6 +212,7 @@ endmenu
|
||||
source "net/ax25/Kconfig"
|
||||
source "net/irda/Kconfig"
|
||||
source "net/bluetooth/Kconfig"
|
||||
source "net/rxrpc/Kconfig"
|
||||
|
||||
config FIB_RULES
|
||||
bool
|
||||
|
||||
@@ -38,6 +38,7 @@ obj-$(CONFIG_IRDA) += irda/
|
||||
obj-$(CONFIG_BT) += bluetooth/
|
||||
obj-$(CONFIG_SUNRPC) += sunrpc/
|
||||
obj-$(CONFIG_RXRPC) += rxrpc/
|
||||
obj-$(CONFIG_AF_RXRPC) += rxrpc/
|
||||
obj-$(CONFIG_ATM) += atm/
|
||||
obj-$(CONFIG_DECNET) += decnet/
|
||||
obj-$(CONFIG_ECONET) += econet/
|
||||
|
||||
@@ -154,7 +154,8 @@ static const char *af_family_key_strings[AF_MAX+1] = {
|
||||
"sk_lock-21" , "sk_lock-AF_SNA" , "sk_lock-AF_IRDA" ,
|
||||
"sk_lock-AF_PPPOX" , "sk_lock-AF_WANPIPE" , "sk_lock-AF_LLC" ,
|
||||
"sk_lock-27" , "sk_lock-28" , "sk_lock-29" ,
|
||||
"sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-AF_MAX"
|
||||
"sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" ,
|
||||
"sk_lock-AF_RXRPC" , "sk_lock-AF_MAX"
|
||||
};
|
||||
static const char *af_family_slock_key_strings[AF_MAX+1] = {
|
||||
"slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" ,
|
||||
@@ -167,7 +168,8 @@ static const char *af_family_slock_key_strings[AF_MAX+1] = {
|
||||
"slock-21" , "slock-AF_SNA" , "slock-AF_IRDA" ,
|
||||
"slock-AF_PPPOX" , "slock-AF_WANPIPE" , "slock-AF_LLC" ,
|
||||
"slock-27" , "slock-28" , "slock-29" ,
|
||||
"slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_MAX"
|
||||
"slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" ,
|
||||
"slock-AF_RXRPC" , "slock-AF_MAX"
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
37
net/rxrpc/Kconfig
Normal file
37
net/rxrpc/Kconfig
Normal file
@@ -0,0 +1,37 @@
|
||||
#
|
||||
# RxRPC session sockets
|
||||
#
|
||||
|
||||
config AF_RXRPC
|
||||
tristate "RxRPC session sockets"
|
||||
depends on EXPERIMENTAL
|
||||
help
|
||||
Say Y or M here to include support for RxRPC session sockets (just
|
||||
the transport part, not the presentation part: (un)marshalling is
|
||||
left to the application).
|
||||
|
||||
These are used for AFS kernel filesystem and userspace utilities.
|
||||
|
||||
This module at the moment only supports client operations and is
|
||||
currently incomplete.
|
||||
|
||||
See Documentation/networking/rxrpc.txt.
|
||||
|
||||
|
||||
config AF_RXRPC_DEBUG
|
||||
bool "RxRPC dynamic debugging"
|
||||
depends on AF_RXRPC
|
||||
help
|
||||
Say Y here to make runtime controllable debugging messages appear.
|
||||
|
||||
See Documentation/networking/rxrpc.txt.
|
||||
|
||||
|
||||
config RXKAD
|
||||
tristate "RxRPC Kerberos security"
|
||||
depends on AF_RXRPC && KEYS
|
||||
help
|
||||
Provide kerberos 4 and AFS kaserver security handling for AF_RXRPC
|
||||
through the use of the key retention service.
|
||||
|
||||
See Documentation/networking/rxrpc.txt.
|
||||
@@ -4,6 +4,35 @@
|
||||
|
||||
#CFLAGS += -finstrument-functions
|
||||
|
||||
af-rxrpc-objs := \
|
||||
af_rxrpc.o \
|
||||
ar-accept.o \
|
||||
ar-ack.o \
|
||||
ar-call.o \
|
||||
ar-connection.o \
|
||||
ar-connevent.o \
|
||||
ar-error.o \
|
||||
ar-input.o \
|
||||
ar-key.o \
|
||||
ar-local.o \
|
||||
ar-output.o \
|
||||
ar-peer.o \
|
||||
ar-recvmsg.o \
|
||||
ar-security.o \
|
||||
ar-skbuff.o \
|
||||
ar-transport.o
|
||||
|
||||
ifeq ($(CONFIG_PROC_FS),y)
|
||||
af-rxrpc-objs += ar-proc.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_AF_RXRPC) += af-rxrpc.o
|
||||
|
||||
obj-$(CONFIG_RXKAD) += rxkad.o
|
||||
|
||||
#
|
||||
# obsolete RxRPC interface, still used by fs/afs/
|
||||
#
|
||||
rxrpc-objs := \
|
||||
call.o \
|
||||
connection.o \
|
||||
@@ -22,4 +51,4 @@ ifeq ($(CONFIG_SYSCTL),y)
|
||||
rxrpc-objs += sysctl.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_RXRPC) := rxrpc.o
|
||||
obj-$(CONFIG_RXRPC) += rxrpc.o
|
||||
|
||||
754
net/rxrpc/af_rxrpc.c
Normal file
754
net/rxrpc/af_rxrpc.c
Normal file
File diff suppressed because it is too large
Load Diff
399
net/rxrpc/ar-accept.c
Normal file
399
net/rxrpc/ar-accept.c
Normal file
@@ -0,0 +1,399 @@
|
||||
/* incoming call handling
|
||||
*
|
||||
* Copyright (C) 2007 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/module.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/errqueue.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/af_rxrpc.h>
|
||||
#include <net/ip.h>
|
||||
#include "ar-internal.h"
|
||||
|
||||
/*
|
||||
* generate a connection-level abort
|
||||
*/
|
||||
static int rxrpc_busy(struct rxrpc_local *local, struct sockaddr_rxrpc *srx,
|
||||
struct rxrpc_header *hdr)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct kvec iov[1];
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
_enter("%d,,", local->debug_id);
|
||||
|
||||
msg.msg_name = &srx->transport.sin;
|
||||
msg.msg_namelen = sizeof(srx->transport.sin);
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
hdr->seq = 0;
|
||||
hdr->type = RXRPC_PACKET_TYPE_BUSY;
|
||||
hdr->flags = 0;
|
||||
hdr->userStatus = 0;
|
||||
hdr->_rsvd = 0;
|
||||
|
||||
iov[0].iov_base = hdr;
|
||||
iov[0].iov_len = sizeof(*hdr);
|
||||
|
||||
len = iov[0].iov_len;
|
||||
|
||||
hdr->serial = htonl(1);
|
||||
_proto("Tx BUSY %%%u", ntohl(hdr->serial));
|
||||
|
||||
ret = kernel_sendmsg(local->socket, &msg, iov, 1, len);
|
||||
if (ret < 0) {
|
||||
_leave(" = -EAGAIN [sendmsg failed: %d]", ret);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* accept an incoming call that needs peer, transport and/or connection setting
|
||||
* up
|
||||
*/
|
||||
static int rxrpc_accept_incoming_call(struct rxrpc_local *local,
|
||||
struct rxrpc_sock *rx,
|
||||
struct sk_buff *skb,
|
||||
struct sockaddr_rxrpc *srx)
|
||||
{
|
||||
struct rxrpc_connection *conn;
|
||||
struct rxrpc_transport *trans;
|
||||
struct rxrpc_skb_priv *sp, *nsp;
|
||||
struct rxrpc_peer *peer;
|
||||
struct rxrpc_call *call;
|
||||
struct sk_buff *notification;
|
||||
int ret;
|
||||
|
||||
_enter("");
|
||||
|
||||
sp = rxrpc_skb(skb);
|
||||
|
||||
/* get a notification message to send to the server app */
|
||||
notification = alloc_skb(0, GFP_NOFS);
|
||||
rxrpc_new_skb(notification);
|
||||
notification->mark = RXRPC_SKB_MARK_NEW_CALL;
|
||||
|
||||
peer = rxrpc_get_peer(srx, GFP_NOIO);
|
||||
if (IS_ERR(peer)) {
|
||||
_debug("no peer");
|
||||
ret = -EBUSY;
|
||||
goto error;
|
||||
}
|
||||
|
||||
trans = rxrpc_get_transport(local, peer, GFP_NOIO);
|
||||
rxrpc_put_peer(peer);
|
||||
if (!trans) {
|
||||
_debug("no trans");
|
||||
ret = -EBUSY;
|
||||
goto error;
|
||||
}
|
||||
|
||||
conn = rxrpc_incoming_connection(trans, &sp->hdr, GFP_NOIO);
|
||||
rxrpc_put_transport(trans);
|
||||
if (IS_ERR(conn)) {
|
||||
_debug("no conn");
|
||||
ret = PTR_ERR(conn);
|
||||
goto error;
|
||||
}
|
||||
|
||||
call = rxrpc_incoming_call(rx, conn, &sp->hdr, GFP_NOIO);
|
||||
rxrpc_put_connection(conn);
|
||||
if (IS_ERR(call)) {
|
||||
_debug("no call");
|
||||
ret = PTR_ERR(call);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* attach the call to the socket */
|
||||
read_lock_bh(&local->services_lock);
|
||||
if (rx->sk.sk_state == RXRPC_CLOSE)
|
||||
goto invalid_service;
|
||||
|
||||
write_lock(&rx->call_lock);
|
||||
if (!test_and_set_bit(RXRPC_CALL_INIT_ACCEPT, &call->flags)) {
|
||||
rxrpc_get_call(call);
|
||||
|
||||
spin_lock(&call->conn->state_lock);
|
||||
if (sp->hdr.securityIndex > 0 &&
|
||||
call->conn->state == RXRPC_CONN_SERVER_UNSECURED) {
|
||||
_debug("await conn sec");
|
||||
list_add_tail(&call->accept_link, &rx->secureq);
|
||||
call->conn->state = RXRPC_CONN_SERVER_CHALLENGING;
|
||||
atomic_inc(&call->conn->usage);
|
||||
set_bit(RXRPC_CONN_CHALLENGE, &call->conn->events);
|
||||
schedule_work(&call->conn->processor);
|
||||
} else {
|
||||
_debug("conn ready");
|
||||
call->state = RXRPC_CALL_SERVER_ACCEPTING;
|
||||
list_add_tail(&call->accept_link, &rx->acceptq);
|
||||
rxrpc_get_call(call);
|
||||
nsp = rxrpc_skb(notification);
|
||||
nsp->call = call;
|
||||
|
||||
ASSERTCMP(atomic_read(&call->usage), >=, 3);
|
||||
|
||||
_debug("notify");
|
||||
spin_lock(&call->lock);
|
||||
ret = rxrpc_queue_rcv_skb(call, notification, true,
|
||||
false);
|
||||
spin_unlock(&call->lock);
|
||||
notification = NULL;
|
||||
if (ret < 0)
|
||||
BUG();
|
||||
}
|
||||
spin_unlock(&call->conn->state_lock);
|
||||
|
||||
_debug("queued");
|
||||
}
|
||||
write_unlock(&rx->call_lock);
|
||||
|
||||
_debug("process");
|
||||
rxrpc_fast_process_packet(call, skb);
|
||||
|
||||
_debug("done");
|
||||
read_unlock_bh(&local->services_lock);
|
||||
rxrpc_free_skb(notification);
|
||||
rxrpc_put_call(call);
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
invalid_service:
|
||||
_debug("invalid");
|
||||
read_unlock_bh(&local->services_lock);
|
||||
|
||||
read_lock_bh(&call->state_lock);
|
||||
if (!test_bit(RXRPC_CALL_RELEASE, &call->flags) &&
|
||||
!test_and_set_bit(RXRPC_CALL_RELEASE, &call->events)) {
|
||||
rxrpc_get_call(call);
|
||||
schedule_work(&call->processor);
|
||||
}
|
||||
read_unlock_bh(&call->state_lock);
|
||||
rxrpc_put_call(call);
|
||||
ret = -ECONNREFUSED;
|
||||
error:
|
||||
rxrpc_free_skb(notification);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* accept incoming calls that need peer, transport and/or connection setting up
|
||||
* - the packets we get are all incoming client DATA packets that have seq == 1
|
||||
*/
|
||||
void rxrpc_accept_incoming_calls(struct work_struct *work)
|
||||
{
|
||||
struct rxrpc_local *local =
|
||||
container_of(work, struct rxrpc_local, acceptor);
|
||||
struct rxrpc_skb_priv *sp;
|
||||
struct sockaddr_rxrpc srx;
|
||||
struct rxrpc_sock *rx;
|
||||
struct sk_buff *skb;
|
||||
__be16 service_id;
|
||||
int ret;
|
||||
|
||||
_enter("%d", local->debug_id);
|
||||
|
||||
read_lock_bh(&rxrpc_local_lock);
|
||||
if (atomic_read(&local->usage) > 0)
|
||||
rxrpc_get_local(local);
|
||||
else
|
||||
local = NULL;
|
||||
read_unlock_bh(&rxrpc_local_lock);
|
||||
if (!local) {
|
||||
_leave(" [local dead]");
|
||||
return;
|
||||
}
|
||||
|
||||
process_next_packet:
|
||||
skb = skb_dequeue(&local->accept_queue);
|
||||
if (!skb) {
|
||||
rxrpc_put_local(local);
|
||||
_leave("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
_net("incoming call skb %p", skb);
|
||||
|
||||
sp = rxrpc_skb(skb);
|
||||
|
||||
/* determine the remote address */
|
||||
memset(&srx, 0, sizeof(srx));
|
||||
srx.srx_family = AF_RXRPC;
|
||||
srx.transport.family = local->srx.transport.family;
|
||||
srx.transport_type = local->srx.transport_type;
|
||||
switch (srx.transport.family) {
|
||||
case AF_INET:
|
||||
srx.transport_len = sizeof(struct sockaddr_in);
|
||||
srx.transport.sin.sin_port = udp_hdr(skb)->source;
|
||||
srx.transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr;
|
||||
break;
|
||||
default:
|
||||
goto busy;
|
||||
}
|
||||
|
||||
/* get the socket providing the service */
|
||||
service_id = sp->hdr.serviceId;
|
||||
read_lock_bh(&local->services_lock);
|
||||
list_for_each_entry(rx, &local->services, listen_link) {
|
||||
if (rx->service_id == service_id &&
|
||||
rx->sk.sk_state != RXRPC_CLOSE)
|
||||
goto found_service;
|
||||
}
|
||||
read_unlock_bh(&local->services_lock);
|
||||
goto invalid_service;
|
||||
|
||||
found_service:
|
||||
_debug("found service %hd", ntohs(rx->service_id));
|
||||
if (sk_acceptq_is_full(&rx->sk))
|
||||
goto backlog_full;
|
||||
sk_acceptq_added(&rx->sk);
|
||||
sock_hold(&rx->sk);
|
||||
read_unlock_bh(&local->services_lock);
|
||||
|
||||
ret = rxrpc_accept_incoming_call(local, rx, skb, &srx);
|
||||
if (ret < 0)
|
||||
sk_acceptq_removed(&rx->sk);
|
||||
sock_put(&rx->sk);
|
||||
switch (ret) {
|
||||
case -ECONNRESET: /* old calls are ignored */
|
||||
case -ECONNABORTED: /* aborted calls are reaborted or ignored */
|
||||
case 0:
|
||||
goto process_next_packet;
|
||||
case -ECONNREFUSED:
|
||||
goto invalid_service;
|
||||
case -EBUSY:
|
||||
goto busy;
|
||||
case -EKEYREJECTED:
|
||||
goto security_mismatch;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
backlog_full:
|
||||
read_unlock_bh(&local->services_lock);
|
||||
busy:
|
||||
rxrpc_busy(local, &srx, &sp->hdr);
|
||||
rxrpc_free_skb(skb);
|
||||
goto process_next_packet;
|
||||
|
||||
invalid_service:
|
||||
skb->priority = RX_INVALID_OPERATION;
|
||||
rxrpc_reject_packet(local, skb);
|
||||
goto process_next_packet;
|
||||
|
||||
/* can't change connection security type mid-flow */
|
||||
security_mismatch:
|
||||
skb->priority = RX_PROTOCOL_ERROR;
|
||||
rxrpc_reject_packet(local, skb);
|
||||
goto process_next_packet;
|
||||
}
|
||||
|
||||
/*
|
||||
* handle acceptance of a call by userspace
|
||||
* - assign the user call ID to the call at the front of the queue
|
||||
*/
|
||||
int rxrpc_accept_call(struct rxrpc_sock *rx, unsigned long user_call_ID)
|
||||
{
|
||||
struct rxrpc_call *call;
|
||||
struct rb_node *parent, **pp;
|
||||
int ret;
|
||||
|
||||
_enter(",%lx", user_call_ID);
|
||||
|
||||
ASSERT(!irqs_disabled());
|
||||
|
||||
write_lock(&rx->call_lock);
|
||||
|
||||
ret = -ENODATA;
|
||||
if (list_empty(&rx->acceptq))
|
||||
goto out;
|
||||
|
||||
/* check the user ID isn't already in use */
|
||||
ret = -EBADSLT;
|
||||
pp = &rx->calls.rb_node;
|
||||
parent = NULL;
|
||||
while (*pp) {
|
||||
parent = *pp;
|
||||
call = rb_entry(parent, struct rxrpc_call, sock_node);
|
||||
|
||||
if (user_call_ID < call->user_call_ID)
|
||||
pp = &(*pp)->rb_left;
|
||||
else if (user_call_ID > call->user_call_ID)
|
||||
pp = &(*pp)->rb_right;
|
||||
else
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* dequeue the first call and check it's still valid */
|
||||
call = list_entry(rx->acceptq.next, struct rxrpc_call, accept_link);
|
||||
list_del_init(&call->accept_link);
|
||||
sk_acceptq_removed(&rx->sk);
|
||||
|
||||
write_lock_bh(&call->state_lock);
|
||||
switch (call->state) {
|
||||
case RXRPC_CALL_SERVER_ACCEPTING:
|
||||
call->state = RXRPC_CALL_SERVER_RECV_REQUEST;
|
||||
break;
|
||||
case RXRPC_CALL_REMOTELY_ABORTED:
|
||||
case RXRPC_CALL_LOCALLY_ABORTED:
|
||||
ret = -ECONNABORTED;
|
||||
goto out_release;
|
||||
case RXRPC_CALL_NETWORK_ERROR:
|
||||
ret = call->conn->error;
|
||||
goto out_release;
|
||||
case RXRPC_CALL_DEAD:
|
||||
ret = -ETIME;
|
||||
goto out_discard;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* formalise the acceptance */
|
||||
call->user_call_ID = user_call_ID;
|
||||
rb_link_node(&call->sock_node, parent, pp);
|
||||
rb_insert_color(&call->sock_node, &rx->calls);
|
||||
if (test_and_set_bit(RXRPC_CALL_HAS_USERID, &call->flags))
|
||||
BUG();
|
||||
if (test_and_set_bit(RXRPC_CALL_ACCEPTED, &call->events))
|
||||
BUG();
|
||||
schedule_work(&call->processor);
|
||||
|
||||
write_unlock_bh(&call->state_lock);
|
||||
write_unlock(&rx->call_lock);
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
/* if the call is already dying or dead, then we leave the socket's ref
|
||||
* on it to be released by rxrpc_dead_call_expired() as induced by
|
||||
* rxrpc_release_call() */
|
||||
out_release:
|
||||
_debug("release %p", call);
|
||||
if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) &&
|
||||
!test_and_set_bit(RXRPC_CALL_RELEASE, &call->events))
|
||||
schedule_work(&call->processor);
|
||||
out_discard:
|
||||
write_unlock_bh(&call->state_lock);
|
||||
_debug("discard %p", call);
|
||||
out:
|
||||
write_unlock(&rx->call_lock);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
1250
net/rxrpc/ar-ack.c
Normal file
1250
net/rxrpc/ar-ack.c
Normal file
File diff suppressed because it is too large
Load Diff
787
net/rxrpc/ar-call.c
Normal file
787
net/rxrpc/ar-call.c
Normal file
File diff suppressed because it is too large
Load Diff
895
net/rxrpc/ar-connection.c
Normal file
895
net/rxrpc/ar-connection.c
Normal file
File diff suppressed because it is too large
Load Diff
387
net/rxrpc/ar-connevent.c
Normal file
387
net/rxrpc/ar-connevent.c
Normal file
@@ -0,0 +1,387 @@
|
||||
/* connection-level event handling
|
||||
*
|
||||
* Copyright (C) 2007 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/module.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/errqueue.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/af_rxrpc.h>
|
||||
#include <net/ip.h>
|
||||
#include "ar-internal.h"
|
||||
|
||||
/*
|
||||
* pass a connection-level abort onto all calls on that connection
|
||||
*/
|
||||
static void rxrpc_abort_calls(struct rxrpc_connection *conn, int state,
|
||||
u32 abort_code)
|
||||
{
|
||||
struct rxrpc_call *call;
|
||||
struct rb_node *p;
|
||||
|
||||
_enter("{%d},%x", conn->debug_id, abort_code);
|
||||
|
||||
read_lock_bh(&conn->lock);
|
||||
|
||||
for (p = rb_first(&conn->calls); p; p = rb_next(p)) {
|
||||
call = rb_entry(p, struct rxrpc_call, conn_node);
|
||||
write_lock(&call->state_lock);
|
||||
if (call->state <= RXRPC_CALL_COMPLETE) {
|
||||
call->state = state;
|
||||
call->abort_code = abort_code;
|
||||
if (state == RXRPC_CALL_LOCALLY_ABORTED)
|
||||
set_bit(RXRPC_CALL_CONN_ABORT, &call->events);
|
||||
else
|
||||
set_bit(RXRPC_CALL_RCVD_ABORT, &call->events);
|
||||
schedule_work(&call->processor);
|
||||
}
|
||||
write_unlock(&call->state_lock);
|
||||
}
|
||||
|
||||
read_unlock_bh(&conn->lock);
|
||||
_leave("");
|
||||
}
|
||||
|
||||
/*
|
||||
* generate a connection-level abort
|
||||
*/
|
||||
static int rxrpc_abort_connection(struct rxrpc_connection *conn,
|
||||
u32 error, u32 abort_code)
|
||||
{
|
||||
struct rxrpc_header hdr;
|
||||
struct msghdr msg;
|
||||
struct kvec iov[2];
|
||||
__be32 word;
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
_enter("%d,,%u,%u", conn->debug_id, error, abort_code);
|
||||
|
||||
/* generate a connection-level abort */
|
||||
spin_lock_bh(&conn->state_lock);
|
||||
if (conn->state < RXRPC_CONN_REMOTELY_ABORTED) {
|
||||
conn->state = RXRPC_CONN_LOCALLY_ABORTED;
|
||||
conn->error = error;
|
||||
spin_unlock_bh(&conn->state_lock);
|
||||
} else {
|
||||
spin_unlock_bh(&conn->state_lock);
|
||||
_leave(" = 0 [already dead]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rxrpc_abort_calls(conn, RXRPC_CALL_LOCALLY_ABORTED, abort_code);
|
||||
|
||||
msg.msg_name = &conn->trans->peer->srx.transport.sin;
|
||||
msg.msg_namelen = sizeof(conn->trans->peer->srx.transport.sin);
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
hdr.epoch = conn->epoch;
|
||||
hdr.cid = conn->cid;
|
||||
hdr.callNumber = 0;
|
||||
hdr.seq = 0;
|
||||
hdr.type = RXRPC_PACKET_TYPE_ABORT;
|
||||
hdr.flags = conn->out_clientflag;
|
||||
hdr.userStatus = 0;
|
||||
hdr.securityIndex = conn->security_ix;
|
||||
hdr._rsvd = 0;
|
||||
hdr.serviceId = conn->service_id;
|
||||
|
||||
word = htonl(abort_code);
|
||||
|
||||
iov[0].iov_base = &hdr;
|
||||
iov[0].iov_len = sizeof(hdr);
|
||||
iov[1].iov_base = &word;
|
||||
iov[1].iov_len = sizeof(word);
|
||||
|
||||
len = iov[0].iov_len + iov[1].iov_len;
|
||||
|
||||
hdr.serial = htonl(atomic_inc_return(&conn->serial));
|
||||
_proto("Tx CONN ABORT %%%u { %d }", ntohl(hdr.serial), abort_code);
|
||||
|
||||
ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 2, len);
|
||||
if (ret < 0) {
|
||||
_debug("sendmsg failed: %d", ret);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* mark a call as being on a now-secured channel
|
||||
* - must be called with softirqs disabled
|
||||
*/
|
||||
void rxrpc_call_is_secure(struct rxrpc_call *call)
|
||||
{
|
||||
_enter("%p", call);
|
||||
if (call) {
|
||||
read_lock(&call->state_lock);
|
||||
if (call->state < RXRPC_CALL_COMPLETE &&
|
||||
!test_and_set_bit(RXRPC_CALL_SECURED, &call->events))
|
||||
schedule_work(&call->processor);
|
||||
read_unlock(&call->state_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* connection-level Rx packet processor
|
||||
*/
|
||||
static int rxrpc_process_event(struct rxrpc_connection *conn,
|
||||
struct sk_buff *skb,
|
||||
u32 *_abort_code)
|
||||
{
|
||||
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
||||
__be32 tmp;
|
||||
u32 serial;
|
||||
int loop, ret;
|
||||
|
||||
if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED)
|
||||
return -ECONNABORTED;
|
||||
|
||||
serial = ntohl(sp->hdr.serial);
|
||||
|
||||
switch (sp->hdr.type) {
|
||||
case RXRPC_PACKET_TYPE_ABORT:
|
||||
if (skb_copy_bits(skb, 0, &tmp, sizeof(tmp)) < 0)
|
||||
return -EPROTO;
|
||||
_proto("Rx ABORT %%%u { ac=%d }", serial, ntohl(tmp));
|
||||
|
||||
conn->state = RXRPC_CONN_REMOTELY_ABORTED;
|
||||
rxrpc_abort_calls(conn, RXRPC_CALL_REMOTELY_ABORTED,
|
||||
ntohl(tmp));
|
||||
return -ECONNABORTED;
|
||||
|
||||
case RXRPC_PACKET_TYPE_CHALLENGE:
|
||||
if (conn->security)
|
||||
return conn->security->respond_to_challenge(
|
||||
conn, skb, _abort_code);
|
||||
return -EPROTO;
|
||||
|
||||
case RXRPC_PACKET_TYPE_RESPONSE:
|
||||
if (!conn->security)
|
||||
return -EPROTO;
|
||||
|
||||
ret = conn->security->verify_response(conn, skb, _abort_code);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = conn->security->init_connection_security(conn);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
conn->security->prime_packet_security(conn);
|
||||
read_lock_bh(&conn->lock);
|
||||
spin_lock(&conn->state_lock);
|
||||
|
||||
if (conn->state == RXRPC_CONN_SERVER_CHALLENGING) {
|
||||
conn->state = RXRPC_CONN_SERVER;
|
||||
for (loop = 0; loop < RXRPC_MAXCALLS; loop++)
|
||||
rxrpc_call_is_secure(conn->channels[loop]);
|
||||
}
|
||||
|
||||
spin_unlock(&conn->state_lock);
|
||||
read_unlock_bh(&conn->lock);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -EPROTO;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set up security and issue a challenge
|
||||
*/
|
||||
static void rxrpc_secure_connection(struct rxrpc_connection *conn)
|
||||
{
|
||||
u32 abort_code;
|
||||
int ret;
|
||||
|
||||
_enter("{%d}", conn->debug_id);
|
||||
|
||||
ASSERT(conn->security_ix != 0);
|
||||
|
||||
if (!conn->key) {
|
||||
_debug("set up security");
|
||||
ret = rxrpc_init_server_conn_security(conn);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
case -ENOENT:
|
||||
abort_code = RX_CALL_DEAD;
|
||||
goto abort;
|
||||
default:
|
||||
abort_code = RXKADNOAUTH;
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(conn->security != NULL);
|
||||
|
||||
if (conn->security->issue_challenge(conn) < 0) {
|
||||
abort_code = RX_CALL_DEAD;
|
||||
ret = -ENOMEM;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
_leave("");
|
||||
return;
|
||||
|
||||
abort:
|
||||
_debug("abort %d, %d", ret, abort_code);
|
||||
rxrpc_abort_connection(conn, -ret, abort_code);
|
||||
_leave(" [aborted]");
|
||||
}
|
||||
|
||||
/*
|
||||
* connection-level event processor
|
||||
*/
|
||||
void rxrpc_process_connection(struct work_struct *work)
|
||||
{
|
||||
struct rxrpc_connection *conn =
|
||||
container_of(work, struct rxrpc_connection, processor);
|
||||
struct rxrpc_skb_priv *sp;
|
||||
struct sk_buff *skb;
|
||||
u32 abort_code = RX_PROTOCOL_ERROR;
|
||||
int ret;
|
||||
|
||||
_enter("{%d}", conn->debug_id);
|
||||
|
||||
atomic_inc(&conn->usage);
|
||||
|
||||
if (test_and_clear_bit(RXRPC_CONN_CHALLENGE, &conn->events)) {
|
||||
rxrpc_secure_connection(conn);
|
||||
rxrpc_put_connection(conn);
|
||||
}
|
||||
|
||||
/* go through the conn-level event packets, releasing the ref on this
|
||||
* connection that each one has when we've finished with it */
|
||||
while ((skb = skb_dequeue(&conn->rx_queue))) {
|
||||
sp = rxrpc_skb(skb);
|
||||
|
||||
ret = rxrpc_process_event(conn, skb, &abort_code);
|
||||
switch (ret) {
|
||||
case -EPROTO:
|
||||
case -EKEYEXPIRED:
|
||||
case -EKEYREJECTED:
|
||||
goto protocol_error;
|
||||
case -EAGAIN:
|
||||
goto requeue_and_leave;
|
||||
case -ECONNABORTED:
|
||||
default:
|
||||
rxrpc_put_connection(conn);
|
||||
rxrpc_free_skb(skb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
rxrpc_put_connection(conn);
|
||||
_leave("");
|
||||
return;
|
||||
|
||||
requeue_and_leave:
|
||||
skb_queue_head(&conn->rx_queue, skb);
|
||||
goto out;
|
||||
|
||||
protocol_error:
|
||||
if (rxrpc_abort_connection(conn, -ret, abort_code) < 0)
|
||||
goto requeue_and_leave;
|
||||
rxrpc_put_connection(conn);
|
||||
rxrpc_free_skb(skb);
|
||||
_leave(" [EPROTO]");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* reject packets through the local endpoint
|
||||
*/
|
||||
void rxrpc_reject_packets(struct work_struct *work)
|
||||
{
|
||||
union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_in sin;
|
||||
} sa;
|
||||
struct rxrpc_skb_priv *sp;
|
||||
struct rxrpc_header hdr;
|
||||
struct rxrpc_local *local;
|
||||
struct sk_buff *skb;
|
||||
struct msghdr msg;
|
||||
struct kvec iov[2];
|
||||
size_t size;
|
||||
__be32 code;
|
||||
|
||||
local = container_of(work, struct rxrpc_local, rejecter);
|
||||
rxrpc_get_local(local);
|
||||
|
||||
_enter("%d", local->debug_id);
|
||||
|
||||
iov[0].iov_base = &hdr;
|
||||
iov[0].iov_len = sizeof(hdr);
|
||||
iov[1].iov_base = &code;
|
||||
iov[1].iov_len = sizeof(code);
|
||||
size = sizeof(hdr) + sizeof(code);
|
||||
|
||||
msg.msg_name = &sa;
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa.sa_family = local->srx.transport.family;
|
||||
switch (sa.sa.sa_family) {
|
||||
case AF_INET:
|
||||
msg.msg_namelen = sizeof(sa.sin);
|
||||
break;
|
||||
default:
|
||||
msg.msg_namelen = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
hdr.type = RXRPC_PACKET_TYPE_ABORT;
|
||||
|
||||
while ((skb = skb_dequeue(&local->reject_queue))) {
|
||||
sp = rxrpc_skb(skb);
|
||||
switch (sa.sa.sa_family) {
|
||||
case AF_INET:
|
||||
sa.sin.sin_port = udp_hdr(skb)->source;
|
||||
sa.sin.sin_addr.s_addr = ip_hdr(skb)->saddr;
|
||||
code = htonl(skb->priority);
|
||||
|
||||
hdr.epoch = sp->hdr.epoch;
|
||||
hdr.cid = sp->hdr.cid;
|
||||
hdr.callNumber = sp->hdr.callNumber;
|
||||
hdr.serviceId = sp->hdr.serviceId;
|
||||
hdr.flags = sp->hdr.flags;
|
||||
hdr.flags ^= RXRPC_CLIENT_INITIATED;
|
||||
hdr.flags &= RXRPC_CLIENT_INITIATED;
|
||||
|
||||
kernel_sendmsg(local->socket, &msg, iov, 2, size);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
rxrpc_free_skb(skb);
|
||||
rxrpc_put_local(local);
|
||||
}
|
||||
|
||||
rxrpc_put_local(local);
|
||||
_leave("");
|
||||
}
|
||||
253
net/rxrpc/ar-error.c
Normal file
253
net/rxrpc/ar-error.c
Normal file
@@ -0,0 +1,253 @@
|
||||
/* Error message handling (ICMP)
|
||||
*
|
||||
* Copyright (C) 2007 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/module.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/errqueue.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/af_rxrpc.h>
|
||||
#include <net/ip.h>
|
||||
#include "ar-internal.h"
|
||||
|
||||
/*
|
||||
* handle an error received on the local endpoint
|
||||
*/
|
||||
void rxrpc_UDP_error_report(struct sock *sk)
|
||||
{
|
||||
struct sock_exterr_skb *serr;
|
||||
struct rxrpc_transport *trans;
|
||||
struct rxrpc_local *local = sk->sk_user_data;
|
||||
struct rxrpc_peer *peer;
|
||||
struct sk_buff *skb;
|
||||
__be32 addr;
|
||||
__be16 port;
|
||||
|
||||
_enter("%p{%d}", sk, local->debug_id);
|
||||
|
||||
skb = skb_dequeue(&sk->sk_error_queue);
|
||||
if (!skb) {
|
||||
_leave("UDP socket errqueue empty");
|
||||
return;
|
||||
}
|
||||
|
||||
rxrpc_new_skb(skb);
|
||||
|
||||
serr = SKB_EXT_ERR(skb);
|
||||
addr = *(__be32 *)(skb_network_header(skb) + serr->addr_offset);
|
||||
port = serr->port;
|
||||
|
||||
_net("Rx UDP Error from "NIPQUAD_FMT":%hu",
|
||||
NIPQUAD(addr), ntohs(port));
|
||||
_debug("Msg l:%d d:%d", skb->len, skb->data_len);
|
||||
|
||||
peer = rxrpc_find_peer(local, addr, port);
|
||||
if (IS_ERR(peer)) {
|
||||
rxrpc_free_skb(skb);
|
||||
_leave(" [no peer]");
|
||||
return;
|
||||
}
|
||||
|
||||
trans = rxrpc_find_transport(local, peer);
|
||||
if (!trans) {
|
||||
rxrpc_put_peer(peer);
|
||||
rxrpc_free_skb(skb);
|
||||
_leave(" [no trans]");
|
||||
return;
|
||||
}
|
||||
|
||||
if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP &&
|
||||
serr->ee.ee_type == ICMP_DEST_UNREACH &&
|
||||
serr->ee.ee_code == ICMP_FRAG_NEEDED
|
||||
) {
|
||||
u32 mtu = serr->ee.ee_info;
|
||||
|
||||
_net("Rx Received ICMP Fragmentation Needed (%d)", mtu);
|
||||
|
||||
/* wind down the local interface MTU */
|
||||
if (mtu > 0 && peer->if_mtu == 65535 && mtu < peer->if_mtu) {
|
||||
peer->if_mtu = mtu;
|
||||
_net("I/F MTU %u", mtu);
|
||||
}
|
||||
|
||||
/* ip_rt_frag_needed() may have eaten the info */
|
||||
if (mtu == 0)
|
||||
mtu = ntohs(icmp_hdr(skb)->un.frag.mtu);
|
||||
|
||||
if (mtu == 0) {
|
||||
/* they didn't give us a size, estimate one */
|
||||
if (mtu > 1500) {
|
||||
mtu >>= 1;
|
||||
if (mtu < 1500)
|
||||
mtu = 1500;
|
||||
} else {
|
||||
mtu -= 100;
|
||||
if (mtu < peer->hdrsize)
|
||||
mtu = peer->hdrsize + 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (mtu < peer->mtu) {
|
||||
peer->mtu = mtu;
|
||||
peer->maxdata = peer->mtu - peer->hdrsize;
|
||||
_net("Net MTU %u (maxdata %u)",
|
||||
peer->mtu, peer->maxdata);
|
||||
}
|
||||
}
|
||||
|
||||
rxrpc_put_peer(peer);
|
||||
|
||||
/* pass the transport ref to error_handler to release */
|
||||
skb_queue_tail(&trans->error_queue, skb);
|
||||
schedule_work(&trans->error_handler);
|
||||
|
||||
/* reset and regenerate socket error */
|
||||
spin_lock_bh(&sk->sk_error_queue.lock);
|
||||
sk->sk_err = 0;
|
||||
skb = skb_peek(&sk->sk_error_queue);
|
||||
if (skb) {
|
||||
sk->sk_err = SKB_EXT_ERR(skb)->ee.ee_errno;
|
||||
spin_unlock_bh(&sk->sk_error_queue.lock);
|
||||
sk->sk_error_report(sk);
|
||||
} else {
|
||||
spin_unlock_bh(&sk->sk_error_queue.lock);
|
||||
}
|
||||
|
||||
_leave("");
|
||||
}
|
||||
|
||||
/*
|
||||
* deal with UDP error messages
|
||||
*/
|
||||
void rxrpc_UDP_error_handler(struct work_struct *work)
|
||||
{
|
||||
struct sock_extended_err *ee;
|
||||
struct sock_exterr_skb *serr;
|
||||
struct rxrpc_transport *trans =
|
||||
container_of(work, struct rxrpc_transport, error_handler);
|
||||
struct sk_buff *skb;
|
||||
int local, err;
|
||||
|
||||
_enter("");
|
||||
|
||||
skb = skb_dequeue(&trans->error_queue);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
serr = SKB_EXT_ERR(skb);
|
||||
ee = &serr->ee;
|
||||
|
||||
_net("Rx Error o=%d t=%d c=%d e=%d",
|
||||
ee->ee_origin, ee->ee_type, ee->ee_code, ee->ee_errno);
|
||||
|
||||
err = ee->ee_errno;
|
||||
|
||||
switch (ee->ee_origin) {
|
||||
case SO_EE_ORIGIN_ICMP:
|
||||
local = 0;
|
||||
switch (ee->ee_type) {
|
||||
case ICMP_DEST_UNREACH:
|
||||
switch (ee->ee_code) {
|
||||
case ICMP_NET_UNREACH:
|
||||
_net("Rx Received ICMP Network Unreachable");
|
||||
err = ENETUNREACH;
|
||||
break;
|
||||
case ICMP_HOST_UNREACH:
|
||||
_net("Rx Received ICMP Host Unreachable");
|
||||
err = EHOSTUNREACH;
|
||||
break;
|
||||
case ICMP_PORT_UNREACH:
|
||||
_net("Rx Received ICMP Port Unreachable");
|
||||
err = ECONNREFUSED;
|
||||
break;
|
||||
case ICMP_FRAG_NEEDED:
|
||||
_net("Rx Received ICMP Fragmentation Needed (%d)",
|
||||
ee->ee_info);
|
||||
err = 0; /* dealt with elsewhere */
|
||||
break;
|
||||
case ICMP_NET_UNKNOWN:
|
||||
_net("Rx Received ICMP Unknown Network");
|
||||
err = ENETUNREACH;
|
||||
break;
|
||||
case ICMP_HOST_UNKNOWN:
|
||||
_net("Rx Received ICMP Unknown Host");
|
||||
err = EHOSTUNREACH;
|
||||
break;
|
||||
default:
|
||||
_net("Rx Received ICMP DestUnreach code=%u",
|
||||
ee->ee_code);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ICMP_TIME_EXCEEDED:
|
||||
_net("Rx Received ICMP TTL Exceeded");
|
||||
break;
|
||||
|
||||
default:
|
||||
_proto("Rx Received ICMP error { type=%u code=%u }",
|
||||
ee->ee_type, ee->ee_code);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SO_EE_ORIGIN_LOCAL:
|
||||
_proto("Rx Received local error { error=%d }",
|
||||
ee->ee_errno);
|
||||
local = 1;
|
||||
break;
|
||||
|
||||
case SO_EE_ORIGIN_NONE:
|
||||
case SO_EE_ORIGIN_ICMP6:
|
||||
default:
|
||||
_proto("Rx Received error report { orig=%u }",
|
||||
ee->ee_origin);
|
||||
local = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* terminate all the affected calls if there's an unrecoverable
|
||||
* error */
|
||||
if (err) {
|
||||
struct rxrpc_call *call, *_n;
|
||||
|
||||
_debug("ISSUE ERROR %d", err);
|
||||
|
||||
spin_lock_bh(&trans->peer->lock);
|
||||
trans->peer->net_error = err;
|
||||
|
||||
list_for_each_entry_safe(call, _n, &trans->peer->error_targets,
|
||||
error_link) {
|
||||
write_lock(&call->state_lock);
|
||||
if (call->state != RXRPC_CALL_COMPLETE &&
|
||||
call->state < RXRPC_CALL_NETWORK_ERROR) {
|
||||
call->state = RXRPC_CALL_NETWORK_ERROR;
|
||||
set_bit(RXRPC_CALL_RCVD_ERROR, &call->events);
|
||||
schedule_work(&call->processor);
|
||||
}
|
||||
write_unlock(&call->state_lock);
|
||||
list_del_init(&call->error_link);
|
||||
}
|
||||
|
||||
spin_unlock_bh(&trans->peer->lock);
|
||||
}
|
||||
|
||||
if (!skb_queue_empty(&trans->error_queue))
|
||||
schedule_work(&trans->error_handler);
|
||||
|
||||
rxrpc_free_skb(skb);
|
||||
rxrpc_put_transport(trans);
|
||||
_leave("");
|
||||
}
|
||||
791
net/rxrpc/ar-input.c
Normal file
791
net/rxrpc/ar-input.c
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user