source: lib/protocols_transport.c @ 54a76f2

cachetimestampsdevelopringdecrementfixringperformance
Last change on this file since 54a76f2 was 54a76f2, checked in by Shane Alcock <salcock@…>, 2 years ago

Fix broken payload lengths when IP length is not yet set.

Presumably, this can happen when the packet segmentation has
been offloaded to the NIC hardware -- the length is not set
until the packet is about to be sent (i.e. *after* an outgoing
packet is captured), so our calculation ends up producing a
negative number (0 - sizeof(IP) - sizeof(TCP / UDP)).

In this case, we instead have to infer the payload length using
the amount of bytes captured less the difference between the start
of the IP header and the start of the captured frame.

  • Property mode set to 100644
File size: 15.0 KB
Line 
1/*
2 *
3 * Copyright (c) 2007-2016 The University of Waikato, Hamilton, New Zealand.
4 * All rights reserved.
5 *
6 * This file is part of libtrace.
7 *
8 * This code has been developed by the University of Waikato WAND
9 * research group. For further information please see http://www.wand.net.nz/
10 *
11 * libtrace is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * libtrace is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 *
24 *
25 */
26
27#include "libtrace_int.h"
28#include "libtrace.h"
29#include "protocols.h"
30#include "checksum.h"
31#include <assert.h>
32#include <stdlib.h>
33#include <stdio.h> // fprintf
34#include <string.h>
35
36/* This file contains all the protocol decoding functions for transport layer
37 * protocols. This includes functions for access port numbers.
38 *
39 * Supported protocols include (but are not limited to):
40 *      TCP
41 *      UDP
42 *      ICMP
43 */
44
45/* Get the size of the payload as it was in the original packet, i.e. prior
46 * to any truncation.
47 *
48 * Basically, wire length minus the packet headers.
49 *
50 * Currently only supports IP (v4 and v6) and TCP, UDP and ICMP. Will return
51 * 0 if an unsupported protocol header is encountered, or if one of the
52 * headers is truncated.
53 */
54DLLEXPORT size_t trace_get_payload_length(const libtrace_packet_t *packet) {
55
56        void *layer;
57        uint16_t ethertype;
58        uint8_t proto;
59        uint32_t rem;
60        libtrace_ip_t *ip;
61        libtrace_ip6_t *ip6;
62        libtrace_tcp_t *tcp;
63        size_t len = 0;
64        uint8_t iplenzero = 0;
65
66        /* Just use the cached length if we can */
67        if (packet->payload_length != -1)
68                return packet->payload_length; 
69
70        /* Set to zero so that we can return early without having to
71         * worry about forgetting to update the cached value */
72        ((libtrace_packet_t *)packet)->payload_length = 0;
73        layer = trace_get_layer3(packet, &ethertype, &rem);
74        if (!layer)
75                return 0;
76        switch (ethertype) {
77                case TRACE_ETHERTYPE_IP:
78                        ip = (libtrace_ip_t *)layer;
79                        if (rem < sizeof(libtrace_ip_t))
80                                return 0;
81                        if (ntohs(ip->ip_len) == 0) {
82                                iplenzero = 1;
83                                break;
84                        }
85                        len = ntohs(ip->ip_len) - (4 * ip->ip_hl);
86               
87                        /* Deal with v6 within v4 */
88                        if (ip->ip_p == TRACE_IPPROTO_IPV6)
89                                len -= sizeof(libtrace_ip6_t);
90                       
91                        break;
92                case TRACE_ETHERTYPE_IPV6:
93                        ip6 = (libtrace_ip6_t *)layer;
94                        if (rem < sizeof(libtrace_ip6_t))
95                                return 0;
96                        len = ntohs(ip6->plen);
97                        if (len == 0) {
98                                iplenzero = 1;
99                        }
100                        break;
101                default:
102                        return 0;
103        }
104
105        if (iplenzero) {
106                /* deal with cases where IP length is zero due to
107                 * hardware segmentation offload */
108                uint8_t *iplayer, *pktstart;
109                libtrace_linktype_t linktype;
110                uint32_t rem;
111
112                iplayer = (uint8_t *)layer;
113                pktstart = (uint8_t *)trace_get_packet_buffer(packet, &linktype, &rem);
114
115                len = rem - (iplayer - pktstart);
116                if (ethertype == TRACE_ETHERTYPE_IP) {
117                        ip = (libtrace_ip_t *)layer;
118                        len -= (4 * ip->ip_hl);
119                } else {
120                        len -= sizeof(libtrace_ip6_t);
121                }
122        }
123
124        layer = trace_get_transport(packet, &proto, &rem);
125        if (!layer)
126                return 0;
127       
128        switch(proto) {
129                case TRACE_IPPROTO_TCP:
130                        if (rem < sizeof(libtrace_tcp_t))
131                                return 0;
132                        tcp = (libtrace_tcp_t *)layer;
133                       
134                        if (len < (size_t)(4 * tcp->doff))
135                                return 0;
136                       
137                        len -= (4 * tcp->doff);
138                        break;
139                case TRACE_IPPROTO_UDP:
140                        if (rem < sizeof(libtrace_udp_t))
141                                return 0;
142                        if (len < sizeof(libtrace_udp_t))
143                                return 0;
144                        len -= sizeof(libtrace_udp_t);
145                        break;
146                case TRACE_IPPROTO_ICMP:
147                        if (rem < sizeof(libtrace_icmp_t))
148                                return 0;
149                        if (len < sizeof(libtrace_icmp_t))
150                                return 0;
151                        len -= sizeof(libtrace_icmp_t);
152                        break;
153                case TRACE_IPPROTO_ICMPV6:
154                        if (rem < sizeof(libtrace_icmp6_t))
155                                return 0;
156                        if (len < sizeof(libtrace_icmp6_t))
157                                return 0;
158                        len -= sizeof(libtrace_icmp6_t);
159                        break;
160                       
161                default:
162                        return 0;
163        }
164
165        ((libtrace_packet_t *)packet)->payload_length = len;
166        return len;
167
168}
169
170DLLEXPORT void *trace_get_transport(const libtrace_packet_t *packet, 
171                uint8_t *proto,
172                uint32_t *remaining
173                ) 
174{
175        uint8_t dummy_proto;
176        uint16_t ethertype;
177        uint32_t dummy_remaining;
178        void *transport;
179
180        if (!proto) proto=&dummy_proto;
181
182        if (!remaining) remaining=&dummy_remaining;
183
184        if (packet->l4_header) {
185                /*
186                void *link;
187                libtrace_linktype_t linktype;
188                link = trace_get_packet_buffer(packet, &linktype, remaining);
189                if (!link)
190                        return NULL;
191                */
192                *proto = packet->transport_proto;
193                /* *remaining -= (packet->l4_header - link); */
194                *remaining = packet->l4_remaining;
195                return packet->l4_header;
196        }
197
198        transport = trace_get_layer3(packet,&ethertype,remaining);
199
200        if (!transport || *remaining == 0)
201                return NULL;
202
203        switch (ethertype) {
204                case TRACE_ETHERTYPE_IP: /* IPv4 */
205                        transport=trace_get_payload_from_ip(
206                                (libtrace_ip_t*)transport, proto, remaining);
207                        /* IPv6 */
208                        if (transport && *proto == TRACE_IPPROTO_IPV6) {
209                                transport=trace_get_payload_from_ip6(
210                                 (libtrace_ip6_t*)transport, proto,remaining);
211                        }
212                        break;
213                case TRACE_ETHERTYPE_IPV6: /* IPv6 */
214                        transport = trace_get_payload_from_ip6(
215                                (libtrace_ip6_t*)transport, proto, remaining);
216                        break;
217                default:
218                        *proto = 0;
219                        transport = NULL;
220                        break;
221                       
222        }
223
224        ((libtrace_packet_t *)packet)->transport_proto = *proto;
225        ((libtrace_packet_t *)packet)->l4_header = transport;
226        ((libtrace_packet_t *)packet)->l4_remaining = *remaining;
227
228
229        return transport;
230}
231
232DLLEXPORT libtrace_tcp_t *trace_get_tcp(libtrace_packet_t *packet) {
233        uint8_t proto;
234        uint32_t rem = 0;
235        libtrace_tcp_t *tcp;
236
237        tcp=(libtrace_tcp_t*)trace_get_transport(packet,&proto,&rem);
238
239        if (!tcp || proto != TRACE_IPPROTO_TCP)
240                return NULL;
241
242        /* We should return NULL if there isn't a full TCP header, because the
243         * caller has no way of telling how much of a TCP header we have
244         * returned - use trace_get_transport() if you want to deal with
245         * partial headers
246         *
247         * NOTE: We're not going to insist that all the TCP options are present
248         * as well, because lots of traces are snapped after 20 bytes of TCP
249         * header and I don't really want to break libtrace programs that
250         * use this function to process those traces */
251
252        if (rem < sizeof(libtrace_tcp_t))
253                return NULL;
254
255        return (libtrace_tcp_t*)tcp;
256}
257
258DLLEXPORT libtrace_tcp_t *trace_get_tcp_from_ip(libtrace_ip_t *ip, uint32_t *remaining)
259{
260        libtrace_tcp_t *tcpptr = 0;
261
262        if (ip->ip_p == TRACE_IPPROTO_TCP)  {
263                tcpptr = (libtrace_tcp_t *)
264                        trace_get_payload_from_ip(ip, NULL, remaining);
265        }
266
267        return tcpptr;
268}
269
270DLLEXPORT libtrace_udp_t *trace_get_udp(libtrace_packet_t *packet) {
271        uint8_t proto;
272        uint32_t rem = 0;
273        libtrace_udp_t *udp;
274
275        udp=(libtrace_udp_t*)trace_get_transport(packet,&proto,&rem);
276
277        if (!udp || proto != TRACE_IPPROTO_UDP)
278                return NULL;
279
280        /* Make sure we return a full UDP header as the caller has no way of
281         * telling how much of the packet is remaining */
282        if (rem < sizeof(libtrace_udp_t))
283                return NULL;
284
285        return udp;
286}
287
288DLLEXPORT libtrace_udp_t *trace_get_udp_from_ip(libtrace_ip_t *ip, uint32_t *remaining)
289{
290        libtrace_udp_t *udpptr = 0;
291
292        if (ip->ip_p == TRACE_IPPROTO_UDP) {
293                udpptr = (libtrace_udp_t *)
294                        trace_get_payload_from_ip(ip, NULL, remaining);
295        }
296
297        return udpptr;
298}
299
300DLLEXPORT libtrace_icmp_t *trace_get_icmp(libtrace_packet_t *packet) {
301        uint8_t proto;
302        uint32_t rem = 0;
303        libtrace_icmp_t *icmp;
304
305        icmp=(libtrace_icmp_t*)trace_get_transport(packet,&proto,&rem);
306
307        if (!icmp || proto != TRACE_IPPROTO_ICMP)
308                return NULL;
309
310        /* Make sure we return a full ICMP header as the caller has no way of
311         * telling how much of the packet is remaining */
312        if (rem < sizeof(libtrace_icmp_t))
313                return NULL;
314
315        return icmp;
316}
317
318DLLEXPORT libtrace_icmp6_t *trace_get_icmp6(libtrace_packet_t *packet) {
319        uint8_t proto;
320        uint32_t rem = 0;
321        libtrace_icmp6_t *icmp;
322
323        icmp=(libtrace_icmp6_t*)trace_get_transport(packet,&proto,&rem);
324
325        if (!icmp || proto != TRACE_IPPROTO_ICMPV6)
326                return NULL;
327
328        /* Make sure we return a full ICMP header as the caller has no way of
329         * telling how much of the packet is remaining */
330        if (rem < sizeof(libtrace_icmp6_t))
331                return NULL;
332
333        return icmp;
334}
335
336DLLEXPORT libtrace_icmp_t *trace_get_icmp_from_ip(libtrace_ip_t *ip, uint32_t *remaining)
337{
338        libtrace_icmp_t *icmpptr = 0;
339
340        if (ip->ip_p == TRACE_IPPROTO_ICMP)  {
341                icmpptr = (libtrace_icmp_t *)trace_get_payload_from_ip(ip, 
342                                NULL, remaining);
343        }
344
345        return icmpptr;
346}
347
348DLLEXPORT void *trace_get_payload_from_udp(libtrace_udp_t *udp, uint32_t *remaining)
349{
350        if (remaining) {
351                if (*remaining < sizeof(libtrace_udp_t)) {
352                        *remaining = 0;
353                        return NULL;
354                }
355                *remaining-=sizeof(libtrace_udp_t);
356        }
357        return (void*)((char*)udp+sizeof(libtrace_udp_t));
358}
359
360DLLEXPORT void *trace_get_payload_from_tcp(libtrace_tcp_t *tcp, uint32_t *remaining)
361{
362        unsigned int dlen = tcp->doff*4;
363        if (remaining) {
364                if (*remaining < dlen) {
365                        *remaining = 0;
366                        return NULL;
367                }
368                *remaining-=dlen;
369        }
370        return (void *)((char *)tcp+dlen);
371}
372
373DLLEXPORT void *trace_get_payload_from_icmp(libtrace_icmp_t *icmp, uint32_t *remaining)
374{
375        if (remaining) {
376                if (*remaining < sizeof(libtrace_icmp_t)) {
377                        *remaining = 0;
378                        return NULL;
379                }
380                *remaining-=sizeof(libtrace_icmp_t);
381        }
382        return (char*)icmp+sizeof(libtrace_icmp_t);
383}
384
385DLLEXPORT void *trace_get_payload_from_icmp6(libtrace_icmp6_t *icmp, uint32_t *remaining)
386{
387        if (remaining) {
388                if (*remaining < sizeof(libtrace_icmp6_t)) {
389                        *remaining = 0;
390                        return NULL;
391                }
392                *remaining-=sizeof(libtrace_icmp6_t);
393        }
394        return (char*)icmp+sizeof(libtrace_icmp6_t);
395}
396
397/* Return the source port
398 */
399DLLEXPORT uint16_t trace_get_source_port(const libtrace_packet_t *packet)
400{
401        uint32_t remaining;
402        uint8_t proto;
403        struct ports_t *port;
404        uint16_t fragoff;
405        uint8_t more;
406
407        fragoff = trace_get_fragment_offset(packet, &more);
408
409        /* If we're not the first fragment, we're unlikely to be able
410         * to get any useful port numbers from this packet.
411         */
412        if (fragoff != 0)
413                return 0;
414       
415       
416        port = (struct ports_t*)trace_get_transport(
417                        (libtrace_packet_t*)packet,
418                        &proto, &remaining);
419
420        /* Snapped too early */
421        if (remaining<2)
422                return 0;
423
424        /* ICMP *technically* doesn't have ports */
425        if (proto == TRACE_IPPROTO_ICMP || proto == TRACE_IPPROTO_ICMPV6)
426                return 0;
427
428        if (port)
429                return ntohs(port->src);
430        else
431                return 0;
432}
433
434/* Same as get_source_port except use the destination port */
435DLLEXPORT uint16_t trace_get_destination_port(const libtrace_packet_t *packet)
436{
437        uint32_t remaining;
438        uint8_t proto;
439        struct ports_t *port;
440        uint16_t fragoff;
441        uint8_t more;
442
443        fragoff = trace_get_fragment_offset(packet, &more);
444
445        /* If we're not the first fragment, we're unlikely to be able
446         * to get any useful port numbers from this packet.
447         */
448        if (fragoff != 0)
449                return 0;
450       
451       
452        port = (struct ports_t*)trace_get_transport(
453                        (libtrace_packet_t*)packet,
454                        &proto, &remaining);
455        /* Snapped too early */
456        if (remaining<4)
457                return 0;
458       
459        /* ICMP *technically* doesn't have ports */
460        if (proto == TRACE_IPPROTO_ICMP || proto == TRACE_IPPROTO_ICMPV6)
461                return 0;
462
463        if (port)
464                return ntohs(port->dst);
465        else
466                return 0;
467}
468
469DLLEXPORT uint16_t *trace_checksum_transport(libtrace_packet_t *packet, 
470                uint16_t *csum) {
471
472        void *header = NULL;
473        uint16_t ethertype;
474        uint32_t remaining;
475        uint32_t sum = 0;
476        uint8_t proto = 0;
477        char *csum_ptr = NULL;
478        int plen = 0;
479
480        uint8_t safety[65536];
481        uint8_t *ptr = safety;
482
483        header = trace_get_layer3(packet, &ethertype, &remaining);
484
485        if (header == NULL)
486                return NULL;
487       
488        if (ethertype == TRACE_ETHERTYPE_IP) {
489                libtrace_ip_t *ip = (libtrace_ip_t *)header;
490
491                if (remaining < sizeof(libtrace_ip_t))
492                        return NULL;
493
494                sum = ipv4_pseudo_checksum(ip);
495
496        } else if (ethertype == TRACE_ETHERTYPE_IPV6) {
497                libtrace_ip6_t *ip = (libtrace_ip6_t *)header;
498               
499                if (remaining < sizeof(libtrace_ip6_t))
500                        return 0;
501
502                sum = ipv6_pseudo_checksum(ip);
503       
504        }
505
506        header = trace_get_transport(packet, &proto, &remaining);
507
508        if (proto == TRACE_IPPROTO_TCP) {
509                libtrace_tcp_t *tcp = (libtrace_tcp_t *)header;
510                header = trace_get_payload_from_tcp(tcp, &remaining);
511               
512                csum_ptr = (char *)(&tcp->check);
513
514                memcpy(ptr, tcp, tcp->doff * 4);
515
516                tcp = (libtrace_tcp_t *)ptr;
517                tcp->check = 0;
518
519                ptr += (tcp->doff * 4);
520        } 
521       
522        else if (proto == TRACE_IPPROTO_UDP) {
523
524                libtrace_udp_t *udp = (libtrace_udp_t *)header;
525                header = trace_get_payload_from_udp(udp, &remaining);
526               
527                csum_ptr = (char *)(&udp->check);
528                memcpy(ptr, udp, sizeof(libtrace_udp_t));
529
530                udp = (libtrace_udp_t *)ptr;
531                udp->check = 0;
532
533                ptr += sizeof(libtrace_udp_t);
534        } 
535       
536        else if (proto == TRACE_IPPROTO_ICMP) {
537                /* ICMP doesn't use the pseudo header */
538                sum = 0;
539
540                libtrace_icmp_t *icmp = (libtrace_icmp_t *)header;
541                header = trace_get_payload_from_icmp(icmp, &remaining);
542               
543                csum_ptr = (char *)(&icmp->checksum);
544                memcpy(ptr, icmp, sizeof(libtrace_icmp_t));
545
546                icmp = (libtrace_icmp_t *)ptr;
547                icmp->checksum = 0;
548               
549                ptr += sizeof(libtrace_icmp_t);
550
551        } 
552        else {
553                return NULL;
554        }
555
556        sum += add_checksum(safety, (uint16_t)(ptr - safety));
557
558        plen = trace_get_payload_length(packet);
559        if (plen < 0)
560                return NULL;
561
562        if (remaining < (uint32_t)plen)
563                return NULL;
564
565        if (header == NULL)
566                return NULL;
567
568        sum += add_checksum(header, (uint16_t)plen);
569        *csum = ntohs(finish_checksum(sum));
570        //assert(0);
571       
572        return (uint16_t *)csum_ptr;
573}
574
575DLLEXPORT void *trace_get_payload_from_gre(libtrace_gre_t *gre,
576        uint32_t *remaining)
577{
578    uint8_t flags = ntohs(gre->flags);
579    uint32_t size = 4; /* GRE is 4 bytes long by default */
580    if (remaining && *remaining < size) {
581        *remaining = 0;
582        return NULL;
583    }
584
585    if((flags & LIBTRACE_GRE_FLAG_VERMASK) == LIBTRACE_GRE_PPTP_VERSION) {
586        size += 4;
587
588        if ((flags & LIBTRACE_GRE_FLAG_SEQ) != 0) {
589            size += 4;
590        }
591        if ((flags & LIBTRACE_GRE_FLAG_ACK) != 0) {
592            size += 4;
593        }
594    } else {
595
596        if ((ntohs(gre->flags) & LIBTRACE_GRE_FLAG_CHECKSUM) != 0) {
597            size += 4;  /* An extra 4 bytes. */
598        }
599
600        if ((ntohs(gre->flags) & LIBTRACE_GRE_FLAG_KEY) != 0) {
601           size += 4;  /* An extra 4 bytes. */
602        }
603
604        if ((ntohs(gre->flags) & LIBTRACE_GRE_FLAG_SEQ) != 0) {
605            size += 4;  /* An extra 4 bytes. */
606        }
607    }
608
609    if (remaining) {
610        if (*remaining < size) {
611            *remaining = 0;
612            return NULL;
613        }
614        *remaining -= size;
615    }
616    return (char*)gre+size;
617}
Note: See TracBrowser for help on using the repository browser.