aboutsummaryrefslogtreecommitdiffstats
path: root/net/rxrpc/ar-error.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2007-04-26 18:48:28 -0400
committerDavid S. Miller <davem@davemloft.net>2007-04-26 18:48:28 -0400
commit17926a79320afa9b95df6b977b40cca6d8713cea (patch)
tree5cedff43b69520ad17b86783d3752053686ec99c /net/rxrpc/ar-error.c
parente19dff1fdd99a25819af74cf0710e147fff4fd3a (diff)
[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>
Diffstat (limited to 'net/rxrpc/ar-error.c')
-rw-r--r--net/rxrpc/ar-error.c253
1 files changed, 253 insertions, 0 deletions
diff --git a/net/rxrpc/ar-error.c b/net/rxrpc/ar-error.c
new file mode 100644
index 000000000000..f5539e2f7b58
--- /dev/null
+++ b/net/rxrpc/ar-error.c
@@ -0,0 +1,253 @@
1/* Error message handling (ICMP)
2 *
3 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
4 * 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 */
11
12#include <linux/module.h>
13#include <linux/net.h>
14#include <linux/skbuff.h>
15#include <linux/errqueue.h>
16#include <linux/udp.h>
17#include <linux/in.h>
18#include <linux/in6.h>
19#include <linux/icmp.h>
20#include <net/sock.h>
21#include <net/af_rxrpc.h>
22#include <net/ip.h>
23#include "ar-internal.h"
24
25/*
26 * handle an error received on the local endpoint
27 */
28void rxrpc_UDP_error_report(struct sock *sk)
29{
30 struct sock_exterr_skb *serr;
31 struct rxrpc_transport *trans;
32 struct rxrpc_local *local = sk->sk_user_data;
33 struct rxrpc_peer *peer;
34 struct sk_buff *skb;
35 __be32 addr;
36 __be16 port;
37
38 _enter("%p{%d}", sk, local->debug_id);
39
40 skb = skb_dequeue(&sk->sk_error_queue);
41 if (!skb) {
42 _leave("UDP socket errqueue empty");
43 return;
44 }
45
46 rxrpc_new_skb(skb);
47
48 serr = SKB_EXT_ERR(skb);
49 addr = *(__be32 *)(skb_network_header(skb) + serr->addr_offset);
50 port = serr->port;
51
52 _net("Rx UDP Error from "NIPQUAD_FMT":%hu",
53 NIPQUAD(addr), ntohs(port));
54 _debug("Msg l:%d d:%d", skb->len, skb->data_len);
55
56 peer = rxrpc_find_peer(local, addr, port);
57 if (IS_ERR(peer)) {
58 rxrpc_free_skb(skb);
59 _leave(" [no peer]");
60 return;
61 }
62
63 trans = rxrpc_find_transport(local, peer);
64 if (!trans) {
65 rxrpc_put_peer(peer);
66 rxrpc_free_skb(skb);
67 _leave(" [no trans]");
68 return;
69 }
70
71 if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP &&
72 serr->ee.ee_type == ICMP_DEST_UNREACH &&
73 serr->ee.ee_code == ICMP_FRAG_NEEDED
74 ) {
75 u32 mtu = serr->ee.ee_info;
76
77 _net("Rx Received ICMP Fragmentation Needed (%d)", mtu);
78
79 /* wind down the local interface MTU */
80 if (mtu > 0 && peer->if_mtu == 65535 && mtu < peer->if_mtu) {
81 peer->if_mtu = mtu;
82 _net("I/F MTU %u", mtu);
83 }
84
85 /* ip_rt_frag_needed() may have eaten the info */
86 if (mtu == 0)
87 mtu = ntohs(icmp_hdr(skb)->un.frag.mtu);
88
89 if (mtu == 0) {
90 /* they didn't give us a size, estimate one */
91 if (mtu > 1500) {
92 mtu >>= 1;
93 if (mtu < 1500)
94 mtu = 1500;
95 } else {
96 mtu -= 100;
97 if (mtu < peer->hdrsize)
98 mtu = peer->hdrsize + 4;
99 }
100 }
101
102 if (mtu < peer->mtu) {
103 peer->mtu = mtu;
104 peer->maxdata = peer->mtu - peer->hdrsize;
105 _net("Net MTU %u (maxdata %u)",
106 peer->mtu, peer->maxdata);
107 }
108 }
109
110 rxrpc_put_peer(peer);
111
112 /* pass the transport ref to error_handler to release */
113 skb_queue_tail(&trans->error_queue, skb);
114 schedule_work(&trans->error_handler);
115
116 /* reset and regenerate socket error */
117 spin_lock_bh(&sk->sk_error_queue.lock);
118 sk->sk_err = 0;
119 skb = skb_peek(&sk->sk_error_queue);
120 if (skb) {
121 sk->sk_err = SKB_EXT_ERR(skb)->ee.ee_errno;
122 spin_unlock_bh(&sk->sk_error_queue.lock);
123 sk->sk_error_report(sk);
124 } else {
125 spin_unlock_bh(&sk->sk_error_queue.lock);
126 }
127
128 _leave("");
129}
130
131/*
132 * deal with UDP error messages
133 */
134void rxrpc_UDP_error_handler(struct work_struct *work)
135{
136 struct sock_extended_err *ee;
137 struct sock_exterr_skb *serr;
138 struct rxrpc_transport *trans =
139 container_of(work, struct rxrpc_transport, error_handler);
140 struct sk_buff *skb;
141 int local, err;
142
143 _enter("");
144
145 skb = skb_dequeue(&trans->error_queue);
146 if (!skb)
147 return;
148
149 serr = SKB_EXT_ERR(skb);
150 ee = &serr->ee;
151
152 _net("Rx Error o=%d t=%d c=%d e=%d",
153 ee->ee_origin, ee->ee_type, ee->ee_code, ee->ee_errno);
154
155 err = ee->ee_errno;
156
157 switch (ee->ee_origin) {
158 case SO_EE_ORIGIN_ICMP:
159 local = 0;
160 switch (ee->ee_type) {
161 case ICMP_DEST_UNREACH:
162 switch (ee->ee_code) {
163 case ICMP_NET_UNREACH:
164 _net("Rx Received ICMP Network Unreachable");
165 err = ENETUNREACH;
166 break;
167 case ICMP_HOST_UNREACH:
168 _net("Rx Received ICMP Host Unreachable");
169 err = EHOSTUNREACH;
170 break;
171 case ICMP_PORT_UNREACH:
172 _net("Rx Received ICMP Port Unreachable");
173 err = ECONNREFUSED;
174 break;
175 case ICMP_FRAG_NEEDED:
176 _net("Rx Received ICMP Fragmentation Needed (%d)",
177 ee->ee_info);
178 err = 0; /* dealt with elsewhere */
179 break;
180 case ICMP_NET_UNKNOWN:
181 _net("Rx Received ICMP Unknown Network");
182 err = ENETUNREACH;
183 break;
184 case ICMP_HOST_UNKNOWN:
185 _net("Rx Received ICMP Unknown Host");
186 err = EHOSTUNREACH;
187 break;
188 default:
189 _net("Rx Received ICMP DestUnreach code=%u",
190 ee->ee_code);
191 break;
192 }
193 break;
194
195 case ICMP_TIME_EXCEEDED:
196 _net("Rx Received ICMP TTL Exceeded");
197 break;
198
199 default:
200 _proto("Rx Received ICMP error { type=%u code=%u }",
201 ee->ee_type, ee->ee_code);
202 break;
203 }
204 break;
205
206 case SO_EE_ORIGIN_LOCAL:
207 _proto("Rx Received local error { error=%d }",
208 ee->ee_errno);
209 local = 1;
210 break;
211
212 case SO_EE_ORIGIN_NONE:
213 case SO_EE_ORIGIN_ICMP6:
214 default:
215 _proto("Rx Received error report { orig=%u }",
216 ee->ee_origin);
217 local = 0;
218 break;
219 }
220
221 /* terminate all the affected calls if there's an unrecoverable
222 * error */
223 if (err) {
224 struct rxrpc_call *call, *_n;
225
226 _debug("ISSUE ERROR %d", err);
227
228 spin_lock_bh(&trans->peer->lock);
229 trans->peer->net_error = err;
230
231 list_for_each_entry_safe(call, _n, &trans->peer->error_targets,
232 error_link) {
233 write_lock(&call->state_lock);
234 if (call->state != RXRPC_CALL_COMPLETE &&
235 call->state < RXRPC_CALL_NETWORK_ERROR) {
236 call->state = RXRPC_CALL_NETWORK_ERROR;
237 set_bit(RXRPC_CALL_RCVD_ERROR, &call->events);
238 schedule_work(&call->processor);
239 }
240 write_unlock(&call->state_lock);
241 list_del_init(&call->error_link);
242 }
243
244 spin_unlock_bh(&trans->peer->lock);
245 }
246
247 if (!skb_queue_empty(&trans->error_queue))
248 schedule_work(&trans->error_handler);
249
250 rxrpc_free_skb(skb);
251 rxrpc_put_transport(trans);
252 _leave("");
253}