source: lib/protocols_transport.c @ 2193905

develop
Last change on this file since 2193905 was 2193905, checked in by Jacob Van Walraven <jcv9@…>, 2 years ago

Apply changes required for pull request #81

  • 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->payload_length != -1)
67                return packet->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)->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)->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->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->transport_proto;
192                /* *remaining -= (packet->l4_header - link); */
193                *remaining = packet->l4_remaining;
194                return packet->l4_header;
195        }
196
197        transport = trace_get_layer3(packet,&ethertype,remaining);
198
199        if (!transport || *remaining == 0)
200                return NULL;
201
202        switch (ethertype) {
203                case TRACE_ETHERTYPE_IP: /* IPv4 */
204                        transport=trace_get_payload_from_ip(
205                                (libtrace_ip_t*)transport, proto, remaining);
206                        /* IPv6 */
207                        if (transport && *proto == TRACE_IPPROTO_IPV6) {
208                                transport=trace_get_payload_from_ip6(
209                                 (libtrace_ip6_t*)transport, proto,remaining);
210                        }
211                        break;
212                case TRACE_ETHERTYPE_IPV6: /* IPv6 */
213                        transport = trace_get_payload_from_ip6(
214                                (libtrace_ip6_t*)transport, proto, remaining);
215                        break;
216                default:
217                        *proto = 0;
218                        transport = NULL;
219                        break;
220                       
221        }
222
223        ((libtrace_packet_t *)packet)->transport_proto = *proto;
224        ((libtrace_packet_t *)packet)->l4_header = transport;
225        ((libtrace_packet_t *)packet)->l4_remaining = *remaining;
226
227
228        return transport;
229}
230
231DLLEXPORT libtrace_tcp_t *trace_get_tcp(libtrace_packet_t *packet) {
232        uint8_t proto;
233        uint32_t rem = 0;
234        libtrace_tcp_t *tcp;
235
236        tcp=(libtrace_tcp_t*)trace_get_transport(packet,&proto,&rem);
237
238        if (!tcp || proto != TRACE_IPPROTO_TCP)
239                return NULL;
240
241        /* We should return NULL if there isn't a full TCP header, because the
242         * caller has no way of telling how much of a TCP header we have
243         * returned - use trace_get_transport() if you want to deal with
244         * partial headers
245         *
246         * NOTE: We're not going to insist that all the TCP options are present
247         * as well, because lots of traces are snapped after 20 bytes of TCP
248         * header and I don't really want to break libtrace programs that
249         * use this function to process those traces */
250
251        if (rem < sizeof(libtrace_tcp_t))
252                return NULL;
253
254        return (libtrace_tcp_t*)tcp;
255}
256
257DLLEXPORT libtrace_tcp_t *trace_get_tcp_from_ip(libtrace_ip_t *ip, uint32_t *remaining)
258{
259        libtrace_tcp_t *tcpptr = 0;
260
261        if (ip->ip_p == TRACE_IPPROTO_TCP)  {
262                tcpptr = (libtrace_tcp_t *)
263                        trace_get_payload_from_ip(ip, NULL, remaining);
264        }
265
266        return tcpptr;
267}
268
269DLLEXPORT libtrace_udp_t *trace_get_udp(libtrace_packet_t *packet) {
270        uint8_t proto;
271        uint32_t rem = 0;
272        libtrace_udp_t *udp;
273
274        udp=(libtrace_udp_t*)trace_get_transport(packet,&proto,&rem);
275
276        if (!udp || proto != TRACE_IPPROTO_UDP)
277                return NULL;
278
279        /* Make sure we return a full UDP header as the caller has no way of
280         * telling how much of the packet is remaining */
281        if (rem < sizeof(libtrace_udp_t))
282                return NULL;
283
284        return udp;
285}
286
287DLLEXPORT libtrace_udp_t *trace_get_udp_from_ip(libtrace_ip_t *ip, uint32_t *remaining)
288{
289        libtrace_udp_t *udpptr = 0;
290
291        if (ip->ip_p == TRACE_IPPROTO_UDP) {
292                udpptr = (libtrace_udp_t *)
293                        trace_get_payload_from_ip(ip, NULL, remaining);
294        }
295
296        return udpptr;
297}
298
299DLLEXPORT libtrace_icmp_t *trace_get_icmp(libtrace_packet_t *packet) {
300        uint8_t proto;
301        uint32_t rem = 0;
302        libtrace_icmp_t *icmp;
303
304        icmp=(libtrace_icmp_t*)trace_get_transport(packet,&proto,&rem);
305
306        if (!icmp || proto != TRACE_IPPROTO_ICMP)
307                return NULL;
308
309        /* Make sure we return a full ICMP header as the caller has no way of
310         * telling how much of the packet is remaining */
311        if (rem < sizeof(libtrace_icmp_t))
312                return NULL;
313
314        return icmp;
315}
316
317DLLEXPORT libtrace_icmp6_t *trace_get_icmp6(libtrace_packet_t *packet) {
318        uint8_t proto;
319        uint32_t rem = 0;
320        libtrace_icmp6_t *icmp;
321
322        icmp=(libtrace_icmp6_t*)trace_get_transport(packet,&proto,&rem);
323
324        if (!icmp || proto != TRACE_IPPROTO_ICMPV6)
325                return NULL;
326
327        /* Make sure we return a full ICMP header as the caller has no way of
328         * telling how much of the packet is remaining */
329        if (rem < sizeof(libtrace_icmp6_t))
330                return NULL;
331
332        return icmp;
333}
334
335DLLEXPORT libtrace_icmp_t *trace_get_icmp_from_ip(libtrace_ip_t *ip, uint32_t *remaining)
336{
337        libtrace_icmp_t *icmpptr = 0;
338
339        if (ip->ip_p == TRACE_IPPROTO_ICMP)  {
340                icmpptr = (libtrace_icmp_t *)trace_get_payload_from_ip(ip, 
341                                NULL, remaining);
342        }
343
344        return icmpptr;
345}
346
347DLLEXPORT void *trace_get_payload_from_udp(libtrace_udp_t *udp, uint32_t *remaining)
348{
349        if (remaining) {
350                if (*remaining < sizeof(libtrace_udp_t)) {
351                        *remaining = 0;
352                        return NULL;
353                }
354                *remaining-=sizeof(libtrace_udp_t);
355        }
356        return (void*)((char*)udp+sizeof(libtrace_udp_t));
357}
358
359DLLEXPORT void *trace_get_payload_from_tcp(libtrace_tcp_t *tcp, uint32_t *remaining)
360{
361        unsigned int dlen = tcp->doff*4;
362        if (remaining) {
363                if (*remaining < dlen) {
364                        *remaining = 0;
365                        return NULL;
366                }
367                *remaining-=dlen;
368        }
369        return (void *)((char *)tcp+dlen);
370}
371
372DLLEXPORT void *trace_get_payload_from_icmp(libtrace_icmp_t *icmp, uint32_t *remaining)
373{
374        if (remaining) {
375                if (*remaining < sizeof(libtrace_icmp_t)) {
376                        *remaining = 0;
377                        return NULL;
378                }
379                *remaining-=sizeof(libtrace_icmp_t);
380        }
381        return (char*)icmp+sizeof(libtrace_icmp_t);
382}
383
384DLLEXPORT void *trace_get_payload_from_icmp6(libtrace_icmp6_t *icmp, uint32_t *remaining)
385{
386        if (remaining) {
387                if (*remaining < sizeof(libtrace_icmp6_t)) {
388                        *remaining = 0;
389                        return NULL;
390                }
391                *remaining-=sizeof(libtrace_icmp6_t);
392        }
393        return (char*)icmp+sizeof(libtrace_icmp6_t);
394}
395
396/* Return the source port
397 */
398DLLEXPORT uint16_t trace_get_source_port(const libtrace_packet_t *packet)
399{
400        uint32_t remaining;
401        uint8_t proto;
402        struct ports_t *port;
403        uint16_t fragoff;
404        uint8_t more;
405
406        fragoff = trace_get_fragment_offset(packet, &more);
407
408        /* If we're not the first fragment, we're unlikely to be able
409         * to get any useful port numbers from this packet.
410         */
411        if (fragoff != 0)
412                return 0;
413       
414       
415        port = (struct ports_t*)trace_get_transport(
416                        (libtrace_packet_t*)packet,
417                        &proto, &remaining);
418
419        /* Snapped too early */
420        if (remaining<2)
421                return 0;
422
423        /* ICMP *technically* doesn't have ports */
424        if (proto == TRACE_IPPROTO_ICMP || proto == TRACE_IPPROTO_ICMPV6)
425                return 0;
426
427        if (port)
428                return ntohs(port->src);
429        else
430                return 0;
431}
432
433/* Same as get_source_port except use the destination port */
434DLLEXPORT uint16_t trace_get_destination_port(const libtrace_packet_t *packet)
435{
436        uint32_t remaining;
437        uint8_t proto;
438        struct ports_t *port;
439        uint16_t fragoff;
440        uint8_t more;
441
442        fragoff = trace_get_fragment_offset(packet, &more);
443
444        /* If we're not the first fragment, we're unlikely to be able
445         * to get any useful port numbers from this packet.
446         */
447        if (fragoff != 0)
448                return 0;
449       
450       
451        port = (struct ports_t*)trace_get_transport(
452                        (libtrace_packet_t*)packet,
453                        &proto, &remaining);
454        /* Snapped too early */
455        if (remaining<4)
456                return 0;
457       
458        /* ICMP *technically* doesn't have ports */
459        if (proto == TRACE_IPPROTO_ICMP || proto == TRACE_IPPROTO_ICMPV6)
460                return 0;
461
462        if (port)
463                return ntohs(port->dst);
464        else
465                return 0;
466}
467
468DLLEXPORT uint16_t *trace_checksum_transport(libtrace_packet_t *packet, 
469                uint16_t *csum) {
470
471        void *header = NULL;
472        uint16_t ethertype;
473        uint32_t remaining;
474        uint32_t sum = 0;
475        uint8_t proto = 0;
476        char *csum_ptr = NULL;
477        int plen = 0;
478
479        uint8_t safety[65536];
480        uint8_t *ptr = safety;
481
482        header = trace_get_layer3(packet, &ethertype, &remaining);
483
484        if (header == NULL)
485                return NULL;
486       
487        if (ethertype == TRACE_ETHERTYPE_IP) {
488                libtrace_ip_t *ip = (libtrace_ip_t *)header;
489
490                if (remaining < sizeof(libtrace_ip_t))
491                        return NULL;
492
493                sum = ipv4_pseudo_checksum(ip);
494
495        } else if (ethertype == TRACE_ETHERTYPE_IPV6) {
496                libtrace_ip6_t *ip = (libtrace_ip6_t *)header;
497               
498                if (remaining < sizeof(libtrace_ip6_t))
499                        return 0;
500
501                sum = ipv6_pseudo_checksum(ip);
502       
503        }
504
505        header = trace_get_transport(packet, &proto, &remaining);
506
507        if (proto == TRACE_IPPROTO_TCP) {
508                libtrace_tcp_t *tcp = (libtrace_tcp_t *)header;
509                header = trace_get_payload_from_tcp(tcp, &remaining);
510               
511                csum_ptr = (char *)(&tcp->check);
512
513                memcpy(ptr, tcp, tcp->doff * 4);
514
515                tcp = (libtrace_tcp_t *)ptr;
516                tcp->check = 0;
517
518                ptr += (tcp->doff * 4);
519        } 
520       
521        else if (proto == TRACE_IPPROTO_UDP) {
522
523                libtrace_udp_t *udp = (libtrace_udp_t *)header;
524                header = trace_get_payload_from_udp(udp, &remaining);
525               
526                csum_ptr = (char *)(&udp->check);
527                memcpy(ptr, udp, sizeof(libtrace_udp_t));
528
529                udp = (libtrace_udp_t *)ptr;
530                udp->check = 0;
531
532                ptr += sizeof(libtrace_udp_t);
533        } 
534       
535        else if (proto == TRACE_IPPROTO_ICMP) {
536                /* ICMP doesn't use the pseudo header */
537                sum = 0;
538
539                libtrace_icmp_t *icmp = (libtrace_icmp_t *)header;
540                header = trace_get_payload_from_icmp(icmp, &remaining);
541               
542                csum_ptr = (char *)(&icmp->checksum);
543                memcpy(ptr, icmp, sizeof(libtrace_icmp_t));
544
545                icmp = (libtrace_icmp_t *)ptr;
546                icmp->checksum = 0;
547               
548                ptr += sizeof(libtrace_icmp_t);
549
550        } 
551        else {
552                return NULL;
553        }
554
555        sum += add_checksum(safety, (uint16_t)(ptr - safety));
556
557        plen = trace_get_payload_length(packet);
558        if (plen < 0)
559                return NULL;
560
561        if (remaining < (uint32_t)plen)
562                return NULL;
563
564        if (header == NULL)
565                return NULL;
566
567        sum += add_checksum(header, (uint16_t)plen);
568        *csum = ntohs(finish_checksum(sum));
569
570        return (uint16_t *)csum_ptr;
571}
572
573DLLEXPORT void *trace_get_payload_from_gre(libtrace_gre_t *gre,
574        uint32_t *remaining)
575{
576    uint8_t flags = ntohs(gre->flags);
577    uint32_t size = 4; /* GRE is 4 bytes long by default */
578    if (remaining && *remaining < size) {
579        *remaining = 0;
580        return NULL;
581    }
582
583    if((flags & LIBTRACE_GRE_FLAG_VERMASK) == LIBTRACE_GRE_PPTP_VERSION) {
584        size += 4;
585
586        if ((flags & LIBTRACE_GRE_FLAG_SEQ) != 0) {
587            size += 4;
588        }
589        if ((flags & LIBTRACE_GRE_FLAG_ACK) != 0) {
590            size += 4;
591        }
592    } else {
593
594        if ((ntohs(gre->flags) & LIBTRACE_GRE_FLAG_CHECKSUM) != 0) {
595            size += 4;  /* An extra 4 bytes. */
596        }
597
598        if ((ntohs(gre->flags) & LIBTRACE_GRE_FLAG_KEY) != 0) {
599           size += 4;  /* An extra 4 bytes. */
600        }
601
602        if ((ntohs(gre->flags) & LIBTRACE_GRE_FLAG_SEQ) != 0) {
603            size += 4;  /* An extra 4 bytes. */
604        }
605    }
606
607    if (remaining) {
608        if (*remaining < size) {
609            *remaining = 0;
610            return NULL;
611        }
612        *remaining -= size;
613    }
614    return (char*)gre+size;
615}
Note: See TracBrowser for help on using the repository browser.