Skip to content

Commit b6c66c4

Browse files
committed
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 <[email protected]> cc: Marc Dionne <[email protected]> cc: [email protected]
1 parent 42fb06b commit b6c66c4

File tree

3 files changed

+42
-217
lines changed

3 files changed

+42
-217
lines changed

net/rxrpc/ar-internal.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,6 @@ void rxrpc_send_keepalive(struct rxrpc_peer *);
998998
/*
999999
* peer_event.c
10001000
*/
1001-
void rxrpc_encap_err_rcv(struct sock *, struct sk_buff *, int, __be16, u32, u8 *);
10021001
void rxrpc_error_report(struct sock *);
10031002
void rxrpc_peer_keepalive_worker(struct work_struct *);
10041003

net/rxrpc/local_object.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@
2323
static void rxrpc_local_processor(struct work_struct *);
2424
static void rxrpc_local_rcu(struct rcu_head *);
2525

26+
/*
27+
* Handle an ICMP/ICMP6 error turning up at the tunnel. Push it through the
28+
* usual mechanism so that it gets parsed and presented through the UDP
29+
* socket's error_report().
30+
*/
31+
static void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, int err,
32+
__be16 port, u32 info, u8 *payload)
33+
{
34+
if (ip_hdr(skb)->version == IPVERSION)
35+
return ip_icmp_error(sk, skb, err, port, info, payload);
36+
return ipv6_icmp_error(sk, skb, err, port, info, payload);
37+
}
38+
2639
/*
2740
* Compare a local to an address. Return -ve, 0 or +ve to indicate less than,
2841
* same or greater than.

net/rxrpc/peer_event.c

Lines changed: 29 additions & 216 deletions
Original file line numberDiff line numberDiff line change
@@ -16,220 +16,12 @@
1616
#include <net/sock.h>
1717
#include <net/af_rxrpc.h>
1818
#include <net/ip.h>
19-
#include <net/icmp.h>
2019
#include "ar-internal.h"
2120

22-
static void rxrpc_adjust_mtu(struct rxrpc_peer *, unsigned int);
2321
static void rxrpc_store_error(struct rxrpc_peer *, struct sock_exterr_skb *);
2422
static void rxrpc_distribute_error(struct rxrpc_peer *, int,
2523
enum rxrpc_call_completion);
2624

27-
/*
28-
* Find the peer associated with an ICMPv4 packet.
29-
*/
30-
static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local,
31-
struct sk_buff *skb,
32-
__be16 udp_port,
33-
struct sockaddr_rxrpc *srx)
34-
{
35-
struct iphdr *ip, *ip0 = ip_hdr(skb);
36-
struct icmphdr *icmp = icmp_hdr(skb);
37-
38-
_enter("%u,%u,%u", ip0->protocol, icmp->type, icmp->code);
39-
40-
switch (icmp->type) {
41-
case ICMP_DEST_UNREACH:
42-
case ICMP_TIME_EXCEEDED:
43-
case ICMP_PARAMETERPROB:
44-
ip = (struct iphdr *)((void *)icmp + 8);
45-
break;
46-
default:
47-
return NULL;
48-
}
49-
50-
memset(srx, 0, sizeof(*srx));
51-
srx->transport_type = local->srx.transport_type;
52-
srx->transport_len = local->srx.transport_len;
53-
srx->transport.family = local->srx.transport.family;
54-
55-
/* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice
56-
* versa?
57-
*/
58-
switch (srx->transport.family) {
59-
case AF_INET:
60-
srx->transport_len = sizeof(srx->transport.sin);
61-
srx->transport.family = AF_INET;
62-
srx->transport.sin.sin_port = udp_port;
63-
memcpy(&srx->transport.sin.sin_addr, &ip->daddr,
64-
sizeof(struct in_addr));
65-
break;
66-
67-
#ifdef CONFIG_AF_RXRPC_IPV6
68-
case AF_INET6:
69-
srx->transport_len = sizeof(srx->transport.sin);
70-
srx->transport.family = AF_INET;
71-
srx->transport.sin.sin_port = udp_port;
72-
memcpy(&srx->transport.sin.sin_addr, &ip->daddr,
73-
sizeof(struct in_addr));
74-
break;
75-
#endif
76-
77-
default:
78-
WARN_ON_ONCE(1);
79-
return NULL;
80-
}
81-
82-
_net("ICMP {%pISp}", &srx->transport);
83-
return rxrpc_lookup_peer_rcu(local, srx);
84-
}
85-
86-
#ifdef CONFIG_AF_RXRPC_IPV6
87-
/*
88-
* Find the peer associated with an ICMPv6 packet.
89-
*/
90-
static struct rxrpc_peer *rxrpc_lookup_peer_icmp6_rcu(struct rxrpc_local *local,
91-
struct sk_buff *skb,
92-
__be16 udp_port,
93-
struct sockaddr_rxrpc *srx)
94-
{
95-
struct icmp6hdr *icmp = icmp6_hdr(skb);
96-
struct ipv6hdr *ip, *ip0 = ipv6_hdr(skb);
97-
98-
_enter("%u,%u,%u", ip0->nexthdr, icmp->icmp6_type, icmp->icmp6_code);
99-
100-
switch (icmp->icmp6_type) {
101-
case ICMPV6_DEST_UNREACH:
102-
case ICMPV6_PKT_TOOBIG:
103-
case ICMPV6_TIME_EXCEED:
104-
case ICMPV6_PARAMPROB:
105-
ip = (struct ipv6hdr *)((void *)icmp + 8);
106-
break;
107-
default:
108-
return NULL;
109-
}
110-
111-
memset(srx, 0, sizeof(*srx));
112-
srx->transport_type = local->srx.transport_type;
113-
srx->transport_len = local->srx.transport_len;
114-
srx->transport.family = local->srx.transport.family;
115-
116-
/* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice
117-
* versa?
118-
*/
119-
switch (srx->transport.family) {
120-
case AF_INET:
121-
_net("Rx ICMP6 on v4 sock");
122-
srx->transport_len = sizeof(srx->transport.sin);
123-
srx->transport.family = AF_INET;
124-
srx->transport.sin.sin_port = udp_port;
125-
memcpy(&srx->transport.sin.sin_addr,
126-
&ip->daddr.s6_addr32[3], sizeof(struct in_addr));
127-
break;
128-
case AF_INET6:
129-
_net("Rx ICMP6");
130-
srx->transport.sin.sin_port = udp_port;
131-
memcpy(&srx->transport.sin6.sin6_addr, &ip->daddr,
132-
sizeof(struct in6_addr));
133-
break;
134-
default:
135-
WARN_ON_ONCE(1);
136-
return NULL;
137-
}
138-
139-
_net("ICMP {%pISp}", &srx->transport);
140-
return rxrpc_lookup_peer_rcu(local, srx);
141-
}
142-
#endif /* CONFIG_AF_RXRPC_IPV6 */
143-
144-
/*
145-
* Handle an error received on the local endpoint as a tunnel.
146-
*/
147-
void rxrpc_encap_err_rcv(struct sock *sk, struct sk_buff *skb, int err,
148-
__be16 port, u32 info, u8 *payload)
149-
{
150-
struct sock_extended_err ee;
151-
struct sockaddr_rxrpc srx;
152-
struct rxrpc_local *local;
153-
struct rxrpc_peer *peer;
154-
u8 version = ip_hdr(skb)->version;
155-
u8 type = icmp_hdr(skb)->type;
156-
u8 code = icmp_hdr(skb)->code;
157-
158-
rcu_read_lock();
159-
local = rcu_dereference_sk_user_data(sk);
160-
if (unlikely(!local)) {
161-
rcu_read_unlock();
162-
return;
163-
}
164-
165-
rxrpc_new_skb(skb, rxrpc_skb_received);
166-
167-
switch (ip_hdr(skb)->version) {
168-
case IPVERSION:
169-
peer = rxrpc_lookup_peer_icmp_rcu(local, skb, port, &srx);
170-
break;
171-
#ifdef CONFIG_AF_RXRPC_IPV6
172-
case 6:
173-
peer = rxrpc_lookup_peer_icmp6_rcu(local, skb, port, &srx);
174-
break;
175-
#endif
176-
default:
177-
rcu_read_unlock();
178-
return;
179-
}
180-
181-
if (peer && !rxrpc_get_peer_maybe(peer))
182-
peer = NULL;
183-
if (!peer) {
184-
rcu_read_unlock();
185-
return;
186-
}
187-
188-
memset(&ee, 0, sizeof(ee));
189-
190-
switch (version) {
191-
case IPVERSION:
192-
if (type == ICMP_DEST_UNREACH &&
193-
code == ICMP_FRAG_NEEDED) {
194-
rxrpc_adjust_mtu(peer, info);
195-
rcu_read_unlock();
196-
rxrpc_put_peer(peer);
197-
return;
198-
}
199-
200-
ee.ee_origin = SO_EE_ORIGIN_ICMP;
201-
ee.ee_type = type;
202-
ee.ee_code = code;
203-
ee.ee_errno = err;
204-
break;
205-
206-
#ifdef CONFIG_AF_RXRPC_IPV6
207-
case 6:
208-
if (type == ICMPV6_PKT_TOOBIG) {
209-
rxrpc_adjust_mtu(peer, info);
210-
rcu_read_unlock();
211-
rxrpc_put_peer(peer);
212-
return;
213-
}
214-
215-
if (err == EACCES)
216-
err = EHOSTUNREACH;
217-
218-
ee.ee_origin = SO_EE_ORIGIN_ICMP6;
219-
ee.ee_type = type;
220-
ee.ee_code = code;
221-
ee.ee_errno = err;
222-
break;
223-
#endif
224-
}
225-
226-
trace_rxrpc_rx_icmp(peer, &ee, &srx);
227-
228-
rxrpc_distribute_error(peer, err, RXRPC_CALL_NETWORK_ERROR);
229-
rcu_read_unlock();
230-
rxrpc_put_peer(peer);
231-
}
232-
23325
/*
23426
* Find the peer associated with a local error.
23527
*/
@@ -246,6 +38,9 @@ static struct rxrpc_peer *rxrpc_lookup_peer_local_rcu(struct rxrpc_local *local,
24638
srx->transport_len = local->srx.transport_len;
24739
srx->transport.family = local->srx.transport.family;
24840

41+
/* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice
42+
* versa?
43+
*/
24944
switch (srx->transport.family) {
25045
case AF_INET:
25146
srx->transport_len = sizeof(srx->transport.sin);
@@ -375,20 +170,38 @@ void rxrpc_error_report(struct sock *sk)
375170
}
376171
rxrpc_new_skb(skb, rxrpc_skb_received);
377172
serr = SKB_EXT_ERR(skb);
173+
if (!skb->len && serr->ee.ee_origin == SO_EE_ORIGIN_TIMESTAMPING) {
174+
_leave("UDP empty message");
175+
rcu_read_unlock();
176+
rxrpc_free_skb(skb, rxrpc_skb_freed);
177+
return;
178+
}
378179

379-
if (serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL) {
380-
peer = rxrpc_lookup_peer_local_rcu(local, skb, &srx);
381-
if (peer && !rxrpc_get_peer_maybe(peer))
382-
peer = NULL;
383-
if (peer) {
384-
trace_rxrpc_rx_icmp(peer, &serr->ee, &srx);
385-
rxrpc_store_error(peer, serr);
386-
}
180+
peer = rxrpc_lookup_peer_local_rcu(local, skb, &srx);
181+
if (peer && !rxrpc_get_peer_maybe(peer))
182+
peer = NULL;
183+
if (!peer) {
184+
rcu_read_unlock();
185+
rxrpc_free_skb(skb, rxrpc_skb_freed);
186+
_leave(" [no peer]");
187+
return;
387188
}
388189

190+
trace_rxrpc_rx_icmp(peer, &serr->ee, &srx);
191+
192+
if ((serr->ee.ee_origin == SO_EE_ORIGIN_ICMP &&
193+
serr->ee.ee_type == ICMP_DEST_UNREACH &&
194+
serr->ee.ee_code == ICMP_FRAG_NEEDED)) {
195+
rxrpc_adjust_mtu(peer, serr->ee.ee_info);
196+
goto out;
197+
}
198+
199+
rxrpc_store_error(peer, serr);
200+
out:
389201
rcu_read_unlock();
390202
rxrpc_free_skb(skb, rxrpc_skb_freed);
391203
rxrpc_put_peer(peer);
204+
392205
_leave("");
393206
}
394207

0 commit comments

Comments
 (0)