source: lib/protocols_transport.c @ 6654714

cachetimestampsdevelopdpdk-ndagetsiliverc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformance
Last change on this file since 6654714 was 6654714, checked in by Shane Alcock <salcock@…>, 3 years ago

Fix various -Waddress-of-packed-member warnings on clang

Most of these fixes are casts to simply hide the issue from the
compiler -- not sure how (or even if) we can fix these properly.

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