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

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

Implement listen, accept, recvfrom.

  • Property mode set to 100644
File size: 10.3 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 <byteorder.h>
39#include <errno.h>
40#include <io/log.h>
41#include <stdio.h>
42#include <task.h>
43
44#include <icmp_remote.h>
45#include <ip_client.h>
46#include <ip_interface.h>
47#include <ipc/services.h>
48#include <ipc/tl.h>
49#include <tl_common.h>
50#include <tl_skel.h>
51#include <packet_client.h>
52#include <packet_remote.h>
53
54#include "ncsim.h"
55#include "pdu.h"
56#include "rqueue.h"
57#include "sock.h"
58#include "std.h"
59#include "tcp.h"
60#include "test.h"
61
62#define NAME "tcp"
63
64async_sess_t *net_sess;
65static async_sess_t *icmp_sess;
66static async_sess_t *ip_sess;
67packet_dimensions_t pkt_dims;
68
69static void tcp_received_pdu(tcp_pdu_t *pdu);
70
71/* Pull up packets into a single memory block. */
72static int pq_pullup(packet_t *packet, void **data, size_t *dsize)
73{
74 packet_t *npacket;
75 size_t tot_len;
76 int length;
77
78 npacket = packet;
79 tot_len = 0;
80 do {
81 length = packet_get_data_length(packet);
82 if (length <= 0)
83 return EINVAL;
84
85 tot_len += length;
86 } while ((npacket = pq_next(npacket)) != NULL);
87
88 uint8_t *buf;
89 uint8_t *dp;
90
91 buf = calloc(tot_len, 1);
92 if (buf == NULL) {
93 free(buf);
94 return ENOMEM;
95 }
96
97 npacket = packet;
98 dp = buf;
99 do {
100 length = packet_get_data_length(packet);
101 if (length <= 0) {
102 free(buf);
103 return EINVAL;
104 }
105
106 memcpy(dp, packet_get_data(packet), length);
107 dp += length;
108 } while ((npacket = pq_next(npacket)) != NULL);
109
110 *data = buf;
111 *dsize = tot_len;
112 return EOK;
113}
114
115/** Process packet received from network layer. */
116static int tcp_received_msg(device_id_t device_id, packet_t *packet,
117 services_t error)
118{
119 int rc;
120 size_t offset;
121 int length;
122 struct sockaddr_in *src_addr;
123 struct sockaddr_in *dest_addr;
124 size_t addr_len;
125
126 log_msg(LVL_DEBUG, "tcp_received_msg()");
127
128 switch (error) {
129 case SERVICE_NONE:
130 break;
131 case SERVICE_ICMP:
132 default:
133 log_msg(LVL_WARN, "Unsupported service number %u",
134 (unsigned)error);
135 pq_release_remote(net_sess, packet_get_id(packet));
136 return ENOTSUP;
137 }
138
139 /* Process and trim off IP header */
140 log_msg(LVL_DEBUG, "tcp_received_msg() - IP header");
141
142 rc = ip_client_process_packet(packet, NULL, NULL, NULL, NULL, NULL);
143 if (rc < 0) {
144 log_msg(LVL_WARN, "ip_client_process_packet() failed");
145 pq_release_remote(net_sess, packet_get_id(packet));
146 return rc;
147 }
148
149 offset = (size_t)rc;
150 length = packet_get_data_length(packet);
151
152 if (length < 0 || (size_t)length < offset) {
153 log_msg(LVL_WARN, "length=%d, dropping.", length);
154 pq_release_remote(net_sess, packet_get_id(packet));
155 return EINVAL;
156 }
157
158 addr_len = packet_get_addr(packet, (uint8_t **)&src_addr,
159 (uint8_t **)&dest_addr);
160 if (addr_len <= 0) {
161 log_msg(LVL_WARN, "Failed to get packet address.");
162 pq_release_remote(net_sess, packet_get_id(packet));
163 return EINVAL;
164 }
165
166 if (addr_len != sizeof(struct sockaddr_in)) {
167 log_msg(LVL_WARN, "Unsupported address size %zu (!= %zu)",
168 addr_len, sizeof(struct sockaddr_in));
169 pq_release_remote(net_sess, packet_get_id(packet));
170 return EINVAL;
171 }
172
173 rc = packet_trim(packet, offset, 0);
174 if (rc != EOK) {
175 log_msg(LVL_WARN, "Failed to trim packet.");
176 pq_release_remote(net_sess, packet_get_id(packet));
177 return rc;
178 }
179
180 /* Pull up packets into a single memory block, pdu_raw. */
181 log_msg(LVL_DEBUG, "tcp_received_msg() - pull up");
182 uint8_t *pdu_raw;
183 size_t pdu_raw_size = 0;
184
185 pq_pullup(packet, (void **)&pdu_raw, &pdu_raw_size);
186
187 /* Split into header and payload. */
188
189 log_msg(LVL_DEBUG, "tcp_received_msg() - split header/payload");
190
191 tcp_pdu_t *pdu;
192 size_t hdr_size;
193
194 /* XXX Header options */
195 hdr_size = sizeof(tcp_header_t);
196
197 if (pdu_raw_size < hdr_size) {
198 log_msg(LVL_WARN, "pdu_raw_size = %zu < hdr_size = %zu",
199 pdu_raw_size, hdr_size);
200 pq_release_remote(net_sess, packet_get_id(packet));
201 return EINVAL;
202 }
203
204 log_msg(LVL_DEBUG, "pdu_raw_size=%zu, hdr_size=%zu",
205 pdu_raw_size, hdr_size);
206 pdu = tcp_pdu_create(pdu_raw, hdr_size, pdu_raw + hdr_size,
207 pdu_raw_size - hdr_size);
208 if (pdu == NULL) {
209 log_msg(LVL_WARN, "Failed creating PDU. Dropped.");
210 return ENOMEM;
211 }
212
213 free(pdu_raw);
214
215 pdu->src_addr.ipv4 = uint32_t_be2host(src_addr->sin_addr.s_addr);
216 pdu->dest_addr.ipv4 = uint32_t_be2host(dest_addr->sin_addr.s_addr);
217 log_msg(LVL_DEBUG, "src: 0x%08x, dest: 0x%08x",
218 pdu->src_addr.ipv4, pdu->dest_addr.ipv4);
219
220 tcp_received_pdu(pdu);
221 tcp_pdu_delete(pdu);
222
223 return EOK;
224}
225
226/** Receive packets from network layer. */
227static void tcp_receiver(ipc_callid_t iid, ipc_call_t *icall, void *arg)
228{
229 packet_t *packet;
230 int rc;
231
232 log_msg(LVL_DEBUG, "tcp_receiver()");
233
234 while (true) {
235 switch (IPC_GET_IMETHOD(*icall)) {
236 case NET_TL_RECEIVED:
237 log_msg(LVL_DEBUG, "method = NET_TL_RECEIVED");
238 rc = packet_translate_remote(net_sess, &packet,
239 IPC_GET_PACKET(*icall));
240 if (rc != EOK) {
241 log_msg(LVL_DEBUG, "Error %d translating packet.", rc);
242 async_answer_0(iid, (sysarg_t)rc);
243 break;
244 }
245 rc = tcp_received_msg(IPC_GET_DEVICE(*icall), packet,
246 IPC_GET_ERROR(*icall));
247 async_answer_0(iid, (sysarg_t)rc);
248 break;
249 default:
250 log_msg(LVL_DEBUG, "method = %u",
251 (unsigned)IPC_GET_IMETHOD(*icall));
252 async_answer_0(iid, ENOTSUP);
253 break;
254 }
255
256 iid = async_get_call(icall);
257 }
258}
259
260/** Transmit PDU over network layer. */
261void tcp_transmit_pdu(tcp_pdu_t *pdu)
262{
263 struct sockaddr_in dest;
264 device_id_t dev_id;
265 void *phdr;
266 size_t phdr_len;
267 packet_dimension_t *pkt_dim;
268 int rc;
269 packet_t *packet;
270 void *pkt_data;
271 size_t pdu_size;
272
273 dest.sin_family = AF_INET;
274 dest.sin_port = 0; /* not needed */
275 dest.sin_addr.s_addr = host2uint32_t_be(pdu->dest_addr.ipv4);
276
277 /* Find route. Obtained pseudo-header is not used. */
278 rc = ip_get_route_req(ip_sess, IPPROTO_TCP, (struct sockaddr *)&dest,
279 sizeof(dest), &dev_id, &phdr, &phdr_len);
280 if (rc != EOK) {
281 log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to find route.");
282 return;
283 }
284
285 rc = tl_get_ip_packet_dimension(ip_sess, &pkt_dims, dev_id, &pkt_dim);
286 if (rc != EOK) {
287 log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to get dimension.");
288 return;
289 }
290
291 pdu_size = pdu->header_size + pdu->text_size;
292
293 packet = packet_get_4_remote(net_sess, pdu_size, pkt_dim->addr_len,
294 pkt_dim->prefix, pkt_dim->suffix);
295 if (!packet) {
296 log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to get packet.");
297 return;
298 }
299
300 pkt_data = packet_suffix(packet, pdu_size);
301 if (!pkt_data) {
302 log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to get pkt_data ptr.");
303 pq_release_remote(net_sess, packet_get_id(packet));
304 return;
305 }
306
307 rc = ip_client_prepare_packet(packet, IPPROTO_TCP, 0, 0, 0, 0);
308 if (rc != EOK) {
309 log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to prepare IP packet part.");
310 pq_release_remote(net_sess, packet_get_id(packet));
311 return;
312 }
313
314 rc = packet_set_addr(packet, NULL, (uint8_t *)&dest, sizeof(dest));
315 if (rc != EOK) {
316 log_msg(LVL_DEBUG, "tcp_transmit_pdu: Failed to set packet address.");
317 pq_release_remote(net_sess, packet_get_id(packet));
318 return;
319 }
320
321 /* Copy PDU data to packet */
322 memcpy(pkt_data, pdu->header, pdu->header_size);
323 memcpy((uint8_t *)pkt_data + pdu->header_size, pdu->text,
324 pdu->text_size);
325
326 /* Transmit packet. XXX Transfers packet ownership to IP? */
327 ip_send_msg(ip_sess, dev_id, packet, SERVICE_TCP, 0);
328}
329
330/** Process received PDU. */
331static void tcp_received_pdu(tcp_pdu_t *pdu)
332{
333 tcp_segment_t *dseg;
334 tcp_sockpair_t rident;
335
336 log_msg(LVL_DEBUG, "tcp_received_pdu()");
337
338 if (tcp_pdu_decode(pdu, &rident, &dseg) != EOK) {
339 log_msg(LVL_WARN, "Not enough memory. PDU dropped.");
340 return;
341 }
342
343 /* Insert decoded segment into rqueue */
344 tcp_rqueue_insert_seg(&rident, dseg);
345}
346
347/* Called from libnet */
348void tl_connection(void)
349{
350 log_msg(LVL_DEBUG, "tl_connection()");
351}
352
353/* Called from libnet */
354int tl_message(ipc_callid_t callid, ipc_call_t *call, ipc_call_t *answer,
355 size_t *answer_count)
356{
357 async_sess_t *callback;
358
359 log_msg(LVL_DEBUG, "tl_message()");
360
361 *answer_count = 0;
362 callback = async_callback_receive_start(EXCHANGE_SERIALIZE, call);
363 if (callback)
364 return tcp_sock_connection(callback, callid, *call);
365
366 return ENOTSUP;
367}
368
369/* Called from libnet */
370int tl_initialize(async_sess_t *sess)
371{
372 int rc;
373
374 net_sess = sess;
375 icmp_sess = icmp_connect_module();
376
377 log_msg(LVL_DEBUG, "tl_initialize()");
378
379 tcp_sock_init();
380
381 ip_sess = ip_bind_service(SERVICE_IP, IPPROTO_TCP, SERVICE_TCP,
382 tcp_receiver);
383 if (ip_sess == NULL)
384 return ENOENT;
385
386 rc = packet_dimensions_initialize(&pkt_dims);
387 if (rc != EOK)
388 return rc;
389
390 return EOK;
391}
392
393int main(int argc, char **argv)
394{
395 int rc;
396
397 printf(NAME ": TCP (Transmission Control Protocol) network module\n");
398
399 rc = log_init(NAME, LVL_DEBUG);
400 if (rc != EOK) {
401 printf(NAME ": Failed to initialize log.\n");
402 return 1;
403 }
404
405// printf(NAME ": Accepting connections\n");
406// task_retval(0);
407
408 tcp_rqueue_init();
409 tcp_rqueue_thread_start();
410
411 tcp_ncsim_init();
412 tcp_ncsim_thread_start();
413
414 tcp_test();
415/*
416 async_manager();
417*/
418 tl_module_start(SERVICE_TCP);
419
420 /* Not reached */
421 return 0;
422}
423
424/**
425 * @}
426 */
Note: See TracBrowser for help on using the repository browser.