neigh_snd: race condition fix, shorter connect_neigh cmd, 32 bit ports
[cor.git] / net / cor / conn_trgt_unconn.c
blobe2eaf64b50b50d33dd86a7ffccb783482b7dead2
1 /**
2 * Connection oriented routing
3 * Copyright (C) 2007-2021 Michael Blizek
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA.
21 #include "cor.h"
23 int cor_encode_len(char *buf, int buflen, __u32 len)
25 BUG_ON(buf == 0);
26 BUG_ON(buflen < 4);
28 if (len < 128) {
29 buf[0] = (__u8) len;
30 return 1;
31 } else if (len < 16512) {
32 __u16 len_be = cpu_to_be16(len - 128);
33 char *len_p = (char *) &len_be;
35 buf[0] = len_p[0] + 128;
36 buf[1] = len_p[1];
37 return 2;
38 } else if (len < 1073758336) {
39 __u32 len_be = cpu_to_be32(len - 16512);
40 char *len_p = (char *) &len_be;
42 buf[0] = len_p[0] + 192;
43 buf[1] = len_p[1];
44 buf[2] = len_p[2];
45 buf[3] = len_p[3];
46 return 4;
47 } else {
48 return -1;
52 int cor_decode_len(char *buf, int buflen, __u32 *len)
54 __u8 b0 = (__u8) buf[0];
56 *len = 0;
58 if (buflen >= 1 && b0 < 128) {
59 *len = (__u8) buf[0];
60 return 1;
61 } else if (buflen >= 2 && b0 >= 128 && b0 < 192) {
62 ((char *) len)[0] = buf[0] - 128;
63 ((char *) len)[1] = buf[1];
65 *len = be16_to_cpu(*len) + 128;
66 return 2;
67 } else if (buflen >= 4 && b0 >= 192) {
68 ((char *) len)[0] = buf[0] - 192;
69 ((char *) len)[1] = buf[1];
70 ((char *) len)[2] = buf[2];
71 ((char *) len)[3] = buf[3];
73 *len = be32_to_cpu(*len) + 16512;
74 return 4;
75 } else {
76 return 0;
80 static void cor_receive_cpacketresp(struct cor_conn *src_unconn_ll, char *buf,
81 __u32 len, int *reset_needed)
83 __u32 rc;
84 if (unlikely((*reset_needed) != 0))
85 return;
86 rc = cor_receive_buf(src_unconn_ll, buf, len, 0, 0);
87 if (unlikely(rc < len))
88 (*reset_needed) = 1;
92 static void cor_send_resp_ok(struct cor_conn *trgt_unconn_ll, int *reset_needed)
94 __u8 respcode = CDR_EXECOK;
96 cor_receive_cpacketresp(trgt_unconn_ll->reversedir, (char *) &respcode,
97 1, reset_needed);
100 static void cor_send_resp_failed(struct cor_conn *trgt_unconn_ll,
101 __u16 reasoncode, int *reset_needed)
103 char msg[3];
105 msg[0] = CDR_EXECFAILED;
107 cor_put_u16(&(msg[1]), reasoncode);
109 cor_receive_cpacketresp(trgt_unconn_ll->reversedir, msg, 3,
110 reset_needed);
113 static void cor_send_resp_bin(struct cor_conn *trgt_unconn_ll, char *buf,
114 __u32 len, int *reset_needed)
116 char hdr[5];
117 int len_len;
119 hdr[0] = CDR_BINDATA;
120 len_len = cor_encode_len(hdr + 1, 4, len);
122 BUG_ON(len_len <= 0);
124 cor_receive_cpacketresp(trgt_unconn_ll->reversedir, hdr, len_len + 1,
125 reset_needed);
126 cor_receive_cpacketresp(trgt_unconn_ll->reversedir, buf, len,
127 reset_needed);
130 static int cor_proc_bindata(struct cor_conn *trgt_unconn_ll, __u16 cmd,
131 int *reset_needed)
133 __u32 len = 0;
134 char *buf;
136 buf = kmalloc(CD_RESP_BIN_MAXSIZE, GFP_ATOMIC);
137 if (unlikely(buf == 0)) {
138 cor_send_resp_failed(trgt_unconn_ll,
139 CDR_EXECFAILED_TEMPORARILY_OUT_OF_RESSOURCES,
140 reset_needed);
141 return 1;
144 cor_send_resp_ok(trgt_unconn_ll, reset_needed);
146 if (cmd == CD_LIST_NEIGH) {
147 len = cor_generate_neigh_list(buf, CD_RESP_BIN_MAXSIZE);
148 } else if (cmd == CD_LIST_SERVICES) {
149 len = cor_list_services(buf, CD_RESP_BIN_MAXSIZE);
150 } else {
151 BUG();
153 cor_send_resp_bin(trgt_unconn_ll, buf, len, reset_needed);
154 return 0;
157 static int cor_proc_connect_port(struct cor_conn *trgt_unconn_ll,
158 int *reset_needed)
160 __be32 port;
161 int rc;
163 if (unlikely(trgt_unconn_ll->target.unconnected.paramlen < 4)) {
164 /* command too short */
165 cor_send_resp_failed(trgt_unconn_ll,
166 CDR_EXECFAILED_INVALID_COMMAND, reset_needed);
167 return 1;
170 memcpy((char *) &port, trgt_unconn_ll->target.unconnected.cmdparams, 4);
172 rc = cor_connect_port(trgt_unconn_ll, port);
174 if (rc == 0) {
175 cor_send_resp_ok(trgt_unconn_ll, reset_needed);
176 return 0;
177 } else if (rc == 2) {
178 cor_send_resp_failed(trgt_unconn_ll,
179 CDR_EXECFAILED_PORTCLOSED, reset_needed);
180 } else if (rc == 3) {
181 cor_send_resp_failed(trgt_unconn_ll,
182 CDR_EXECFAILED_TEMPORARILY_OUT_OF_RESSOURCES,
183 reset_needed);
184 } else {
185 BUG();
187 return 1;
190 static int cor_proc_connect_nb(struct cor_conn *trgt_unconn_ll,
191 int *reset_needed)
193 char *addr = trgt_unconn_ll->target.unconnected.cmdparams;
194 __u32 addrlen = trgt_unconn_ll->target.unconnected.paramlen;
196 int rc;
198 if (unlikely(addrlen > 65535)) {
199 cor_send_resp_failed(trgt_unconn_ll,
200 CDR_EXECFAILED_NB_DOESNTEXIST, reset_needed);
201 return 1;
204 rc = cor_connect_neigh(trgt_unconn_ll, addr, (__u16) addrlen);
206 if (rc == 0) {
207 cor_send_resp_ok(trgt_unconn_ll, reset_needed);
208 return 0;
209 } else if (rc == 3) {
210 cor_send_resp_failed(trgt_unconn_ll,
211 CDR_EXECFAILED_NB_DOESNTEXIST, reset_needed);
212 } else if (rc == 4) {
213 cor_send_resp_failed(trgt_unconn_ll,
214 CDR_EXECFAILED_TEMPORARILY_OUT_OF_RESSOURCES,
215 reset_needed);
216 } else {
217 BUG();
219 return 1;
222 static void cor_proc_cmd(struct cor_conn *trgt_unconn_ll, int *reset_needed)
224 __u16 cmd = trgt_unconn_ll->target.unconnected.cmd;
225 char *params = trgt_unconn_ll->target.unconnected.cmdparams;
226 int discard_on_error = (cmd & CD_CONTINUE_ON_ERROR_FLAG) == 0;
227 int rc;
229 cmd = cmd & (~CD_CONTINUE_ON_ERROR_FLAG);
231 BUG_ON(trgt_unconn_ll->target.unconnected.cmdparams == 0);
233 if (cmd == CD_CONNECT_NB) {
234 rc = cor_proc_connect_nb(trgt_unconn_ll, reset_needed);
235 } else if (cmd == CD_CONNECT_PORT) {
236 rc = cor_proc_connect_port(trgt_unconn_ll, reset_needed);
237 } else if (cmd == CD_LIST_NEIGH || cmd == CD_LIST_SERVICES) {
238 rc = cor_proc_bindata(trgt_unconn_ll, cmd, reset_needed);
239 } else {
240 cor_send_resp_failed(trgt_unconn_ll,
241 CDR_EXECFAILED_INVALID_COMMAND, reset_needed);
242 rc = 1;
245 kfree(params);
247 if (trgt_unconn_ll->targettype == TARGET_UNCONNECTED) {
248 trgt_unconn_ll->target.unconnected.cmdparams = 0;
249 trgt_unconn_ll->target.unconnected.cmdread = 0;
250 trgt_unconn_ll->target.unconnected.paramlen_read = 0;
251 trgt_unconn_ll->target.unconnected.cmd = 0;
252 trgt_unconn_ll->target.unconnected.paramlen = 0;
254 if (rc != 0 && discard_on_error) {
255 trgt_unconn_ll->targettype = TARGET_DISCARD;
260 static void cor_read_cmd(struct cor_conn *trgt_unconn_ll)
262 int pull;
264 pull = min(trgt_unconn_ll->target.unconnected.paramlen -
265 trgt_unconn_ll->target.unconnected.cmdread,
266 trgt_unconn_ll->data_buf.read_remaining);
268 BUG_ON(pull < 0);
269 BUG_ON(trgt_unconn_ll->target.unconnected.cmdparams == 0);
271 if (pull == 0)
272 return;
274 cor_databuf_pull(trgt_unconn_ll,
275 trgt_unconn_ll->target.unconnected.cmdparams +
276 trgt_unconn_ll->target.unconnected.cmdread, pull);
277 cor_databuf_ackread(trgt_unconn_ll);
279 trgt_unconn_ll->target.unconnected.cmdread += pull;
282 static void cor_read_discard(struct cor_conn *trgt_unconn_ll)
284 BUG_ON(trgt_unconn_ll->target.unconnected.paramlen_read == 0);
286 while (trgt_unconn_ll->target.unconnected.paramlen <
287 trgt_unconn_ll->target.unconnected.cmdread &&
288 trgt_unconn_ll->data_buf.read_remaining > 0) {
289 char buf[1];
290 cor_databuf_pull(trgt_unconn_ll, buf, 1);
291 cor_databuf_ackread(trgt_unconn_ll);
293 trgt_unconn_ll->target.unconnected.cmdread++;
297 #define RC_READ_HDR_FINISHED 0
298 #define RC_READ_HDR_INCOMPLETE 1
299 static int cor_read_hdr(struct cor_conn *trgt_unconn_ll)
301 BUG_ON(trgt_unconn_ll->target.unconnected.cmdparams != 0);
303 while (1) {
304 int rc;
305 if (trgt_unconn_ll->target.unconnected.paramlen_read != 0)
306 return RC_READ_HDR_FINISHED;
307 else if (trgt_unconn_ll->data_buf.read_remaining == 0)
308 return RC_READ_HDR_INCOMPLETE;
310 BUG_ON(trgt_unconn_ll->target.unconnected.cmdread >= 6);
312 if (trgt_unconn_ll->target.unconnected.cmdread < 2) {
313 char buf[1];
314 cor_databuf_pull(trgt_unconn_ll, buf, 1);
315 trgt_unconn_ll->target.unconnected.cmd <<= 8;
316 trgt_unconn_ll->target.unconnected.cmd += buf[0];
317 trgt_unconn_ll->target.unconnected.cmdread += 1;
318 continue;
321 cor_databuf_pull(trgt_unconn_ll,
322 trgt_unconn_ll->target.unconnected.paramlen_buf +
323 trgt_unconn_ll->target.unconnected.cmdread - 2,
325 cor_databuf_ackread(trgt_unconn_ll);
326 trgt_unconn_ll->target.unconnected.cmdread += 1;
328 rc = cor_decode_len(
329 trgt_unconn_ll->target.unconnected.paramlen_buf,
330 trgt_unconn_ll->target.unconnected.cmdread - 2,
331 &(trgt_unconn_ll->target.unconnected.paramlen));
333 if (rc > 0) {
334 trgt_unconn_ll->target.unconnected.paramlen_read = 1;
335 trgt_unconn_ll->target.unconnected.cmdread = 0;
336 return RC_READ_HDR_FINISHED;
341 #define RC_PC_FINISHED 0
342 #define RC_PC_OUT_OF_BUFSPACE 1
343 #define RC_PC_INCOMPLETEREAD 2
344 static int _cor_proc_cpacket(struct cor_conn *trgt_unconn_ll, int *reset_needed)
346 int rc;
348 if (cor_cpacket_write_allowed(trgt_unconn_ll->reversedir) == 0)
349 return RC_PC_OUT_OF_BUFSPACE;
351 if (trgt_unconn_ll->target.unconnected.paramlen_read != 0)
352 goto hdrdone;
354 rc = cor_read_hdr(trgt_unconn_ll);
355 if (rc == RC_READ_HDR_INCOMPLETE) {
356 return RC_PC_INCOMPLETEREAD;
357 } else if (rc == RC_READ_HDR_FINISHED) {
358 trgt_unconn_ll->target.unconnected.paramlen_read = 1;
360 if (trgt_unconn_ll->target.unconnected.paramlen >
361 MAX_CONN_CMD_LEN) {
362 /* command too long */
363 cor_send_resp_failed(trgt_unconn_ll,
364 CDR_EXECFAILED_INVALID_COMMAND,
365 reset_needed);
367 } else {
368 BUG();
371 hdrdone:
372 if (trgt_unconn_ll->target.unconnected.paramlen >
373 MAX_CONN_CMD_LEN) {
374 cor_read_discard(trgt_unconn_ll);
375 return RC_PC_INCOMPLETEREAD;
378 if (trgt_unconn_ll->target.unconnected.cmdparams == 0) {
379 trgt_unconn_ll->target.unconnected.cmdparams = kmalloc(
380 trgt_unconn_ll->target.unconnected.paramlen,
381 GFP_ATOMIC);
382 if (trgt_unconn_ll->target.unconnected.cmdparams == 0) {
383 *reset_needed = 1;
384 return RC_PC_INCOMPLETEREAD;
388 cor_read_cmd(trgt_unconn_ll);
390 BUG_ON(trgt_unconn_ll->target.unconnected.cmdread >
391 trgt_unconn_ll->target.unconnected.paramlen);
393 if (trgt_unconn_ll->target.unconnected.cmdread <
394 trgt_unconn_ll->target.unconnected.paramlen)
395 return RC_PC_INCOMPLETEREAD;
397 cor_proc_cmd(trgt_unconn_ll, reset_needed);
399 return RC_PC_FINISHED;
402 void cor_proc_cpacket(struct cor_conn *trgt_unconn_l)
404 int reset_needed = 0;
406 if (unlikely(trgt_unconn_l->isreset != 0))
407 return;
409 BUG_ON(trgt_unconn_l->targettype != TARGET_UNCONNECTED);
411 BUG_ON(trgt_unconn_l->is_client == 0);
412 spin_lock_bh(&(trgt_unconn_l->reversedir->rcv_lock));
414 while (1) {
415 int rc = _cor_proc_cpacket(trgt_unconn_l, &reset_needed);
417 BUG_ON(rc != RC_PC_FINISHED &&
418 rc != RC_PC_OUT_OF_BUFSPACE &&
419 rc != RC_PC_INCOMPLETEREAD);
421 if (trgt_unconn_l->targettype == TARGET_UNCONNECTED) {
422 if (trgt_unconn_l->flush != 0 &&
423 trgt_unconn_l->data_buf.read_remaining == 0)
424 trgt_unconn_l->reversedir->flush = 1;
425 else
426 trgt_unconn_l->reversedir->flush = 0;
428 cor_flush_buf(trgt_unconn_l->reversedir);
430 if (rc == RC_PC_OUT_OF_BUFSPACE ||
431 rc == RC_PC_INCOMPLETEREAD)
432 break;
433 } else {
434 BUG_ON(rc != RC_PC_FINISHED);
436 if (trgt_unconn_l->targettype == TARGET_SOCK ||
437 trgt_unconn_l->is_highlatency == 0 || (
438 trgt_unconn_l->flush != 0 &&
439 trgt_unconn_l->data_buf.read_remaining == 0))
440 trgt_unconn_l->reversedir->flush = 1;
441 else
442 trgt_unconn_l->reversedir->flush = 0;
444 cor_flush_buf(trgt_unconn_l->reversedir);
445 cor_flush_buf(trgt_unconn_l);
446 break;
449 if (reset_needed != 0)
450 cor_reset_conn_locked(trgt_unconn_l);
453 spin_unlock_bh(&(trgt_unconn_l->reversedir->rcv_lock));
456 MODULE_LICENSE("GPL");