source: lib/protocols_l3.c @ 5699389

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