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.
18 int cor_encode_len(char *buf
, int buflen
, __u32 len
)
26 } else if (len
< 16512) {
27 __u16 len_be
= cpu_to_be16(len
- 128);
28 char *len_p
= (char *) &len_be
;
30 buf
[0] = len_p
[0] + 128;
33 } else if (len
< 1073758336) {
34 __u32 len_be
= cpu_to_be32(len
- 16512);
35 char *len_p
= (char *) &len_be
;
37 buf
[0] = len_p
[0] + 192;
47 int cor_decode_len(char *buf
, int buflen
, __u32
*len
)
49 __u8 b0
= (__u8
) buf
[0];
53 if (buflen
>= 1 && b0
< 128) {
56 } else if (buflen
>= 2 && b0
>= 128 && b0
< 192) {
57 ((char *) len
)[0] = buf
[0] - 128;
58 ((char *) len
)[1] = buf
[1];
60 *len
= be16_to_cpu(*len
) + 128;
62 } else if (buflen
>= 4 && b0
>= 192) {
63 ((char *) len
)[0] = buf
[0] - 192;
64 ((char *) len
)[1] = buf
[1];
65 ((char *) len
)[2] = buf
[2];
66 ((char *) len
)[3] = buf
[3];
68 *len
= be32_to_cpu(*len
) + 16512;
75 static void cor_receive_cpacketresp(struct cor_conn
*src_unconn_ll
, char *buf
,
76 __u32 len
, int *reset_needed
)
80 if (unlikely((*reset_needed
) != 0))
82 rc
= cor_receive_buf(src_unconn_ll
, buf
, len
, 0, 0);
83 if (unlikely(rc
< len
))
88 static void cor_send_resp_ok(struct cor_conn
*trgt_unconn_ll
, int *reset_needed
)
90 __u8 respcode
= CDR_EXECOK
;
92 cor_receive_cpacketresp(cor_get_conn_reversedir(trgt_unconn_ll
),
93 (char *) &respcode
, 1, reset_needed
);
96 static void cor_send_resp_failed(struct cor_conn
*trgt_unconn_ll
,
97 __u16 reasoncode
, int *reset_needed
)
101 msg
[0] = CDR_EXECFAILED
;
103 cor_put_u16(&msg
[1], reasoncode
);
105 cor_receive_cpacketresp(cor_get_conn_reversedir(trgt_unconn_ll
),
106 msg
, 3, reset_needed
);
109 static void cor_send_resp_bin(struct cor_conn
*trgt_unconn_ll
, char *buf
,
110 __u32 len
, int *reset_needed
)
116 hdr
[0] = CDR_EXECOK_BINDATA_NORESP
;
119 hdr
[0] = CDR_EXECOK_BINDATA
;
120 len_len
= cor_encode_len(hdr
+ 1, 4, len
);
121 BUG_ON(len_len
<= 0);
124 cor_receive_cpacketresp(cor_get_conn_reversedir(trgt_unconn_ll
),
125 hdr
, len_len
+ 1, reset_needed
);
126 cor_receive_cpacketresp(cor_get_conn_reversedir(trgt_unconn_ll
),
127 buf
, len
, reset_needed
);
130 static __u32
cor_list_l4_protocols(char *buf
, __u32 buflen
)
132 if (unlikely(buflen
< 2)) {
137 cor_put_u16(buf
, L4PROTO_STREAM
);
141 static int cor_proc_bindata(struct cor_conn
*trgt_unconn_ll
, __u16 cmd
,
147 buf
= kmalloc(CD_RESP_BIN_MAXSIZE
, GFP_ATOMIC
);
148 if (unlikely(buf
== 0)) {
149 cor_send_resp_failed(trgt_unconn_ll
,
150 CDR_EXECFAILED_TEMPORARILY_OUT_OF_RESOURCES
,
155 if (cmd
== CD_LIST_NEIGH
) {
156 len
= cor_generate_neigh_list(buf
, CD_RESP_BIN_MAXSIZE
);
157 } else if (cmd
== CD_LIST_SERVICES
) {
158 len
= cor_list_services(buf
, CD_RESP_BIN_MAXSIZE
);
159 } else if (cmd
== CD_LIST_L4PROTOCOLS
) {
160 len
= cor_list_l4_protocols(buf
, CD_RESP_BIN_MAXSIZE
);
164 cor_send_resp_bin(trgt_unconn_ll
, buf
, len
, reset_needed
);
169 static int cor_proc_connect_port(struct cor_conn
*trgt_unconn_ll
,
176 if (unlikely(trgt_unconn_ll
->trgt
.unconnected
.paramlen
< 6)) {
177 /* command too short */
178 cor_send_resp_failed(trgt_unconn_ll
,
179 CDR_EXECFAILED_INVALID_COMMAND
, reset_needed
);
183 proto
= cor_parse_u16(trgt_unconn_ll
->trgt
.unconnected
.cmdparams
);
184 port
= cor_parse_be32(trgt_unconn_ll
->trgt
.unconnected
.cmdparams
+ 2);
186 if (proto
!= L4PROTO_STREAM
) {
187 cor_send_resp_failed(trgt_unconn_ll
,
188 CDR_EXECFAILED_UNKNOWN_L4PROTOCOL
,
193 rc
= cor_connect_port(trgt_unconn_ll
, port
);
195 if (rc
== CONNECT_PORT_OK
) {
196 cor_send_resp_ok(trgt_unconn_ll
, reset_needed
);
198 } else if (rc
== CONNECT_PORT_PORTCLOSED
) {
199 cor_send_resp_failed(trgt_unconn_ll
,
200 CDR_EXECFAILED_PORTCLOSED
, reset_needed
);
201 } else if (rc
== CONNECT_PORT_TEMPORARILY_OUT_OF_RESOURCES
) {
202 cor_send_resp_failed(trgt_unconn_ll
,
203 CDR_EXECFAILED_TEMPORARILY_OUT_OF_RESOURCES
,
211 static int cor_proc_connect_nb(struct cor_conn
*trgt_unconn_ll
,
218 if (unlikely(trgt_unconn_ll
->trgt
.unconnected
.paramlen
!= 8)) {
219 cor_send_resp_failed(trgt_unconn_ll
,
220 CDR_EXECFAILED_COMMAND_PARSE_ERROR
,
225 addr
= cor_parse_be64(trgt_unconn_ll
->trgt
.unconnected
.cmdparams
);
227 rc
= cor_connect_neigh(trgt_unconn_ll
, addr
);
230 cor_send_resp_ok(trgt_unconn_ll
, reset_needed
);
232 } else if (rc
== 3) {
233 cor_send_resp_failed(trgt_unconn_ll
,
234 CDR_EXECFAILED_NB_DOESNTEXIST
, reset_needed
);
235 } else if (rc
== 4) {
236 cor_send_resp_failed(trgt_unconn_ll
,
237 CDR_EXECFAILED_TEMPORARILY_OUT_OF_RESOURCES
,
245 static void cor_proc_cmd(struct cor_conn
*trgt_unconn_ll
, int *reset_needed
)
247 __u16 cmd
= trgt_unconn_ll
->trgt
.unconnected
.cmd
;
248 char *params
= trgt_unconn_ll
->trgt
.unconnected
.cmdparams
;
249 int discard_on_error
= (cmd
& CD_CONTINUE_ON_ERROR_FLAG
) == 0;
252 cmd
= cmd
& (~CD_CONTINUE_ON_ERROR_FLAG
);
253 cmd
= cmd
& (~CD_NOPARAM_FLAG
);
255 BUG_ON(trgt_unconn_ll
->trgt
.unconnected
.cmdparams
== 0);
257 if (cmd
== CD_CONNECT_NB
) {
258 rc
= cor_proc_connect_nb(trgt_unconn_ll
, reset_needed
);
259 } else if (cmd
== CD_CONNECT_PORT
) {
260 rc
= cor_proc_connect_port(trgt_unconn_ll
, reset_needed
);
261 } else if (cmd
== CD_LIST_NEIGH
|| cmd
== CD_LIST_SERVICES
||
262 cmd
== CD_LIST_L4PROTOCOLS
) {
263 rc
= cor_proc_bindata(trgt_unconn_ll
, cmd
, reset_needed
);
265 cor_send_resp_failed(trgt_unconn_ll
,
266 CDR_EXECFAILED_INVALID_COMMAND
, reset_needed
);
272 if (trgt_unconn_ll
->targettype
== TARGET_UNCONNECTED
) {
273 trgt_unconn_ll
->trgt
.unconnected
.cmdparams
= 0;
274 trgt_unconn_ll
->trgt
.unconnected
.cmdread
= 0;
275 trgt_unconn_ll
->trgt
.unconnected
.paramlen_read
= 0;
276 trgt_unconn_ll
->trgt
.unconnected
.cmd
= 0;
277 trgt_unconn_ll
->trgt
.unconnected
.paramlen
= 0;
279 if (rc
!= 0 && discard_on_error
) {
280 trgt_unconn_ll
->targettype
= TARGET_DISCARD
;
285 static void cor_read_cmd(struct cor_conn
*trgt_unconn_ll
)
289 pull
= min(trgt_unconn_ll
->trgt
.unconnected
.paramlen
-
290 trgt_unconn_ll
->trgt
.unconnected
.cmdread
,
291 trgt_unconn_ll
->data_buf
.read_remaining
);
294 BUG_ON(trgt_unconn_ll
->trgt
.unconnected
.cmdparams
== 0);
299 cor_databuf_pull(trgt_unconn_ll
,
300 trgt_unconn_ll
->trgt
.unconnected
.cmdparams
+
301 trgt_unconn_ll
->trgt
.unconnected
.cmdread
, pull
);
302 cor_databuf_ackread(trgt_unconn_ll
);
304 trgt_unconn_ll
->trgt
.unconnected
.cmdread
+= pull
;
307 static void cor_read_discard(struct cor_conn
*trgt_unconn_ll
)
309 BUG_ON(trgt_unconn_ll
->trgt
.unconnected
.paramlen_read
== 0);
311 while (trgt_unconn_ll
->trgt
.unconnected
.paramlen
<
312 trgt_unconn_ll
->trgt
.unconnected
.cmdread
&&
313 trgt_unconn_ll
->data_buf
.read_remaining
> 0) {
316 cor_databuf_pull(trgt_unconn_ll
, buf
, 1);
317 cor_databuf_ackread(trgt_unconn_ll
);
319 trgt_unconn_ll
->trgt
.unconnected
.cmdread
++;
323 #define RC_READ_HDR_FINISHED 0
324 #define RC_READ_HDR_INCOMPLETE 1
325 static int cor_read_hdr(struct cor_conn
*trgt_unconn_ll
)
327 BUG_ON(trgt_unconn_ll
->trgt
.unconnected
.cmdparams
!= 0);
329 if (trgt_unconn_ll
->trgt
.unconnected
.paramlen_read
!= 0)
330 return RC_READ_HDR_FINISHED
;
333 while (trgt_unconn_ll
->trgt
.unconnected
.cmdread
< 2) {
336 if (trgt_unconn_ll
->data_buf
.read_remaining
== 0)
337 return RC_READ_HDR_INCOMPLETE
;
339 cor_databuf_pull(trgt_unconn_ll
, buf
, 1);
340 trgt_unconn_ll
->trgt
.unconnected
.cmd
<<= 8;
341 trgt_unconn_ll
->trgt
.unconnected
.cmd
+= buf
[0];
342 trgt_unconn_ll
->trgt
.unconnected
.cmdread
+= 1;
345 if ((trgt_unconn_ll
->trgt
.unconnected
.cmd
& CD_NOPARAM_FLAG
) != 0) {
346 trgt_unconn_ll
->trgt
.unconnected
.paramlen
= 0;
347 trgt_unconn_ll
->trgt
.unconnected
.paramlen_read
= 1;
348 trgt_unconn_ll
->trgt
.unconnected
.cmdread
= 0;
349 return RC_READ_HDR_FINISHED
;
355 if (trgt_unconn_ll
->data_buf
.read_remaining
== 0)
356 return RC_READ_HDR_INCOMPLETE
;
358 cor_databuf_pull(trgt_unconn_ll
,
359 trgt_unconn_ll
->trgt
.unconnected
.paramlen_buf
+
360 trgt_unconn_ll
->trgt
.unconnected
.cmdread
- 2,
362 cor_databuf_ackread(trgt_unconn_ll
);
363 trgt_unconn_ll
->trgt
.unconnected
.cmdread
+= 1;
365 BUG_ON(trgt_unconn_ll
->trgt
.unconnected
.cmdread
>= 6);
368 trgt_unconn_ll
->trgt
.unconnected
.paramlen_buf
,
369 trgt_unconn_ll
->trgt
.unconnected
.cmdread
- 2,
370 &trgt_unconn_ll
->trgt
.unconnected
.paramlen
);
373 trgt_unconn_ll
->trgt
.unconnected
.paramlen_read
= 1;
374 trgt_unconn_ll
->trgt
.unconnected
.cmdread
= 0;
375 return RC_READ_HDR_FINISHED
;
380 #define RC_PC_FINISHED 0
381 #define RC_PC_OUT_OF_BUFSPACE 1
382 #define RC_PC_INCOMPLETEREAD 2
383 static int _cor_proc_cpacket(struct cor_conn
*trgt_unconn_ll
, int *reset_needed
)
387 if (cor_conn_src_unconn_write_allowed(cor_get_conn_reversedir(
388 trgt_unconn_ll
)) == 0)
389 return RC_PC_OUT_OF_BUFSPACE
;
391 if (trgt_unconn_ll
->trgt
.unconnected
.paramlen_read
!= 0)
394 rc
= cor_read_hdr(trgt_unconn_ll
);
395 if (rc
== RC_READ_HDR_INCOMPLETE
) {
396 return RC_PC_INCOMPLETEREAD
;
397 } else if (rc
== RC_READ_HDR_FINISHED
) {
398 trgt_unconn_ll
->trgt
.unconnected
.paramlen_read
= 1;
400 if (trgt_unconn_ll
->trgt
.unconnected
.paramlen
>
402 /* command too long */
403 cor_send_resp_failed(trgt_unconn_ll
,
404 CDR_EXECFAILED_INVALID_COMMAND
,
412 if (trgt_unconn_ll
->trgt
.unconnected
.paramlen
> MAX_CONN_CMD_LEN
) {
413 cor_read_discard(trgt_unconn_ll
);
414 return RC_PC_INCOMPLETEREAD
;
417 if (trgt_unconn_ll
->trgt
.unconnected
.cmdparams
== 0) {
418 trgt_unconn_ll
->trgt
.unconnected
.cmdparams
= kmalloc(
419 trgt_unconn_ll
->trgt
.unconnected
.paramlen
,
421 if (trgt_unconn_ll
->trgt
.unconnected
.cmdparams
== 0) {
423 return RC_PC_INCOMPLETEREAD
;
427 cor_read_cmd(trgt_unconn_ll
);
429 BUG_ON(trgt_unconn_ll
->trgt
.unconnected
.cmdread
>
430 trgt_unconn_ll
->trgt
.unconnected
.paramlen
);
432 if (trgt_unconn_ll
->trgt
.unconnected
.cmdread
<
433 trgt_unconn_ll
->trgt
.unconnected
.paramlen
)
434 return RC_PC_INCOMPLETEREAD
;
436 cor_proc_cmd(trgt_unconn_ll
, reset_needed
);
438 return RC_PC_FINISHED
;
441 void cor_proc_cpacket(struct cor_conn
*trgt_unconn_l
)
443 struct cor_conn
*src_in
= cor_get_conn_reversedir(trgt_unconn_l
);
444 int reset_needed
= 0;
446 if (unlikely(trgt_unconn_l
->isreset
!= 0))
449 BUG_ON(trgt_unconn_l
->targettype
!= TARGET_UNCONNECTED
);
451 BUG_ON(trgt_unconn_l
->is_client
== 0);
452 spin_lock_bh(&src_in
->rcv_lock
);
455 int rc
= _cor_proc_cpacket(trgt_unconn_l
, &reset_needed
);
457 BUG_ON(rc
!= RC_PC_FINISHED
&&
458 rc
!= RC_PC_OUT_OF_BUFSPACE
&&
459 rc
!= RC_PC_INCOMPLETEREAD
);
461 if (trgt_unconn_l
->targettype
== TARGET_UNCONNECTED
) {
462 if (trgt_unconn_l
->flush
!= 0 &&
463 trgt_unconn_l
->data_buf
.read_remaining
== 0)
468 cor_flush_buf(cor_get_conn_reversedir(trgt_unconn_l
));
470 if (rc
== RC_PC_OUT_OF_BUFSPACE
||
471 rc
== RC_PC_INCOMPLETEREAD
)
474 BUG_ON(rc
!= RC_PC_FINISHED
);
476 if (trgt_unconn_l
->targettype
== TARGET_SOCK
|| (
477 trgt_unconn_l
->flush
!= 0 &&
478 trgt_unconn_l
->data_buf
.read_remaining
== 0))
483 cor_flush_buf(src_in
);
484 cor_flush_buf(trgt_unconn_l
);
488 if (reset_needed
!= 0)
489 cor_reset_conn_locked(cor_get_conn_bidir(
493 spin_unlock_bh(&src_in
->rcv_lock
);
496 MODULE_LICENSE("GPL");