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
|
* 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_error_report(struct sock *);
|
||||||
void rxrpc_peer_keepalive_worker(struct work_struct *);
|
void rxrpc_peer_keepalive_worker(struct work_struct *);
|
||||||
|
|
||||||
|
@ -23,6 +23,19 @@
|
|||||||
static void rxrpc_local_processor(struct work_struct *);
|
static void rxrpc_local_processor(struct work_struct *);
|
||||||
static void rxrpc_local_rcu(struct rcu_head *);
|
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,
|
* Compare a local to an address. Return -ve, 0 or +ve to indicate less than,
|
||||||
* same or greater than.
|
* same or greater than.
|
||||||
|
@ -16,220 +16,12 @@
|
|||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
#include <net/af_rxrpc.h>
|
#include <net/af_rxrpc.h>
|
||||||
#include <net/ip.h>
|
#include <net/ip.h>
|
||||||
#include <net/icmp.h>
|
|
||||||
#include "ar-internal.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_store_error(struct rxrpc_peer *, struct sock_exterr_skb *);
|
||||||
static void rxrpc_distribute_error(struct rxrpc_peer *, int,
|
static void rxrpc_distribute_error(struct rxrpc_peer *, int,
|
||||||
enum rxrpc_call_completion);
|
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.
|
* 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_len = local->srx.transport_len;
|
||||||
srx->transport.family = local->srx.transport.family;
|
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) {
|
switch (srx->transport.family) {
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
srx->transport_len = sizeof(srx->transport.sin);
|
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);
|
rxrpc_new_skb(skb, rxrpc_skb_received);
|
||||||
serr = SKB_EXT_ERR(skb);
|
serr = SKB_EXT_ERR(skb);
|
||||||
|
if (!skb->len && serr->ee.ee_origin == SO_EE_ORIGIN_TIMESTAMPING) {
|
||||||
if (serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL) {
|
_leave("UDP empty message");
|
||||||
peer = rxrpc_lookup_peer_local_rcu(local, skb, &srx);
|
rcu_read_unlock();
|
||||||
if (peer && !rxrpc_get_peer_maybe(peer))
|
rxrpc_free_skb(skb, rxrpc_skb_freed);
|
||||||
peer = NULL;
|
return;
|
||||||
if (peer) {
|
|
||||||
trace_rxrpc_rx_icmp(peer, &serr->ee, &srx);
|
|
||||||
rxrpc_store_error(peer, serr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
rcu_read_unlock();
|
||||||
rxrpc_free_skb(skb, rxrpc_skb_freed);
|
rxrpc_free_skb(skb, rxrpc_skb_freed);
|
||||||
rxrpc_put_peer(peer);
|
rxrpc_put_peer(peer);
|
||||||
|
|
||||||
_leave("");
|
_leave("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user