.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* Peer event handling, typically ICMP messages. |
---|
2 | 3 | * |
---|
3 | 4 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. |
---|
4 | 5 | * Written by David Howells (dhowells@redhat.com) |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or |
---|
7 | | - * modify it under the terms of the GNU General Public License |
---|
8 | | - * as published by the Free Software Foundation; either version |
---|
9 | | - * 2 of the License, or (at your option) any later version. |
---|
10 | 6 | */ |
---|
11 | 7 | |
---|
12 | 8 | #include <linux/module.h> |
---|
.. | .. |
---|
47 | 43 | */ |
---|
48 | 44 | switch (srx->transport.family) { |
---|
49 | 45 | case AF_INET: |
---|
| 46 | + srx->transport_len = sizeof(srx->transport.sin); |
---|
| 47 | + srx->transport.family = AF_INET; |
---|
50 | 48 | srx->transport.sin.sin_port = serr->port; |
---|
51 | 49 | switch (serr->ee.ee_origin) { |
---|
52 | 50 | case SO_EE_ORIGIN_ICMP: |
---|
.. | .. |
---|
70 | 68 | |
---|
71 | 69 | #ifdef CONFIG_AF_RXRPC_IPV6 |
---|
72 | 70 | case AF_INET6: |
---|
73 | | - srx->transport.sin6.sin6_port = serr->port; |
---|
74 | 71 | switch (serr->ee.ee_origin) { |
---|
75 | 72 | case SO_EE_ORIGIN_ICMP6: |
---|
76 | 73 | _net("Rx ICMP6"); |
---|
| 74 | + srx->transport.sin6.sin6_port = serr->port; |
---|
77 | 75 | memcpy(&srx->transport.sin6.sin6_addr, |
---|
78 | 76 | skb_network_header(skb) + serr->addr_offset, |
---|
79 | 77 | sizeof(struct in6_addr)); |
---|
80 | 78 | break; |
---|
81 | 79 | case SO_EE_ORIGIN_ICMP: |
---|
82 | 80 | _net("Rx ICMP on v6 sock"); |
---|
83 | | - srx->transport.sin6.sin6_addr.s6_addr32[0] = 0; |
---|
84 | | - srx->transport.sin6.sin6_addr.s6_addr32[1] = 0; |
---|
85 | | - srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff); |
---|
86 | | - memcpy(srx->transport.sin6.sin6_addr.s6_addr + 12, |
---|
| 81 | + srx->transport_len = sizeof(srx->transport.sin); |
---|
| 82 | + srx->transport.family = AF_INET; |
---|
| 83 | + srx->transport.sin.sin_port = serr->port; |
---|
| 84 | + memcpy(&srx->transport.sin.sin_addr, |
---|
87 | 85 | skb_network_header(skb) + serr->addr_offset, |
---|
88 | 86 | sizeof(struct in_addr)); |
---|
89 | 87 | break; |
---|
.. | .. |
---|
149 | 147 | { |
---|
150 | 148 | struct sock_exterr_skb *serr; |
---|
151 | 149 | struct sockaddr_rxrpc srx; |
---|
152 | | - struct rxrpc_local *local = sk->sk_user_data; |
---|
| 150 | + struct rxrpc_local *local; |
---|
153 | 151 | struct rxrpc_peer *peer; |
---|
154 | 152 | struct sk_buff *skb; |
---|
155 | 153 | |
---|
156 | | - if (unlikely(!local)) |
---|
| 154 | + rcu_read_lock(); |
---|
| 155 | + local = rcu_dereference_sk_user_data(sk); |
---|
| 156 | + if (unlikely(!local)) { |
---|
| 157 | + rcu_read_unlock(); |
---|
157 | 158 | return; |
---|
158 | | - |
---|
| 159 | + } |
---|
159 | 160 | _enter("%p{%d}", sk, local->debug_id); |
---|
| 161 | + |
---|
| 162 | + /* Clear the outstanding error value on the socket so that it doesn't |
---|
| 163 | + * cause kernel_sendmsg() to return it later. |
---|
| 164 | + */ |
---|
| 165 | + sock_error(sk); |
---|
160 | 166 | |
---|
161 | 167 | skb = sock_dequeue_err_skb(sk); |
---|
162 | 168 | if (!skb) { |
---|
| 169 | + rcu_read_unlock(); |
---|
163 | 170 | _leave("UDP socket errqueue empty"); |
---|
164 | 171 | return; |
---|
165 | 172 | } |
---|
166 | | - rxrpc_new_skb(skb, rxrpc_skb_rx_received); |
---|
| 173 | + rxrpc_new_skb(skb, rxrpc_skb_received); |
---|
167 | 174 | serr = SKB_EXT_ERR(skb); |
---|
168 | 175 | if (!skb->len && serr->ee.ee_origin == SO_EE_ORIGIN_TIMESTAMPING) { |
---|
169 | 176 | _leave("UDP empty message"); |
---|
170 | | - rxrpc_free_skb(skb, rxrpc_skb_rx_freed); |
---|
| 177 | + rcu_read_unlock(); |
---|
| 178 | + rxrpc_free_skb(skb, rxrpc_skb_freed); |
---|
171 | 179 | return; |
---|
172 | 180 | } |
---|
173 | 181 | |
---|
174 | | - rcu_read_lock(); |
---|
175 | 182 | peer = rxrpc_lookup_peer_icmp_rcu(local, skb, &srx); |
---|
176 | 183 | if (peer && !rxrpc_get_peer_maybe(peer)) |
---|
177 | 184 | peer = NULL; |
---|
178 | 185 | if (!peer) { |
---|
179 | 186 | rcu_read_unlock(); |
---|
180 | | - rxrpc_free_skb(skb, rxrpc_skb_rx_freed); |
---|
| 187 | + rxrpc_free_skb(skb, rxrpc_skb_freed); |
---|
181 | 188 | _leave(" [no peer]"); |
---|
182 | 189 | return; |
---|
183 | 190 | } |
---|
.. | .. |
---|
189 | 196 | serr->ee.ee_code == ICMP_FRAG_NEEDED)) { |
---|
190 | 197 | rxrpc_adjust_mtu(peer, serr); |
---|
191 | 198 | rcu_read_unlock(); |
---|
192 | | - rxrpc_free_skb(skb, rxrpc_skb_rx_freed); |
---|
| 199 | + rxrpc_free_skb(skb, rxrpc_skb_freed); |
---|
193 | 200 | rxrpc_put_peer(peer); |
---|
194 | 201 | _leave(" [MTU update]"); |
---|
195 | 202 | return; |
---|
.. | .. |
---|
197 | 204 | |
---|
198 | 205 | rxrpc_store_error(peer, serr); |
---|
199 | 206 | rcu_read_unlock(); |
---|
200 | | - rxrpc_free_skb(skb, rxrpc_skb_rx_freed); |
---|
| 207 | + rxrpc_free_skb(skb, rxrpc_skb_freed); |
---|
201 | 208 | rxrpc_put_peer(peer); |
---|
202 | 209 | |
---|
203 | 210 | _leave(""); |
---|
.. | .. |
---|
264 | 271 | break; |
---|
265 | 272 | |
---|
266 | 273 | case SO_EE_ORIGIN_ICMP6: |
---|
| 274 | + if (err == EACCES) |
---|
| 275 | + err = EHOSTUNREACH; |
---|
| 276 | + fallthrough; |
---|
267 | 277 | default: |
---|
268 | 278 | _proto("Rx Received error report { orig=%u }", ee->ee_origin); |
---|
269 | 279 | break; |
---|
.. | .. |
---|
282 | 292 | |
---|
283 | 293 | hlist_for_each_entry_rcu(call, &peer->error_targets, error_link) { |
---|
284 | 294 | rxrpc_see_call(call); |
---|
285 | | - if (call->state < RXRPC_CALL_COMPLETE && |
---|
286 | | - rxrpc_set_call_completion(call, compl, 0, -error)) |
---|
287 | | - rxrpc_notify_socket(call); |
---|
| 295 | + rxrpc_set_call_completion(call, compl, 0, -error); |
---|
288 | 296 | } |
---|
289 | | -} |
---|
290 | | - |
---|
291 | | -/* |
---|
292 | | - * Add RTT information to cache. This is called in softirq mode and has |
---|
293 | | - * exclusive access to the peer RTT data. |
---|
294 | | - */ |
---|
295 | | -void rxrpc_peer_add_rtt(struct rxrpc_call *call, enum rxrpc_rtt_rx_trace why, |
---|
296 | | - rxrpc_serial_t send_serial, rxrpc_serial_t resp_serial, |
---|
297 | | - ktime_t send_time, ktime_t resp_time) |
---|
298 | | -{ |
---|
299 | | - struct rxrpc_peer *peer = call->peer; |
---|
300 | | - s64 rtt; |
---|
301 | | - u64 sum = peer->rtt_sum, avg; |
---|
302 | | - u8 cursor = peer->rtt_cursor, usage = peer->rtt_usage; |
---|
303 | | - |
---|
304 | | - rtt = ktime_to_ns(ktime_sub(resp_time, send_time)); |
---|
305 | | - if (rtt < 0) |
---|
306 | | - return; |
---|
307 | | - |
---|
308 | | - spin_lock(&peer->rtt_input_lock); |
---|
309 | | - |
---|
310 | | - /* Replace the oldest datum in the RTT buffer */ |
---|
311 | | - sum -= peer->rtt_cache[cursor]; |
---|
312 | | - sum += rtt; |
---|
313 | | - peer->rtt_cache[cursor] = rtt; |
---|
314 | | - peer->rtt_cursor = (cursor + 1) & (RXRPC_RTT_CACHE_SIZE - 1); |
---|
315 | | - peer->rtt_sum = sum; |
---|
316 | | - if (usage < RXRPC_RTT_CACHE_SIZE) { |
---|
317 | | - usage++; |
---|
318 | | - peer->rtt_usage = usage; |
---|
319 | | - } |
---|
320 | | - |
---|
321 | | - spin_unlock(&peer->rtt_input_lock); |
---|
322 | | - |
---|
323 | | - /* Now recalculate the average */ |
---|
324 | | - if (usage == RXRPC_RTT_CACHE_SIZE) { |
---|
325 | | - avg = sum / RXRPC_RTT_CACHE_SIZE; |
---|
326 | | - } else { |
---|
327 | | - avg = sum; |
---|
328 | | - do_div(avg, usage); |
---|
329 | | - } |
---|
330 | | - |
---|
331 | | - /* Don't need to update this under lock */ |
---|
332 | | - peer->rtt = avg; |
---|
333 | | - trace_rxrpc_rtt_rx(call, why, send_serial, resp_serial, rtt, |
---|
334 | | - usage, avg); |
---|
335 | 297 | } |
---|
336 | 298 | |
---|
337 | 299 | /* |
---|