mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
rxrpc: Use the core ICMP/ICMP6 parsers
Make rxrpc_encap_rcv_err() pass the ICMP/ICMP6 skbuff to ip_icmp_error() or ipv6_icmp_error() as appropriate to do the parsing rather than trying to do it in rxrpc. This pushes an error report onto the UDP socket's error queue and calls ->sk_error_report() from which point rxrpc can pick it up. It would be preferable to steal the packet directly from ip*_icmp_error() rather than letting it get queued, but this is probably good enough. Also note that __udp4_lib_err() calls sk_error_report() twice in some cases. Signed-off-by: David Howells <dhowells@redhat.com> cc: Marc Dionne <marc.dionne@auristor.com> cc: linux-afs@lists.infradead.org
This commit is contained in:
parent
42fb06b391
commit
b6c66c4324
@ -998,7 +998,6 @@ void rxrpc_send_keepalive(struct rxrpc_peer *);
|
||||
/*
|
||||
* peer_event.c
|
||||
*/
|
||||
void rxrpc_encap_err_rcv(struct sock *, struct sk_buff *, int, __be16, u32, u8 *);
|
||||
void rxrpc_error_report(struct sock *);
|
||||
void rxrpc_peer_keepalive_worker(struct work_struct *);
|
||||
|
||||
|
@ -23,6 +23,19 @@
|
||||
static void rxrpc_local_processor(struct work_struct *);
|
||||
static void rxrpc_local_rcu(struct rcu_head *);
|
||||
|
||||
/*
|
||||
* Handle an ICMP/ICMP6 error turning up at the tunnel. Push it through the
|
||||
* usual mechanism so that it gets parsed and presented through the UDP
|
||||
* socket's error_report().
|
||||
*/
|
||||
static void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, int err,
|
||||
__be16 port, u32 info, u8 *payload)
|
||||
{
|
||||
if (ip_hdr(skb)->version == IPVERSION)
|
||||
return ip_icmp_error(sk, skb, err, port, info, payload);
|
||||
return ipv6_icmp_error(sk, skb, err, port, info, payload);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare a local to an address. Return -ve, 0 or +ve to indicate less than,
|
||||
* same or greater than.
|
||||
|
@ -16,220 +16,12 @@
|
||||
#include <net/sock.h>
|
||||
#include <net/af_rxrpc.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/icmp.h>
|
||||
#include "ar-internal.h"
|
||||
|
||||
static void rxrpc_adjust_mtu(struct rxrpc_peer *, unsigned int);
|
||||
static void rxrpc_store_error(struct rxrpc_peer *, struct sock_exterr_skb *);
|
||||
static void rxrpc_distribute_error(struct rxrpc_peer *, int,
|
||||
enum rxrpc_call_completion);
|
||||
|
||||
/*
|
||||
* Find the peer associated with an ICMPv4 packet.
|
||||
*/
|
||||
static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local,
|
||||
struct sk_buff *skb,
|
||||
__be16 udp_port,
|
||||
struct sockaddr_rxrpc *srx)
|
||||
{
|
||||
struct iphdr *ip, *ip0 = ip_hdr(skb);
|
||||
struct icmphdr *icmp = icmp_hdr(skb);
|
||||
|
||||
_enter("%u,%u,%u", ip0->protocol, icmp->type, icmp->code);
|
||||
|
||||
switch (icmp->type) {
|
||||
case ICMP_DEST_UNREACH:
|
||||
case ICMP_TIME_EXCEEDED:
|
||||
case ICMP_PARAMETERPROB:
|
||||
ip = (struct iphdr *)((void *)icmp + 8);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(srx, 0, sizeof(*srx));
|
||||
srx->transport_type = local->srx.transport_type;
|
||||
srx->transport_len = local->srx.transport_len;
|
||||
srx->transport.family = local->srx.transport.family;
|
||||
|
||||
/* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice
|
||||
* versa?
|
||||
*/
|
||||
switch (srx->transport.family) {
|
||||
case AF_INET:
|
||||
srx->transport_len = sizeof(srx->transport.sin);
|
||||
srx->transport.family = AF_INET;
|
||||
srx->transport.sin.sin_port = udp_port;
|
||||
memcpy(&srx->transport.sin.sin_addr, &ip->daddr,
|
||||
sizeof(struct in_addr));
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_AF_RXRPC_IPV6
|
||||
case AF_INET6:
|
||||
srx->transport_len = sizeof(srx->transport.sin);
|
||||
srx->transport.family = AF_INET;
|
||||
srx->transport.sin.sin_port = udp_port;
|
||||
memcpy(&srx->transport.sin.sin_addr, &ip->daddr,
|
||||
sizeof(struct in_addr));
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_net("ICMP {%pISp}", &srx->transport);
|
||||
return rxrpc_lookup_peer_rcu(local, srx);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AF_RXRPC_IPV6
|
||||
/*
|
||||
* Find the peer associated with an ICMPv6 packet.
|
||||
*/
|
||||
static struct rxrpc_peer *rxrpc_lookup_peer_icmp6_rcu(struct rxrpc_local *local,
|
||||
struct sk_buff *skb,
|
||||
__be16 udp_port,
|
||||
struct sockaddr_rxrpc *srx)
|
||||
{
|
||||
struct icmp6hdr *icmp = icmp6_hdr(skb);
|
||||
struct ipv6hdr *ip, *ip0 = ipv6_hdr(skb);
|
||||
|
||||
_enter("%u,%u,%u", ip0->nexthdr, icmp->icmp6_type, icmp->icmp6_code);
|
||||
|
||||
switch (icmp->icmp6_type) {
|
||||
case ICMPV6_DEST_UNREACH:
|
||||
case ICMPV6_PKT_TOOBIG:
|
||||
case ICMPV6_TIME_EXCEED:
|
||||
case ICMPV6_PARAMPROB:
|
||||
ip = (struct ipv6hdr *)((void *)icmp + 8);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(srx, 0, sizeof(*srx));
|
||||
srx->transport_type = local->srx.transport_type;
|
||||
srx->transport_len = local->srx.transport_len;
|
||||
srx->transport.family = local->srx.transport.family;
|
||||
|
||||
/* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice
|
||||
* versa?
|
||||
*/
|
||||
switch (srx->transport.family) {
|
||||
case AF_INET:
|
||||
_net("Rx ICMP6 on v4 sock");
|
||||
srx->transport_len = sizeof(srx->transport.sin);
|
||||
srx->transport.family = AF_INET;
|
||||
srx->transport.sin.sin_port = udp_port;
|
||||
memcpy(&srx->transport.sin.sin_addr,
|
||||
&ip->daddr.s6_addr32[3], sizeof(struct in_addr));
|
||||
break;
|
||||
case AF_INET6:
|
||||
_net("Rx ICMP6");
|
||||
srx->transport.sin.sin_port = udp_port;
|
||||
memcpy(&srx->transport.sin6.sin6_addr, &ip->daddr,
|
||||
sizeof(struct in6_addr));
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_net("ICMP {%pISp}", &srx->transport);
|
||||
return rxrpc_lookup_peer_rcu(local, srx);
|
||||
}
|
||||
#endif /* CONFIG_AF_RXRPC_IPV6 */
|
||||
|
||||
/*
|
||||
* Handle an error received on the local endpoint as a tunnel.
|
||||
*/
|
||||
void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, int err,
|
||||
__be16 port, u32 info, u8 *payload)
|
||||
{
|
||||
struct sock_extended_err ee;
|
||||
struct sockaddr_rxrpc srx;
|
||||
struct rxrpc_local *local;
|
||||
struct rxrpc_peer *peer;
|
||||
u8 version = ip_hdr(skb)->version;
|
||||
u8 type = icmp_hdr(skb)->type;
|
||||
u8 code = icmp_hdr(skb)->code;
|
||||
|
||||
rcu_read_lock();
|
||||
local = rcu_dereference_sk_user_data(sk);
|
||||
if (unlikely(!local)) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
rxrpc_new_skb(skb, rxrpc_skb_received);
|
||||
|
||||
switch (ip_hdr(skb)->version) {
|
||||
case IPVERSION:
|
||||
peer = rxrpc_lookup_peer_icmp_rcu(local, skb, port, &srx);
|
||||
break;
|
||||
#ifdef CONFIG_AF_RXRPC_IPV6
|
||||
case 6:
|
||||
peer = rxrpc_lookup_peer_icmp6_rcu(local, skb, port, &srx);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (peer && !rxrpc_get_peer_maybe(peer))
|
||||
peer = NULL;
|
||||
if (!peer) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&ee, 0, sizeof(ee));
|
||||
|
||||
switch (version) {
|
||||
case IPVERSION:
|
||||
if (type == ICMP_DEST_UNREACH &&
|
||||
code == ICMP_FRAG_NEEDED) {
|
||||
rxrpc_adjust_mtu(peer, info);
|
||||
rcu_read_unlock();
|
||||
rxrpc_put_peer(peer);
|
||||
return;
|
||||
}
|
||||
|
||||
ee.ee_origin = SO_EE_ORIGIN_ICMP;
|
||||
ee.ee_type = type;
|
||||
ee.ee_code = code;
|
||||
ee.ee_errno = err;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_AF_RXRPC_IPV6
|
||||
case 6:
|
||||
if (type == ICMPV6_PKT_TOOBIG) {
|
||||
rxrpc_adjust_mtu(peer, info);
|
||||
rcu_read_unlock();
|
||||
rxrpc_put_peer(peer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (err == EACCES)
|
||||
err = EHOSTUNREACH;
|
||||
|
||||
ee.ee_origin = SO_EE_ORIGIN_ICMP6;
|
||||
ee.ee_type = type;
|
||||
ee.ee_code = code;
|
||||
ee.ee_errno = err;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
trace_rxrpc_rx_icmp(peer, &ee, &srx);
|
||||
|
||||
rxrpc_distribute_error(peer, err, RXRPC_CALL_NETWORK_ERROR);
|
||||
rcu_read_unlock();
|
||||
rxrpc_put_peer(peer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the peer associated with a local error.
|
||||
*/
|
||||
@ -246,6 +38,9 @@ static struct rxrpc_peer *rxrpc_lookup_peer_local_rcu(struct rxrpc_local *local,
|
||||
srx->transport_len = local->srx.transport_len;
|
||||
srx->transport.family = local->srx.transport.family;
|
||||
|
||||
/* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice
|
||||
* versa?
|
||||
*/
|
||||
switch (srx->transport.family) {
|
||||
case AF_INET:
|
||||
srx->transport_len = sizeof(srx->transport.sin);
|
||||
@ -375,20 +170,38 @@ void rxrpc_error_report(struct sock *sk)
|
||||
}
|
||||
rxrpc_new_skb(skb, rxrpc_skb_received);
|
||||
serr = SKB_EXT_ERR(skb);
|
||||
|
||||
if (serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL) {
|
||||
peer = rxrpc_lookup_peer_local_rcu(local, skb, &srx);
|
||||
if (peer && !rxrpc_get_peer_maybe(peer))
|
||||
peer = NULL;
|
||||
if (peer) {
|
||||
trace_rxrpc_rx_icmp(peer, &serr->ee, &srx);
|
||||
rxrpc_store_error(peer, serr);
|
||||
}
|
||||
if (!skb->len && serr->ee.ee_origin == SO_EE_ORIGIN_TIMESTAMPING) {
|
||||
_leave("UDP empty message");
|
||||
rcu_read_unlock();
|
||||
rxrpc_free_skb(skb, rxrpc_skb_freed);
|
||||
return;
|
||||
}
|
||||
|
||||
peer = rxrpc_lookup_peer_local_rcu(local, skb, &srx);
|
||||
if (peer && !rxrpc_get_peer_maybe(peer))
|
||||
peer = NULL;
|
||||
if (!peer) {
|
||||
rcu_read_unlock();
|
||||
rxrpc_free_skb(skb, rxrpc_skb_freed);
|
||||
_leave(" [no peer]");
|
||||
return;
|
||||
}
|
||||
|
||||
trace_rxrpc_rx_icmp(peer, &serr->ee, &srx);
|
||||
|
||||
if ((serr->ee.ee_origin == SO_EE_ORIGIN_ICMP &&
|
||||
serr->ee.ee_type == ICMP_DEST_UNREACH &&
|
||||
serr->ee.ee_code == ICMP_FRAG_NEEDED)) {
|
||||
rxrpc_adjust_mtu(peer, serr->ee.ee_info);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rxrpc_store_error(peer, serr);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
rxrpc_free_skb(skb, rxrpc_skb_freed);
|
||||
rxrpc_put_peer(peer);
|
||||
|
||||
_leave("");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user