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

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

TCP and UDP servers can make use of inet/endpoint.h types internally.

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