source: lib/protocols_l3.c @ ee58d0d

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since ee58d0d was ee58d0d, checked in by Shane Alcock <salcock@…>, 10 years ago
  • Improved performance by caching more stuff, especially L2 headers and various "remaining" values - not much point caching something if you need to re-read the entire packet to calculate "remaining" still :)
  • Also improved performance of trace_get_layer2 by avoiding an effective double call of trace_get_packet_buffer
  • Fixed bug with payload length calculation if the packet has extra padding beyond what the IP len states
  • Improved (hopefully) performance when reading ERF traces by avoiding updating the drops counter if the loss counter was zero (should save an ntohs at least)
  • Property mode set to 100644
File size: 14.0 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 <assert.h>
39#include <stdlib.h>
40
41#ifdef HAVE_NETPACKET_PACKET_H
42#include <sys/socket.h>
43#include <netpacket/packet.h>
44#include <net/ethernet.h>
45#include <net/if_arp.h>
46#include <string.h>
47#else
48#include <net/if_dl.h>
49#include <string.h>
50#endif
51
52/* This file contains all the protocol decoding functions for layer 3
53 * (the IP layer) protocols. This includes functions for accessing IP
54 * addresses.
55 *
56 * Supported protocols include:
57 *      IPv4
58 *      IPv6
59 */
60
61/* Gets an IPv4 header */
62libtrace_ip_t *trace_get_ip(libtrace_packet_t *packet) 
63{
64        uint16_t ethertype;
65        void *ret;
66
67        uint32_t remaining = trace_get_capture_length(packet);
68
69        ret = trace_get_layer3(packet,&ethertype,&remaining);
70
71        if (!ret || ethertype!=TRACE_ETHERTYPE_IP)
72                return NULL;
73
74        /* Make sure we have at least a base IPv4 header */
75        if (remaining < sizeof(libtrace_ip_t)) 
76                return NULL;
77       
78        /* Not an IPv4 packet */
79        if (((libtrace_ip_t*)ret)->ip_v != 4)
80                return NULL;
81
82        return (libtrace_ip_t*)ret;
83}
84
85libtrace_ip6_t *trace_get_ip6(libtrace_packet_t *packet) 
86{
87        uint16_t ethertype;
88        void *ret;
89
90        uint32_t remaining = trace_get_capture_length(packet);
91
92        ret = trace_get_layer3(packet,&ethertype,&remaining);
93
94        if (!ret || ethertype!=TRACE_ETHERTYPE_IPV6)
95                return NULL;
96
97        /* Make sure we have at least the base IPv6 header */
98        if (remaining < sizeof(libtrace_ip6_t))
99                return NULL;
100
101        return (libtrace_ip6_t*)ret;
102}
103
104#define SW_IP_OFFMASK 0x1fff
105
106DLLEXPORT void *trace_get_payload_from_ip(libtrace_ip_t *ipptr, uint8_t *prot,
107                uint32_t *remaining) 
108{
109        void *trans_ptr = 0;
110
111        assert(ipptr != NULL);
112       
113        /* Er? IPv5? */
114        if (ipptr->ip_v != 4)
115                return NULL;
116
117        if ((ntohs(ipptr->ip_off) & SW_IP_OFFMASK) != 0) {
118                if (remaining)
119                        *remaining = 0;         
120                return NULL;
121        }
122
123        if (remaining) {
124                if (*remaining<(ipptr->ip_hl*4U)) {
125                        *remaining = 0;
126                        return NULL;
127                }
128                /* If the packet features extra "padding", we probably
129                 * don't want that counting as possible payload, e.g. for
130                 * payload length calculations */
131                if (*remaining > ntohs(ipptr->ip_len))
132                        *remaining = ntohs(ipptr->ip_len);
133
134                *remaining-=(ipptr->ip_hl * 4);
135        }
136
137        trans_ptr = (void *)((char *)ipptr + (ipptr->ip_hl * 4));
138
139        if (prot) *prot = ipptr->ip_p;
140
141        return trans_ptr;
142}
143
144void *trace_get_payload_from_ip6(libtrace_ip6_t *ipptr, uint8_t *prot,
145                uint32_t *remaining) 
146{
147        void *payload = (char*)ipptr+sizeof(libtrace_ip6_t);
148        uint8_t nxt;
149        uint16_t len;
150
151        assert (ipptr != NULL);
152        nxt = ipptr->nxt;       
153        if (remaining) {
154                if (*remaining<sizeof(libtrace_ip6_t)) {
155                        *remaining = 0;
156                        return NULL;
157                }
158                *remaining-=sizeof(libtrace_ip6_t);
159        }
160
161        while(1) {
162                switch (nxt) {
163                        case 0: /* hop by hop options */
164                        case TRACE_IPPROTO_ROUTING:
165                        case TRACE_IPPROTO_AH:
166                        case TRACE_IPPROTO_DSTOPTS:
167                        {
168                                /* Length does not include the first 8 bytes */
169                                len=((libtrace_ip6_ext_t*)payload)->len * 8;
170                                len += 8;
171
172
173                                if (remaining) {
174                                        if (*remaining < len) {
175                                                /* Snap too short */
176                                                *remaining = 0;
177                                                return NULL;
178                                        }
179                                        *remaining-=len;
180                                }
181
182                                nxt=((libtrace_ip6_ext_t*)payload)->nxt;
183                                payload=(char*)payload+len;
184                                continue;
185                        }
186                        case TRACE_IPPROTO_ESP:
187                        {
188                                if (prot) *prot=TRACE_IPPROTO_ESP;
189                                return payload;
190                        }
191                        case TRACE_IPPROTO_FRAGMENT:
192                                {
193                                        len = sizeof(libtrace_ip6_frag_t);
194                                        if (remaining) {
195                                                if (*remaining < len) {
196                                                        /* Snap too short */
197                                                        *remaining = 0;
198                                                        return NULL;
199                                                }
200                                                *remaining-=len;
201                                        }
202                                        nxt=((libtrace_ip6_frag_t*)payload)->nxt;
203                                        payload=(char*)payload+len;
204                                        continue;
205                                }
206
207                        default:
208                                if (prot) *prot=nxt;
209                                return payload;
210                }
211        }
212}
213
214DLLEXPORT void *trace_get_layer3(const libtrace_packet_t *packet,
215                uint16_t *ethertype,
216                uint32_t *remaining)
217{
218        void *iphdr;
219        uint16_t dummy_ethertype;
220        void *link;
221        uint32_t dummy_remaining;
222        libtrace_linktype_t linktype;
223
224        if (!ethertype) ethertype=&dummy_ethertype;
225
226        if (!remaining) remaining=&dummy_remaining;
227
228        /* use l3 cache */
229        if (packet->l3_header)
230        {
231                /*
232                link = trace_get_packet_buffer(packet,&linktype,remaining);
233
234                if (!link)
235                        return NULL;
236                */
237
238                *ethertype = packet->l3_ethertype;
239                /* *remaining -= (packet->l3_header - link); */
240                *remaining = packet->l3_remaining;
241
242                return packet->l3_header;
243        }
244
245        link = trace_get_layer2(packet,&linktype,remaining);
246        iphdr = trace_get_payload_from_layer2(
247                        link,
248                        linktype,
249                        ethertype,
250                        remaining);
251
252        for(;;) {
253                if (!iphdr || *remaining == 0)
254                        break;
255                switch(*ethertype) {
256                case TRACE_ETHERTYPE_8021Q: /* VLAN */
257                        iphdr=trace_get_payload_from_vlan(
258                                          iphdr,ethertype,remaining);
259                        continue;
260                case TRACE_ETHERTYPE_MPLS: /* MPLS */
261                        iphdr=trace_get_payload_from_mpls(
262                                          iphdr,ethertype,remaining);
263
264                        if (iphdr && ethertype == 0x0) {
265                                iphdr=trace_get_payload_from_ethernet(
266                                                iphdr,ethertype,remaining);
267                        }
268                        continue;
269                case TRACE_ETHERTYPE_PPP_SES: /* PPPoE */
270                        iphdr = trace_get_payload_from_pppoe(iphdr, ethertype,
271                                        remaining);
272                        continue;
273                default:
274                        break;
275                }
276
277                break;
278        }
279
280        if (!iphdr || *remaining == 0)
281                return NULL;
282
283        /* Store values in the cache for later */
284        /* Cast away constness, nasty, but this is just a cache */
285        ((libtrace_packet_t*)packet)->l3_ethertype = *ethertype;
286        ((libtrace_packet_t*)packet)->l3_header = iphdr;
287        ((libtrace_packet_t*)packet)->l3_remaining = *remaining;
288
289        return iphdr;
290}
291
292/* Parse an ip or tcp option
293 * @param[in,out] ptr   the pointer to the current option
294 * @param[in,out] len   the length of the remaining buffer
295 * @param[out] type     the type of the option
296 * @param[out] optlen   the length of the option
297 * @param[out] data     the data of the option
298 *
299 * @returns bool true if there is another option (and the fields are filled in)
300 *               or false if this was the last option.
301 *
302 * This updates ptr to point to the next option after this one, and updates
303 * len to be the number of bytes remaining in the options area.  Type is updated
304 * to be the code of this option, and data points to the data of this option,
305 * with optlen saying how many bytes there are.
306 *
307 * @note Beware of fragmented packets.
308 * @author Perry Lorier
309 */
310DLLEXPORT int trace_get_next_option(unsigned char **ptr,int *len,
311                        unsigned char *type,
312                        unsigned char *optlen,
313                        unsigned char **data)
314{
315        if (*len<=0)
316                return 0;
317        *type=**ptr;
318        switch(*type) {
319                case 0: /* End of options */
320                        return 0;
321                case 1: /* Pad */
322                        (*ptr)++;
323                        (*len)--;
324                        return 1;
325                default:
326                        *optlen = *(*ptr+1);
327                        if (*optlen<2)
328                                return 0; /* I have no idea wtf is going on
329                                           * with these packets
330                                           */
331
332                        /* Ensure that optlen is not greater than the
333                         * amount of buffer remaining */
334                        if (*optlen > *len) 
335                                return 0;
336                       
337                        (*len)-=*optlen;
338                        (*data)=(*ptr+2);
339                        (*ptr)+=*optlen;
340                        if (*len<0)
341                                return 0;
342                        return 1;
343        }
344        assert(0);
345}
346
347/* Extract the source mac address from a frame and bundle it up into a sockaddr */
348static struct sockaddr *get_source_ethernet_address(
349        libtrace_ether_t *ethernet, struct sockaddr *addr)
350{
351        static struct sockaddr_storage dummy;
352#ifdef HAVE_NETPACKET_PACKET_H
353/* Use linux's sockaddr_ll structure */
354        struct sockaddr_ll *l2addr;
355
356        if (addr)
357                l2addr = (struct sockaddr_ll*)addr;
358        else
359                l2addr = (struct sockaddr_ll*)&dummy;
360       
361        l2addr->sll_family = AF_PACKET;
362        l2addr->sll_protocol = ethernet->ether_type;
363        l2addr->sll_ifindex = 0; /* Irrelevant */
364        l2addr->sll_hatype = ARPHRD_ETHER; 
365        l2addr->sll_pkttype = PACKET_OTHERHOST;
366        l2addr->sll_halen = 6;
367        memcpy(l2addr->sll_addr,ethernet->ether_shost, 6);
368
369        return (struct sockaddr*)l2addr;
370#else
371/* Use BSD's sockaddr_dl structure */
372        struct sockaddr_dl *l2addr;
373
374        if (addr)
375                l2addr = (struct sockaddr_dl *)addr;
376        else
377                l2addr = (struct sockaddr_dl *)&dummy;
378       
379        l2addr->sdl_family = AF_LINK;
380#if HAVE_SDL_LEN == 1
381        l2addr->sdl_len = sizeof(struct sockaddr_dl);
382#endif
383        l2addr->sdl_index = 0; /* Unused */
384        l2addr->sdl_alen = 6; /* Address length  */
385        l2addr->sdl_nlen = 0; /* No name in here - this *should* work, right? */
386        l2addr->sdl_slen = 0;   
387        l2addr->sdl_type = 0; /* Hopefully zero is OK for this value too */
388        memcpy(l2addr->sdl_data, ethernet->ether_shost, 6);
389
390        return (struct sockaddr *)l2addr;
391#endif
392}
393
394static struct sockaddr *get_source_l2_address(
395        const libtrace_packet_t *packet, struct sockaddr *addr)
396{
397        static struct sockaddr_storage dummy;
398        void *l2;
399        libtrace_linktype_t linktype;
400        uint32_t remaining;
401
402        if (!addr)
403                addr =(struct sockaddr*)&dummy;
404
405        l2=trace_get_layer2(packet, &linktype, &remaining);
406        if (!l2) {
407                return NULL;
408        }
409
410        switch (linktype) {
411                case TRACE_TYPE_ETH:
412                        return get_source_ethernet_address((libtrace_ether_t*)l2, addr);
413                default:
414                        return NULL;
415        }
416}
417
418DLLEXPORT struct sockaddr *trace_get_source_address(
419                const libtrace_packet_t *packet, struct sockaddr *addr)
420{
421        uint16_t ethertype;
422        uint32_t remaining;
423        void *l3;
424        struct ports_t *ports;
425        static struct sockaddr_storage dummy;
426
427        if (!addr)
428                addr=(struct sockaddr*)&dummy;
429
430        l3 = trace_get_layer3(packet,&ethertype,&remaining);
431
432        if (!l3)
433                return get_source_l2_address(packet,addr);
434
435        switch (ethertype) {
436                case TRACE_ETHERTYPE_IP: /* IPv4 */
437                {
438                        struct sockaddr_in *addr4=(struct sockaddr_in*)addr;
439                        libtrace_ip_t *ip = (libtrace_ip_t*)l3;
440                        ports = (struct ports_t*)
441                                trace_get_payload_from_ip(ip,NULL,&remaining);
442                        addr4->sin_family=AF_INET;
443                        if (ports && remaining>=sizeof(*ports))
444                                addr4->sin_port=ports->src;
445                        else
446                                addr4->sin_port=0;
447                        addr4->sin_addr=ip->ip_src;
448                        return addr;
449                }
450                case TRACE_ETHERTYPE_IPV6: /* IPv6 */
451                {
452                        struct sockaddr_in6 *addr6=(struct sockaddr_in6*)addr;
453                        libtrace_ip6_t *ip6 = (libtrace_ip6_t*)l3;
454                        ports = (struct ports_t*)
455                                trace_get_payload_from_ip6(ip6,NULL,&remaining);
456                        addr6->sin6_family=AF_INET6;
457                        if (ports && remaining>=sizeof(*ports))
458                                addr6->sin6_port=ports->src;
459                        else
460                                addr6->sin6_port=0;
461                        addr6->sin6_flowinfo=0;
462                        addr6->sin6_addr=ip6->ip_src;
463                        return addr;
464                }
465                default:
466                        return get_source_l2_address(packet, addr);
467        }
468}
469
470
471static struct sockaddr *get_destination_ethernet_address(
472        libtrace_ether_t *ethernet, struct sockaddr *addr)
473{
474        static struct sockaddr_storage dummy;
475#ifdef HAVE_NETPACKET_PACKET_H
476/* Use linux's sockaddr_ll structure */
477        struct sockaddr_ll *l2addr;
478        if (addr)
479                l2addr = (struct sockaddr_ll*)addr;
480        else
481                l2addr = (struct sockaddr_ll*)&dummy;
482       
483        l2addr->sll_family = AF_PACKET;
484        l2addr->sll_protocol = ethernet->ether_type;
485        l2addr->sll_ifindex = 0; /* Irrelevant */
486        l2addr->sll_hatype = ARPHRD_ETHER; 
487        l2addr->sll_pkttype = PACKET_OTHERHOST;
488        l2addr->sll_halen = 6;
489        memcpy(l2addr->sll_addr,ethernet->ether_dhost, 6);
490
491        return (struct sockaddr*)l2addr;
492#else
493/* Use BSD's sockaddr_dl structure */
494        struct sockaddr_dl *l2addr;
495
496        if (addr)
497                l2addr = (struct sockaddr_dl *)addr;
498        else
499                l2addr = (struct sockaddr_dl *)&dummy;
500       
501        l2addr->sdl_family = AF_LINK;
502#if HAVE_SDL_LEN == 1
503        l2addr->sdl_len = sizeof(struct sockaddr_dl);
504#endif
505        l2addr->sdl_index = 0; /* Unused */
506        l2addr->sdl_alen = 6; /* Address length  */
507        l2addr->sdl_nlen = 0; /* No name in here - this *should* work, right? */
508        l2addr->sdl_slen = 0;   
509        l2addr->sdl_type = 0; /* Hopefully zero is OK for this value too */
510        memcpy(l2addr->sdl_data, ethernet->ether_dhost, 6);
511
512        return (struct sockaddr *)l2addr;
513#endif
514}
515
516static struct sockaddr *get_destination_l2_address(
517        const libtrace_packet_t *packet, struct sockaddr *addr)
518{
519        static struct sockaddr_storage dummy;
520        void *l2;
521        libtrace_linktype_t linktype;
522        uint32_t remaining;
523        if (!addr)
524                addr =(struct sockaddr*)&dummy;
525        l2=trace_get_layer2(packet, &linktype, &remaining);
526        if (!l2)
527                return NULL;
528
529        switch (linktype) {
530                case TRACE_TYPE_ETH:
531                        return get_destination_ethernet_address((libtrace_ether_t*)l2, addr);
532                default:
533                        return NULL;
534        }
535}
536
537DLLEXPORT struct sockaddr *trace_get_destination_address(
538                const libtrace_packet_t *packet, struct sockaddr *addr)
539{
540        uint16_t ethertype;
541        uint32_t remaining;
542        void *l3;
543        struct ports_t *ports;
544        static struct sockaddr_storage dummy;
545
546        if (!addr)
547                addr=(struct sockaddr*)&dummy;
548
549        l3 = trace_get_layer3(packet,&ethertype,&remaining);
550
551        if (!l3)
552                return get_destination_l2_address(packet,addr);
553
554        switch (ethertype) {
555                case TRACE_ETHERTYPE_IP: /* IPv4 */
556                {
557                        struct sockaddr_in *addr4=(struct sockaddr_in*)addr;
558                        libtrace_ip_t *ip = (libtrace_ip_t*)l3;
559                        ports = (struct ports_t*)
560                                trace_get_payload_from_ip(ip,NULL,&remaining);
561                        addr4->sin_family=AF_INET;
562                        if (ports && remaining>=sizeof(*ports))
563                                addr4->sin_port=ports->dst;
564                        else
565                                addr4->sin_port=0;
566                        addr4->sin_addr=ip->ip_dst;
567                        return addr;
568                }
569                case TRACE_ETHERTYPE_IPV6: /* IPv6 */
570                {
571                        struct sockaddr_in6 *addr6=(struct sockaddr_in6*)addr;
572                        libtrace_ip6_t *ip6 = (libtrace_ip6_t*)l3;
573                        ports = (struct ports_t*)
574                                trace_get_payload_from_ip6(ip6,NULL,&remaining);
575                        addr6->sin6_family=AF_INET6;
576                        if (ports && remaining>=sizeof(*ports))
577                                addr6->sin6_port=ports->dst;
578                        else
579                                addr6->sin6_port=0;
580                        addr6->sin6_flowinfo=0;
581                        addr6->sin6_addr=ip6->ip_dst;
582                        return addr;
583                }
584                default:
585                        return get_destination_l2_address(packet, addr);
586        }
587}
588
589
Note: See TracBrowser for help on using the repository browser.