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

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

Send meaningful Data Offset field.

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