source: mainline/uspace/srv/net/tcp/pdu.c@ a2e3ee6

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a2e3ee6 was a2e3ee6, checked in by Martin Decky <martin@…>, 12 years ago

use new network address infrastructure (towards IPv6 support)

  • Property mode set to 100644
File size: 7.8 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 header encoding and decoding
35 */
36
37#include <bitops.h>
38#include <byteorder.h>
39#include <errno.h>
40#include <mem.h>
41#include <stdlib.h>
42#include "pdu.h"
43#include "segment.h"
44#include "seq_no.h"
45#include "std.h"
46#include "tcp_type.h"
47
48#define TCP_CHECKSUM_INIT 0xffff
49
50/** One's complement addition.
51 *
52 * Result is a + b + carry.
53 */
54static uint16_t tcp_ocadd16(uint16_t a, uint16_t b)
55{
56 uint32_t s;
57
58 s = (uint32_t)a + (uint32_t)b;
59 return (s & 0xffff) + (s >> 16);
60}
61
62static uint16_t tcp_checksum_calc(uint16_t ivalue, void *data, size_t size)
63{
64 uint16_t sum;
65 uint16_t w;
66 size_t words, i;
67 uint8_t *bdata;
68
69 sum = ~ivalue;
70 words = size / 2;
71 bdata = (uint8_t *)data;
72
73 for (i = 0; i < words; i++) {
74 w = ((uint16_t)bdata[2*i] << 8) | bdata[2*i + 1];
75 sum = tcp_ocadd16(sum, w);
76 }
77
78 if (size % 2 != 0) {
79 w = ((uint16_t)bdata[2*words] << 8);
80 sum = tcp_ocadd16(sum, w);
81 }
82
83 return ~sum;
84}
85
86static void tcp_header_decode_flags(uint16_t doff_flags, tcp_control_t *rctl)
87{
88 tcp_control_t ctl;
89
90 ctl = 0;
91
92 if ((doff_flags & BIT_V(uint16_t, DF_URG)) != 0)
93 ctl |= 0 /* XXX */;
94 if ((doff_flags & BIT_V(uint16_t, DF_ACK)) != 0)
95 ctl |= CTL_ACK;
96 if ((doff_flags & BIT_V(uint16_t, DF_PSH)) != 0)
97 ctl |= 0 /* XXX */;
98 if ((doff_flags & BIT_V(uint16_t, DF_RST)) != 0)
99 ctl |= CTL_RST;
100 if ((doff_flags & BIT_V(uint16_t, DF_SYN)) != 0)
101 ctl |= CTL_SYN;
102 if ((doff_flags & BIT_V(uint16_t, DF_FIN)) != 0)
103 ctl |= CTL_FIN;
104
105 *rctl = ctl;
106}
107
108static void tcp_header_encode_flags(tcp_control_t ctl, uint16_t doff_flags0,
109 uint16_t *rdoff_flags)
110{
111 uint16_t doff_flags;
112
113 doff_flags = doff_flags0;
114
115 if ((ctl & CTL_ACK) != 0)
116 doff_flags |= BIT_V(uint16_t, DF_ACK);
117 if ((ctl & CTL_RST) != 0)
118 doff_flags |= BIT_V(uint16_t, DF_RST);
119 if ((ctl & CTL_SYN) != 0)
120 doff_flags |= BIT_V(uint16_t, DF_SYN);
121 if ((ctl & CTL_FIN) != 0)
122 doff_flags |= BIT_V(uint16_t, DF_FIN);
123
124 *rdoff_flags = doff_flags;
125}
126
127static void tcp_header_setup(tcp_sockpair_t *sp, tcp_segment_t *seg, tcp_header_t *hdr)
128{
129 uint16_t doff_flags;
130 uint16_t doff;
131
132 hdr->src_port = host2uint16_t_be(sp->local.port);
133 hdr->dest_port = host2uint16_t_be(sp->foreign.port);
134 hdr->seq = host2uint32_t_be(seg->seq);
135 hdr->ack = host2uint32_t_be(seg->ack);
136
137 doff = (sizeof(tcp_header_t) / sizeof(uint32_t)) << DF_DATA_OFFSET_l;
138 tcp_header_encode_flags(seg->ctrl, doff, &doff_flags);
139
140 hdr->doff_flags = host2uint16_t_be(doff_flags);
141 hdr->window = host2uint16_t_be(seg->wnd);
142 hdr->checksum = 0;
143 hdr->urg_ptr = host2uint16_t_be(seg->up);
144}
145
146static void tcp_phdr_setup(tcp_pdu_t *pdu, tcp_phdr_t *phdr)
147{
148 // FIXME: Check for correctness
149
150 uint32_t src_addr;
151 inet_addr_pack(&pdu->src_addr, &src_addr);
152
153 uint32_t dest_addr;
154 inet_addr_pack(&pdu->dest_addr, &dest_addr);
155
156 phdr->src_addr = host2uint32_t_be(src_addr);
157 phdr->dest_addr = host2uint32_t_be(dest_addr);
158 phdr->zero = 0;
159 phdr->protocol = 6; /* XXX Magic number */
160 phdr->tcp_length = host2uint16_t_be(pdu->header_size + pdu->text_size);
161}
162
163static void tcp_header_decode(tcp_header_t *hdr, tcp_segment_t *seg)
164{
165 tcp_header_decode_flags(uint16_t_be2host(hdr->doff_flags), &seg->ctrl);
166 seg->seq = uint32_t_be2host(hdr->seq);
167 seg->ack = uint32_t_be2host(hdr->ack);
168 seg->wnd = uint16_t_be2host(hdr->window);
169 seg->up = uint16_t_be2host(hdr->urg_ptr);
170}
171
172static int tcp_header_encode(tcp_sockpair_t *sp, tcp_segment_t *seg,
173 void **header, size_t *size)
174{
175 tcp_header_t *hdr;
176
177 hdr = calloc(1, sizeof(tcp_header_t));
178 if (hdr == NULL)
179 return ENOMEM;
180
181 tcp_header_setup(sp, seg, hdr);
182 *header = hdr;
183 *size = sizeof(tcp_header_t);
184
185 return EOK;
186}
187
188static tcp_pdu_t *tcp_pdu_new(void)
189{
190 return calloc(1, sizeof(tcp_pdu_t));
191}
192
193/** Create PDU with the specified header and text data.
194 *
195 * Note that you still need to set addresses in the returned PDU.
196 *
197 * @param hdr Header data
198 * @param hdr_size Header size in bytes
199 * @param text Text data
200 * @param text_size Text size in bytes
201 * @return New PDU
202 */
203tcp_pdu_t *tcp_pdu_create(void *hdr, size_t hdr_size, void *text,
204 size_t text_size)
205{
206 tcp_pdu_t *pdu;
207
208 pdu = tcp_pdu_new();
209 if (pdu == NULL)
210 return NULL;
211
212 pdu->header = malloc(hdr_size);
213 pdu->text = malloc(text_size);
214 if (pdu->header == NULL || pdu->text == NULL)
215 goto error;
216
217 memcpy(pdu->header, hdr, hdr_size);
218 memcpy(pdu->text, text, text_size);
219
220 pdu->header_size = hdr_size;
221 pdu->text_size = text_size;
222
223 return pdu;
224
225error:
226 if (pdu->header != NULL)
227 free(pdu->header);
228 if (pdu->text != NULL)
229 free(pdu->text);
230
231 return NULL;
232}
233
234void tcp_pdu_delete(tcp_pdu_t *pdu)
235{
236 free(pdu->header);
237 free(pdu->text);
238 free(pdu);
239}
240
241static uint16_t tcp_pdu_checksum_calc(tcp_pdu_t *pdu)
242{
243 uint16_t cs_phdr;
244 uint16_t cs_headers;
245 uint16_t cs_all;
246 tcp_phdr_t phdr;
247
248 tcp_phdr_setup(pdu, &phdr);
249 cs_phdr = tcp_checksum_calc(TCP_CHECKSUM_INIT, (void *)&phdr,
250 sizeof(tcp_phdr_t));
251 cs_headers = tcp_checksum_calc(cs_phdr, pdu->header, pdu->header_size);
252 cs_all = tcp_checksum_calc(cs_headers, pdu->text, pdu->text_size);
253
254 return cs_all;
255}
256
257static void tcp_pdu_set_checksum(tcp_pdu_t *pdu, uint16_t checksum)
258{
259 tcp_header_t *hdr;
260
261 hdr = (tcp_header_t *)pdu->header;
262 hdr->checksum = host2uint16_t_be(checksum);
263}
264
265/** Decode incoming PDU */
266int tcp_pdu_decode(tcp_pdu_t *pdu, tcp_sockpair_t *sp, tcp_segment_t **seg)
267{
268 tcp_segment_t *nseg;
269 tcp_header_t *hdr;
270
271 nseg = tcp_segment_make_data(0, pdu->text, pdu->text_size);
272 if (nseg == NULL)
273 return ENOMEM;
274
275 tcp_header_decode(pdu->header, nseg);
276 nseg->len += seq_no_control_len(nseg->ctrl);
277
278 hdr = (tcp_header_t *)pdu->header;
279
280 sp->local.port = uint16_t_be2host(hdr->dest_port);
281 sp->local.addr = pdu->dest_addr;
282 sp->foreign.port = uint16_t_be2host(hdr->src_port);
283 sp->foreign.addr = pdu->src_addr;
284
285 *seg = nseg;
286 return EOK;
287}
288
289/** Encode outgoing PDU */
290int tcp_pdu_encode(tcp_sockpair_t *sp, tcp_segment_t *seg, tcp_pdu_t **pdu)
291{
292 tcp_pdu_t *npdu;
293 size_t text_size;
294 uint16_t checksum;
295
296 npdu = tcp_pdu_new();
297 if (npdu == NULL)
298 return ENOMEM;
299
300 npdu->src_addr = sp->local.addr;
301 npdu->dest_addr = sp->foreign.addr;
302 tcp_header_encode(sp, seg, &npdu->header, &npdu->header_size);
303
304 text_size = tcp_segment_text_size(seg);
305 npdu->text = calloc(1, text_size);
306 if (npdu->text == NULL)
307 return ENOMEM;
308
309 npdu->text_size = text_size;
310 memcpy(npdu->text, seg->data, text_size);
311
312 /* Checksum calculation */
313 checksum = tcp_pdu_checksum_calc(npdu);
314 tcp_pdu_set_checksum(npdu, checksum);
315
316 *pdu = npdu;
317 return EOK;
318}
319
320/**
321 * @}
322 */
Note: See TracBrowser for help on using the repository browser.