source: mainline/uspace/srv/net/tl/tcp/tcp.c@ d9f53877

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d9f53877 was fe5e9629, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Decode PDUs with header options correctly. Form reset reply correctly.

  • Property mode set to 100644
File size: 10.9 KB
Line 
1/*
2 * Copyright (c) 2011 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup tcp
30 * @{
31 */
32
33/**
34 * @file TCP (Transmission Control Protocol) network module
35 */
36
37#include <async.h>
38#include <bitops.h>
39#include <byteorder.h>
40#include <errno.h>
41#include <io/log.h>
42#include <stdio.h>
43#include <task.h>
44
45#include <icmp_remote.h>
46#include <ip_client.h>
47#include <ip_interface.h>
48#include <ipc/services.h>
49#include <ipc/tl.h>
50#include <tl_common.h>
51#include <tl_skel.h>
52#include <packet_client.h>
53#include <packet_remote.h>
54
55#include "ncsim.h"
56#include "pdu.h"
57#include "rqueue.h"
58#include "sock.h"
59#include "std.h"
60#include "tcp.h"
61#include "test.h"
62
63#define NAME "tcp"
64
65async_sess_t *net_sess;
66static async_sess_t *icmp_sess;
67async_sess_t *ip_sess;
68packet_dimensions_t pkt_dims;
69
70static void tcp_received_pdu(tcp_pdu_t *pdu);
71
72/* Pull up packets into a single memory block. */
73static int pq_pullup(packet_t *packet, void **data, size_t *dsize)
74{
75 packet_t *npacket;
76 size_t tot_len;
77 int length;
78
79 npacket = packet;
80 tot_len = 0;
81 do {
82 length = packet_get_data_length(packet);
83 if (length <= 0)
84 return EINVAL;
85
86 tot_len += length;
87 } while ((npacket = pq_next(npacket)) != NULL);
88
89 uint8_t *buf;
90 uint8_t *dp;
91
92 buf = calloc(tot_len, 1);
93 if (buf == NULL) {
94 free(buf);
95 return ENOMEM;
96 }
97
98 npacket = packet;
99 dp = buf;
100 do {
101 length = packet_get_data_length(packet);
102 if (length <= 0) {
103 free(buf);
104 return EINVAL;
105 }
106
107 memcpy(dp, packet_get_data(packet), length);
108 dp += length;
109 } while ((npacket = pq_next(npacket)) != NULL);
110
111 *data = buf;
112 *dsize = tot_len;
113 return EOK;
114}
115
116/** Process packet received from network layer. */
117static int tcp_received_msg(nic_device_id_t device_id, packet_t *packet,
118 services_t error)
119{
120 int rc;
121 size_t offset;
122 int length;
123 struct sockaddr_in *src_addr;
124 struct sockaddr_in *dest_addr;
125 size_t addr_len;
126
127 log_msg(LVL_DEBUG, "tcp_received_msg()");
128
129 switch (error) {
130 case SERVICE_NONE:
131 break;
132 case SERVICE_ICMP:
133 default:
134 log_msg(LVL_WARN, "Unsupported service number %u",
135 (unsigned)error);
136 pq_release_remote(net_sess, packet_get_id(packet));
137 return ENOTSUP;
138 }
139
140 /* Process and trim off IP header */
141 log_msg(LVL_DEBUG, "tcp_received_msg() - IP header");
142
143 rc = ip_client_process_packet(packet, NULL, NULL, NULL, NULL, NULL);
144 if (rc < 0) {
145 log_msg(LVL_WARN, "ip_client_process_packet() failed");
146 pq_release_remote(net_sess, packet_get_id(packet));
147 return rc;
148 }
149
150 offset = (size_t)rc;
151 length = packet_get_data_length(packet);
152
153 if (length < 0 || (size_t)length < offset) {
154 log_msg(LVL_WARN, "length=%d, dropping.", length);
155 pq_release_remote(net_sess, packet_get_id(packet));
156 return EINVAL;
157 }
158
159 addr_len = packet_get_addr(packet, (uint8_t **)&src_addr,
160 (uint8_t **)&dest_addr);
161 if (addr_len <= 0) {
162 log_msg(LVL_WARN, "Failed to get packet address.");
163 pq_release_remote(net_sess, packet_get_id(packet));
164 return EINVAL;
165 }
166
167 if (addr_len != sizeof(struct sockaddr_in)) {
168 log_msg(LVL_WARN, "Unsupported address size %zu (!= %zu)",
169 addr_len, sizeof(struct sockaddr_in));
170 pq_release_remote(net_sess, packet_get_id(packet));
171 return EINVAL;
172 }
173
174 rc = packet_trim(packet, offset, 0);
175 if (rc != EOK) {
176 log_msg(LVL_WARN, "Failed to trim packet.");
177 pq_release_remote(net_sess, packet_get_id(packet));
178 return rc;
179 }
180
181 /* Pull up packets into a single memory block, pdu_raw. */
182 log_msg(LVL_DEBUG, "tcp_received_msg() - pull up");
183 uint8_t *pdu_raw;
184 size_t pdu_raw_size = 0;
185
186 pq_pullup(packet, (void **)&pdu_raw, &pdu_raw_size);
187
188 /* Split into header and payload. */
189
190 log_msg(LVL_DEBUG, "tcp_received_msg() - split header/payload");
191
192 tcp_pdu_t *pdu;
193 size_t hdr_size;
194 tcp_header_t *hdr;
195 uint32_t data_offset;
196
197 if (pdu_raw_size < sizeof(tcp_header_t)) {
198 log_msg(LVL_WARN, "pdu_raw_size = %zu < sizeof(tcp_header_t) = %zu",
199 pdu_raw_size, sizeof(tcp_header_t));
200 pq_release_remote(net_sess, packet_get_id(packet));
201 return EINVAL;
202 }
203
204 hdr = (tcp_header_t *)pdu_raw;
205 data_offset = BIT_RANGE_EXTRACT(uint32_t, DF_DATA_OFFSET_h, DF_DATA_OFFSET_l,
206 uint16_t_be2host(hdr->doff_flags));
207
208 hdr_size = sizeof(uint32_t) * data_offset;
209
210 if (pdu_raw_size < hdr_size) {
211 log_msg(LVL_WARN, "pdu_raw_size = %zu < hdr_size = %zu",
212 pdu_raw_size, hdr_size);
213 pq_release_remote(net_sess, packet_get_id(packet));
214 return EINVAL;
215 }
216
217 if (hdr_size < sizeof(tcp_header_t)) {
218 log_msg(LVL_WARN, "hdr_size = %zu < sizeof(tcp_header_t) = %zu",
219 hdr_size, sizeof(tcp_header_t));
220 pq_release_remote(net_sess, packet_get_id(packet));
221 return EINVAL;
222 }
223
224 log_msg(LVL_DEBUG, "pdu_raw_size=%zu, hdr_size=%zu",
225 pdu_raw_size, hdr_size);
226 pdu = tcp_pdu_create(pdu_raw, hdr_size, pdu_raw + hdr_size,
227 pdu_raw_size - hdr_size);
228 if (pdu == NULL) {
229 log_msg(LVL_WARN, "Failed creating PDU. Dropped.");
230 return ENOMEM;
231 }
232
233 free(pdu_raw);
234
235 pdu->src_addr.ipv4 = uint32_t_be2host(src_addr->sin_addr.s_addr);
236 pdu->dest_addr.ipv4 = uint32_t_be2host(dest_addr->sin_addr.s_addr);
237 log_msg(LVL_DEBUG, "src: 0x%08x, dest: 0x%08x",
238 pdu->src_addr.ipv4, pdu->dest_addr.ipv4);
239
240 tcp_received_pdu(pdu);
241 tcp_pdu_delete(pdu);
242
243 return EOK;
244}
245
246/** Receive packets from network layer. */
247static void tcp_receiver(ipc_callid_t iid, ipc_call_t *icall, void *arg)
248{
249 packet_t *packet;
250 int rc;
251
252 log_msg(LVL_DEBUG, "tcp_receiver()");
253
254 while (true) {
255 switch (IPC_GET_IMETHOD(*icall)) {
256 case NET_TL_RECEIVED:
257 log_msg(LVL_DEBUG, "method = NET_TL_RECEIVED");
258 rc = packet_translate_remote(net_sess, &packet,
259 IPC_GET_PACKET(*icall));
260 if (rc != EOK) {
261 log_msg(LVL_DEBUG, "Error %d translating packet.", rc);
262 async_answer_0(iid, (sysarg_t)rc);
263 break;
264 }
265 rc = tcp_received_msg(IPC_GET_DEVICE(*icall), packet,
266 IPC_GET_ERROR(*icall));
267 async_answer_0(iid, (sysarg_t)rc);
268 break;
269 default:
270 log_msg(LVL_DEBUG, "method = %u",
271 (unsigned)IPC_GET_IMETHOD(*icall));
272 async_answer_0(iid, ENOTSUP);
273 break;
274 }
275
276 iid = async_get_call(icall);
277 }
278}
279
280/** Transmit PDU over network layer. */
281void tcp_transmit_pdu(tcp_pdu_t *pdu)
282{
283 struct sockaddr_in dest;
284 nic_device_id_t dev_id;
285 void *phdr;
286 size_t phdr_len;
287 packet_dimension_t *pkt_dim;
288 int rc;
289 packet_t *packet;
290 void *pkt_data;
291 size_t pdu_size;
292
293 dest.sin_family = AF_INET;
294 dest.sin_port = 0; /* not needed */
295 dest.sin_addr.s_addr = host2uint32_t_be(pdu->dest_addr.ipv4);
296
297 /* Find route. Obtained pseudo-header is not used. */
298 rc = ip_get_route_req(ip_sess, IPPROTO_TCP, (struct sockaddr *)&dest,
299 sizeof(dest), &dev_id, &phdr, &phdr_len);
300 if (rc != EOK) {
301 log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to find route.");
302 return;
303 }
304
305 rc = tl_get_ip_packet_dimension(ip_sess, &pkt_dims, dev_id, &pkt_dim);
306 if (rc != EOK) {
307 log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to get dimension.");
308 return;
309 }
310
311 pdu_size = pdu->header_size + pdu->text_size;
312
313 packet = packet_get_4_remote(net_sess, pdu_size, pkt_dim->addr_len,
314 pkt_dim->prefix, pkt_dim->suffix);
315 if (!packet) {
316 log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to get packet.");
317 return;
318 }
319
320 pkt_data = packet_suffix(packet, pdu_size);
321 if (!pkt_data) {
322 log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to get pkt_data ptr.");
323 pq_release_remote(net_sess, packet_get_id(packet));
324 return;
325 }
326
327 rc = ip_client_prepare_packet(packet, IPPROTO_TCP, 0, 0, 0, 0);
328 if (rc != EOK) {
329 log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to prepare IP packet part.");
330 pq_release_remote(net_sess, packet_get_id(packet));
331 return;
332 }
333
334 rc = packet_set_addr(packet, NULL, (uint8_t *)&dest, sizeof(dest));
335 if (rc != EOK) {
336 log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to set packet address.");
337 pq_release_remote(net_sess, packet_get_id(packet));
338 return;
339 }
340
341 /* Copy PDU data to packet */
342 memcpy(pkt_data, pdu->header, pdu->header_size);
343 memcpy((uint8_t *)pkt_data + pdu->header_size, pdu->text,
344 pdu->text_size);
345
346 /* Transmit packet. XXX Transfers packet ownership to IP? */
347 ip_send_msg(ip_sess, dev_id, packet, SERVICE_TCP, 0);
348}
349
350/** Process received PDU. */
351static void tcp_received_pdu(tcp_pdu_t *pdu)
352{
353 tcp_segment_t *dseg;
354 tcp_sockpair_t rident;
355
356 log_msg(LVL_DEBUG, "tcp_received_pdu()");
357
358 if (tcp_pdu_decode(pdu, &rident, &dseg) != EOK) {
359 log_msg(LVL_WARN, "Not enough memory. PDU dropped.");
360 return;
361 }
362
363 /* Insert decoded segment into rqueue */
364 tcp_rqueue_insert_seg(&rident, dseg);
365}
366
367/* Called from libnet */
368void tl_connection(void)
369{
370 log_msg(LVL_DEBUG, "tl_connection()");
371}
372
373/* Called from libnet */
374int tl_message(ipc_callid_t callid, ipc_call_t *call, ipc_call_t *answer,
375 size_t *answer_count)
376{
377 async_sess_t *callback;
378
379 log_msg(LVL_DEBUG, "tl_message()");
380
381 *answer_count = 0;
382 callback = async_callback_receive_start(EXCHANGE_SERIALIZE, call);
383 if (callback)
384 return tcp_sock_connection(callback, callid, *call);
385
386 return ENOTSUP;
387}
388
389/* Called from libnet */
390int tl_initialize(async_sess_t *sess)
391{
392 int rc;
393
394 net_sess = sess;
395 icmp_sess = icmp_connect_module();
396
397 log_msg(LVL_DEBUG, "tl_initialize()");
398
399 tcp_sock_init();
400
401 ip_sess = ip_bind_service(SERVICE_IP, IPPROTO_TCP, SERVICE_TCP,
402 tcp_receiver);
403 if (ip_sess == NULL)
404 return ENOENT;
405
406 rc = packet_dimensions_initialize(&pkt_dims);
407 if (rc != EOK)
408 return rc;
409
410 return EOK;
411}
412
413int main(int argc, char **argv)
414{
415 int rc;
416
417 printf(NAME ": TCP (Transmission Control Protocol) network module\n");
418
419 rc = log_init(NAME, LVL_ERROR);
420 if (rc != EOK) {
421 printf(NAME ": Failed to initialize log.\n");
422 return 1;
423 }
424
425// printf(NAME ": Accepting connections\n");
426// task_retval(0);
427
428 tcp_rqueue_init();
429 tcp_rqueue_thread_start();
430
431 tcp_ncsim_init();
432 tcp_ncsim_thread_start();
433
434 if (0) tcp_test();
435/*
436 async_manager();
437*/
438 tl_module_start(SERVICE_TCP);
439
440 /* Not reached */
441 return 0;
442}
443
444/**
445 * @}
446 */
Note: See TracBrowser for help on using the repository browser.