source: lib/protocols_transport.c @ 10553bf

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivelibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since 10553bf was 3fc3267, checked in by Shane Alcock <salcock@…>, 6 years ago

Add protocol decoder functions for GRE and VXLAN

Many thanks to Perry Lorier for providing this patch.

Includes a test-case for VXLAN parsing.

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