source: lib/protocols_transport.c @ 10fd24b

develop
Last change on this file since 10fd24b was d439067, checked in by Shane Alcock <salcock@…>, 3 years ago

Move packet cached fields into a distinct structure.

This will help tidy up the packet structure a little, as well as
simplify the cache clearing process (and maybe even speed it up
a little).

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