buffering bugfixes
[cor_2_6_31.git] / net / cor / cpacket_parse.c
blob5b724fd194f3e98c7648d2f506cf5412d590ab95
1 /*
2 * Connection oriented routing
3 * Copyright (C) 2007-2010 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 static char *get_error_reason_text(__u16 reasoncode)
25 if (reasoncode == CDR_EXECFAILED_UNKNOWN_COMMAND)
26 return "Unknown command";
27 else if (reasoncode == CDR_EXECFAILED_PERMISSION_DENIED)
28 return "Permission denied";
29 else if (reasoncode == CDR_EXECFAILED_TEMPORARILY_OUT_OF_RESSOURCES)
30 return "Temporarily out of ressources";
31 else if (reasoncode == CDR_EXECFAILED_CMD_TOO_SHORT)
32 return "The length of the command is too short for all params";
33 else if (reasoncode == CDR_EXECFAILED_CMD_TOO_LONG)
34 return "command param is too long";
35 else if (reasoncode == CDR_EXECFAILED_TARGETADDRTYPE_UNKNOWN)
36 return "targettype unknown";
37 else if (reasoncode == CDR_EXECFAILED_TARGETADDR_DOESNTEXIST)
38 return "targetaddr does not exist";
39 else if (reasoncode == CDR_EXECFAILED_TARGETADDR_PORTCLOSED)
40 return "Port is on open";
41 else if (reasoncode == CDR_EXECFAILED_LISTENERQUEUE_FULL)
42 return "Listener queue full";
43 else
44 return 0;
47 static void send_resp(struct conn *rconn, __u16 respcode,
48 __u16 reasoncode)
50 struct conn *sconn = rconn->reversedir;
52 int reasonlen = 0;
53 __u16 reasonlen_be;
55 char *reasontext = 0;
57 if (unlikely(respcode == CDR_EXECFAILED))
58 reasontext = get_error_reason_text(respcode);
60 if (reasontext)
61 reasonlen = strnlen(reasontext, 1024);
63 respcode = cpu_to_be16(respcode);
64 reasoncode = cpu_to_be16(reasoncode);
65 reasonlen_be = cpu_to_be16(reasonlen);
67 mutex_lock(&(sconn->rcv_lock));
68 BUG_ON(databuf_maypush(&(sconn->buf)) < (6 + reasonlen));
69 receive_buf(sconn, (char *) &respcode, 2);
70 receive_buf(sconn, (char *) &reasoncode, 2);
71 receive_buf(sconn, (char *) &reasonlen_be, 2);
72 receive_buf(sconn, reasontext, reasonlen);
73 mutex_unlock(&(sconn->rcv_lock));
76 static void send_resp_bin(struct conn *rconn, char *buf, __u32 len)
78 struct conn *sconn = rconn->reversedir;
80 __u32 code = cpu_to_be16(CDR_BINDATA);
81 __u32 len_be = cpu_to_be32(len);
83 mutex_lock(&(sconn->rcv_lock));
84 BUG_ON(databuf_maypush(&(sconn->buf)) < (6 + len));
85 receive_buf(sconn, (char *) &code, 2);
86 receive_buf(sconn, (char *) &len_be, 4);
87 receive_buf(sconn, buf, len);
88 mutex_unlock(&(sconn->rcv_lock));
91 static void parse_set_timeout(struct conn *rconn, int backwards)
93 __u32 timeout;
95 if (unlikely(rconn->target.unconnected.paramlen < 4)) {
96 send_resp(rconn, CDR_EXECFAILED, CDR_EXECFAILED_CMD_TOO_SHORT);
97 return;
100 ((char *)&timeout)[0] = rconn->target.unconnected.cmdparams[0];
101 ((char *)&timeout)[1] = rconn->target.unconnected.cmdparams[1];
102 ((char *)&timeout)[2] = rconn->target.unconnected.cmdparams[2];
103 ((char *)&timeout)[3] = rconn->target.unconnected.cmdparams[3];
105 timeout = be32_to_cpu(timeout);
107 if (backwards) {
108 if (rconn->reversedir->targettype != TARGET_OUT) {
109 send_resp(rconn, CDR_EXECFAILED,
110 CDR_EXECFAILED_ILLEGAL_COMMAND);
111 return;
113 rconn->reversedir->target.out.stall_timeout_ms = timeout;
114 } else {
115 rconn->target.unconnected.stall_timeout_ms = timeout;
118 send_resp(rconn, CDR_EXECOK, CDR_EXECOK_OK);
121 static void parse_list_neigh(struct conn *rconn)
123 __u32 limit;
124 __u32 offset;
125 __u32 len;
127 char *buf;
129 if (unlikely(rconn->target.unconnected.paramlen < 8)) {
130 send_resp(rconn, CDR_EXECFAILED, CDR_EXECFAILED_CMD_TOO_SHORT);
131 return;
134 ((char *)&limit)[0] = rconn->target.unconnected.cmdparams[0];
135 ((char *)&limit)[1] = rconn->target.unconnected.cmdparams[1];
136 ((char *)&limit)[2] = rconn->target.unconnected.cmdparams[2];
137 ((char *)&limit)[3] = rconn->target.unconnected.cmdparams[3];
139 ((char *)&offset)[0] = rconn->target.unconnected.cmdparams[4];
140 ((char *)&offset)[1] = rconn->target.unconnected.cmdparams[5];
141 ((char *)&offset)[2] = rconn->target.unconnected.cmdparams[6];
142 ((char *)&offset)[3] = rconn->target.unconnected.cmdparams[7];
144 limit = be32_to_cpu(limit);
145 offset = be32_to_cpu(offset);
147 buf = kmalloc(2048, GFP_KERNEL);
149 if (unlikely(buf == 0)) {
150 send_resp(rconn, CDR_EXECFAILED,
151 CDR_EXECFAILED_TEMPORARILY_OUT_OF_RESSOURCES);
152 return;
155 send_resp(rconn, CDR_EXECOK, CDR_EXECOK_OK);
157 len = generate_neigh_list(buf, 2048, limit, offset);
158 send_resp_bin(rconn, buf, len);
161 static void parse_connect_port(struct conn *rconn)
163 __be64 addr;
164 int rc;
166 BUG_ON(0 == rconn->target.unconnected.cmdparams);
168 if (unlikely(rconn->target.unconnected.paramlen < 8)) {
169 send_resp(rconn, CDR_EXECFAILED, CDR_EXECFAILED_CMD_TOO_SHORT);
170 return;
173 memcpy((char *) &addr, rconn->target.unconnected.cmdparams, 8);
175 rc = connect_port(rconn, addr);
177 if (rc == 0) {
178 send_resp(rconn, CDR_EXECOK, CDR_EXECOK_OK);
179 } else if (rc == 2) {
180 send_resp(rconn, CDR_EXECFAILED,
181 CDR_EXECFAILED_TARGETADDR_DOESNTEXIST);
182 } else if (rc == 3) {
183 send_resp(rconn, CDR_EXECFAILED,
184 CDR_EXECFAILED_LISTENERQUEUE_FULL);
185 } else {
186 BUG();
190 static void parse_connect_nb(struct conn *rconn)
192 __u16 addrtypelen;
193 __u16 addrlen;
194 __u8 *addrtype;
195 __u8 *addr;
196 int rc;
198 BUG_ON(0 == rconn->target.unconnected.cmdparams);
200 if (unlikely(rconn->target.unconnected.paramlen < 4)) {
201 send_resp(rconn, CDR_EXECFAILED, CDR_EXECFAILED_CMD_TOO_SHORT);
202 return;
205 ((char *)&addrtypelen)[0] = rconn->target.unconnected.cmdparams[0];
206 ((char *)&addrtypelen)[1] = rconn->target.unconnected.cmdparams[1];
208 ((char *)&addrlen)[0] = rconn->target.unconnected.cmdparams[2];
209 ((char *)&addrlen)[1] = rconn->target.unconnected.cmdparams[3];
211 addrtypelen = be16_to_cpu(addrtypelen);
212 addrlen = be16_to_cpu(addrlen);
214 if (unlikely((4 + (__u32) addrtypelen + (__u32) addrlen) >
215 rconn->target.unconnected.paramlen)) {
216 send_resp(rconn, CDR_EXECFAILED, CDR_EXECFAILED_CMD_TOO_SHORT);
217 return;
220 addrtype = rconn->target.unconnected.cmdparams + 4;
221 addr = addrtype + addrtypelen;
223 rc = connect_neigh(rconn, addrtypelen, addrtype, addrlen, addr);
225 if (rc == 0) {
226 send_resp(rconn, CDR_EXECOK, CDR_EXECOK_OK);
227 } else if (rc == 2) {
228 send_resp(rconn, CDR_EXECFAILED,
229 CDR_EXECFAILED_TARGETADDRTYPE_UNKNOWN);
230 } else if (rc == 3) {
231 send_resp(rconn, CDR_EXECFAILED,
232 CDR_EXECFAILED_TARGETADDR_DOESNTEXIST);
233 } else if (rc == 4) {
234 send_resp(rconn, CDR_EXECFAILED,
235 CDR_EXECFAILED_TEMPORARILY_OUT_OF_RESSOURCES);
236 } else {
237 BUG();
241 static void parse_cmd(struct conn *rconn)
243 __u16 code = rconn->target.unconnected.cmd;
245 if (code == CD_CONNECT_NB) {
246 parse_connect_nb(rconn);
247 } else if (code == CD_CONNECT_PORT) {
248 parse_connect_port(rconn);
249 } else if (code == CD_LIST_NEIGH) {
250 parse_list_neigh(rconn);
251 } else if (code == CD_SET_FORWARD_TIMEOUT) {
252 parse_set_timeout(rconn, 0);
253 } else if (code == CD_SET_BACKWARD_TIMEOUT) {
254 parse_set_timeout(rconn, 1);
255 } else {
256 send_resp(rconn, CDR_EXECFAILED,
257 CDR_EXECFAILED_UNKNOWN_COMMAND);
261 static void read_cmd(struct conn *rconn)
263 int pull;
265 pull = min(rconn->target.unconnected.paramlen + 6 -
266 rconn->target.unconnected.cmdread,
267 rconn->buf.read_remaining);
269 BUG_ON(0 > pull);
270 BUG_ON(0 == rconn->target.unconnected.cmdparams);
271 BUG_ON(6 > rconn->target.unconnected.cmdread);
273 if (pull == 0)
274 return;
276 databuf_pull(&(rconn->buf), rconn->target.unconnected.cmdparams +
277 rconn->target.unconnected.cmdread - 6, pull);
278 databuf_ackread(rconn);
280 rconn->target.unconnected.cmdread += pull;
283 static void read_discard(struct conn *rconn)
285 BUG_ON(rconn->target.unconnected.cmdread < 6);
287 while (rconn->target.unconnected.paramlen + 6 <
288 rconn->target.unconnected.cmdread &&
289 rconn->buf.read_remaining > 0) {
290 char buf[1];
291 databuf_pull(&(rconn->buf), buf, 1);
292 databuf_ackread(rconn);
294 rconn->target.unconnected.cmdread++;
298 static void read_hdr(struct conn *rconn)
300 BUG_ON(rconn->target.unconnected.cmdparams == 0);
302 while (rconn->target.unconnected.cmdread < 6 &&
303 rconn->buf.read_remaining > 0) {
305 char buf[1];
306 databuf_pull(&(rconn->buf), buf, 1);
307 databuf_ackread(rconn);
309 if (2 > rconn->target.unconnected.cmdread) {
310 rconn->target.unconnected.cmd <<= 8;
311 rconn->target.unconnected.cmd += buf[0];
312 } else {
313 rconn->target.unconnected.paramlen <<= 8;
314 rconn->target.unconnected.paramlen += buf[0];
316 rconn->target.unconnected.cmdread++;
320 void parse(struct conn *rconn)
322 __u8 *cmd;
324 BUG_ON(TARGET_UNCONNECTED != rconn->targettype);
326 if (rconn->target.unconnected.cmdparams == 0)
327 rconn->target.unconnected.cmdparams = kmalloc(MAX_CONN_CMD_LEN,
328 GFP_KERNEL);
330 cmd = rconn->target.unconnected.cmdparams;
332 if (rconn->target.unconnected.cmdread == 0) {
333 rconn->target.unconnected.cmd = 0;
334 rconn->target.unconnected.paramlen = 0;
337 if (rconn->target.unconnected.cmdread < 6) {
338 read_hdr(rconn);
339 if (rconn->target.unconnected.cmdread < 6)
340 return;
342 if (rconn->target.unconnected.paramlen > MAX_CONN_CMD_LEN) {
343 send_resp(rconn, CDR_EXECFAILED,
344 CDR_EXECFAILED_CMD_TOO_LONG);
348 if (rconn->target.unconnected.paramlen > MAX_CONN_CMD_LEN) {
349 read_discard(rconn);
350 return;
353 if (rconn->target.unconnected.cmdread >= 6 &&
354 rconn->target.unconnected.paramlen >
355 rconn->target.unconnected.cmdread - 6) {
356 read_cmd(rconn);
359 if (rconn->target.unconnected.paramlen ==
360 rconn->target.unconnected.cmdread - 6) {
361 rconn->target.unconnected.cmdread = 0;
362 parse_cmd(rconn);
365 if (rconn->targettype != TARGET_UNCONNECTED)
366 kfree(cmd);