qos fixes, reset bufferusage on conn reset, conn refcnt fix
[cor_2_6_31.git] / net / cor / cpacket_parse.c
blob7036557d10b3ed594e6fcf25e23febce05e7205a
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));
74 flush_buf(sconn);
77 static void send_resp_bin(struct conn *rconn, char *buf, __u32 len)
79 struct conn *sconn = rconn->reversedir;
81 __u32 code = cpu_to_be16(CDR_BINDATA);
82 __u32 len_be = cpu_to_be32(len);
84 mutex_lock(&(sconn->rcv_lock));
85 BUG_ON(databuf_maypush(&(sconn->buf)) < (6 + len));
86 receive_buf(sconn, (char *) &code, 2);
87 receive_buf(sconn, (char *) &len_be, 4);
88 receive_buf(sconn, buf, len);
89 mutex_unlock(&(sconn->rcv_lock));
90 flush_buf(sconn);
93 static void parse_set_timeout(struct conn *rconn, int backwards)
95 __u32 timeout;
97 if (unlikely(rconn->target.unconnected.paramlen < 4)) {
98 send_resp(rconn, CDR_EXECFAILED, CDR_EXECFAILED_CMD_TOO_SHORT);
99 return;
102 ((char *)&timeout)[0] = rconn->target.unconnected.cmdparams[0];
103 ((char *)&timeout)[1] = rconn->target.unconnected.cmdparams[1];
104 ((char *)&timeout)[2] = rconn->target.unconnected.cmdparams[2];
105 ((char *)&timeout)[3] = rconn->target.unconnected.cmdparams[3];
107 timeout = be32_to_cpu(timeout);
109 if (backwards) {
110 if (rconn->reversedir->targettype != TARGET_OUT) {
111 send_resp(rconn, CDR_EXECFAILED,
112 CDR_EXECFAILED_ILLEGAL_COMMAND);
113 return;
115 rconn->reversedir->target.out.stall_timeout_ms = timeout;
116 } else {
117 rconn->target.unconnected.stall_timeout_ms = timeout;
120 send_resp(rconn, CDR_EXECOK, CDR_EXECOK_OK);
123 static void parse_list_neigh(struct conn *rconn)
125 __u32 limit;
126 __u32 offset;
127 __u32 len;
129 char *buf;
131 if (unlikely(rconn->target.unconnected.paramlen < 8)) {
132 send_resp(rconn, CDR_EXECFAILED, CDR_EXECFAILED_CMD_TOO_SHORT);
133 return;
136 ((char *)&limit)[0] = rconn->target.unconnected.cmdparams[0];
137 ((char *)&limit)[1] = rconn->target.unconnected.cmdparams[1];
138 ((char *)&limit)[2] = rconn->target.unconnected.cmdparams[2];
139 ((char *)&limit)[3] = rconn->target.unconnected.cmdparams[3];
141 ((char *)&offset)[0] = rconn->target.unconnected.cmdparams[4];
142 ((char *)&offset)[1] = rconn->target.unconnected.cmdparams[5];
143 ((char *)&offset)[2] = rconn->target.unconnected.cmdparams[6];
144 ((char *)&offset)[3] = rconn->target.unconnected.cmdparams[7];
146 limit = be32_to_cpu(limit);
147 offset = be32_to_cpu(offset);
149 buf = kmalloc(2048, GFP_KERNEL);
151 if (unlikely(buf == 0)) {
152 send_resp(rconn, CDR_EXECFAILED,
153 CDR_EXECFAILED_TEMPORARILY_OUT_OF_RESSOURCES);
154 return;
157 send_resp(rconn, CDR_EXECOK, CDR_EXECOK_OK);
159 len = generate_neigh_list(buf, 2048, limit, offset);
160 send_resp_bin(rconn, buf, len);
163 static void parse_connect_port(struct conn *rconn)
165 __be64 addr;
166 int rc;
168 BUG_ON(0 == rconn->target.unconnected.cmdparams);
170 if (unlikely(rconn->target.unconnected.paramlen < 8)) {
171 send_resp(rconn, CDR_EXECFAILED, CDR_EXECFAILED_CMD_TOO_SHORT);
172 return;
175 memcpy((char *) &addr, rconn->target.unconnected.cmdparams, 8);
177 rc = connect_port(rconn, addr);
179 if (rc == 0) {
180 send_resp(rconn, CDR_EXECOK, CDR_EXECOK_OK);
181 } else if (rc == 2) {
182 send_resp(rconn, CDR_EXECFAILED,
183 CDR_EXECFAILED_TARGETADDR_DOESNTEXIST);
184 } else if (rc == 3) {
185 send_resp(rconn, CDR_EXECFAILED,
186 CDR_EXECFAILED_LISTENERQUEUE_FULL);
187 } else {
188 BUG();
192 static void parse_connect_nb(struct conn *rconn)
194 __u16 addrtypelen;
195 __u16 addrlen;
196 __u8 *addrtype;
197 __u8 *addr;
198 int rc;
200 BUG_ON(0 == rconn->target.unconnected.cmdparams);
202 if (unlikely(rconn->target.unconnected.paramlen < 4)) {
203 send_resp(rconn, CDR_EXECFAILED, CDR_EXECFAILED_CMD_TOO_SHORT);
204 return;
207 ((char *)&addrtypelen)[0] = rconn->target.unconnected.cmdparams[0];
208 ((char *)&addrtypelen)[1] = rconn->target.unconnected.cmdparams[1];
210 ((char *)&addrlen)[0] = rconn->target.unconnected.cmdparams[2];
211 ((char *)&addrlen)[1] = rconn->target.unconnected.cmdparams[3];
213 addrtypelen = be16_to_cpu(addrtypelen);
214 addrlen = be16_to_cpu(addrlen);
216 if (unlikely((4 + (__u32) addrtypelen + (__u32) addrlen) >
217 rconn->target.unconnected.paramlen)) {
218 send_resp(rconn, CDR_EXECFAILED, CDR_EXECFAILED_CMD_TOO_SHORT);
219 return;
222 addrtype = rconn->target.unconnected.cmdparams + 4;
223 addr = addrtype + addrtypelen;
225 rc = connect_neigh(rconn, addrtypelen, addrtype, addrlen, addr);
227 if (rc == 0) {
228 send_resp(rconn, CDR_EXECOK, CDR_EXECOK_OK);
229 } else if (rc == 2) {
230 send_resp(rconn, CDR_EXECFAILED,
231 CDR_EXECFAILED_TARGETADDRTYPE_UNKNOWN);
232 } else if (rc == 3) {
233 send_resp(rconn, CDR_EXECFAILED,
234 CDR_EXECFAILED_TARGETADDR_DOESNTEXIST);
235 } else if (rc == 4) {
236 send_resp(rconn, CDR_EXECFAILED,
237 CDR_EXECFAILED_TEMPORARILY_OUT_OF_RESSOURCES);
238 } else {
239 BUG();
243 static void parse_cmd(struct conn *rconn)
245 __u16 code = rconn->target.unconnected.cmd;
247 if (code == CD_CONNECT_NB) {
248 parse_connect_nb(rconn);
249 } else if (code == CD_CONNECT_PORT) {
250 parse_connect_port(rconn);
251 } else if (code == CD_LIST_NEIGH) {
252 parse_list_neigh(rconn);
253 } else if (code == CD_SET_FORWARD_TIMEOUT) {
254 parse_set_timeout(rconn, 0);
255 } else if (code == CD_SET_BACKWARD_TIMEOUT) {
256 parse_set_timeout(rconn, 1);
257 } else {
258 send_resp(rconn, CDR_EXECFAILED,
259 CDR_EXECFAILED_UNKNOWN_COMMAND);
263 static void read_cmd(struct conn *rconn)
265 int pull;
267 pull = min(rconn->target.unconnected.paramlen + 6 -
268 rconn->target.unconnected.cmdread,
269 rconn->buf.read_remaining);
271 BUG_ON(0 > pull);
272 BUG_ON(0 == rconn->target.unconnected.cmdparams);
273 BUG_ON(6 > rconn->target.unconnected.cmdread);
275 if (pull == 0)
276 return;
278 databuf_pull(&(rconn->buf), rconn->target.unconnected.cmdparams +
279 rconn->target.unconnected.cmdread - 6, pull);
280 databuf_ackread(rconn);
282 rconn->target.unconnected.cmdread += pull;
285 static void read_discard(struct conn *rconn)
287 BUG_ON(rconn->target.unconnected.cmdread < 6);
289 while (rconn->target.unconnected.paramlen + 6 <
290 rconn->target.unconnected.cmdread &&
291 rconn->buf.read_remaining > 0) {
292 char buf[1];
293 databuf_pull(&(rconn->buf), buf, 1);
294 databuf_ackread(rconn);
296 rconn->target.unconnected.cmdread++;
300 static void read_hdr(struct conn *rconn)
302 BUG_ON(rconn->target.unconnected.cmdparams == 0);
304 while (rconn->target.unconnected.cmdread < 6 &&
305 rconn->buf.read_remaining > 0) {
307 char buf[1];
308 databuf_pull(&(rconn->buf), buf, 1);
309 databuf_ackread(rconn);
311 if (2 > rconn->target.unconnected.cmdread) {
312 rconn->target.unconnected.cmd <<= 8;
313 rconn->target.unconnected.cmd += buf[0];
314 } else {
315 rconn->target.unconnected.paramlen <<= 8;
316 rconn->target.unconnected.paramlen += buf[0];
318 rconn->target.unconnected.cmdread++;
322 void parse(struct conn *rconn)
324 __u8 *cmd;
326 BUG_ON(TARGET_UNCONNECTED != rconn->targettype);
328 if (rconn->target.unconnected.cmdparams == 0)
329 rconn->target.unconnected.cmdparams = kmalloc(MAX_CONN_CMD_LEN,
330 GFP_KERNEL);
332 cmd = rconn->target.unconnected.cmdparams;
334 if (rconn->target.unconnected.cmdread == 0) {
335 rconn->target.unconnected.cmd = 0;
336 rconn->target.unconnected.paramlen = 0;
339 if (rconn->target.unconnected.cmdread < 6) {
340 read_hdr(rconn);
341 if (rconn->target.unconnected.cmdread < 6)
342 return;
344 if (rconn->target.unconnected.paramlen > MAX_CONN_CMD_LEN) {
345 send_resp(rconn, CDR_EXECFAILED,
346 CDR_EXECFAILED_CMD_TOO_LONG);
350 if (rconn->target.unconnected.paramlen > MAX_CONN_CMD_LEN) {
351 read_discard(rconn);
352 return;
355 if (rconn->target.unconnected.cmdread >= 6 &&
356 rconn->target.unconnected.paramlen >
357 rconn->target.unconnected.cmdread - 6) {
358 read_cmd(rconn);
361 if (rconn->target.unconnected.paramlen ==
362 rconn->target.unconnected.cmdread - 6) {
363 rconn->target.unconnected.cmdread = 0;
364 parse_cmd(rconn);
367 if (rconn->targettype != TARGET_UNCONNECTED)
368 kfree(cmd);